Way back in Lab 3 we learned how to make shared and static libraries and link with them. Today we will learn how to load shared libraries at run-time and manually call specific functions in them. This technique is used by various programs to support plug-ins, or extensible modules that the application can load and use. Examples of UNIX programs that support plug-ins are GIMP, Apache, and the PAM (Pluggable Authentication Module) security framework.
The chapter correponding to this lab is Chapter 25.
Before we plunge into talking about calling functions in libraries loaded at run-time, we first need to talk about function pointers. As you know, the C language supports pointers, which is to say, a variable that stores the address of another variable.
Example Program: intptr.c - A simple program that illustrates how variable pointers work.
C allows you to make pointers to functions as well as variables. The syntax is just slightly more involved.
Example Program: funcptr.c - A simple program that shows how function pointers work.
(As you've probably already guessed, this is how signal handler functions are called when you register them with signal or sigaction.)
Note that the return type and argument types are significant; the function pointer you declare must match the function it will point to both for the argument number, types and order (called the function signature), and for the return type as well.
Example Program: typefuncptrs.c - A program that illustrates the need for a differently declared function pointer based on signature.
First things first: all programs that use the dl* functions should do the following:
The goal is to call a function from some arbitrary library. These are the steps you need to take to do it:
You can man any of the above functions to see the documentation for them.
If you're looking for a specific function from a specific library, it's critical that you know the name of the library, the name of the function, and the signature of the function.
Example Program: dlcos.c - This program dynamically loads the math library (libm.so) at run-time, extracts the cosine (cos) function and calls it go get the cosine of 2.0.
Error checking is always a good idea. It's entirely possible that your program has tried to load a library that doesn't exist or has tried to retrieve a function that doesn't exist. The dlerror function can be used to return a string containing the last error message. (See p. 458, 461)
Example Program: dlcos-check.c - Same program as before, only with error-checking using dlerror. This example comes from the man page for dlopen.
The dl* functions work for pre-installed system libraries, but you can also load symbols from your own libraries.
Example Program: loadhello.c - This example program comes from you book on p. 462. It uses the library libhello.c and the header file libhello.h. The Makefile shows how to build all of these.
The man page for dlopen, dlerror, dlsym, and dlclose.
Dynamic Class Loading for C++ on Linux - an article in the online version of Linux Journal.
Your book, Chapter 25.
For this lab, you will create a small library libmathops.so, containing four functions that perform the four basic math operations, add, subtract, multiply, and divide. You will also create a program dlmath.c that takes three command line arguments: the name of one of the functions in the library you created, and two numbers. Your program will load the library libmathops.so, extract the function passed on the command-line, and call the function with the 2 other numbers passed on the command-line.
In a file called libmathops.c, create four funcitons add, sub, mul, and div each of which take two doubles and returns a double. The whole file can actually be only 4 lines long. (It's fine with me if you make it longer.)
In a file called dlmath.c, do the following:
num1 = strtod(argv[2], NULL);See the man page for strtod and the strtodex.c example program if you're interested.
The dlmath.c program should be about 45 lines long.
Be sure to have a Makefile as well with rules to build both the libmathops.so library and the dlmath program that uses it. The Makefile should be about 15 lines long. For a reminder on how to build shared libraries, look at the Makefile included with the lab programs. You can also review the Lab 3 notes.
Put all of these files in a lab15/ directory.
Here are some example tests:
$ ./dlmath usage: dlmath mathfunc num1 num2 $ ./dlmath add 3 4 add called with 3.00 and 4.00 = 7.00 $ ./dlmath sub 7 1 sub called with 7.00 and 1.00 = 6.00 $ ./dlmath mul 9 2 mul called with 9.00 and 2.00 = 18.00 $ ./dlmath mul 4.28 9.19 mul called with 4.28 and 9.19 = 39.33 $ ./dlmath div 12 6 div called with 12.00 and 6.00 = 2.00 $ ./dlmath div 12 7 div called with 12.00 and 7.00 = 1.71 $ ./dlmath foo 4 5 ./dlmath: undefined symbol: foo.
If you can run the same tests with your program and get the same results, you probably did it right.
You may get a compiler error that says
If you want to only display, say, two decimal places, just use "%0.2f" as the format specifier in printf.
Here is a list of the files created in this lab.
Please turn in ALL of the above files with your name, lab # and class # at the top.