< Previous PageNext Page > Hide TOC

Dynamic Library Usage Guidelines

The dynamic loader compatibility functions provide a portable and efficient way to load code at runtime. However, using the functions incorrectly can degrade application performance. This article shows how to correctly load and use dynamic libraries in your applications.

Dynamic libraries help to distribute an application’s functionality into distinct modules that can be loaded as they are needed. Dynamic libraries can be loaded either when the application launches or as it runs. Libraries that are loaded at launch time are called dependent libraries. Libraries that are loaded at runtime are called dynamically loaded libraries. You specify which dynamic libraries your application depends on by linking your application against them. However, it’s more efficient to use dynamic libraries as dynamically loaded libraries instead of dependent libraries. That is, you should open libraries when you’re about to use symbols they export and close them when you’re done. In some cases, the system unloads dynamically loaded libraries when it determines that they aren’t being used.

This article uses the word image to refer to an application file or a dynamic library. Application files contain the application’s code and the code from the static libraries the application uses. The dynamic libraries the application loads at launch time or runtime are separate images.

Contents:

Opening Dynamic Libraries
Using Symbols
Using Weakly Linked Symbols
Using C++ Classes
Using Objective-C Classes
Getting Information on an Address


Opening Dynamic Libraries

The dynamic loader loads an image’s dependent libraries when the image is opened; that is, when an application is loaded or when an dynamic library is opened. The dynamic loader binds references to symbols exported by dependent libraries lazily. Lazy binding means the symbol references are bound only when the image actually uses the symbols. As a debugging measure, you can specify that all references to the exported symbols of a library be bound when the dynamic loader opens the library. You use the gcc -bind_at_load flag when generating the dynamic library.

To use a dynamic library that is not a dependent library of your image, you use the dlopen function. This function tells the dynamic loader to load a specific dynamic library into the address space of the current process. This function also allows you to specify when the dynamic loader binds the library’s references to the corresponding exported symbols in its dependent libraries and whether to place the library’s exported symbols in the current process’s global scope or a local scope. This function returns a handle called library handle. This handle represents the dynamically loaded library in calls to dlsym (to use an exported symbol) and dlclose (to close the library). The library handle provides dlsym a limited domain within which to search for a symbol (see “Using Symbols” for details). The client must call dlclose when it’s finished using the dynamically loaded library (for example, when the module that opened the library has finished its task).

A dynamic library may itself have dependent libraries. To find out which libraries a dynamic library depends on, use the otool -L command. Before using the library, you must ensure that all its dependent libraries are present in your computer. Otherwise, the dynamic loader doesn’t load your application or library when requested at application launch time or when the library is opened with dlopen.

A process can open the same dynamic library several times without closing it. The dlopen function returns the same library handle it returned in the first call, but it also increments the reference count associated with the handle. Calls to dlclose decrement the library handle’s reference count. Therefore, you must balance every call to dlopen with a call to dlclose. When the reference count for a library handle reaches 0, the dynamic loader may remove the library from the address space of the application.

The Library Search Process

The first parameter to dlopen is the name of the dynamic library to open. This may be a filename or a partially or fully qualified pathname. For example, libCelsus.dylib , lib/libCelsus.dylib , or /usr/local/libCelsus.dylib.

The dynamic loader searches for libraries in the directories specified by a set of environment variables and the process’s current working directory. These variables, when defined, must contain a colon-separated list of pathnames (absolute or relative) in which the dynamic loader searches for libraries. Table 1 lists the variables.

Table 1  Environment variables that define dynamic-loader search paths

Environment variable

Default value

LD_LIBRARY_PATH

No default value

DYLD_LIBRARY_PATH

No default value

DYLD_FALLBACK_LIBRARY_PATH

$HOME/lib;/usr/local/lib;/usr/lib

When the library name is a filename (that is, when it doesn’t include directory names), the dynamic loader searches for the library in several locations until it finds it, in the following order:

  1. $LD_LIBRARY_PATH

  2. $DYLD_LIBRARY_PATH

  3. The process’s working directory

  4. $DYLD_FALLBACK_LIBRARY_PATH

When the library name contains at least one directory name, that is, when the name is a pathname (relative or fully qualified), the dynamic loader searches for the library in the following order:

  1. $DYLD_LIBRARY_PATH using the filename

  2. The given pathname

  3. $DYLD_FALLBACK_LIBRARY_PATH using the filename

For example, say you set the environment variables introduced earlier as shown in the following table.

Environment variable

Value

LD_LIBRARY_PATH

./lib

DYLD_LIBRARY_PATH

/usr/local/dylibs

DYLD_FALLBACK_LIBRARY_PATH

/usr/local/lib

Assuming your application calls dlopen with the filename libCelsus.dylib, the dynamic loader would attempt to open the library using the following pathnames, in order:

Pathname

Description

./lib/libCelsus.dylib

LD_LIBRARY_PATH environment variable

/usr/local/dylibs/libCelsus.dylib

DYLD_LIBRARY_PATH environment variable

libCelsus.dylib

Current working directory

/usr/local/lib/libCelsus.dylib

DYLD_FALLBACK_LIBRARY_PATH environment variable

If the application calls dlopen with the pathname /libs/libCelsus.dylib, the dynamic loader tries to find the library using these pathnames, in order:

Pathname

Description

/usr/local/dylibs/libCelsus.dylib

DYLD_LIBRARY_PATH environment variable

/libs/libCelsus.dylib

Path as given

/usr/local/lib/libCelsus.dylib

DYLD_FALLBACK_LIBRARY_PATH environment variable

Specifying the Scope and Binding Behavior of Exported Symbols

The second parameter of the dlopen function specifies two properties: the scope of the library's exported symbols in the current process and when to bind the application's references the those symbols.

Symbol scope directly affects the performance of applications. Therefore, it’s important that you set the appropriate scope for a library you open at runtime.

A dynamically loaded library’s exported symbols can be in one of two levels of scope in the current process: global and local. The main difference between the scopes is that the symbols in the global scope are available to all images in the process, including other dynamically loaded libraries. Symbols in the local scope can be used only by the image that opened the library. See “Using Symbols” for more information.

When the dynamic loader searches for symbols, it performs string comparisons against every symbol in the search scope. Reducing the number of symbols the dynamic loader has to go through to find the desired symbol improves your application’s performance. Opening all dynamically loaded libraries into the local scope instead of the global scope maximizes symbol search performance.

You should never need to open a dynamic library into the process’s global scope so that all modules in the application have access to its symbols. Instead, each module that uses the library should open it into its local scope. When done, the module should close the library. If you want the symbols exported by the library to be available to all images in the process, consider making the library a dependent library of the application.

The parameter used to specify symbol scope is also used to specify when the undefined external symbols of the dynamically loaded library are resolved (or bound with their definitions the library’s own dependent libraries). Undefined external symbols of dynamically loaded libraries can be resolved either immediately or lazily. If a client application uses immediate binding when opening a dynamic library with dlopen , the dynamic loader binds all the undefined external symbols of the dynamically loaded library before returning control to the client application. For example, Listing 1 shows the log messages the dynamic loader produces when the DYLD_PRINT_BINDINGS environment variable is set and a client application loads a dynamic library called libArt.dylib :

Listing 1  Bindings resolved during call to dlopen using immediate binding

dyld: bind: client:_dlopen$lazy_ptr = libSystem.B.dylib:_dlopen
dyld: bind: libArt.dylib:_dlopen$lazy_ptr = libSystem.B.dylib:_dlopen
dyld: bind: libArt.dylib:_printf$LDBLStub$lazy_ptr = libA.dylib:_printf$LDBLStub
dyld: bind: libArt.dylib:_dlerror$lazy_ptr = libSystem.B.dylib:_dlerror
dyld: bind: libArt.dylib:_dlclose$lazy_ptr = libSystem.B.dylib:_dlclose
dyld: bind: libArt.dylib:_dlsym$lazy_ptr = libSystem.B.dylib:_dlsym
dyld: bind: libArt.dylib:___stub_getrealaddr$lazy_ptr = libA.dylib:___stub_getrealaddr
dyld: bind: libArt.dylib:_NSIsSymbolNameDefinedWithHint$lazy_ptr =
                            libSystem.B.dylib:_NSIsSymbolNameDefinedWithHint
dyld: bind: libArt.dylib:_strcpy$lazy_ptr = libSystem.B.dylib:_strcpy
dyld: bind: libArt.dylib:_NSLookupAndBindSymbolWithHint$lazy_ptr =
                            libSystem.B.dylib:_NSLookupAndBindSymbolWithHint
dyld: bind: libArt.dylib:_NSAddressOfSymbol$lazy_ptr =
                            libSystem.B.dylib:_NSAddressOfSymbol
dyld: bind: libArt.dylib:_strlen$lazy_ptr = libSystem.B.dylib:_strlen

The first log message indicates that the client application’s _dlopen undefined symbol was bound. The remaining messages are the bindings the dynamic loader performs on the dynamic library as part of the loading process before returning control to the calling routine. When using lazy binding, the dynamic loader resolves only the client application’s reference to the dlopen function, returning control to the calling routine much sooner. For more information on dynamic loader logging, see “Logging Dynamic Loader Events.”

Once a library has been opened with dlopen, the scope defined for it cannot be changed by subsequent calls to dlopen to load the same library. For example, if the process opens a library that hasn’t been loaded into the local scope and later opens the same library into the global scope, the opened library retains its local status. That is, the symbols the library exports do not become available in the global scope with the latter call. This is true even if the library is closed before reopening it within the same process.

Important: All runtime loaded should be opened into the local scope. Adhering to this rule makes finding symbols at runtime as fast as possible.

Immediate binding slows the loading of dynamic libraries, especially when those libraries contain many undefined external symbols. However, immediate binding can help during development and testing of dynamic libraries because when the dynamic loader cannot resolve all the undefined external symbols of a dynamically loaded library, the application terminates with an error. When deploying the application, however, you should use lazy loading because undefined external symbols are bound only when necessary. Loading dynamic libraries this way can help make your application feel more responsive to its users.

The external undefined symbols in dependent libraries are bound when they are first used unless the client image’s compile line includes the gcc -bind_at_load option. See the ld man page for details.

Using Symbols

After opening a dynamic library using dlopen, an image uses the dlsym function to get the address of the desired symbol before using it. This function takes two parameters. The first one specifies in which libraries the dynamic loader looks for the symbol. The second parameter specifies the name of the symbol. For example:

symbol_pointer = dlsym(library_handle, "my_symbol")

This invocation tells the dynamic loader to search for a symbol named my_symbol among the symbols exported by the dynamically loaded library represented by the library_handle variable.

There are three scopes the dynamic loader can search for a symbol: a specific dynamic library, the current image's dependent libraries, and the global scope of the process:

Note: Name conflicts between dynamic shared libraries are not discovered at compile time, link time, or runtime. The dlsym function uses string matching to find symbols. If two libraries use the same name for a function, the first one that matches the symbol name given to dlsym is returned.

To illustrate the concepts introduced in this section, take the application depicted in Figure 1. It shows that the application has two dependent libraries, libArt.dylib and libBus.dylib. The libBus.dylib library itself has two dependent libraries, libBus1.dylib and libBus2.dylib. The libBus1.dylib library has one dependent library, libBus1a.dylib. In addition, there are four dynamic libraries the application doesn’t depend on, libCar.dylib, libCar1.dylib, libDot.dylib, and libDot1.dylib. The libCar1.dylib library is a dependent library of libCar.dylib and libDot1.dylib is a dependent library of libDot.dylib. All the libraries except libArt.dylib export the dependencies function. Each library has a unique implementation of the ...name function.


Figure 1  Application with dependent library hierarchy


The application image can access the exported symbols in libArt.dylib and libBus.dylib directly, as shown in Listing 2.

Listing 2  Application image using symbols exported by dependent libraries through undefined external references

#include <stdio.h>
 
extern char* A_name();          // libArt.dylib
extern char* dependencies();    // libBus.dylib
 
int main(void) {
    printf("[%s] libArt.A_name() = %s\n", __FILE__, A_name());
 
    printf("[%s] libBus.dependencies() = %s\n", __FILE__, dependencies());
}

The application image, however, cannot directly access the symbols exported by libBus1.dylib, libBus1a.dylib, and libBus2.dylib because those libraries are not dependent libraries of the application image. To gain access to those symbols, the application image has to open the corresponding libraries using dlopen, as shown in Listing 3.

Listing 3  Application image using a symbol exported by a dynamic library loaded at runtime

#include <stdio.h>
#include <dlfcn.h>
 
int main(void) {
    void* Bus1a_handle = dlopen("libBus1a.dylib", RTLD_LOCAL);
    if (Bus1a_handle) {
        char* (*b1a_name)() = dlsym(Bus1a_handle, "B1a_name");
        if (b1a_name) {
            printf("[%s] libBus1a.B1a_name() = %s\n",
                __FILE__, b1a_name());
        }
    }
    else {
        printf("[%s] Unable to open libBus1a.dylib: %s\n",
            __FILE__, dlerror());
    }
    dlclose(Bus1a_handle);
}

So far you have seen how to access symbols either through references to imported symbols or by obtaining the address of the desired symbol by calling dlsym with the handle of the corresponding library or with the RTLD_DEFAULT special handle. As mentioned earlier, interposed symbols offer the ability to change the definition of a symbol exported by a dependent library.

To access the original definition of interposed symbols, you call dlsym with the RTLD_NEXT special handle. Listing 4 shows the implementation of the dependencies function in the Bus library (the implementation is identical in Bus1 and Bus1a). The function in Bus returns the name of the library (contained in the k_lib_name variable) concatenated with a separator string and the text returned by the next definition of dependencies, which is found in the Bus1 library. The definition in Bus1 concatenates its name with a separator string and the text returned by the definition in Bus1a. The definition in Bus1a is the last that would’ve been found if none of the client images had defined their own version. Therefore, when Bus1a invokes dlsym(RTLD_NEXT, "dependencies") no other definitions for dependencies are found. That’s the end of the interposition hierarchy of the dependencies function.

Listing 4  Library image using an interposed symbol

#include <string.h>
static char* k_lib_name = "libBus";
char* dependencies(void) {
    char _dependencies[50] = "";
    strcpy(_dependencies, k_lib_name);
    char* (*next_dependencies)() =
        dlsym(RTLD_NEXT, "dependencies");// look for next definition
    if (next_dependencies) {
        strncat(_dependencies, ", ",
            sizeof(_dependencies) - strlen(_dependencies) - 1);
        strncat(_dependencies, next_dependencies(),
            sizeof(_dependencies) - strlen(_dependencies) - 1);
    }
    return strdup(_dependencies);
}

When the application image calls the dependencies function in the Bus library, it obtains the names of all the libraries the Bus library depends on, as shown in Listing 5.

Listing 5  Application image calling an interposed function

#include <stdio.h>
extern char* dependencies();    // libBus.dylib
 
int main(void) {
    printf("[%s] libBus.dependencies() = %s\n",
        __FILE__, dependencies());
}

Using Weakly Linked Symbols

To promote compatibility with earlier or later revisions, a dynamic library may export some or all its public symbols as weakly linked symbols. A weakly linked symbol is one for which the compiler generates a weak reference when a client is compiled against a library. Weakly linked symbols may have the weak_import attribute in their declarations in the library’s header files, or the library’s developer may otherwise document which of the library’s public symbols are weakly linked. A third way to identify weakly linked symbols it by executing the command:

nm -m <client_file> | grep weak

This command lists the weakly linked symbols imported from dependent libraries.

A weakly linked symbol may or may not be defined by a dependent library. That is, although the symbol is declared in a header file, the corresponding dynamic library file may not contain an implementation of that symbol. Listing 6 shows how a weakly linked symbol may be declared in a header file for a dynamic library. Clients that use this header file as their interface to the corresponding dependent library are guaranteed that name and set_name are defined. However, clear_name may not be implemented. The dependent library loads successfully whether or not it implements clear_name. But it doesn’t load if it doesn’t define either name or set_name . When the library doesn’t implement a weakly linked symbol, the dynamic loader sets to 0 any client references to the symbol.

Listing 6  Header file with a weakly linked symbol declaration

/* File: Person.h */
#define WEAK_IMPORT __attribute__((weak_import))
 
char* name(void);
 
void set_name(char* name);
 
WEAK_IMPORT
void clear_name(void);

Weakly linked symbols are used by library developers to maximize the compatibility of a client with earlier or newer versions of a dependent library. For example, a symbol that was implemented in a particular revision of a library may not be available in a later revision. But a client linked against the first revision also works with the second revision. Client developers, however, must ensure the existence of the symbol in the running process before executing it. This mechanism is also used to provide a standard interface to plug-ins, which may or may not implement the entire interface.

Listing 7 shows code that ensures that a particular function is defined before using it. When the function is not found, the client uses a different function to accomplish the desired task. In this case, the fallback function is not a weakly linked symbol, so no test is required. Other situations may not offer an alternate interface. In such cases the client may not be able to perform the desired task.

Listing 7  Using a weakly linked symbol

// Clear the 'name' property.
if (clear_name) {
    clear_name();
}
else {
    set_name(" ");
}

Using C++ Classes

How client developers use a C++ class depends on whether the dynamic library that implements it is loaded when the client is loaded (dependent library) or at a later point (runtime loaded library). Dependent-library classes can be used directly. That is, clients can create and delete objects with the new and delete operators. Classes implemented in libraries loaded at runtime with dlopen are called runtime loaded classes.

A runtime loaded class must be instantiated by the client using that class’s factory functions, declared as part of the class’s interface. Factory functions create and destroy instances of a specific class: Constructor functions instantiate objects and destructor functions destroy them. Clients must use factory functions instead of new and delete because the dynamic loader doesn’t have access to a runtime loaded class’s constructors and destructors. When the client calls a factory function, the library invokes the appropriate constructor and destructor on the client’s behalf. After you create an instance of a runtime loaded class, you invoke its member functions the same way you would call them if the class were defined locally.

The interface for C++ classes implemented in dynamic libraries is made up of at least the class declaration and a set of factory functions. The class interface includes one type definition per constructor function. To use a factory function, you must create an object of the appropriate type and get the address of the function with dlsym. You can then call the factory function to create or destroy an object of the class.

Listing 8 shows the interface to the Person class, implemented in the Person library.

Listing 8  C++ class interface

/* File: Person.h */
class Person {
    private:
        char _person_name[30];
    public:
        Person();
        Person(char* name);
        virtual void set_name(char person_name[]);
        virtual char* name();
};
 
// Constructor functions and function types.
extern "C" Person* NewPerson(void);
typedef Person * Person_creator(void);
extern "C" Person* NewPersonWithName(char name[]);
typedef Person * PersonWithName_creator(char name);
 
// Destructor function and function type.
extern "C" void DeletePerson(Person* person);
typedef void Person_disposer(Person*);

Note that the Person class has two constructor functions, NewPerson and NewPersonWithName . Each function declaration has a corresponding type, Person_creator and PersonWithName_creator. Listing 9 shows how a client may use the Person library.

Listing 9  Client using a C++ runtime loaded class

/* File: Client.cpp */
#include <iostream>
#include <dlfcn.h>
#include "Person.h"
 
int main() {
    using std::cout;
    using std::cerr;
 
    // Open the library.
    void* lib_handle = dlopen("./libPerson.dylib", RTLD_LOCAL);
    if (!lib_handle) {
        exit(EXIT_FAILURE);
    }
 
    // Get the NewPerson function.
    Person_creator* NewPerson = (Person_creator*)dlsym(lib_handle, "NewPerson");
    if (!NewPerson) {
        exit(EXIT_FAILURE);
    }
 
    // Get the NewPersonWithName function.
    PersonWithName_creator* NewPersonWithName = (PersonWithName_creator*)dlsym(lib_handle, "NewPersonWithName");
    if (!NewPersonWithName) {
        exit(EXIT_FAILURE);
    }
 
    // Get the DeletePerson function.
    Person_disposer* DeletePerson =
        (Person_disposer*)dlsym(lib_handle, "DeletePerson");
    if (!DeletePerson) {
        exit(EXIT_FAILURE);
    }
 
    // Create Person objects.
    Person* person1 = NewPerson();
    Person* person2 = NewPersonWithName("Cendrine");
 
    // Use Person objects.
    person1->set_name("Floriane");
    cout << "[" << __FILE__ << "] person1->name() = " << person1->name() << "\n";
    person2->set_name("Marcelle");
    cout << "[" << __FILE__ << "] person2->name() = " << person2->name() << "\n";
 
    // Destroy Person objects.
    DeletePerson(person1);
    DeletePerson(person2);
 
    // Close the library.
    if (dlclose(lib_handle) != 0) {
        exit(EXIT_FAILURE);
    }
 
    return 0;
}

Using Objective-C Classes

To use an Objective-C class or category implemented in a dynamic library, a client should have an interface to the class or category. With knowledge of the class’s correct interface, the client can create instances of the class that are appropriately typed. Otherwise, the compiler produces warnings for methods with missing declarations.

The interfaces of Objective-C classes and categories are published in the library’s header files as protocols. Instantiating a class implemented in a dependent library is no different from doing the same for a locally defined class. However, when you load a dynamic library at runtime using dlopen, you must obtain the appropriate class by calling the objc_getClass function.

For example, Listing 10 contains the interfaces for the Person class and the Titling category to that class, which are implemented by the Person dynamic library.

Listing 10  Interface to the Person class and its Titling category

/* File: Person.h */
#import <Foundation/Foundation.h>
 
@protocol Person
- (void)setName:(NSString*)name;
- (NSString*)name;
@end
 
@interface Person : NSObject <Person> {
    @private
NSString* _person_name;
}
@end
 
/* File: Titling.h */
#import <Foundation/Foundation.h>
#import "Person.h"
 
@protocol Titling
- (void)setTitle:(NSString*)title;
@end
 
@interface Person (Titling) <Titling>
@end

A client compiled with these interfaces and linked against the Person library can create objects that implement the interfaces in a very straightforward way, as shown in Listing 11.

Listing 11  Example of a client that uses the Person library as a dependent library

/* File: Client.m */
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Titling.h"
 
int main() {
    NSAutoreleasePool* pool = [NSAutoreleasePool new];
 
    // Create an instance of Person.
    Person<Titling>* person = [Person new];
 
    // Use person.
    [person setName:@"Perrine"];
    NSLog(@"[%s] main: [person name] = %@", __FILE__, [person name]);
    [person setTitle:@"Ms."];
    NSLog(@"[%s] main: [person name] = %@", __FILE__, [person name]);
 
    // Dispose of the Person instance.
    [person release];
 
    [pool release];
    return EXIT_SUCCESS;
}

When the Person library is a runtime loaded library, however, the client must obtain a reference to the Person class from the Objective-C runtime after loading the library, using objc_getClass. It can then use that reference to instantiate a Person object. However, the variable that holds the instance must by typed as an NSObject that implements the Person and Titling protocols to avoid compiler warnings. When done, the client closes the library, as shown in “Using Weakly Linked Symbols.”

Listing 12  Example of a client that uses the Person library as a runtime loaded library

/* File: Client.m */
#import <Foundation/Foundation.h>
#import <dlfcn.h>
#import "Person.h"
#import "Titling.h"
 
int main() {
    NSAutoreleasePool* pool = [NSAutoreleasePool new];
 
    // Open the Person library.
    void* lib_handle = dlopen("./libPerson.dylib", RTLD_LOCAL);
    if (!lib_handle) {
        exit(EXIT_FAILURE);
    }
 
    // Get the Person class (required with runtime loaded libraries).
    Class Person_class = objc_getClass("Person");
    if (!Person_class) {
        exit(EXIT_FAILURE);
    }
 
    // Create an instance of Person.
    NSObject<Person,Titling>* person = [Person_class new];
 
    // Use person.
    [person setName:@"Perrine LeVan"];
    NSLog(@"[%s] main: [person name] = %@", __FILE__, [person name]);
    [person setTitle:@"Ms."];
    NSLog(@"[%s] main: [person name] = %@", __FILE__, [person name]);
 
    // Dispose of person.
    [person release];
 
    // Close the Person library.
    if (dlclose(lib_handle) != 0) {
        exit(EXIT_FAILURE);
    }
 
    [pool release];
    return EXIT_SUCCESS;
}

Getting Information on an Address

One of the dynamic loader compatibility (DLC) functions, dladdr, provides information on the image and nearest symbol that corresponds to an address. You can use this function to obtain information about the library that exports a particular symbol.

The information dladdr provides is returned through an output parameter of type Dl_info . These are the names of the structure’s fields as well as their descriptions:

Listing 13 shows how an application image can get information about a symbol:

Listing 13  Getting information about a symbol

#include <stdio.h>
#include <dlfcn.h>
 
extern char* dependencies();
 
int main(void) {
    // Get information on dependencies().
    Dl_info info;
    if (dladdr(dependencies, &info)) {
        printf("[%s] Info on dependencies():\n", __FILE__);
        printf("[%s]    Pathname: %s\n",         __FILE__, info.dli_fname);
        printf("[%s]    Base address: %p\n",     __FILE__, info.dli_fbase);
        printf("[%s]    Nearest symbol: %s\n",   __FILE__, info.dli_sname);
        printf("[%s]    Symbol address: %p\n",   __FILE__, info.dli_saddr);
    }
    else {
        printf("[%s] Unable to find image containing the address %x\n",
    __FILE__, &dependencies);
    }
}


< Previous PageNext Page > Hide TOC


© 2009 Apple Inc. All Rights Reserved. (Last updated: 2009-02-26)


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.