dlopen()

Gain access to an executable object file

Synopsis:

#include <dlfcn.h>

void * dlopen( const char * pathname, 
               int mode );

Arguments:

pathname
NULL, or the path to the executable object file that you want to access.
mode
Flags that control how dlopen() operates. POSIX defines these bits:

The following bits are Unix extensions:

The following bits are QNX Neutrino extensions:

For more information, see The mode,” below.

Library:

libc

Use the -l c option to qcc to link against this library. This library is usually included automatically.

Description:

The dlopen() function gives you direct access to the dynamic linking facilities by making the executable object file specified in pathname available to the calling process. It returns a handle that you can use in subsequent calls to dlsym() and dlclose().


Note: The dlopen() function is available only to a dynamically-linked process. A statically-linked process (one where libc is linked statically) can't call dlopen() because a statically-linked executable:
  • doesn't export any of its symbols
  • can't export the required structure for libraries to link against
  • can't fill structures at startup needed to load subsequent shared objects

Any dependencies recorded within pathname are loaded as part of the dlopen() call. These dependencies are searched in load-order to locate any additional dependencies. This process continues until all of the dependencies for pathname have been satisfied. This dependency tree is called a group.

If pathname is NULL, dlopen() provides a handle to the running process's global symbol object. This provides access to the symbols from the original program image file, the dependencies it loaded at startup, plus any objects opened with dlopen() calls using the RTLD_GLOBAL flag. This set of symbols can change dynamically if the application subsequently calls dlopen() using RTLD_GLOBAL.

You can use dlopen() any number of times to open objects whose names resolve to the same absolute or relative path name; the object is loaded into the process's address space only once.

In order to find the shared objects, dlopen() searches the following, in this order:

Note that LD_LIBRARY_PATH is ignored if the binary is setuid and the effective user ID isn't the same as the actual ID of the user running the binary. This is done for security purposes.


Note: The above directories are set as follows:
  • The LD_LIBRARY_PATH is generally set up by a startup script, either in the boot image or by a secondary script. For example, on self-hosted Neutrino systems, the ph script (which starts Photon) adds some libraries to this list. It isn't part of any default environment.
  • _CS_LIBPATH is populated by the kernel, and the default value is based on the LD_LIBRARY_PATH value of the procnto command line in the boot image. Note that you can use getconf to inspect this value and setconf to set it. For example:
    setconf _CS_LIBPATH 'getconf _CS_LIBPATH':/new/path

When loading shared objects, the application should open a specific version instead of relying on the version pointed to by a symbolic link.

The mode

The mode argument indicates how dlopen() operates on pathname when handling relocations, and controls the visibility of symbols found in pathname and its dependencies.

The mode argument is a bitwise-OR of the constants described below. Note that the relocation and visibility constants are mutually exclusive.

Relocation

When you load an object by calling dlopen(), the object may contain references to symbols whose addresses aren't known until the object has been loaded; these references must be relocated before accessing the symbols. The mode controls when relocations take place, and can be one of:

RTLD_LAZY
(QNX Neutrino 6.5.0 and later) References to data symbols are relocated when the object is loaded. References to functions aren't relocated until that function is invoked. This improves performance by preventing unnecessary relocations.
RTLD_NOW
All references are relocated when the object is loaded. This may waste cycles if relocations are performed for functions that never get called, but this behavior could be useful for applications that need to know that all symbols referenced during execution are available as soon as the object is loaded.

Visibility

The following mode bits determine the scope of visibility for symbols loaded with dlopen():

RTLD_GLOBAL
Make the object's global symbols available to any other object that's opened later with RTLD_WORLD. Symbol lookup using dlopen( 0, mode ) and an associated dlsym() are also able to find the object's symbols.
RTLD_LOCAL
Make the object's global symbols available only to objects in the same group.
RTLD_LAZYLOAD
Open the shared object and form its resolution scope by appending its immediate dependencies. This is different from the normal resolution scope, which is formed by appending the whole dependency tree in breadth-first order. For more information, see Lazy loading in the Compiling and Debugging chapter of the QNX Neutrino Programmer's Guide.

The program's image and any objects loaded at program startup have a mode of RTLD_GLOBAL; the default mode for objects acquired with dlopen() is RTLD_LOCAL. A local object may be part of the dependencies for more than one group; any object with a RTLD_LOCAL mode referenced as a dependency of an object with a RTLD_GLOBAL mode is promoted to RTLD_GLOBAL.

Objects loaded with dlopen() that require relocations against global symbols can reference the symbols in any RTLD_GLOBAL object.

Symbol scope

You can OR the mode with the following values to affect the symbol scope:

RTLD_GROUP
Only symbols from the associated group are available. All dependencies between group members must be satisfied by the objects in the group.
RTLD_WORLD
Only symbols from RTLD_GLOBAL objects are available.

If you don't specify either of these values, dlopen() uses RTLD_WORLD | RTLD_GROUP.


Note: If you specify RTLD_WORLD without RTLD_GROUP, dlopen() doesn't load any of the DLL's dependencies.

Other flags

The following flags provide additional capabilities:

RTLD_NODELETE
Don't delete the specified object from the address space as part of a call to dlclose().
RTLD_NOLOAD
Don't load the specified object, but return a valid handle if if the object already exists as part of the process address space. You can specify addition modes, which are ORed with the present mode of the object and its dependencies. This flag gives you a means of querying the presence or promoting the modes of an existing dependency.
RTLD_NOSHARE
Don't share the specified object. This flag forces the loading of multiple instances of libraries.

Symbol resolution

When resolving the symbols in the shared object, the runtime linker searches for them in the dynamic symbol table using the following order:

By default:
  1. main executable
  2. the shared object being loaded
  3. objects specified by the LD_PRELOAD environment variable
  4. all other loaded shared objects that were loaded with the RTLD_GLOBAL flag
When -Bsymbolic is specified:
  1. the shared object being loaded
  2. main executable
  3. objects specified by the LD_PRELOAD environment variable
  4. all other loaded shared objects that were loaded with the RTLD_GLOBAL flag

For executables, the dynamic symbol table typically contains only those symbols that are known to be needed by any shared libraries. This is determined by the linker when the executable is linked against a shared library.

Since you don't link your executable against a shared object that you load with dlopen(), the linker can't determine which executable symbols need to be made available to the shared object.

If your shared object needs to resolve symbols in the executable, then you may force the linker to make all of the symbols in the executable available for dynamic linking by specifying the -E linker option. For example:

qcc -Vgcc_ntox86 -Wl,-E -o main main.o

Shared objects always place all their symbols in dynamic symbol tables, so this option isn't needed when linking a shared object.

Returns:

A handle to the object, or NULL if an error occurs.


Note: Don't interpret the value of this handle in any way. For example, if you open the same object repeatedly, don't assume that dlopen() returns the same handle.

Errors:

If an error occurs, more detailed diagnostic information is available from dlerror().

Environment variables:

DL_DEBUG
If this environment variable is set, the shared library loader displays debugging information about the libraries as they're opened. The value can be a comma-separated list of the following:

A value of 1 (one) is the same as all.

LD_BIND_NOW
Affects lazy-load dependencies due to full symbol resolution. Typically, it forces the loading of all lazy-load dependencies (until all symbols have been resolved).
LD_DEBUG
A synonym for DL_DEBUG. If you set both DL_DEBUG and LD_DEBUG, then DL_DEBUG takes precedence.
LD_DEBUG_OUTPUT
The name of a file in which the dynamic linker writes its output. By default, output is written to stderr.

Note: For security reasons, the use of LD_DEBUG_OUTPUT with setuid binaries is disabled.

LD_LIBRARY_PATH
A colon-separated list of directories to search for shared libraries.
LD_PRELOAD
A list of full paths to the shared libraries on an ELF system that you want to load before loading other libraries. Use a colon (:) to separate the libraries in this list. You can use this environment variable to add or change functionality when you run a program. For setuid or setgid ELF binaries, only libraries in the standard search directories that are also setuid will be loaded.

Classification:

POSIX 1003.1 XSI

Safety:
Cancellation point Yes
Interrupt handler No
Signal handler No
Thread Yes

Caveats:

Some symbols defined in executables or shared objects might not be available to the runtime linker. The symbol table created by ld for use by the runtime linker might contain a subset of the symbols defined in the object.

See also:

dladdr(), dlclose(), dlerror(), dlsym()

ld, qcc in the Utilities Reference

Lazy loading in the Compiling and Debugging chapter of the QNX Neutrino Programmer's Guide