wrapper routine and the corresponding routine from the C library are copied into the a.out file. If, on the other hand, a shared wrapper library and archive C library are specified, in that order, then all routines that can be referenced by any routine in the wrapper library are copied from the C library. To avoid this, link with archive or shared versions for both the wrapper library and C library, or use an archive version of the wrapper library and a shared version of the C library.

Absolute Virtual Addresses

Writing code that relies on the linker to locate a symbol in a particular location or in a particular order in relation to other symbols is known as making an implicit address dependency. Because of the nature of shared libraries, the linker cannot always preserve the exact ordering of symbols declared in shared libraries. In particular, variables declared in a shared library may be located far from the main program's virtual address space, and they may not reside in the same relative order within the library as they were linked. Therefore, code that has implicit address dependencies may not work as expected with shared libraries.

An example of an implicit address dependency is a function that assumes that two global variables that were defined adjacently in the source code is actually adjacent in virtual memory. Because the linker may rearrange data in shared libraries, this is no longer guaranteed. Another example is a function that assumes variables it declares statically (for example, C static variables) reside below the reserved symbol _end in memory (see end(3)). In general, it is a bad idea to depend on the relative addresses of global variables, because the linker may move them around.

In assembly language, using the address of a label to calculate the size of the immediately preceding data structure is not affected: the assemblers still calculate the size correctly.

Stack Usage

To load shared libraries, a program must have a copy of the dynamic loader (dld.so) mapped into its address space. This copy of the dynamic loader shares the stack with the program. The dynamic loader uses the stack during startup and whenever a program calls a shared library routine for the first time. If you specify -B immediate, the dynamic loader only uses the stack at startup and for explicit calls to loader routines, such as dlopen.

NOTE: For PA-32 compatibility mode (with +compat) only: Although it is not a recommended programming practice, some programs may use stack space "above" the program's current stack. To preserve the contents "above" the program's logical top of the stack, the dynamic loader attempts to use stack space far away from program's stack pointer. If a program is doing its own stack manipulations, such as those implemented by a "threads" package, the dynamic loader may inadvertently use stack space that the program had reserved for another thread. Programs doing such stack manipulations must link with archive libraries, or at least use immediate binding and avoid calling loader routines, if this could potentially cause problems.

Also be aware that if a program sets its stack pointer to memory allocated in the heap, the dynamic loader may use the space directly "above" the top of this stack.

Version Control

You can maintain multiple versions of a shared library using library-level versioning. This allows you to make incompatible changes to shared libraries and ensure programs linked with the older versions continue to run. (See “Library-Level Versioning” (page 111) for more information.)

Debugger Limitations

You can debug shared libraries just like archive libraries with few exceptions. Support is provided by the WDB Debugger. See the WDB documentation at:

http://www.hp.com/go/wdb

116 Creating and Using Libraries