Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Dynamic Linking By Example

Last updated Jan 1, 2003.

Last week, I explained the principles of dynamic linking and discussed its advantages. Now it's time to see dynamic linking in action.

My example is based on the POSIX library. This isn't a complete tutorial; rather, my aim is to exemplify the implementation of dynamic linking in general. Therefore, Windows programmers should benefit from this discussion as well.

Building a shared library

The first step is to build a shared library. Use as an example the following simple function hello() that prints a greeting message on the screen:

/*hello.c*/
#include <stdio.h>
void hello() 
{
 printf("have a nice day.\n");
} 

Next, compile hello.c with the suitable option (e.g., -fPIC in GCC) to build an object file for a shared library, and link it with the appropriate linker options to create a shared library (for the sake of brevity, I will skip these technical details; they are highly dependent on the OS, compiler, and linker). The end result of the linkage process is a file called hello.so that may be installed either in a user's directory or in a system's default directory for shared libraries defined by the LD_LIBRARY_PATH environment variable.

The <dlfcn.h> Interface

The <dlfcn.h> library contains four functions for loading a dynamic library into a core program, accessing a library's components (known as symbols), unloading a library and error handling.

dlerror()

The dlerror() function has the following prototype:

const char * dlerror();

It returns a C-string describing the most recent error that occurred in each of the other dl functions, or NULL if no error occurred. Each call clears the previous error. Therefore, if you need to store the error message, you should copy it to local buffer.

dlopen()

The dlopen() function opens a shared library. Opening a library involves finding the relevant .so file, opening it, and performing additional processing. It has the following prototype:

void * dlopen(const char * filename, int flags);

If filename contains an absolute path, the function doesn't perform additional searching. If it contains only a file name, dlopen() searches for it in the default directories that contain shared libraries, the user's local directory for shared libraries, and finally, the /lib directory.

The function returns a non-NULL value serving as a handler. dlopen() returns NULL on error.

The flag argument specifies the manner in which symbols are located and loaded. RTLD_LAZY indicates that symbol resolution occurs on demand. RTLD_NOW forces immediate resolution. This is the preferred option when you debug applications, because it ensures that errors are reported immediately (there are additional flags that I won't discuss here).

dlsym()

The dlsym() looks up a symbol in a shared library. It has the following prototype:

void *dlsym(void * handle, const char * symbol);

The first argument is a value returned by dlopen(). Symbol is a C-string containing the name of a symbol you wish to load. Upon success, dlsym() returns the address of the sought-after symbol, or NULL if the lookup has failed.

Notice that checking whether the return value isn't NULL isn't always sufficient. In the case of functions, a NULL value always indicates an error, because a valid function address is never NULL. However, certain symbols may have a zero value, and thus may cause dlsym() to return NULL even if it succeeds. In such cases, you should call dlerror() to determine whether the lookup has succeeded. dlsym() returns a value of type void *. You should cast it to the appropriate type before using it.

dlclose()

The last function is dlclose() which has the following prototype:

void * dlclose(void *handle);

This function closes the library to which handle is pointing; if there are several open sessions of the same library, dlclose() doesn't really close the library but decrements a counter. (For a complete description of all the <dlfcn.h> functions, see the following manual.)

A <dlfcn.h> Example

The following program uses the <dlfcn.h> library to open the hello.so library, locate the symbol "hello", and call this function. Notice how the program checks for errors after each step.

#include <dlfcn.h>
#include <stdio.h> /*for printf() */
#include <stdlib.h> /* for exit() */

typedef void (*pf)();
int main()
{
 void *lib;
 pf greet;
 const char * err;
 
 lib=dlopen("hello.so", RTLD_NOW);
 if (!lib)
 {
  printf("failed to open hello.so: %s \n", dlerror());
  exit(1);
 }
 dlerror(); /*first clear any previous error; redundant 
            in this case but a useful habit*/
 greet= (pf) dlsym(lib, "hello");/*locate hello() */
 err=dlerror();/*check for errors and copy error message*/
 if (err)
 {
  printf("failed to locate hello(): %s \n", err);
  exit(1);
 }
 greet(); /*call hello() */
 dlclose(lib); 
 return 0;
}

Summary

Although the <dlfcn.h> interface is confined to the POSIX world, dynamic linking in Windows and other modern operating systems is implemented more or less in the same manner, albeit with a different set of APIs. Hopefully, a standard dynamic linkage library will materialize in the next revision round of the C++ standard.

Related Books

"Linux Programming by Example," by Arnold Robbins (to appear in April 2004) describes the Linux/C interface very well. Although it misses two important topics, i.e., dynamic linking and multithreading, it's an excellent guide for programmers who are making their first steps in the Linux world.

"Linux Application Development" by Michael K. Johnson and Eric W. Troan has been my favorite guide to Linux system programming. The title is somewhat out of focus — it doesn't really cover full-blown application development issues such as GUI and multimedia — but it's exactly the book every system programmer should have at his or her disposal: succinct, accurate, authoritative, and totally fluff-free.