Sun Microsystems, Inc.
spacer |
black dot
3.  Runtime Linker Runtime Linking Programming Interface Obtaining New Symbols  Previous   Contents   Next 

The symbols foo and bar will be searched for in the file, followed by any dependencies that are associated with this file. The function foo is then called with the single argument bar as part of the return() statement.

If the application prog is built using the above file main.c and its initial dependencies are:

$ ldd prog =>    /usr/lib/ =>     /usr/lib/

then if the file name specified in the dlopen(3DL) had the value 0, the symbols foo and bar will be searched for in prog, followed by /usr/lib/, and finally /usr/lib/

Once the handle has indicated the root at which to start a symbol search, the search mechanism follows the same model as described in "Symbol Lookup".

If the required symbol cannot be located, dlsym(3DL) will return a NULL value. In this case, dlerror(3DL) can be used to indicate the true reason for the failure. In the following example the application prog was unable to locate the symbol bar.

$ prog
dlsym: main: fatal: bar: can't find symbol

Testing for Functionality

The special handle RTLD_DEFAULT enables an application to test for the existence of another symbol. The symbol search follows the same model as used to relocate the calling object. See "Default Symbol Lookup Model". For example, if the application prog contained the following code fragment:

        if ((fptr = (int (*)())dlsym(RTLD_DEFAULT, "foo")) != NULL)

then foo will be searched for in prog, followed by /usr/lib/, and then /usr/lib/ If this code fragment was contained in the file from the example shown in Figure 3-1, then the search for foo will continue into and then

This mechanism provides a robust and flexible alternative to the use of undefined weak references, discussed in "Weak Symbols".

Using Interposition

The special handle RTLD_NEXT enables an application to locate the next symbol in a symbol scope. For example, if the application prog contained the following code fragment:

        if ((fptr = (int (*)())dlsym(RTLD_NEXT, "foo")) == NULL) {
                (void) printf("dlsym: %s\n", dlerror());
                exit (1);
        return ((*fptr)());

then foo will be searched for in the shared objects associated with prog, which in this case are /usr/lib/ and then /usr/lib/ If this code fragment was contained in the file from the example shown in Figure 3-1, then foo will be searched for in the associated shared object only.

Using RTLD_NEXT provides a means to exploit symbol interposition. For example, a function within an object can be interposed upon by a preceding object, which can then augment the processing of the original function. For example, if the following code fragment is placed in the shared object

#include    <sys/types.h>
#include    <dlfcn.h>
#include    <stdio.h>
void *
malloc(size_t size)
        static void * (* fptr)() = 0;
        char             buffer[50];
        if (fptr == 0) {
                fptr = (void * (*)())dlsym(RTLD_NEXT, "malloc");
                if (fptr == NULL) {
                        (void) printf("dlopen: %s\n", dlerror());
                        return (0);
        (void) sprintf(buffer, "malloc: %#x bytes\n", size);
        (void) write(1, buffer, strlen(buffer));
        return ((*fptr)(size));

then by interposing this shared object between the system library /usr/lib/ where malloc(3C) usually resides, any calls to this function will be interposed on before the original function is called to complete the allocation:

$ cc -o -G -K pic malloc.c
$ cc -o prog file1.o file2.o ..... -R.
$ prog
malloc: 0x32 bytes
malloc: 0x14 bytes

Alternatively, this same interposition can be achieved by:

$ cc -o -G -K pic malloc.c
$ cc -o prog main.c
$ LD_PRELOAD=./ prog
malloc: 0x32 bytes
malloc: 0x14 bytes

Note - Users of any interposition technique must be careful to handle any possibility of recursion. The previous example formats the diagnostic message using sprintf(3C), instead of using printf(3C) directly, to avoid any recursion caused by printf(3C)'s possible use of malloc(3C).

The use of RTLD_NEXT within a dynamic executable or preloaded object provides a predictable and useful interposition technique. Be careful when using this technique in a generic object dependency, as the actual load order of objects is not always predictable.

Feature Checking

Dynamic objects built by the link-editor sometimes require new runtime linker features. The function _check_rtld_feature() can be used to check if the runtime features required for execution are supported by the running runtime linker. The runtime features currently identified are listed in Table 7-46.

Debugging Aids

A debugging library and mdb(1) module are provided with the Solaris linkers. The debugging library enables you to trace the runtime linking process in more detail. The mdb(1) module enables interactive process debugging.

Debugging Library

This debugging library helps you understand, or debug, the execution of applications and dependencies. Although the type of information displayed using this library is expected to remain constant, the exact format of the information might change slightly from release to release.

Some of the debugging output might be unfamiliar to those who do not have an intimate knowledge of the runtime linker. However, many aspects may be of general interest to you.

Debugging is enabled by using the environment variable LD_DEBUG. All debugging output is prefixed with the process identifier and by default is directed to the standard error. This environment variable must be augmented with one or more tokens to indicate the type of debugging required.

The tokens available with this debugging option can be displayed by using LD_DEBUG=help. Any dynamic executable can be used to solicit this information, as the process itself will terminate following the display of the information. For example:

$ LD_DEBUG=help prog
11693:           For debugging the runtime linking of an application:
11693:                  LD_DEBUG=token1,token2  prog
11693:           enables diagnostics to the stderr.  The additional
11693:           option:
11693:                  LD_DEBUG_OUTPUT=file
11693:           redirects the diagnostics to an output file created
11593:           using the specified name and the process id as a
11693:           suffix.  All diagnostics are prepended with the
11693:           process id.
11693: basic     provide basic trace information/warnings
11693: bindings  display symbol binding; detail flag shows
11693:           absolute:relative addresses
11693: detail    provide more information in conjunction with other
11693:           options
11693: files     display input file processing (files and libraries)
11693: help      display this help message
11693: libs      display library search paths
11693: move      display move section processing
11693: reloc     display relocation processing
11693: symbols   display symbol table processing;
11693:           detail flag shows resolution and linker table addition
11693: versions  display version processing
11693: audit     display runtime link-audit processing

This example shows the options meaningful to the runtime linker. The exact options might differ from release to release.

The environment variable LD_DEBUG_OUTPUT can be used to specify an output file for use instead of the standard error. The process identifier is added as a suffix to the output file.

Debugging of secure applications is not allowed.

One of the most useful debugging options is to display the symbol bindings that occur at runtime. The following example uses a very trivial dynamic executable that has a dependency on two local shared objects.

$ cat bar.c
int bar = 10;
$ cc -o -Kpic -G bar.c
$ cat foo.c
foo(int data)
        return (data);
$ cc -o -Kpic -G foo.c
$ cat main.c
extern  int     foo();
extern  int     bar;
        return (foo(bar));
$ cc -o prog main.c -R/tmp:.
  Previous   Contents   Next