The interface used by Linux is essentially the same as that used in Solaris, which I'll call the ``dlopen()'' API. However, this same interface is not supported by all platforms; HP-UX uses the different shl_load() mechanism, and Windows platforms use DLLs with a completely different interface. If your goal is wide portability, you probably ought to consider using some wrapping library that hides differences between platforms. One approach is the glib library with its support for Dynamic Loading of Modules; it uses the underlying dynamic loading routines of the platform to implement a portable interface to these functions. You can learn more about glib at http://developer.gnome.org/doc/API/glib/glib-dynamic-loading-of-modules.html. Since the glib interface is well-explained in its documentation, I won't discuss it further here. Another approach is to use libltdl, which is part of GNU libtool. If you want much more functionality than this, you might want to look into a CORBA Object Request Broker (ORB). If you're still interested in directly using the interface supported by Linux and Solaris, read on.
The dlopen(3) function opens a library and prepares it for use. In C its prototype is:
void * dlopen(const char *filename, int flag); |
A colon-separated list of directories in the user's LD_LIBRARY path environment variable.
The list of libraries specified in /etc/ld.so.cache (which is generated from /etc/ld.so.conf).
If the libraries depend on each other (e.g., X depends on Y), then you need to load the dependees first (in this example, load Y first, and then X).
The return value of dlopen() is a ``handle'' that should be considered an opaque value to be used by the other DL library routines. dlopen() will return NULL if the attempt to load does not succeed, and you need to check for this. If the same library is loaded more than once with dlopen(), the same file handle is returned.
If the library exports a routine named _init, then that code is executed before dlopen() returns. You can use this fact in your own libraries to implement initialization routines. See Section 5.2 for more information.
void * dlsym(void *handle, char *symbol); |
dlerror(); /* clear error code */ s = (actual_type) dlsym(handle, symbol_being_searched_for); if ((err = dlerror()) != NULL) { /* handle error, the symbol wasn't found */ } else { /* symbol found, its value is in s */ } |
The converse of dlopen() is dlclose(), which closes a DL library. The dl library maintains link counts for dynamic file handles, so a dynamic library is not actually deallocated until dlclose has been called on it as many times as dlopen has succeeded on it. Thus, it's not a problem for the same program to load the same library multiple times. If a library is deallocated, its function _fini is called (if it exists); see Section 5.2 for more information. Note: dlclose() returns 0 on success, and non-zero on error; some Linux manual pages don't mention this.
#include <stdlib.h> #include <stdio.h> #include <dlfcn.h> int main(int argc, char **argv) { void *handle; double (*cosine)(double); char *error; handle = dlopen ("/lib/libm.so.6", RTLD_LAZY); if (!handle) { fputs (dlerror(), stderr); exit(1); } cosine = dlsym(handle, "cos"); if ((error = dlerror()) != NULL) { fputs(error, stderr); exit(1); } printf ("%f\n", (*cosine)(2.0)); dlclose(handle); } |
gcc -o foo foo.c -ldl |