Linux System Programming

Dynamically Loaded (DL) Libraries

Dynamically loaded (DL) libraries are libraries that are loaded by request other than during the startup of a program(linked).
They're particularly useful for implementing plugins or modules, because they permit waiting to load the plugin until it's needed.

DL libraries are built as standard object files or standard shared libraries.
The main difference is that the libraries aren't automatically loaded at program link time or start-up; instead, there is an API for opening a library, looking up symbols, handling errors, and closing the library.
C users will need to include the header file <dlfcn.h> to use this API.

dlopen()

The dlopen(3) function opens a library and prepares it for use.

void * dlopen(const char *filename, int flag);
If filename begins with ``/'' (i.e., it's an absolute path), dlopen() will just try to use it (it won't search for a library). Otherwise, dlopen() will search for the library in the following order:
  1. A colon-separated list of directories in the user's LD_LIBRARY_PATH environment variable.
  2. The list of libraries specified in /etc/ld.so.cache (which is generated from /etc/ld.so.conf).
  3. /lib, followed by /usr/lib.
  4. Note the order here; this is the reverse of the order used by the old a.out loader.
    The old a.out loader, when loading a program, first searched /usr/lib, then /lib (see the man page ld.so(8)). This shouldn't normally matter, since a library should only be in one or the other directory (never both), and different libraries with the same name are a disaster waiting to happen.
The value of flag :
  • RTLD_LAZY
  • Resolve undefined symbols as code from the dynamic library is executed
  • RTLD_NOW
  • Resolve all undefined symbols before dlopen() returns and fail if this cannot be done
  • RTLD_GLOBAL
  • Optionally or'ed with either value in the above.
    The external symbols defined in the library will be made available to subsequently loaded libraries.
Using RTLD_NOW makes opening the library take slightly longer (but it speeds up lookups later); if this causes a user interface problem you can switch to RTLD_LAZY later.

If there is a library dependency, you need to load the dependees first.
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.

4.2. dlerror()

Errors can be reported by calling dlerror(), which returns a string describing the error from the last call to dlopen(), dlsym(), or dlclose().
One oddity is that after calling dlerror(), future calls to dlerror() will return NULL until another error has been encountered.

4.3. dlsym()

The main routine for using a DL library is dlsym(3), which looks up the value of a symbol in a given (opened) library.

  
 void * dlsym(void *handle, char *symbol);  
  • the handle is the value returned from dlopen
  • symbol is a NIL-terminated string
  • dlsym() will return a NULL result if the symbol wasn't found.
  • The standard solution is to call dlerror() first (to clear any error condition that may have existed), then call dlsym() to request a symbol, then call dlerror() again to see if an error occurred.

 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 */
 }

4.4. dlclose()

dlclose() closes a DL library.

dlclose() returns 0 on success, and non-zero on error.

4.5. DL Library Example

This example loads the math library and prints the cosine of 2.0.


    #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) {
            fprintf (stderr, "dlopen() failed: %s\n", dlerror());
            exit(1);
        }

        cosine = dlsym(handle, "cos");
        if ((error = dlerror()) != NULL)  {
            fprintf (stderr, "dlsym() failed: %s\n", dlerror());
            exit(1);
        }

        printf ("%f\n", (*cosine)(2.0));
        dlclose(handle);
    }
Build:

gcc -o cos cos.c -ldl
Test:

$ ./cos
/lib/libm.so.6: cannot open shared object file: No such file or directory
Find the path of the library:

$ ldconfig -p | grep libm.so
	libmysofa.so.1 (libc6,x86-64) => /lib/x86_64-linux-gnu/libmysofa.so.1
	libm.so.6 (libc6,x86-64, OS ABI: Linux 3.2.0) => /lib/x86_64-linux-gnu/libm.so.6
Correct the library path, build then test it again:

$ ./cos
-0.416147

留言

熱門文章