Book HomeMac OS X for Unix GeeksSearch this book

5.3. 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. Most Unix-based software ports usually produce bundles with a .so extension, for the sake of consistency across platforms. Although Apple recommends giving bundles a .bundle extension, it isn't mandatory.

WARNING: You cannot link directly against a bundle. Instead, bundles must be dynamically loaded and unloaded by the dyld APIs. When porting Unix-based software, you'll often need to translate dlopen( ) function calls to dylib actions. You can implement a temporary fix by using the dlcompat library from the Finkdistribution, but the ideal solution is to do a complete port using the dyld APIs.

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 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.

5.3.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 5-3.

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

Example 5-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 5-5 will compile and link the library, and then compile, link, and execute the test program.

Example 5-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

5.3.2. Dynamically Loading Libraries

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

You do not 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 5-7 loads libanswer.bundle and invokes the get_answer function. Example 5-7 is similar to Example 5-4, but many lines (shown in bold) have been added.

Example 5-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 5-7, use the following command:

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

5.3.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, if there wasn't a conflict at compile time, there is no 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. (Mac OS X 10.2 does not introduce 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.



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.