Section 3.6. Dynamic Linker (or Runtime Linker)

3.6. Dynamic Linker (or Runtime Linker)

The Linux dynamic linker (/lib/ or /lib64/ finds and loads the shared libraries needed by a program, prepares the program to run, and then runs it. Linux binaries require dynamic linking unless the static option was given to ld during compilation.

On all modern UNIX operating systems, environment variables can be used to affect the operation of the runtime linker. An example is the environment variable LIBPATH on AIX, which alters the search path of the runtime linker. The following environment variables affect the operation of the runtime linker on Linux:

  • LD_LIBRARY_PATH. A colon-separated list of directories in which to search for libraries at runtime.

  • LD_PRELOAD. A whitespace-separated list of additional libraries to be loaded before all others. This can be used to selectively override functions in other shared libraries.

  • LD_BIND_NOW. If set to a nonempty string, causes the dynamic linker to resolve all symbols at program startup instead of deferring function call resolution when they are first referenced. This is useful when using a debugger.

  • LD_TRACE_LOADED_OBJECTS. If set to a nonempty string, causes the program to list its dynamic library dependencies, as if run by ldd, instead of executing normally.

The Linux dynamic linker loads library dependencies breadth first. That is, first the dependencies of the executable are added in the order listed in its dynamic section. Then the dependencies of the first dependency are added in the same fashion.

Typing the following from a shell prompt gives more information about the Linux dynamic linker:


3.6.1. Programming Interface

Linux provides a set of APIs used to dynamically load libraries. The dynamic linking interfaces used by Linux are as follows:

  • dlopen Opens a library and prepares it for use

  • dlsym Looks up the value of a symbol in the opened library

  • dlclose Closes a library

  • dlerror Returns a string describing the error from the last call to dlopen, dlsym, or dlclose

C users need to include the header file dlfcn.h to use these APIs. Glibc adds two APIs not specified by POSIX:

  • dladdr Resolves name and file where a symbol is located from a function pointer

  • dlvsym Similar to dlsym(), but takes a version string as additional argument

In Linux, a program that uses dynamic linking is linked with the library, -ldl. The libraries it loads do not need to be linked in during the build process. Example 3-1 is a sample program using dl* routines in Linux.

Example 3-1. A Sample Program Illustrating How to Use dl* Routines


#include <stdio.h> #include <stdlib.h> #include <dlfcn.h> #include <errno.h> typedef void (*FP)(void); int main( int argc, char** argv ) {   void*  handle;   void*  handle1;   void*  symptr;   FP   fp; /* Load the shared object containing the  * definition of the function - defsym.  */ if ( NULL == ( handle = dlopen( "./",               RTLD_NOW|RTLD_GLOBAL))) {   perror( dlerror() );   exit( 1 ); } /* Load the shared object containing the  * definition of the function - calldefsym.  */ if ( NULL == ( handle1 = dlopen( "./", RTLD_NOW))) {   perror( dlerror() );   exit( 1 ); } /* Obtain the address to the function - calldefsym. */ if ( NULL == ( symptr = dlsym( handle1, "function1" ))) {   if ( 0 == errno )   {     fprintf( stderr, "Symbol calldefsym not found.               Exiting\n");     exit( 1 );   }   perror( dlerror() );   exit( 1 ); } fp = ( FP )symptr; /* Call calldefsym which in turn calls defsym  * via the address obtained above.  */ fp(); /* Unload the shared library */ if ( 0 != ( dlclose( handle1 ) ) ) {   perror( dlerror() );   exit( 1 ); } /* Unload the shared library */ if ( 0 != ( dlclose( handle ) ) ) {   perror( dlerror() );     exit( 1 );   }   exit( 0 ); } 


#include <stdio.h> #include <stdlib.h> extern void function2( void ); void function1( void ) {     printf( "Calling function2 module function1\n" );     function2(); } 


#include <stdio.h> #include <stdlib.h> void function2() {     printf( "function2 called.\n" ); } 


CC=gcc all:  main main:  main.o     $(CC) -o main main.o -g -ldl main.o: main.c     $(CC) -c main.c -g function2.o     $(CC) -o -shared -fPIC -Wl, function2.o -lc function2.o: function2.c     $(CC) -c function2.c -fPIC    function1.o     $(CC) -o -shared -fPIC -Wl, function1.o -lc function1.o:  function1.c     $(CC) -c function1.c -fPIC .PHONY: clean clean:     rm *.o *.so main 

We can then build the executable:

$ make gcc -c main.c -g gcc -o main main.o -g -ldl gcc -c function1.c -fPIC gcc -o -shared -fPIC -Wl, function1.o -lc gcc -c function2.c -fPIC gcc -o -shared -fPIC -Wl, function2.o -lc 

Run the sample program:

$ ./main Calling function2 module function1 function2 called. 

Using the ldd command to inspect the executable gives the following:

$ ldd ./main => (0xffffe000) => /lib/ (0x40028000) => /lib/tls/ (0x4002b000)     /lib/ => /lib/ (0x40000000) 

Notice that the executable main does not reference a dynamically loaded library.

3.6.2. Lazy Relocation

Lazy relocation or loading is a feature that allows the relocations associated with a symbol to be delayed until the symbol is needed. This is used on different UNIX architectures for calls to functions. This feature can prove useful if an application that is linked to a shared library may have the slightest possibility of not calling the function in the shared library. In this case, the library is only loaded if the function is called by the application; otherwise, the shared library is never loaded, thereby saving system resources. However, by setting the environment variable LD_BIND_NOW to a nonempty value, all the relocations are performed at the program startup time. Lazy relocation can also be disabled for an individual object by adding the z now option to the linker command line. This setting cannot be undone without relinking the shared objects.

3.6.3. Initializing and Finalizing Routine

Sometimes legacy code may include two special functions, _init and _fini. The _init and _fini functions are used to control constructors and destructors when loading and unloading a module,[14] respectively. They have the following C prototypes:

[14] Objects in a shared library.

void _init(void); void _fini(void); 

When a library is opened through dlopen() or simply as a shared library, the _init function is called if it is exported within the same library.[15] When the library is closed with dlclose() or unloaded because no other applications are using symbols from that library, the routine _fini is called just before the library is unloaded. When using your own _init and _fini routines, you may need to avoid linking against the system startup files. To do this, use the GCC option -nostartfiles.

[15] This is the same as saying the _init function is defined as a function within the library.

However, using these routines or the GCC -nostartfiles option is not recommended because it may result in undesired behavior. Instead, libraries should export routines using the __attribute__((constructor)) and __attribute__((destructor)) function attributes, as in the following:

void __attribute__ ((constructor)) x_init(void); void __attribute__ ((destructor)) x_fini(void); 

The constructor routine is called before dlopen() returns or when the library is loaded at load time.[16] The destructor routine is called before dlclose() returns or after main() returns or when exit() is called if the library is loaded at load time.

[16] Before main() is called.

UNIX to Linux Porting. A Comprehensive Reference
UNIX to Linux Porting: A Comprehensive Reference
ISBN: 0131871099
EAN: 2147483647
Year: 2004
Pages: 175 © 2008-2017.
If you may any questions please contact us: