• No results found

Runtime Linking Programming Interface

In document Linker and Libraries Guide (Page 104-107)

Dependencies specified during the link-edit of an application are processed by the runtime linker during process initialization. In addition to this mechanism, the application can extend its address space during its execution by binding to additional objects. The application effectively uses the same services of the runtime linker that are used to process the applications standard dependencies.

Delayed object binding has several advantages.

By processing an object when the object is required rather than during the initialization of an application, startup time can be greatly reduced. If the services provided by an object are not needed during a particular run of the application, the object is not required. This scenario can occur for objects that provide help or debugging information.

The application can choose between several different objects, depending on the exact services required, such as for a networking protocol.

Any objects added to the process address space during execution can be freed after use.

An application can use the following typical scenario to access an additional shared object.

A shared object is located and added to the address space of a running application using dlopen(3C). Any dependencies of this shared object are located and added at this time.

The added shared object and its dependencies are relocated. Any initialization sections within these objects are called.

The application locates symbols within the added objects usingdlsym(3C). The application can then reference the data or call the functions defined by these new symbols.

After the application has finished with the objects, the address space can be freed using dlclose(3C). Any termination sections that exist within the objects that are being freed are called at this time.

Any error conditions that occur as a result of using the runtime linker interface routines can be displayed usingdlerror(3C).

The services of the runtime linker are defined in the header file dlfcn.h and are made available to an application by the shared object libc.so.1. In the following example, the file main.c can make reference to any of thedlopen(3C)family of routines, and the application prog can bind to these routines at runtime.

$ cc -o prog main.c

Note –In previous releases of the Oracle Solaris OS, the dynamic linking interfaces were made available by the shared object libdl.so.1. libdl.so.1 remains available to support any existing dependencies. However, the dynamic linking interfaces offered by libdl.so.1 are now available from libc.so.1. Linking with -ldl is no longer necessary.

Loading Additional Objects

Additional objects can be added to a running process's address space by usingdlopen(3C). This function takes a path name and a binding mode as arguments, and returns a handle to the application. This handle can be used to locate symbols for use by the application using dlsym(3C).

If the path name is specified as a simple file name, one with no ‘/' in the name, then the runtime linker uses a set of rules to generate an appropriate path name. Path names that contain a ‘/' are used as provided.

These search path rules are exactly the same as are used to locate any initial dependencies. See

“Directories Searched by the Runtime Linker” on page 84. For example, the file main.c contains the following code fragment.

#include <stdio.h>

#include <dlfcn.h>

int main(int argc, char **argv) {

void *handle;

...

if ((handle = dlopen("foo.so.1", RTLD_LAZY)) == NULL) { (void) printf("dlopen: %s\n", dlerror());

return (1);

} ...

To locate the shared object foo.so.1, the runtime linker uses any LD_LIBRARY_PATH definition that is present at process initialization. Next, the runtime linker uses any runpath specified during the link-edit of prog. Finally, the runtime linker uses the default locations /lib and /usr/libfor 32–bit objects, or /lib/64 and /usr/lib/64 for 64–bit objects.

If the path name is specified as:

if ((handle = dlopen("./foo.so.1", RTLD_LAZY)) == NULL) {

then the runtime linker searches for the file only in the current working directory of the process.

Note –Any shared object that is specified usingdlopen(3C)should be referenced by its versioned file name. For more information on versioning, see“Coordination of Versioned Filenames” on page 170.

If the required object cannot be located,dlopen(3C)returns a NULL handle. In this case dlerror(3C)can be used to display the true reason for the failure. For example.

$ cc -o prog main.c

$ prog

dlopen: ld.so.1: prog: fatal: foo.so.1: open failed: No such \ file or directory

If the object being added bydlopen(3C)has dependencies on other objects, they too are brought into the process's address space. This process continues until all the dependencies of the specified object are loaded. This dependency tree is referred to as a group.

If the object specified bydlopen(3C), or any of its dependencies, are already part of the process image, then the objects are not processed any further. A valid handle is returned to the application. This mechanism prevents the same object from being loaded more than once, and enables an application to obtain a handle to itself. For example, from the previous example, main.ccan contain the following dlopen() call.

if ((handle = dlopen(0, RTLD_LAZY)) == NULL) {

The handle returned from thisdlopen(3C)can be used to locate symbols within the application itself, within any of the dependencies loaded as part of the process's initialization, or within any objects added to the process's address space, using adlopen(3C)that specified the RTLD_GLOBAL flag.

Relocation Processing

After locating and loading any objects, the runtime linker must process each object and

perform any necessary relocations. Any objects that are brought into the process's address space withdlopen(3C)must also be relocated in the same manner.

For simple applications this process is straightforward. However, for users who have more complex applications with manydlopen(3C)calls involving many objects, possibly with common dependencies, this process can be quite important.

Relocations can be categorized according to when they occur. The default behavior of the runtime linker is to process all immediate reference relocations at initialization and all lazy references during process execution, a mechanism commonly referred to as lazy binding.

This same mechanism is applied to any objects added withdlopen(3C)when the mode is defined as RTLD_LAZY. An alternative is to require all relocations of an object to be performed immediately when the object is added. You can use a mode of RTLD_NOW, or record this requirement in the object when it is built using the link-editor's -z now option. This relocation requirement is propagated to any dependencies of the object being opened.

Relocations can also be categorized into non-symbolic and symbolic. The remainder of this section covers issues regarding symbolic relocations, regardless of when these relocations occur, with a focus on some of the subtleties of symbol lookup.

In document Linker and Libraries Guide (Page 104-107)