12.5. Library Versions

 < Day Day Up > 

12.4. Shared Libraries Versus Loadable Modules

The Executable and Linking Format (ELF) , developed by the Unix System Laboratories, is common in the Unix world. On ELF systems, there is no distinction between shared libraries and loadable modules; shared code can be used as a library for dynamic loading. ELF is the default binary format on Linux, Solaris 2.x, and SVR4. Since these systems cover a large share of the Unix base, most Unix developers have experience on ELF systems. Thus, it may come as a surprise to experienced Unix developers that shared libraries and loadable modules are not the same on Mac OS X. This is because the binary format used in Mac OS X is Mach-O, which is different from ELF.

Mach-O shared libraries have the file type MH_DYLIB and the .dylib (dynamic library) suffix and can be linked to with static linker flags. So, if you have a shared library named libcool.dylib, you can link to this library by specifying the -lcool flag. Although shared libraries cannot be loaded dynamically as modules, they can be loaded through the dyld API (see the manpage for dyld, the dynamic link editor). It is important to point out that shared libraries cannot be unloaded.

Loadable modules, called bundles in Mac OS X, have the file type MH_BUNDLE. To maintain consistency across platforms, most Unix-based software ports usually produce bundles with a .so extension. Although Apple recommends giving bundles a .bundle extension, it isn't mandatory.

You need to use special flags with cc when compiling a shared library or a bundle on Darwin. One difference between Darwin and many other Unix systems is that no position-independent code (PIC) flag is needed, since it is

Loading a Bundle

You cannot link directly against a bundle. Instead, bundles must be dynamically loaded and unloaded by the dyld APIs. /usr/lib/libdl.dylib is provided as a symbolic link to libSystem.dylib.

In Panther dlopen( ), dlclose( ), dlsym( ), dlerror( ) functions were provided interfaces to the dynamic linker using the native dyld, NSModule, and NSObjectFileImage functions. This made porting common Unix source code relatively painless.

In Tiger dlopen( ), dlclose( ), dlsym( ), dlerror( ) functions are natively part of dyld, providing both improved performance and better standards compliance.

Another common porting problem on earlier versions of Mac OS X was the lack of the System V poll( ) system call function. Panther emulated the poll( ) function as an interface to the BSD native select( ) API. In Tiger, poll( ) is native.


the default for Darwin. Next, since the linker does not allow common symbols, the compiler flag -fno-common is required for both shared libraries and bundles. (A common symbol is one that is defined multiple times. You should instead define a symbol once and use C's extern keyword to declare it in places where it is needed.)

To build a shared library, use cc's -dynamiclib option. Use the -bundle option to build a loadable module or bundle.

12.4.1. Building a Shared Library

Suppose you want to create a shared library containing one or more C functions, such as the one shown in Example 12-3.

Example 12-3. A simple C program
 /*  * answer.c: The answer to life, the universe, and everything.  */ int get_answer( ) {   return 42; } 

If you compile the program containing the function into a shared library, you could test it with the program shown in Example 12-4.

Example 12-4. Compiling answer.c into a shared library
 /*  * deep_thought.c: Obtain the answer to life, the universe,  * and everything, and act startled when you actually hear it.  */ #include <stdio.h> int main( ) {   int the_answer;   the_answer = get_answer( );   printf("The answer is... %d\n", the_answer);   fprintf(stderr, "%d??!!\n", the_answer);   return 0; } 

The makefile shown in Example 12-5 compiles and links the library, and then compiles, links, and executes the test program.

Example 12-5. Sample makefile for creating and testing a shared library
 # Makefile: Create and test a shared library. # # Usage: make test # CC = cc LD = cc CFLAGS = -O -fno-common all: deep_thought # Create the shared library. # answer.o: answer.c         $(CC) $(CFLAGS) -c answer.c libanswer.dylib: answer.o         $(LD) -dynamiclib  -install_name  libanswer.dylib \         -o libanswer.dylib answer.o # Test the shared library with the deep_thought program. # deep_thought.o: deep_thought.c         $(CC) $(CFLAGS) -c deep_thought.c deep_thought: deep_thought.o libanswer.dylib         $(LD) -o deep_thought deep_thought.o -L. -lanswer test: all         ./deep_thought clean:         rm -f *.o core deep_thought libanswer.dylib 

The preceding makefile made use of the ld flag -install_name, which is the Mach-O analog of -soname, used for building shared libraries on ELF systems. The -install_name flag is used to specify where the executable, linked against it, should look for the library. The -install_name in the makefile shown in Example 12-5 specifies that the deep_thought executable is to look for the library libanswer.dylib in the same directory as the executable itself. The command otool can be used to verify this:

     $ otool -L deep_thought     deep_thought:             libanswer.dylib (compatibility version 0.0.0, current version 0.0.0)             /usr/lib/libmx.A.dylib (compatibility version 1.0.0, current version     92.0.0)             /usr/lib/libSystem.B.dylib (compaatibility version 1.0.0, current     version     88.0.0) 

The -install_name flag is often used with @execution_path to specify a relative pathname of the library. The pathname of the library is relative to the executable. For example, suppose we change the makefile in Example 12-5 by adding an install target:

     install: libanswer.dylib         cp libanswer.dylib ../lib/. 

Then add install to the all target's dependency list and change the libanswer target to the following:

     libanswer.dylib: answer.o         $(LD) -dynamiclib -install_name  @execution_path/../lib/libanswer.dylib \         -o libanswer.dylib answer.o 

Then the deep_thought executable built using this makefile looks for the libanswer.dylib in a directory ../lib. The output from otool shows this change:

     $ otool -L deep_thought     deep_thought:             @execution_path/../lib/libanswer.dylib (compatibility version 0.0.0,     current version 0.0.0)             /usr/lib/libmx.A.dylib (compatibility version 1.0.0, current version     92.0.0)             /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current     version 88.0.0) 

The -install_name flag is often used with @execution_path when building a private framework associated with an application, since private frameworks are located within the application's contents.

12.4.2. Dynamically Loading Libraries

You can turn answer.o into a bundle, which can be dynamically loaded using the commands shown in Example 12-6.

Example 12-6. Commands for converting answer.o into a bundle
 cc -bundle -o libanswer.bundle answer.o 

You don't need to specify the bundle at link time. Instead, use the dyld functions NSCreateObjectFileImageFromFile and NSLinkModule to load the library. Then, you can use NSLookupSymbolInModule and NSAddressOfSymbol to access the symbols that the library exports. Example 12-7 loads libanswer.bundle and invokes the get_answer function. Example 12-7 is similar to Example 12-4, but many lines (shown in bold) have been added.

Example 12-7. Dynamically loading a bundle and invoking a function
 /*  * deep_thought_dyld.c: Obtain the answer to life, the universe,  * and everything, and act startled when you actually hear it.  */ #include <stdio.h> #import <mach-o/dyld.h> int main( ) {   int the_answer;   int rc;                // Success or failure result value   NSObjectFileImage img; // Represents the bundle's object file   NSModule handle;       // Handle to the loaded bundle   NSSymbol sym;          // Represents a symbol in the bundle   int (*get_answer) (void);  // Function pointer for get_answer   /* Get an object file for the bundle. */   rc = NSCreateObjectFileImageFromFile("libanswer.bundle", &img);   if (rc != NSObjectFileImageSuccess) {     fprintf(stderr, "Could not load libanswer.bundle.\n");     exit(-1);   }   /* Get a handle for the bundle. */   handle = NSLinkModule(img, "libanswer.bundle", FALSE);   /* Look up the get_answer function. */   sym = NSLookupSymbolInModule(handle, "_get_answer");   if (sym == NULL)   {     fprintf(stderr, "Could not find symbol: _get_answer.\n");     exit(-2);   }   /* Get the address of the function. */   get_answer = NSAddressOfSymbol(sym);   /* Invoke the function and display the answer. */   the_answer = get_answer( );   printf("The answer is... %d\n", the_answer);   fprintf(stderr, "%d??!!\n", the_answer);   return 0; } 

For more information on these functions, see the NSObjectFileImage, NSModule, and NSSymbol manpages. To compile the code in Example 12-7, use the following command:

     cc -O -fno-common -o deep_thought_dyld deep_thought_dyld.c 

12.4.3. Two-Level Namespaces

In Mac OS X 10.0, the dynamic linker merged symbols into a single (flat) namespace. So, if you link against two different libraries that both define the same function, the dynamic linker complains because the same symbol was defined in both places. This approach prevented collisions that were known at compile time. However, a lack of conflict at compile time does not guarantee that a future version of the library won't introduce a conflict.

Suppose you linked your application against Version 1 of libfoo and Version 1 of libbar. At the time you compiled your application, libfoo defined a function called logerror( ), and libbar did not. But when Version 2 of libbar came out, it included a function called logerror( ). Since the conflict was not known at compile time, your application doesn't expect libbar to contain this function. If your application happens to load libbar before libfoo, it will call libbar's logerror( ) method, which is not what you want.

So, Mac OS X 10.1 introduced two-level namespaces , which the compiler uses by default. (None of the subsequent releases of Mac OS X, 10.2-10.4, introduced any changes to two-level namespaces.) With this feature, you can link against Version 1 of libfoo and libbar. The linker creates an application that knows logerror( ) lives in libfoo. So, even if a future version of libbar includes a logerror( ) function, your application will know which logerror( ) it should use.

If you want to build an application using a flat namespace, use the -flat_ namespace linker flag. See the ld manpage for more details.

     < Day Day Up > 


    Mac OS X Tiger for Unix Geeks
    Mac OS X Tiger for Unix Geeks
    ISBN: 0596009127
    EAN: 2147483647
    Year: 2006
    Pages: 176

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net