Intermediate Update:
The HP-UX March 2001 linker toolset contains new features:
32-bit support for the +init and +fini initializer/finalizer options. See Initializers for Shared Librariesfor more information.
Virtual Environment Support for HP-UX kernel. See for ld(1) for more information.
Improving Performance with Function Symbol Aliasing describes link time function name aliasing to improve performance.
The HP-UX 11i linker toolset contains new features:
32-bit support for the dl*(3C) shared library management routines, dlopen, dlclose, dlsym, and dlerror. See Shared Library Management Routines for more information.
+objdebugonly option. See for ld(1) for more information.
Odump dump utility to display the contents of a 32-bit native code (SOM) object file. See odump(1) for more information.
gprof 32-bit support for profiling shared libraries. The gprof tool is used to produce profile information about an executable. Enhancements in this release enable these tools to work with incomplete executables and shared libraries. Note that you can profile one and only one incomplete executable or shared library at a time. See Profiling Shared Libraries with gprof(1) and gprof(1) for more information.
Using filtered shared libraries (32-bit mode only). 32-bit support for filtered shared libraries.
Incremental Linking. 64-bit support for incremental linking.
The HP-UX 10.20 and 11.x releases contain performance enhancements:
The HP-UX 11.00 linker toolset contains new features:
If you use the 32-bit mode linker toolset, see the following items:
Changes in Future Releases updated in this chapter.
64-bit Mode Linker Toolset Compatibility with De Facto Industry Standards described in this chapter.
64-bit Mode ELF Object File Format described in this chapter.
Dynamic Path Searching for Shared Libraries describes differences in the run time searching of shared libraries.
Shared Library Symbol Binding Semantics describes differences in shared library binding semantics.
New 64-bit mode linker options, symbols, and features, described inNew Features for 64-bit Mode Linking in this chapter.
Unsupported 32-bit mode features, behavior, and linker options, described in 64-bit Mode Link-time Differences and64-bit Mode Run Time Differences in this chapter.
Using Init/Fini Initializers describes the init/fini support for 64-bit mode shared libraries.
The dlopen Shared Library Management Routines describes the dl* family of shared library management routines for 64-bit mode.
BIND_BREADTH_FIRST Modifier describes the flag added to the shl_load routine to modify search behavior.
Changes in Future Releases updated in this chapter.
The following items were added in the HP-UX 10.30 release:
Options to Improve TLB Hit Rates .
The +k linker option (see ld(1)) to remove an executable if the link fails.
The +kchatr option (see chatr(1)) to improve branch prediction on PA-RISC 2.0.
Improving Shared Library Start-Up Time with fastbind .
Online Help for Linker and Libraries described in this chapter.
PA-RISC Changes in Hardware Compatibility described in this chapter.
Linker Compatibility Warnings.
Dynamic Loader Compatibility Warnings.
The +Ostaticprediction linker option described in the ld(1) man page to use with profile-based optimization
In previous releases, the compilers generated PA-RISC 1.0 code on all HP 9000 Series 800 servers and PA-RISC 1.1 code on Series 700 workstations. HP compilers now by default generate PA-RISC 1.1 code on 1.1 systems and 2.0 code on 2.0 systems.
Using the +DAportable compiler option provides compatibility
of code between PA-RISC 1.1 and 2.0 systems. Note that the HP-UX 10.10
release is the last supported release for PA-RISC 1.0 systems, so 1.1 and
2.0 code generated by the HP-UX 10.20 release (or later) of HP compilers
is not supported on PA-RISC 1.0 systems.
| NOTE | The +DA1.0 option will be obsolete in a future release. You cannot build PA1.0 executables on 11.x and run them on 10.x systems. You can achieve better performance on PA-RISC 1.1 and 2.0 systems by not using this option. |
/usr/ccs/bin/ld: (Warning) At least one PA 2.0 object file (sum.o) was detected. The linked output may not run on PA 1.x system.If you try to run a PA-RISC 2.0 program on a 1.1 system, you'll see a message like:
$ a.out ksh: ./a.out: Executable file incompatible with hardwareIn this example, the +DAportable compiler option can be used to create code compatible for PA-RISC 1.1 and 2.0 systems.
See the file /opt/langtools/lib/sched.models for a complete list of model numbers and their architectures. Use the command model to determine the model number of your system.
Dynamic path searching
Library-level versioning
dl* family of dynamic loading routines
Breadth-first symbol searching
ld +compat option for compatibility with 32-bit linking and loading behavior.
The following options instruct the compiler to generate 64-bit ELF object
code.
| Option | Compiler |
|---|---|
| +DA2.0W | C and aC++ |
| +DD64 | C |
See the HP-UX Software Transition Toolkit (STK) at http://www.software.hp.com/STK/
for more information on the structure of ELF object files.
| Symbol | Definition |
|---|---|
| __SYSTEM_ID | Largest architecture revision level used by any compilation unit |
| _FPU_STATUS | Initial value of FPU status register |
| _end or end | Address of first byte following the end of the main program's data segment; identifies the beginning of the heap segment |
| __TLS_SIZE | Size of the Thread Local Storage segment required by the program |
| __text_start | Beginning of the text segment |
| __text_start_f | Beginning of text segment, declared as a function |
| _etext or etext | End of the text segment |
| _etext_f | End of text segment, declared as a function |
| __data_start | Beginning of the data segment |
| _edata or edata | End of initialized data |
| __gp | Global pointer value |
| __init_start | Beginning of the .init section |
| __init_end | End of the .init section |
| __preinit_start | Beginning of the .preinit section |
| __preinit_end | End of the .preinit section |
| __fini_start | Beginning of the .fini section |
| __fini_end | End of the .fini section |
| __unwind_start | Beginning of the unwind table |
| __unwind_end | End of the unwind table |
| NOTE | The linker generates an error if a user application also defines these symbols. |
| Option or Behavior | Description |
|---|---|
| -A name | Specifies incremental loading. 64-bit applications must use shared libraries instead. |
| -C n | Does parameter type checking. This option is unsupported. |
| -S | Generates an initial program loader header file. This option is unsupported. |
| -T | Save data and relocation information in temporary files to reduce virtual memory requirements during linking. This option is unsupported. |
| -q, -Q, -n | Generates an executable with file type DEMAND_MAGIC, EXEC_MAGIC, and SHARE_MAGIC respectively. These options have no effect and are ignored in 64-bit mode. |
| -N | Causes the data segment to be placed immediately after the text segment. This option is accepted but ignored in 64-bit mode. If this option is used because your application data segment is large, then the option is no longer needed in 64-bit mode. If this option is used because your program is used in an embedded system or other specialized application, consider using mapfile support with the -k option. |
| +cg pathname | Specifies pathname for compiling I-SOMs to SOMs. This option is unsupported. |
| +dpv | Displays verbose messages regarding procedures which have been removed due to dead procedure elimination. Use the -v linker option instead. |
| Intra-library versioning | Specified by using the HP_SHLIB_VERSION pragma (C and aC++)
or SHLIB_VERSION directive (Fortran90).
In 32-bit mode, the linker lets you version your library by object files. 64-bit applications must use SVR4 library-level versioning instead. |
| Duplicate code and data symbols | Code and data cannot share the same namespace in 64-bit mode. You should rename the conflicting symbols. |
| All internal and undocumented linker options | These options are unsupported. |
For more information, see the HP-UX Linker and Libraries Online User
Guide (ld +help).
Symbol searching in dependent libraries.
The following table summarizes the dynamic loader differences between
32-bit and 64-bit mode:
| Linker and Loader Functions | 32-bit Mode Behavior | 64-bit Mode Behavior |
|---|---|---|
| +s and +b path_list ordering | Ordering is significant. | Ordering is insignificant by default.
Use +compat to enforce ordering. |
| Symbol searching in dependent libraries | Depth-first search order. | Breadth-first search order.
Use +compat to enforce depth first ordering. |
| Run time path environment variables | No run time environment variables by default.
If +s is specified, then SHLIB_PATH is available. |
LD_LIBRARY_PATH and SHLIB_PATH are available.
Use +noenv or +compat to turn off run-time path environment variables. |
| +b path_list and -L directories interaction | -L directories recorded as absolute paths in executables. | -L directories are not recorded in executables, if
-L
and +b are both used.
To record all the directories in executables, add all directories specified in -L to +b path_list. |
For more information on transition issues, see HP-UX 64-bit Porting
and Transition Guide.
A future release will support the ELF 32 object file format.
Future of ld +compat option
The +compat linker option and support of compatibility mode may be discontinued in a future release.
Support of shl_load shared library management routines
A future release may discontinue support of the shl_load family of shared library management routines.
To access the Linker and Libraries Online User Guide from the ld command line:
ld +help
Looking "inside" a Compiler describes the process of creating an executable file in more detail.
Linking Programs on HP-UX describes how ld creates an executable file from one or more object files.
Linking with Libraries describes conventions for using libraries with ld.
Running the Program describes the process of loading and binding programs at run time.
Linker Thread-Safe Featuresdescribes the thread-safe features.
$ cc -Aa sumnum.cThe compiler displays status, warning, and error messages to standard error output (stderr). If no errors occur, the compiler creates an executable file named a.out in the current working directory. If your PATH environment variable includes the current working directory, you can run a.out as follows:
$ a.out Enter a number: 4 Sum 1 to 4: 10The process is essentially the same for all HP-UX compilers. For instance, to compile and run a similar FORTRAN program named sumnum.f:
$ f77 sumnum.f Compile and link sumnum.f. ... The compiler displays any messages here. $ a.out Run the program. ... Output from the program is displayed here.Program source can also be divided among separate files. For example, sumnum.c could be divided into two files: main.c, containing the main program, and func.c, containing the function sum_n. The command for compiling the two together is:
$ cc -Aa main.c func.c main.c: func.c:Notice that cc displays the name of each source file it compiles. This way, if errors occur, you know where they occur.
#include <stdio.h> /* contains standard I/O defs */
int sum_n( int n ) /* sum numbers from n to 1 */
{
int sum = 0; /* running total; initially 0 */
for (; n >= 1; n--) /* sum from n to 1 */
sum += n; /* add n to sum */
return sum; /* return the value of sum */
}
main() /* begin main program */
{
int n; /* number to input from user */
printf("Enter a number: "); /* prompt for number */
scanf("%d", &n); /* read the number into n */
printf("Sum 1 to %d: %d\\n", n, sum_n(n)); /* display the sum */
}
Generally speaking, the compiler reads one or more source files, one of
which contains a main program, and outputs an executable
a.out
file, as shown in Figure 1: High-Level
View of the Compiler .

Then, the driver calls the HP-UX linker (ld) which builds an a.out file from the object files. This is known as the link-edit phase of compilation. (See Also Compiler-Linker Interaction .)

The C, C++, FORTRAN, and Pascal compilers provide the -v (verbose) option to display the phases a compiler is performing. Compiling main.c and func.c with the -v option produced this output on a Series 700 workstation (\ at the end of a line indicates the line is continued to the next line):
$ cc -Aa -v main.c func.c -lm cc: CCOPTS is not set. main.c: /opt/langtools/lbin/cpp.ansi main.c /var/tmp/ctmAAAa10102 \\ -D__hp9000s700 -D__hp9000s800 -D__hppa -D__hpux \\ -D__unix -D_PA_RISC1_1 cc: Entering Preprocessor. /opt/ansic/lbin/ccom /var/tmp/ctmAAAa10102 main.o -O0 -Aa \\ func.c: /opt/langtools/lbin/cpp.ansi func.c /var/tmp/ctmAAAa10102 \\ -D__hp9000s700 -D__hp9000s800 -D__hppa -D__hpux \\ -D__unix -D_PA_RISC1_1 cc: Entering Preprocessor. /opt/ansic/lbin/ccom /var/tmp/ctmAAAa10102 func.o -O0 -Aa cc: LPATH is /usr/lib/pa1.1:/usr/lib:/opt/langtools/lib: /usr/ccs/bin/ld /opt/langtools/lib/crt0.o -u main main.o func.o \\ -lm -lc cc: Entering Link editor.This example shows that the cc driver calls the C preprocessor (/opt/langtools/lbin/cpp.ansi) for each source file, then calls the actual C compiler (/opt/ansic/lbin/ccom) to create the object files. Finally, the driver calls the linker (/usr/ccs/bin/ld) on the object files created by the compiler (main.o and func.o).
To keep track of where all the symbols and external references occur,
an object file has a symbol table. The linker uses the symbol tables
of all input object files to match up external references to global definitions.
$ cc -Aa -v main.c func.c -lm cc: CCOPTS is not set. main.c: /opt/langtools/lbin/cpp.ansi main.c /var/tmp/ctmAAAa10102 \\ -D__hp9000s700 -D__hp9000s800 -D__hppa -D__hpux \\ -D__unix -D_PA_RISC1_1 cc: Entering Preprocessor. /opt/ansic/lbin/ccom /var/tmp/ctmAAAa10102 main.o -O0 -Aa func.c: /opt/langtools/lbin/cpp.ansi func.c /var/tmp/ctmAAAa10102 \\ -D__hp9000s700 -D__hp9000s800 -D__hppa -D__hpux \\ -D__unix -D_PA_RISC1_1 cc: Entering Preprocessor. /opt/ansic/lbin/ccom /var/tmp/ctmAAAa10102 func.o -O0 -Aa cc: LPATH is /usr/lib/pa1.1:/usr/lib:/opt/langtools/lib: /usr/ccs/bin/ld /opt/langtools/lib/crt0.o -u main main.o func.o -lm -lc cc: Entering Link editor.The next-to-last line in the above example is the command line the compiler used to invoke the 32-bit mode linker, /usr/ccs/bin/ld. In this command, ld combines a startup file (crt0.o) and the two object files created by the compiler (main.o and func.o). Also, ld searches the libm and libc libraries.
In 64-bit mode, the startup functions are handled by the dynamic loader,
dld.sl.
In most cases, the ld command line does not include
crt0.o.
| NOTE | If you are linking any C++ object files to create an executable or a shared library, you must use the CC command to link. This ensures that c++patch executes and chains together your nonlocal static constructors and destructors. If you use ld, the library or executable may not work correctly and you may not get any error messages. For more information see the HP C++ Programmer's Guide. |
In the C program example (see Compiling Programs
on HP-UX: An Example ) main.o contains an external reference
to sum_n, which has a global definition in func.o.
ld
matches the external reference to the global definition, allowing the main
program code in a.out to access sum_n (see Figure
3: Matching the External Reference to sum_n ).

If ld cannot match an external reference to a global definition, it displays a message to standard error output. If, for instance, you compile main.cwithoutfunc.c, ld cannot match the external reference to sum_n and displays this output:
$ cc -Aa main.c /usr/ccs/bin/ld: Unsatisfied symbols: sum_n (code)
The 64-bit linker uses the startup file, /opt/langtools/lib/pa_64/crt0.o, when:
The linker is in default standard mode (+std) with the -noshared option.
If the linker option -I is specified to create an executable file with profile-based optimization, in 32-bit mode icrt0.o is used, and in 64-bit mode the linker inserts /usr/ccs/lib/pa20_64/fdp_init.o. If the linker options -I and -b are specified to create a shared library with profile-based optimization, in 32-bit mode scrt0.o is used, and in 64-bit mode, the linker inserts /usr/ccs/lib/pa20_64/fdp_init_sl.o. In 64-bit mode, the linker uses the single 64-bit crt0.o to support these option.
For details on startup files, see crt0(3).
In 64-bit mode for dynamically bound executables, the entry point, defined
by the symbol $START$ in the dynamic loader (dld.sl).
| To set the magic number to: | Use this option: |
|---|---|
| SHARE_MAGIC | -n |
| DEMAND_MAGIC | -q |
| EXEC_MAGIC | -N |
An executable file's magic number can also be changed using the chatr command (see Changing a Program's Attributes with chatr(1) ). However, chatr can only toggle between SHARE_MAGIC and DEMAND_MAGIC; it cannot be used to change from or to EXEC_MAGIC. This is because the file format of SHARE_MAGIC and DEMAND_MAGIC is exactly the same, while EXEC_MAGIC files have a different format. For details on magic numbers, refer to magic(4).
In 64-bit mode, the linker sets the magic number to the predefined type
for ELF object files (\177ELF). The value of the E_TYPE
in the ELF object file specifies how the file should be loaded.
$ umask 022 $ ls -l a.out -rwxr-xr-x 1 michael users 74440 Apr 4 14:38 a.out
libname.suffix
$ cc -Aa -v main.c func.c ... /usr/ccs/bin/ld /opt/langtools/lib/crt0.o -u main main.o \ func.o -lc cc: Entering Link editor.Similarly, the Series 700/800 FORTRAN compiler automatically links with the libcl (C interface), libisamstub (ISAM file I/O), and libc libraries:
$ f77 -v sumnum.f ... /usr/ccs/bin/ld -x /opt/langtools/lib/crt0.o \ sumnum.o -lcl -lisamstub -lc
| NOTE | If multiple definitions of a symbol occur in the specified libraries, ld does not necessarily choose the first definition. It depends on whether the program is linked with archive libraries, shared libraries, or a combination of both. Depending on link order to resolve such library definition conflicts is risky because it relies on undocumented linker behavior that may change in future releases. (See Also Caution When Mixing Shared and Archive Libraries .) |
Determine where to begin execution of the program - that is, the entry point - usually in crt0.o. (See Also The crt0.o Startup File.)
When the program uses shared libraries, the crt0.o startup code invokes the dynamic loader (dld.sl), which in turn attaches any required shared libraries. If immediate binding was specified at link time, then the libraries are bound immediately. If deferred binding was specified, then libraries are bound as they are referenced. (See Also What are Shared Libraries?.)
The dynamic loader binds only those symbols that are reachable during
the execution of the program. This is similar to how archive libraries
are treated by the linker; namely, ld pulls in an object file
from an archive library only if the object file is needed for program execution.
Also, beginning with the HP-UX 10.30 release, the linker toolset provides thread local storage support in:
dld.sl - the shared library dynamic loader
crt0.o - the program startup file
| NOTE | A program with thread local storage is only supported on systems running HP-UX 10.30 or later versions of the operating system. |
| NOTE | Use of the __thread keyword in a shared library prevents that shared library from being dynamically loaded, that is, loaded by an explicit call to shl_load(). |
See Programming with Threads on HP-UX for information on threads.
As per the libc cumulative patches, PHCO_22923 (11.00) and PHCO_23772 (11.11), t he libc shared library contains stubs for the pthread_* functions in libpthread and libcma. The stubs allow non-threaded applications to dynamically load threa d-safe libraries successfully, so that the pthread symbols are resolved. Applica tions that resolves pthread/cma calls to the stub must be built without -lpthread or -lcma on the link line.
Stubs provided in libc do not have any functionality, these are dummy functions returning zero except pthread_getspecific() family of APIs which has full functi onality implemented in the stubs.
List of pthread calls for which the stubs are provided in the C library is given below.
The pthread calls to any of the above stub returns zero.
The stub for the following pthread calls has full functionality. More informatio n about their functionalities is in the pthread (3T) man pages.
Calls to the stub,
An application may inadvertently pick up the stubs present in libc when it is in tended to use the real pthread APIs, or cma APIs. These are link order problems. An application that needs cma behavior must link to libcma and must do so in th e supported link order, i.e. the link line should only be shared and not contain -lc before -lcma.
So long as this condition is met, the correct cma functions will be referenced. Similarly, a multithreaded application that needs pthread library behavior must link to libpthread and must do so in a supported link order, and only use shared libc and libpthread.
The applications or any library linked that will resolve pthread/cma calls to th e stubs must be built without -lpthread or -lcma on the link line. If you specif y -lc before -lpthread, your application will use the pthread stubs in libc, but other problems may occur as given in the examples below:
$ cat thread.c #include#include void *thread_nothing(void *p) { printf("Success\n"); } int main() { int err; pthread_t thrid; err = pthread_create(&thrid, (pthread_attr_t *) NULL, thread_nothing, (void *) NULL); sleep(1); if (err) { printf("Error\n"); return err; } } $ cc thread.c -lc -lpthread $ a.out Error $ chatr a.out a.out: shared executable shared library dynamic path search: SHLIB_PATH disabled second embedded path disabled first Not Defined shared library list: dynamic /usr/lib/libc.2 <- libc before libpthread dynamic /usr/lib/libpthread.1 shared library binding: deferred global hash table disabled $ cc thread.c -lpthread $ a.out Success $ chatr a.out a.out: shared executable shared library dynamic path search: SHLIB_PATH disabled second embedded path disabled first Not Defined shared library list: dynamic /usr/lib/libpthread.1 dynamic /usr/lib/libc.2 shared library binding: deferred global hash table disabled ...
Specifying -lc before -lpthread in threaded applications can cause run-time prob lems as in the following example. Because the pthread/cma stubs are resolved ins tead of the real pthread/cma functions:
$ cat a.c
#include
#include
extern int errno;
main()
{
shl_load("/usr/lib/librt.2", BIND_DEFERRED, 0);
printf("Error %d, %s\n", errno, strerror(errno));
}
$ cc a.c -lc -lpthread $ a.out Error 22, Invalid argument $ LD_PRELOAD=/usr/lib/libpthread.1 ./a.out Error 0, Error 0
$ cat b.c
#include
#include
void* handle;
extern int errno;
main()
{
handle = dlopen("lib_not_found", RTLD_LAZY);
printf("Error %d, %s\n", errno, strerror(errno));
if (handle == NULL) {
printf("Error: %s\n",dlerror());
}
}
$ cc b.c -lc -lpthread $ a.out Error 22, Invalid argument Error: $ LD_PRELOAD=/usr/lib/libpthread.1 ./a.out Error 0, Error 0 Error: Can't open shared library: lib_not_found
Therefore, -lc should never be specified in the build command of an executable o r shared library.
By default, the compiler drivers (cc, aCC, f90) automatically pass -lc to the li nker at the end of the link line of the executables. It is not necessary to spec ify -lc when building a shared library because libc will be resolved by the refe rence to libc in the executable build command. The libc will be resolved by -lc that is automatically passed by the compiler drivers to the linker.
To see if a shared library was built with -lc, look at the shared library list i n the chatr() output, or list the dependent libraries with ldd(1):
$ cc +z -c lib1.c
$ ld -b -o lib1.sl lib1.o -lc
$ ldd lib1.sl
/usr/lib/libc.2 => /usr/lib/libc.2
/usr/lib/libdld.2 => /usr/lib/libdld.2
/usr/lib/libc.2 => /usr/lib/libc.2
$ cc +DA2.0W +z -c lib1.c
$ ld -b -o lib1.sl lib1.o -lc
$ ldd lib1.sl
libc.2 => /lib/pa20_64/libc.2
libdl.1 => /usr/lib/pa20_64/libdl.1
To see the order in which dependent shared libraries will be loaded at run-time (order is only valid in 64-bit mode), use ldd(1) on the executable (ldd in 32-bi t mode displays the order in which libraries are loaded in reverse order):
$ cc +DA2.0W thread.c -lpthread
$ ldd a.out
libpthread.1 => /usr/lib/pa20_64/libpthread.1
libc.2 => /usr/lib/pa20_64/libc.2
libdl.1 => /usr/lib/pa20_64/libdl.1
$ cc +DA2.0W thread.c -lc -lpthread
$ ldd a.out
libc.2 => /usr/lib/pa20_64/libc.2
libpthread.1 => /usr/lib/pa20_64/libpthread.1
libdl.1 => /usr/lib/pa20_64/libdl.1
$ cc +DA2.0W thread.c -lpthread -lc
$ ldd a.out
libpthread.1 => /usr/lib/pa20_64/libpthread.1
libc.2 => /usr/lib/pa20_64/libc.2
libdl.1 => /usr/lib/pa20_64/libdl.1
If a 64-bit shared library is built with -lpthread but the executable is not, li bc is loaded before libpthread (due to breadth-first searching), and the pthread calls are resolved to the pthread stubs in libc. At run-time, after the a.out i s loaded, the dependencies of a.out are loaded in breadth-first order: libc is loaded as a dependent of a.out before libpthread is loaded as a dependent of libc.2. The dependency list of the first case is:
a.out
/ / \
lib1 lib2 libc
| |
libc libpthread
Therefore the load graph is constructed as:
lib1.sl --> lib2.sl -->libc.2 --> libpthread.1
This is the desired behavior for non-threaded applications, but causes threaded applications (that use either libpthread or libcma) to fail.
# lib1.sl specifies -lc; lib2.sl specifies -lpthread;
no -lpthread on a.out
$ cc -c +z +DA2.0W lib1.c lib2.c
lib1.c:
lib2.c:
$ ld -b -o lib1.sl -lc lib1.o
$ ld -b -o lib2.sl -lpthread lib2.o
$ cc +DA2.0W thread.c -L. -l1 -l2
$ a.out
Error
$ ldd a.out
lib1.sl => ./lib1.sl
lib2.sl => ./lib2.sl
libc.2 => /usr/lib/pa20_64/libc.2
libc.2 => /lib/pa20_64/libc.2
libpthread.1 => /lib/pa20_64/libpthread.1
libdl.1 => /usr/lib/pa20_64/libdl.1
# lib2.sl specifies -lpthread; no -lpthread on a.out
$ ld -b -o lib1.sl lib1.o
$ ld -b -o lib2.sl -lpthread lib2.o
$ cc +DA2.0W thread.c -L. -l1 -l2
$ a.out
Error
$ ldd a.out
lib1.sl => ./lib1.sl
lib2.sl => ./lib2.sl
libc.2 => /usr/lib/pa20_64/libc.2
libpthread.1 => /lib/pa20_64/libpthread.1
libdl.1 => /usr/lib/pa20_64/libdl.1
The same problem will occur if -lcma is listed as a dependent library of a share d library, and you would need to link the executable with -lcma.
For threaded applications, run the executable with LD_PRELOAD set to the libpthr ead library or link the executable with -lpthread:
# use LD_PRELOAD to load libpthread first
$ ld -b -o lib1.sl lib1.o
$ ld -b -o lib2.sl -lpthread lib2.o
$ cc +DA2.0W thread.c -L. -l1 -l2
$ a.out
Error
$ ldd a.out
lib1.sl => ./lib1.sl
lib2.sl => ./lib2.sl
libc.2 => /usr/lib/pa20_64/libc.2
libpthread.1 => /lib/pa20_64/libpthread.1
libdl.1 => /usr/lib/pa20_64/libdl.1
$ LD_PRELOAD="/lib/pa20_64/libpthread.1" a.out
Success
# a.out correctly lists -lpthread for a threaded application
$ ld -b -o lib1.sl lib1.o
$ ld -b -o lib2.sl -lpthread lib2.o
$ cc +DA2.0W thread.c -L. -l1 -l2 -lpthread
$ a.out
Success
$ ldd a.out
lib1.sl => ./lib1.sl
lib2.sl => ./lib2.sl
libpthread.1 => /usr/lib/pa20_64/libpthread.1
libc.2 => /usr/lib/pa20_64/libc.2
libpthread.1 => /lib/pa20_64/libpthread.1
libdl.1 => /usr/lib/pa20_64/libdl.1
If the link line of your shared library contains -lc to explicitly link in libc, remove -lc. Otherwise, shared libraries may be referencing libc.2 while the a.out may reference some older (archived) libc version. Thus the application will actually be using two different versions of libc and possibly mixing code. This may cause compatibility problems. Basically, an application or library should ne ver directly link against libc. All programs need to be linked against libc (which the compiler does automatically), so a shared library will always have the interfaces it needs to execute properly without needing to specify -lc on the link line.
Getting Verbose Output with -v
Passing Linker Options from the Compiler Command with -Wl
Changing the Default Library Search Path with -L and LPATH
Changing the Default Shared Library Binding with -B
Choosing Archive or Shared Libraries with -a
Dynamic Linking with -A and -R
Exporting Symbols from main with -E
Getting Verbose Output with -v
Improving Shared Library Performance with -B symbolic
Moving Libraries after Linking with +b
Moving Libraries After Linking with +s and SHLIB_PATH
Passing Linker Options from the Compiler Command with -Wl
Passing Linker Options in a file with -c
Passing Linker Options with LDOPTS
Specifying Libraries with -l and l:
Stripping Symbol Table Information from the Output File with -s and -x
Linking Shared Libraries with -dynamic
Linking Archived Libraries with -noshared
Controlling Archive Library Loading with +[no]forceload
Flagging Unsatisfied Symbols with +[no]allowunsats
Hiding Symbols from export with +hideallsymbols
Changing Mapfiles with -k and +nodefaultmap
Changing Mapfiles with -k and +nodefaultmap
Ignoring Dynamic Path Environment Variables with +noenvvar
Linking in 64-bit Mode with +std
Linking in 32-bit Mode Style with +compat
Controlling Output from the Unwind Table with +strip wind
The -Llibpath option to ld augments the default
search path; that is, it causes ld to search the specified libpath
before the default places. The C compiler (cc), the C++ compiler
(CC), the POSIX FORTRAN compiler (fort77), and the HP
Fortran 90 compiler (f90) recognize the -L option and
pass it directly to ld. However, the HP FORTRAN compiler (f77)
and Pascal compiler (pc) do not recognize -L; it must
be passed to ld with the -Wl option.
$f77 prog.f -Wl,-L,/usr/local/lib -llocal
(The f77 compiler searches /opt/fortran/lib and /usr/lib as default directories.)
To make the f90 compiler search /usr/local/lib to find a locally developed library named liblocal,, use this command line:
$f90 prog.f90 -L/usr/local/lib -llocal
(The f90 compiler searches /opt/fortran90/lib and /usr/lib as default directories.)
For the C compiler, use this command line:
$ cc -Aa prog.c -L /usr/local/lib -llocalThe LPATH environment variable provides another way to override the default search path. For details, see Changing the Default Library Search Path with -L and LPATH .
$ pc -v prog.p /opt/pascal/lbin/pascomp prog.p prog.o -O0 LPATH = /usr/lib/pa1.1:/usr/lib:/opt/langtools/lib /usr/ccs/bin/ld /opt/langtools/lib/crt0.o -z prog.o -lcl -lm -lc unlink prog.o
-Wl,arg1 [,arg2]...
where each argn is an option or argument passed to the linker.
For example, to make ld use the archive version of a library instead
of the shared, you must specify -a archive on the ld
command line before the library.
$ cc -Aa mathprog.c -Wl,-a,archive,-lm,-a,defaultThe command for telling the linker to use an archive version of libm is:
$ ld /opt/langtools/lib/crt0.o mathprog.o -a archive -lm \ -a default -lc
$ cc -Aa -o sum_num prog.c Compile using -o option. $ sum_num Run the program. Enter a number to sum: 5 The sum of 1 to 5: 15
For example, if a C program calls library routines in the curses library (libcurses), you must specify -lcurses on the cc command line:
$ cc -Aa -v cursesprog.c -lcurses ... /usr/ccs/bin/ld /opt/langtools/lib/crt0.o -u main \ cursesprog.o -lcurses -lc cc: Entering Link editor.
$ f77 -c func.f Produce .o for func.f. $ ls func.o func.o Verify that func.o was created. $ f77 main.f func.o Compile main.f with func.o $ a.out Run it to verify it worked.
| NOTE | Unless otherwise noted, all examples show 32-bit behavior. |
In 64-bit mode, you must include crt0.o on the link line for all fully archive links (ld -noshared) and in compatibility mode (+compat). You do not need to include the crt0.o startup file on the ld command line for shared bound links. In 64-bit mode, the dynamic loader, dld.sl, does some of the startup duties previously done by crt0.o.
See The crt0.o Startup File, and the
crt0(3)
man page for more information.
If set, LPATH should contain a list of colon-separated directory path names ld should search. For example, to include /usr/local/lib in the search path after the default directories, set LPATH as follows:
$ LPATH=/usr/lib:/usr/local/lib Korn and Bourne shell syntax. $ export LPATH
For example, suppose you have a locally developed version of libc, which resides in the directory /usr/local/lib. To make ld find this version of libc before the default libc, use the -L option as follows:
$ ld /opt/langtools/lib/crt0.o prog.o -L /usr/local/lib -lcMultiple -L options can be specified. For example, to search /usr/contrib/lib and /usr/local/lib before the default places:
$ ld /opt/langtools/lib/crt0.o prog.o -L /usr/contrib/lib \ -L /usr/local/lib -lcIf LPATH is set, then the -L option specifies the directories to search before the directories specified in LPATH.
To add $ORIGIN to the environment variables LD_LIBRARY_PATH or SHLIB_PATH, just place $ORIGIN in the value of these environment variables. To add $ORIGIN to the RUNPATH, use the linker options +b or -L. To add $ORIGIN to the path of a shared library in the shared library list, use the linker option +origin.
+origin -lxor
+origin shared_library_name(You can only use the +origin option before the -l option or the name of a shared library.) The option causes the linker to add $ORIGIN before the shared library name in the shared library list and set the DF_ORIGIN flag for the output module. At runtime, the dynamic loader determines the directory of the parent module (object module, shared library, or executable) and replaces $ORIGIN for that directory name. For example,
$ ld main.o +origin libx.sl -L -lc
| NOTE | While the +origin option is available, the recommended way
to specify $origin is in the embedded path with the +b
option. For example,
$ ld main.o -lc +b $ORIGINIf you use +b,\$ORIGIN; the $ORIGIN only affects libraries that are subject to dynamic path lookup; that is, the library shared_library_name is specified with -l or with no embedded / character. If you use +origin shared_library_name, the library will be located using $ORIGIN, which is recorded in the full library name. |
$ ld -B immediate /opt/langtools/lib/crt0.o prog.o -lc -lm
To use nonfatal binding, specify the -B nonfatal option along with the -B immediate option on the linker command line. The order of the options is not important, nor is the placement of the options on the line. For example, the following ld command uses nonfatal immediate binding:
$ ld /opt/langtools/lib/crt0.o prog.o -B nonfatal \ -B immediate -lm -lcNote that the -B nonfatal modifier does not work with deferred binding because a symbol must have been bound by the time a program actually references or calls it. A program attempting to call or access a nonexistent symbol is a fatal error.
The -B nonfatal modifier alters this behavior slightly: If the dynamic loader cannot find a symbol in the restricted set, it looks in the global symbol set (the symbols defined in all libraries) to resolve the symbol. If it still cannot find the symbol, then a run-time symbol-binding error occurs and the program aborts.
When is -B restricted most useful? Consider a program that creates duplicate symbol definitions by either of these methods:
The program calls shl_definesym to define a symbol that is already defined in a library that was loaded at program startup.
But what happens when, to take advantage of the performance benefits
of deferred binding, the same program is linked with -B deferred?
If a duplicate, more visible symbol definition is created prior
to referencing the symbol, it binds to the more visible definition, and
the program might run incorrectly. In such cases, -B restricted
is useful, because symbols bind the same way as they do with -B immediate,
but actual binding is still deferred.
A benefit of -B symbolic is that it can help improve application
performance and the resulting shared library will be slightly smaller.
The -B symbolic option is useful for applications that make a
lot of calls between procedures inside a shared library and when
these same procedures are called by programs outside of the shared library.
| NOTE | The -B symbolic option applies only to function, but not variable, references in a shared library. |
$ ld -B symbolic -b func1.o func2.o -o lib1.sl
| NOTE | The +e option overrides the -B symbolic option. For
example, you use +esymbol, only symbol is exported
and all other symbols are hidden. Similarly, if you use +eesymbol,
only symbol is exported, but other symbols exported by default remain
visible.
Since all internal calls inside the shared library are resolved inside the shared library, user-supplied modules with the same name are not seen by routines inside the library. For example, you could not replace internal libc.sl malloc() calls with your own version of malloc() if libc.sl was linked with -B symbolic. |
$ ld -B symbolic -b convert.o volume.o -o libunits.slTwo main programs link to the shared library. main1 calls convert_rtn() and main2 calls gal_to_liter().
$ cc -Aa main1.c libunits.sl -o main1 $ cc -Aa main1.c libunits.sl -o main2

For example, you could hide the gal_to_liter symbol as shown:
$ ld -b convert.o -h gal_to_liter volume.o -o libunits.slor export the convert_rtn symbol:
$ ld -b +e convert_rtn convert.o volume.o -o libunits.slIn both cases, main2 will not be able to resolve its reference to gal_to_liter() because only the convert_rtn() symbol is exported as shown below inFigure 5: Symbol hidden in a Shared Library:

As an example, suppose you write an application that will run on a system
on which shared libraries may not be present. Since the program could not
run without the shared library, it would be best to link with the archive
library, resulting in executable code that contains the required library
routines. See also Caution When Mixing
Shared and Archive Libraries .
-a {archive | shared | default | archive_shared | shared_archive}
The different option settings are:
| CAUTION | You should avoid mixing shared libraries and archive libraries in the same application. For more information see Caution When Mixing Shared and Archive Libraries . |
$ ld /opt/langtools/lib/crt0.o prog.o -a archive -lcurses -lm -lc
However, be aware that dynamic linking is incompatible with shared libraries.
That is, a running program cannot be linked to shared libraries
and also use ld -A to dynamically load object modules.
| NOTE | Another reason to use shared library management routines instead of dynamic linking is that dynamic linking may not be supported in a future release. See Linker Compatibility Warnings and Changes in Future Releasesfor additional future changes. |
Topics in this section include:
An Example Program provides an example dynamic linking scenario.
Allocate the required memory and obtain its starting address.
Link the module from the running application.
Get information about the module's text, data, and bss segments from the module's header.
Read the text and data into the allocated space.
Clear (fill with zeros) the bss segment.
Flush the text from the data cache before executing code from the loaded module.
Get the addresses of routines and data that are referenced in the module.
The useful members of the som_exec_auxhdr structure are:
The location of the text and data segments in the file is defined by
the .exec_tfile and .exec_dfile members of the som_exec_auxhdr
structure. The address at which to place the segments in the allocated
memory is defined by the .exec_tmem and .exec_dmem members.
The size of the segments to read in is defined by the .exec_tsize
and .exec_dsize members.
The end of the data segment can be determined by adding the
.exec_dmem
and
.exec_dsize members of the som_exec_auxhdr structure.
The bss's size is defined by the .exec_bsize member.
Use an assembly language routine named flush_cache (see The
flush_cache Function in this chapter). You must assemble this routine
separately (with the as command) and link it with the main program.
If the module contains multiple routines and data that must be accessed from the main program, the main program can use the nlist(3C) function to get their addresses.
Another approach that can be used is to have the entry point routine
return the addresses of required routines and data.
The program allocates space for dynobj.o by calling a function named alloc_load_space (see The alloc_load_space Function later in this chapter). The program then calls a function named dyn_load to dynamically link and load dynobj.o (see The dyn_load Function later in this chapter). Both functions are defined in a file called dynload.c (see dynload.c ).
As a return value, dyn_load provides the address of the entry point in dynobj.o - in this case, the function foo. To get the addresses of the function bar and the variable counter, the program uses the nlist(3C) function.
Source for dynprog shows the C source code for the dynprog program.
Output of dynprog shows the run time output of the dynprog program.
The flush_cache Function provides example source code in assembly language to flush text from the data cache.
CFLAGS = -Aa -D_POSIX_SOURCE dynprog: dynprog.o dynload.o flush_cache.o # Compile line: cc -o dynprog dynprog.o dynload.o flush_cache.o -Wl,-a,archive file1.o: file1.c dynprog.c file2.o: file2.c # Create flush_cache.o: flush_cache.o: as flush_cache.sThis makefile assumes that the following files are found in the current directory:
$ make dynprog file1.o file2.o cc -Aa -D_POSIX_SOURCE -c dynprog.c cc -Aa -D_POSIX_SOURCE -c dynload.c cc -o dynprog dynprog.o dynload.o -Wl,-a,archive cc -Aa -D_POSIX_SOURCE -c file1.c cc -Aa -D_POSIX_SOURCE -c file2.c as -o flush_cache flush_cache.sNote that the line CFLAGS = causes any C files to be compiled in ANSI mode (-Aa) and causes the compiler to search for routines that are defined in the Posix standard (-D_POSIX_SOURCE).
For details on using make refer to make(1).
#include <stdio.h>
#include <nlist.h>
extern void * alloc_load_space(const char * base_prog,
const char * obj_files,
const char * dest_file);
extern void * dyn_load(const char * base_prog,
unsigned int addr,
const char * obj_files,
const char * dest_file,
const char * entry_pt);
const char * base_prog = "dynprog"; /* this executable's name */
const char * obj_files = "file1.o file2.o"; /* .o files to combine */
const char * dest_file = "dynobj.o"; /* .o file to load */
const char * entry_pt = "foo"; /* define entry pt name */
void glorp (const char *); /* prototype for local function */
void (* foo_ptr) (); /* pointer to entry point foo */
void (* bar_ptr) (); /* pointer to function bar */
int * counter_ptr; /* pointer to variable counter [file2.c] */
main()
{
unsigned int addr; /* address at which to load dynobj.o */
struct nlist nl[3]; /* nlist struct to retrieve addresse */
/*
STEP 1: Allocate space for module:
*/
addr = (unsigned int) alloc_load_space(base_prog,
obj_files, dest_file);
/*
STEP 2: Load the file at the address, and get address of entry point:
*/
foo_ptr = (void (*)()) dyn_load(base_prog, addr, obj_files,
dest_file, entry_pt);
/*
STEP 3: Get the addresses of all desired routines using nlist(3C):
*/
nl[0].n_name = "bar";
nl[1].n_name = "counter";
nl[2].n_name = NULL;
if (nlist(dest_file, nl)) {
fprintf(stderr, "error obtaining namelist for %s\n", dest_file);
exit(1);
}
/*
* Assign the addresses to meaningful variable names:
*/
bar_ptr = (void (*)()) nl[0].n_value;
counter_ptr = (int *) nl[1].n_value;
/*
* Now you can call the routines and modify the variables:
*/
glorp("main");
(*foo_ptr) ();
(*bar_ptr) ();
(*counter_ptr) ++;
printf("counter = %d\n", *counter_ptr);
}
void glorp(const char * from)
{
printf("glorp called from %s\n", from);
}
/****************************************************************
* file1.c - Contains routines foo() and bar().
****************************************************************/
extern int * counter_ptr; /* defined in dynprog.c */
extern int counter; /* defined in file2.c */
extern void glorp(const char * from); /* defined in dynprog.c */
void foo()
{
glorp("foo");
(*counter_ptr) ++; /* update counter indirectly with global pointer */
}
void bar()
{
glorp("bar");
counter ++; /* update counter directly */
}
/****************************************************************
* file2.c - Global counter variable referenced by dynprog.c
* and file1.c.
****************************************************************/
int counter = 0;
glorp called from main glorp called from foo glorp called from bar counter = 3
#include <stdio.h> #include <stdlib.h> #include <nlist.h> # include <filehdr.h> # include <aouthdr.h> # define PAGE_SIZE 4096 /* memory page size */
void * alloc_load_space(const char * base_prog, const char * obj_files, const char * dest_file)
The alloc_load_space function allocates only the required amount of space. To determine how much memory is required, alloc_load_space performs these steps:
Get text, data, and bss segment location and size information to determine how much space to allocate.
Return a pointer to the space. (The address of the space is adjusted to begin on a memory page boundary - that is, a 4096-byte boundary.)
void * alloc_load_space(const char * base_prog,
const char * obj_files,
const char * dest_file)
{
char cmd_buf[256]; /* linker command line */
int ret_val; /* value returned by various lib calls */
size_t space; /* size of space to allocate for module */
size_t addr; /* address of allocated space */
size_t bss_size; /* size of bss (uninitialized data) */
FILE * destfp; /* file pointer for dest_file */
struct som_exec_auxhdr aux_hdr; /* file header */
unsigned int tdb_size; /* size of text, data, and bss combined */
/* ---------------------------------------------------------------
* STEP 1: Pre-link the destination module so we can get its size:
*/
sprintf(cmd_buf, "/bin/ld -a archive -R80000 -A %s -N %s -o %s -lc",
base_prog, obj_files, dest_file);
if (ret_val = system(cmd_buf)) {
fprintf(stderr, "link failed: %s\n", cmd_buf);
exit(ret_val);
}
/* ---------------------------------------------------------------
* STEP 2: Get the size of the module's text, data, and bss segments
* from the auxiliary header for dest_file; add them together to
* determine size:
*/
if ((destfp = fopen(dest_file, "r")) == NULL) {
fprintf(stderr, "error opening %s\n", dest_file);
exit(1);
}
/*
* Must seek past SOM "header" to get to the desired
* "som_exec_auxhdr":
*/
if (fseek(destfp, sizeof(struct header), 0)) {
fprintf(stderr, "error seeking past header in %s\n", dest_file);
exit(1);
}
if (fread(&aux_hdr, sizeof(aux_hdr), 1, destfp) <= 0) {
fprintf(stderr, "error reading som aux header from %s\n", dest_file);
exit(1);
}
/* allow for page-alignment of data segment */
space = aux_hdr.exec_tsize + aux_hdr.exec_dsize
+ aux_hdr.exec_bsize + 2 * PAGE_SIZE;
fclose(destfp); /* done reading from module file */
/* ---------------------------------------------------------------
* STEP 3: Call malloc(3C) to allocate the required memory and get
* its address; then return a pointer to the space:
*/
addr = (size_t) malloc(space);
/*
* Make sure allocated area is on page-aligned address:
*/
if (addr % PAGE_SIZE != 0) addr += PAGE_SIZE - (addr % PAGE_SIZE);
return((void *) addr);
}
void * dyn_load(const char * base_prog, unsigned int addr, const char * obj_files, const char * dest_file, const char * entry_pt)The base_prog, obj_files, and dest_file parameters are the same parameters supplied to alloc_load_space. The addr parameter is the address returned by alloc_load_space, and the entry_pt parameter specifies a symbol name that you want to act as the entry point in the module.
To dynamically link and load dest_file into base_prog, the dyn_load function performs these steps:
Open dest_file and get its header information on the text, data, and bss segments. Read this information into a som_exec_auxhdr structure, which starts immediately after a header structure.
Read the text and data segments into the area allocated by alloc_load_space. (The text and data segments are read separately.)
Initialize (fill with zeros) the bss, which starts immediately after the data segment.
Flush text from the data cache before execution, using the flush_cache routine. (See The flush_cache Function later in this chapter.)
Return a pointer to the entry point, specified by the -e option in Step 1.
void * dyn_load(const char * base_prog,
unsigned int addr,
const char * obj_files,
const char * dest_file,
const char * entry_pt)
{
char cmd_buf[256]; /* buffer holding linker command */
int ret_val; /* holds return value of library calls */
FILE * destfp; /* file pointer for destination file */
unsigned int bss_start; /* start address of bss in VM */
unsigned int bss_size; /* size of bss */
unsigned int entry_pt_addr; /* address of entry point */
struct som_exec_auxhdr aux_hdr; /* som file auxiliary header */
unsigned int tdb_size; /* size of text, data, and bss combined*/
/* -----------------------------------------------------------------
* STEP 1: Dynamically link the module to be loaded:
*/
sprintf(cmd_buf,
"/bin/ld -a archive -A %s -R %x -N %s -o %s -lc -e %s",
base_prog, addr, obj_files, dest_file, entry_pt);
if (ret_val = system(cmd_buf))
{
fprintf(stderr, "link command failed: %s\n", cmd_buf);
exit(ret_val);
}
/* -----------------------------------------------------------------
* STEP 2: Open dest_file. Read its auxiliary header for text, data,
* and bss info:
*/
if ((destfp = fopen(dest_file, "r")) == NULL)
{
fprintf(stderr, "error opening %s for loading\n", dest_file);
exit(1);
}
/*
* Get auxiliary header information from "som_exec_auxhdr" struct,
* which is after SOM header.
*/
if (fseek(destfp, sizeof(struct header), 0))
{
fprintf(stderr, "error seeking past header in %s\n", dest_file);
exit(1);
}
if (fread(&aux_hdr, sizeof(aux_hdr), 1, destfp) <= 0)
{
fprintf(stderr, "error reading som aux header from %s\n", dest_file);
exit(1);
}
/* -----------------------------------------------------------------
* STEP 3: Read the text and data segments into the buffer area:
*/
/*
* Read text and data separately. First load the text:
*/
if (fseek(destfp, aux_hdr.exec_tfile, 0))
{
fprintf(stderr, "error seeking start of text in %s\n", dest_file);
exit(1);
}
if ((fread(aux_hdr.exec_tmem, aux_hdr.exec_tsize, 1, destfp)) <= 0)
{
fprintf(stderr, "error reading text from %s\n", dest_file);
exit(1);
}
/*
* Now load the data, if any:
*/
if (aux_hdr.exec_dsize) {
if (fseek(destfp, aux_hdr.exec_dfile, 0))
{
fprintf(stderr, "error seeking start of data in %s\n", dest_file);
exit(1);
}
if ((fread(aux_hdr.exec_dmem, aux_hdr.exec_dsize, 1, destfp))<= 0)
{
fprintf(stderr, "error reading data from %s\n", dest_file);
exit(1);
}
}
fclose(destfp); /* done reading from module file */
/* -----------------------------------------------------------------
* STEP 4: Zero out the bss (uninitialized data segment):
*/
bss_start = aux_hdr.exec_dmem + aux_hdr.exec_dsize;
bss_size = aux_hdr.exec_bsize;
memset(bss_start, 0, bss_size);
/* -----------------------------------------------------------------
* STEP 5: Flush the text from the data cache before execution:
*/
/*
* The flush_cache routine must know the exact size of the
* text, data, and bss, computed as follows:
* Size = (Data Addr - Text Addr) + Data Size + BSS Size
* where (Data Addr - Text Addr) = Text Size + alignment between
* Text and Data.
*/
tdb_size = (aux_hdr.exec_dmem - aux_hdr.exec_tmem) +
aux_hdr.exec_dsize + aux_hdr.exec_bsize;
flush_cache(addr, tdb_size);
/* -----------------------------------------------------------------
* STEP 6: Return a pointer to the entry point specified by -e:
*/
entry_pt_addr = (unsigned int) aux_hdr.exec_entry;
return ((void *) entry_pt_addr);
}
; flush_cache.s ; ; Routine to flush and synchronize data and instruction caches ; for dynamic loading ; ; Copyright Hewlett-Packard Co. 1985,1991, 1995 ; ; All HP VARs and HP customers have a non-exclusive royalty-free ; license to copy and use this flush_cashe() routine in source ; code and/or object code. .code ; flush_cache(addr, len) - executes FDC and FIC instructions for ; every cache line in the text region given by starting addr and ; len. When done, it executes a SYNC instruction and then enough ; NOPs to assure the cache has been flushed. ; ; Assumption: Cache line size is at least 16 bytes. Seven NOPs ; is enough to assure cache has been flushed. This routine is ; called to flush the cache for just-loaded dynamically linked ; code which will be executed from SR5 (data) space. ; %arg0=GR26, %arg1=GR25, %arg2=GR24, %arg3=GR23, %sr0=SR0. ; loop1 flushes data cache. arg0 holds address. arg1 holds offset. ; SR=0 means that SID of data area is used for fdc. ; loop2 flushes inst cache. arg2 holds address. arg3 holds offset. ; SR=sr0 means that SID of data area is used for fic. ; fdc x(0,y) -> 0 means use SID of data area. ; fic x(%sr0,y) -> SR0 means use SR0 SID (which is set to data area). .proc .callinfo .export flush_cache,entry flush_cache .enter ldsid (0,%arg0),%r1 ; Extract SID (SR5) from address mtsp %r1,%sr0 ; SID -> SR0 ldo -1(%arg1),%arg1 ; offset = length -1 copy %arg0,%arg2 ; Copy address from GR26 to GR24 copy %arg1,%arg3 ; Copy offset from GR25 to GR23 fdc %arg1(0,%arg0) ; Flush data cache @SID.address+offset loop1 addib,>,n -16,%arg1,loop1 ; Decrement offset by cache line size fdc %arg1(0,%arg0) ; Flush data cache @SID.address+offset ; flush first word at addr, to handle arbitrary cache line boundary fdc 0(0,%arg0) sync fic %arg3(%sr0,%arg2) ; Flush inst cache @SID.address+offset loop2 addib,>,n -16,%arg3,loop2 ; Decrement offset by cache line size fic %arg3(%sr0,%arg2) ; Flush inst cache @SID.address+offset ; flush first word at addr, to handle arbitrary cache line boundary fic 0(%sr0,%arg2) sync nop nop nop nop nop nop nop .leave .procend .end
The syntax of the +e option is:
$ nm -p sem.o 0000000000 U $global$ 1073741824 d $THIS_DATA$ 1073741864 b $THIS_BSS$ 0000000004 cS sem_val 0000000000 T check_sem_val 0000000036 T foo 0000000000 U printf 0000000088 T bar 0000000140 T semIn this example, check_sem_val, foo, bar, and sem are all global definitions. To create a shared library where check_sem_val is a hidden, local definition, you could use either of the following commands:
$ ld -b -h check_sem_val sem.o One -h option. $ ld -b +e foo +e bar +e sem sem.o Three +e options.In contrast, suppose you want to export only the check_sem_val symbol. Either of the following commands would work:
$ ld -b -h foo -h bar -h sem sem.o Three -h options. $ ld -b +e check_sem_val sem.o One +e option.
You should not combine -h and +e options on the same command line. For instance, suppose you specify +e sem. This would export the symbol sem and hide all other symbols. Any additional -h options would be unnecessary. If both -h and +e are used on the same symbol, the -h overrides the +e option.
The linker command line could get quite lengthy and difficult to read if several such options were specified. And in fact, you could exceed the maximum HP-UX command line length if you specify too many options. To get around this, use ld linker option files, described under Passing Linker Options in a file with -c . You can specify any number of -h or +e options in this file.
You can use -h or +e options when building a shared
library (with -b) and when linking to create an a.out
file. When combining .o files with -r, you can still
use only the -h option.
The +e option allows you to be more selective about which symbols
are exported, resulting in better performance. For details on +e,
see Exporting Symbols with +e.
You can use -h option when building a shared library (with -b) and when linking to create an a.out file. When combining .o files with -r, you can use the -h option.
The syntax of the -h option is:
-h symbol
The -h option hides symbol. Any other global symbols
remain exported unless hidden with -h.
$ nm -p sem.o 0000000000 U $global$ 1073741824 d $THIS_DATA$ 1073741864 b $THIS_BSS$ 0000000004 cS sem_val 0000000000 T check_sem_val 0000000036 T foo 0000000000 U printf 0000000088 T bar 0000000140 T semIn this example, check_sem_val, foo, bar, and sem are all global definitions. To create a shared library where check_sem_val is a hidden, local definition, you could do the following:
$ ld -b -h check_sem_val sem.o
The linker command line could get quite lengthy and difficult to read
if several such options were specified. And in fact, you could exceed the
maximum HP-UX command line length if you specify too many options. To get
around this, use ld linker option files, described under Passing
Linker Options in a file with -c . You can specify any number of -h
or +e options in this file.
It ensures that the definition can only be accessed by other routines in the same library. When linking with other object modules or libraries, the definition will be hidden from them.
When linking with other libraries (to create an executable), it ensures that the library will use the local definition of a routine rather than a definition that occurs earlier in the link order.
When using +e, be sure to export any data symbols defined in the shared library that will be used by another shared library or the program, even if these other files have definitions of the data symbols. Otherwise, your shared library will use its own private copy of the global data, and another library or the program file will not see any change.
One example of a data symbol that should almost always be exported from
a shared library is errno.
errno is defined in every
shared library and program; if this definition is hidden, the value of
errno
will not be shared outside of the library.
$ ld /usr/ccs/lib/crt0.o prog.o -lm -lcWith libraries that are explicitly loaded with shl_load, this behavior may not always be sufficient because the linker does not search explicitly loaded libraries (they aren't even present on the command line). You can work around this using the -E or +e linker option.
As mentioned previously in the section Exporting Symbols from main with -E , the -E option forces the export of all symbols from the program, regardless of whether they are referenced by shared libraries on the linker command line. The +e option allows you to be more selective in what symbols are exported. You can use +e to limit the exported symbols to only those symbols you want to be visible.
For example, the following ld command exports the symbols main and foo. The symbol main is referenced by libc. The symbol foo is referenced at run time by an explicitly loaded library not specified at link time:
$ ld /usr/ccs/lib/crt0.o prog.o +e main +e foo -lm -lc -ldldWhen using +e, be sure to export any data symbols defined in the program that may also be defined in explicitly loaded libraries. If a data symbol that a shared library imports is not exported from the program file, the program uses its own copy while the shared library uses a different copy if a definition exists outside the program file. In such cases, a shared library might update a global variable needed by the program, but the program would never see the change because it would be referencing its own copy.
One example of a data symbol that should almost always be exported from
a program is errno.
errno is defined in every shared
library and program; if this definition is hidden, the value of errno
will not be shared outside of the program in which it is hidden.
Note that dynamic path list search works only for libraries specified
with -l on the linker command line (for example, -lfoo).
It won't work for libraries whose full path name is specified (for example,
/usr/contrib/lib/libfoo.sl).
However, it can be enabled for such libraries with the -l option
to the chatr command (see
Changing a
Program's Attributes with chatr(1)
).
+b path_listwhere path_list is the list of directories you want the dynamic loader to search at run time. For example, the following linker command causes the path .:/app/lib:: to be stored in the executable. At run time, the dynamic loader would search for libfoo.sl, libm.sl, and libc.sl in the current working directory (.), the directory /app/lib, and lastly in the location in which the libraries were found at link time (::):
$ ld /opt/langtools/lib/crt0.o +b .:/app/lib:: prog.o -lfoo \ -lm -lcIf path_list is only a single colon, the linker constructs a path list consisting of all the directories specified by -L, followed by all the directories specified by the LPATH environment variable. For instance, the following linker command records the path list as /app/lib:/tmp:
$ LPATH=/tmp ; export LPATH $ ld /opt/langtools/lib/crt0.o +b : -L/app/lib prog.o -lfoo \ -lm -lc
Absolute and relative path names are allowed. Relative paths are searched relative to the program's current working directory at run time.
Remember that a shared library's full path name is stored in the executable. When searching for a library in an absolute or relative path at run time, the dynamic loader uses only the basename of the library path name stored in the executable. For instance, if a program is linked with /usr/local/lib/libfoo.sl, and the directory path list contains /apps/lib:xyz, the dynamic loader searches for /apps/lib/libfoo.sl, then ./xyz/libfoo.sl.
The full library path name stored in the executable is referred to as the default library path. To cause the dynamic loader to search for the library in the default location, use a null directory path (). When the loader comes to a null directory path, it uses the default shared library path stored in the executable. For instance, if the directory path list in the previous example were /apps/lib::xyz, the dynamic loader would search for /apps/lib/libfoo.sl, /usr/local/lib/libfoo.sl, then ./xyz/libfoo.sl.
If the dynamic loader cannot find a required library in any of the directories specified in the path list, it searches for the library in the default location () recorded by the linker.
Note that dynamic path list search works only for libraries specified
with -l on the linker command line (for example, -lfoo).
It won't work for libraries whose full path name is specified (for example,
/usr/contrib/lib/libfoo.sl).
However, it can be enabled for such libraries with the -l option
to the chatr command (see
Changing a
Program's Attributes with chatr(1)
).
Moving Libraries after Linking with +b provides another way to move libraries.
$ ld -o libmods.sl -b -c eopts mod*.o $ cat eopts Display the file. +e foo +e bar +e reverse_tree +e preorder_traversal +e shift_reduce_parse . . .Note that the linker ignores lines in that option file that begin with a pound sign (#). You can use such lines as comment lines or to temporarily disable certain linker options in the file. For instance, the following linker option file for an application contains a disabled -O option:
# Exporting only the "compress" symbol resulted # in better run-time performance: +e compress # When the program is debugged, remove the pound sign # from the following optimization option: # -O
$ LDOPTS="-v -t" Korn and Bourne shell syntax. $ export LDOPTSThereafter, the following commands would be equivalent:
$ ld /opt/langtools/lib/crt0.o -u main prog.o -lc $ ld -v -t /opt/langtools/lib/crt0.o -u main prog.o -lc
The x argument corresponds to the identifying portion of the library path name - the part following lib and preceding the suffix .a or .sl. For example, for the libm.sl or libm.a library, x is the letter m:
$ cc -Aa mathprog.c -lmThe linker searches libraries in the order in which they are specified on the command line (that is, the link order). In addition, libraries specified with -l are searched before the libraries that the compiler links by default.
The advantage of using this option is that it allows you to specify an archive or shared library explicitly without having to change the state of the -a option. (See also Caution When Mixing Shared and Archive Libraries .)
For instance, suppose you use the LDOPTS environment variable
(see Passing Linker Options with
LDOPTS ) to set the -a option that you want to use by default
when linking. And depending on what environment you are building an application
for, you might set LDOPTS to
-a archive or
-a shared.
You can use -l: to ensure that the linker will always link with
a particular library regardless of the setting of the -a option
in the LDOPTS variable.
$ ld /opt/langtools/lib/crt0.o -u main prog.o -L/usr/mylibs \ -l:libfoo.a -l:libc.a -l:libm.a
Standard mode, with the +std option, set by default in 64-bit mode, to create a link and load operation in 64-bit style. This mode uses the new behaviors and features of the 64-bit linker.
When you use the +compat option, the linker:
Lists all dependent shared libraries in a DT_HP_NEEDED entry the dynamic table using the 32 bit-style shared library naming conventions. These dependent libraries are recorded as compatibility mode libraries even if they are really created as standard mode dependent libraries.
If an error occurs during the link, the linker creates an a.out without the executable permission bits set.
Does not use embedded paths at link time to find dependent libraries.
Considers the order of ld +b and +s.
To get the default RPATH, you must specify ld +b. This instructs the linker to construct a default RPATH consisting of the -L directories and LPATH.
+s first means the dynamic loader looks at the SHLIB_PATH environment variable first when searching for dependent shared libraries.
You must specify ld +s to force the dynamic loader to use SHLIB_PATH to search for shared libraries at runtime.
Uses SHLIB_PATH only if you specify ld +s (or chatr +s) for compatibility-mode shared libraries.
Allows RPATH inheritance from ancestors to children when searching for dependent compatibility-mode shared libraries specified with ld -l. This is only allowed in an a.out that was linked with +compat. If the a.out was linked +std, no library (even a compatibility mode shared library) uses embedded RPATH inheritance.
Allows dynamic path searching on shared libraries loaded by shl_load routines, if the DYNAMIC_FLAG is passed to shl_load().
Does a depth-first search of all compatibility-mode dependent libraries.
Looks at RPATH or SHLIB_PATH first, depending on the ld +b/+s ordering for all ld -l dependent shared libraries. The dynamic loader looks at whichever has second precedence next, and then looks for the shared library as specified in the dynamic load entry.
Looks for the dynamic table entry as if the dynamic path bit is not set.
This default may change in future releases.
When you use +std, the linker:
All dependent shared libraries are output in the dynamic table in a DT_NEEDED entry. These dependent shared libraries are recorded as standard mode shared libraries.
ld +b and +s ordering is ignored. ld +s is on by default.
If an error occurs during the link, the linker does not generate an a.out file.
Uses de facto standard internal name processing for dependent shared libraries.
Uses embedded RPATHs at link time to find dependent shared libraries.
If you do not specify ld +b, the linker uses a default RPATH consisting of the -L directories, LPATH, and the default directories /usr/lib/pa20_64:/usr/ccs/lib/pa20_64.
Looks for the shared library as specified in the DT_NEEDED dynamic table entry if it contains a path.
Looks at LD_LIBRARY_PATH and SHLIB_PATH environment variables at runtime by default when doing dynamic path searching for standard-mode shared libraries.
Does not allow RPATH inheritance from ancestors to children (only allowed from parent to child).
Does a breadth-first search for all standard-mode dependent shared libraries.
Looks at the environment variables first, followed by RPATH, and the default directories by default when doing dynamic path searching for standard-mode dependentshared libraries.
This option is on by default in standard mode.
In the following example, the linker only looks for shared libraries:
$ld main.o -dynamic -L. -lbar -lcIf you specified an archive library, the linker links it in, but the resulting executable is still a dynamically linked executable. This is true even if the linker finds no shared libraries at link time.
| NOTE | You cannot link in shared libraries if you specify this option. |
In the following example, the linker only looks for /usr/lib/pa20_64/libfoo.a and /usr/lib/pa20_64/libc.a:
ld crt0.o main.o -noshared -L. -lfoo -lcIf you specify a shared library with this option, the linker emits an error message.
ld: The shared library "libbar.sl" cannot be processed in a static link. Fatal error.
+noforceload is the default on both 32-bit and 64-bit modes.
In the following example, main() references foo(), which is a module in mylib.a. foo() doesn't reference any other module in mylib.a and libc.a. If mylib.a contains foo.o and bar.o, then only foo.o is linked in.
ld crt0.o main.o +vtype libraries mylib.a -lc
... Selecting liba.a[foo.o] to resolve foo
ld crt0.o main.o +forceload mylib.a -lc +vtype libraries
... Selecting liba.a[foo.o] to forcibly load Selecting liba.a[bar.o] to forcibly load
Use the +noallowunsat option to instruct the linker to flag as an error any unsatisfied symbol in the resulting output file. The linker still creates a.out, but the file does not have any execute permission bits set. This is the default for program files (same behavior as in 32-bit mode).
For example, where main() references functions foo() and bar(). bar() resides in libbar.sl. foo() resides in libfoo.sl
ld main.o +allowunsats -L. -lbar -lc
ld: (warning) Unsatisfied symbol "foo". 1 warning.+allowunsats still causes the linker to emit a warning message and output a.out. If you do not specify the option and the linker finds an unsatisfied symbol, the linker emits an error message and an unexecutable a.out only if linking with +compat set.
ld main.o -L. -lbar -lc
ld: Unsatisfied symbol "foo". 1 error.
In the following example, main() exports func() and test(). Using +hideallsymbols, the linker does not export these two routines in the a.out.
ld main.o +hideallsymbols -L. -lfoo -lc
elfdump -t a.out
a.out: ... .symtab index Type Bind Other Sect Value Size Name 1 FUNC LOCL 0 0xb 0x4000000000001104 0 test ... 10 FUNC LOCL 0 0xb 0x4000000000001200 0 func
Use the -k filename option to provide a memory map. The linker uses the file specified by filename as the output file memory map.
The +nodefaultmap option used with -k option prevents
the linker from concatenating the default memory map to the map provided
by filename. If you specify +nodefaultmap, the linker does
not append the default mapfile to your mapfile. If you do not specify +nodefaultmap
with -k, the linker appends the output file to the default mapfile.
| NOTE | In most cases, the linker produces a correct executable without the use of the mapfile option. The mapfile option is an advanced feature of the linker toolset intended for systems programming use, not application programming use. When using the mapfile option, you can create executable files that do not execute. |
For more information on mapfiles and examples using these options, see
Using
Mapfiles.
For example, if libbar.sl has dependent library libfee.sl that is ./ at link time, but is moved to /tmp by runtime:
ld main.o -L. -lbar -lc export LD_LIBRARY_PATH=/tmp mv libbar.sl /tmp a.out called bar() called fee() mv /tmp/libbar.sl ./ ld main.o +noenvvar -L. -lbar -lc mv libbar.sl /tmp a.out dld.sl: Unable to find library "libbar.sl"
ld -b foo.o -o libfoo.sl +stripunwind elfdump -U libfoo.sl
libfoo.sl:
Dump information about each object file loaded.
ld main.o +vtype files -L. -lfile1 -lfile2 -lc Loading main.o: Loading ./libfile1.sl: Loading ./libfile2.sl: Loading /usr/lib/pa20_64/libc.2: Loading /usr/lib/pa20_64/libdl.1:
Dump information about libraries searched.
ld main.o +vtype libraries -L. -lfile1 -lfile2 -lc Searching /usr/lib/pa20_64/libc.a: Selecting /usr/lib/pa20_64/libc.a[printf.o] to resolve printf Selecting /usr/lib/pa20_64/libc.a[data.o] to resolve __iob ...
Dump information about each section added to the output file.
ld main.o +vtype sections -L. -lfile1 -lfile2 -lc main.o: section .text PROG_BITS AX 116 8 added to text segment section .PARISC.unwind UNWIND 16 4 added to text segment section .data PROG_BITS AW 96 8 added to data segment
Dump information about global symbols referenced/defined from/in the input files.
ld main.o +vtype symbols -L. -lfile1 -lfile2 -lc main.o: main is DEFINED GLOBAL FUNC printf is UNDEF GLOBAL FUNC lib1_func is UNDEF GLOBAL FUNC lib2_func is UNDEF GLOBAL FUNC ./libfile1.s: printf is UNDEF GLOBAL FUNC _DYNAMIC is DEFINED GLOBAL OBJECT lib1_func is DEFINED GLOBAL FUNC ...
cc -c +DA2.0W 1.c || exit 1
/usr/ccs/bin/ld /opt/langtools/lib/pa20_64/crt0.o +vtype heap 1.o -lc
===> results in
Heap: initial break value (heap begin) = 1654 KB
Heap: main entry break value = 1672 KB
Heap: before begin pass1 break value = 3808 KB
Heap: before pass1 break value = 4100 KB
Heap: before end pass1 break value = 6304 KB
Heap: end pass1 break value = 6368 KB
Heap: end pass2 break value = 6440 KB
Heap: main exit break value = 6440 KB, max heap usage = 4786 KB
Dump all of the above. Same as -v.
ld main.o +vtype all -L. -lfile1 -lfile2 -lc Loading main.o: main.o: main is DEFINED GLOBAL FUNC printf is UNDEF GLOBAL FUNC lib1_func is UNDEF GLOBAL FUNC lib2_func is UNDEF GLOBAL FUNC main.o:section .text PROG_BITS AX 116 8 added to text segment section .PARISC.unwind UNWIND 16 4 added to text segment section .data PROG_BITS AW 96 8 added to data segment Loading ./libfile1.sl: ./libfile1.sl: ...
In 64-bit mode, you must include crt0.o on the link line for all fully archive links (ld -noshared) and in compatibility mode (+compat). You do not need to include the crt0.o startup file on the ld command line for shared bound links. In 64-bit mode, the dynamic loader, dld.sl, does some of the startup duties previously done by crt0.o.
See The crt0.o Startup File, and
crt0(3) manual page for more information.
Link-time compatibility warnings include the following:
Dynamic linking with -A - If you do dynamic linking with -A, you should migrate to using the shared library management routines described in Shared Library Management Routines . These routines are also described in the sh_load(3X) and dl*(3X) man page.
The 64-bit mode linker does not support the -A option.
Procedure call parameter and return type checking (which can be specified with -C) - The 32-bit linker checks the number and types of parameters in procedure calls across object modules. In a future release, you should expect HP compilers to perform cross-module type checking, instead of the linker. This impacts HP Pascal and HP Fortran programs.
The 64-bit mode linker does not support the -C option.
Duplicate names found for code and data symbols - The 32-bit linker can create a program that has a code and data symbol with the same name. In the HP-UX 11.00 release, the 64-bit mode linker adopts a single name space for all symbols. This means that code and data symbols cannot share the same name. Renaming the conflicting symbols solves this problem.
Unsatisfied symbols found when linking to archive libraries - If you specify the -v option with the +vallcompatwarnings option and link to archive libraries, you may see new warnings. For an example, see Linking to Archive Libraries with Unsatisfied Symbols in this chapter.
Versioning within a shared library - If you do versioning within a shared library with the HP_SHLIB_VERSION (C and C++); or the SHLIB_VERSION (Fortran and Pascal) compiler directive, you should migrate to the industry standard and faster-performing library-level versioning. See Library-Level Versioning to learn how to do library-level versioning. In the HP-UX 11.00 release, the 64-bit mode linker does not support internal library versioning.
ld: (Warning) The file library.a(x.o) has not been fully checked for unsatisfied symbols. This behavior may change in future releases.The 32-bit mode linker does not include an object from an archive library simply because it contains a needed definition of an uninitialized global data symbol. Instead, it changes the existing undefined symbol to an uninitialized data symbol. This symbol has the same size as the definition of the global variable in the library.
For example, given these source files:
archive.c
int foo; /* definition of uninitialized
global data symbol */
void func()
{
unsat();
}
main.c
extern int foo; /* declaration of global data symbol */
main()
{
printf ("\tfoo = %d\n", foo);
}
If these files are compiled and linked as:
cc -c main.c cc -c archive.c ar rv liba.a archive.o ld /opt/langtools/lib/crt0.o -v \ +vallcompatwarnings main.o liba.a -lc -o testThe linker issues the following warning:
ld: (Warning) The file liba.a(archive.o) has not been fully checked for unsatisfied symbols. This behavior may change in future releases.due to an unresolved symbol for unsat().
In the HP-UX 11.00 release, the linker includes the archive library
object definition rather than fixing up the external reference.
The following table lists the linker toolset.
| Tool | Mode | Description |
|---|---|---|
| chatr | 32-bit/64-bit | Displays or modifies the internal attributes of an object file. See Changing a Program's Attributes with chatr(1) . |
| elfdump | 64-bit | Displays the contents of an ELF object file. See Viewing the Contents of an Object File with elfdump(1). |
| fastbind | 32-bit/64-bit | Improves startup time of programs that use shared libraries. See Improving Program Start-up with fastbind(1). |
| ldd | 32-bit/64-bit | Lists dynamic dependencies of executable files and shared libraries. Viewing library dependencies with ldd(1). |
| lorder | 32-bit/64-bit | Finds ordering relationship for an object library. SeeFinding Object Library Ordering Relationships with lorder(1). |
| nm | 32-bit/64-bit | Displays the symbol table of an object file. See Viewing Symbols in an Object file with nm(1) . |
| odump | 32-bit | Displays the contents of a SOM object file. See the odump(1) man page. |
| size | 32-bit/64-bit | Prints sizes of object file elements. See Viewing the Size of Object File Elements with size(1). |
| strip | 32-bit/64-bit | Strips symbol and debugging information from an object file, executable, or archive library. See Reducing Storage Space with strip(1). |
nm provides three general output formats: the default (neither
-p
nor -P specified), -p, and -P. See the nm(1)
man page for a detailed description of the output formats.
| To | Use This Option |
|---|---|
| Prefix each output line with the name of the object file or archive, file. Equivalent to -r. | -A |
| 64-bit mode ELF files only: Demangle C++ names before printing them. | -C |
| Display the value and size of a symbol in decimal. This is the default for the default format or the -p format. Equivalent to -t d. | -d |
| Display only external and static symbols. This option is ignored (see -f). | -e |
| Display full output. This option is in force by default. | -f |
| Display only external (global) symbol information. | -g |
| Do not display the output header data. | -h |
| Distinguish between weak and global symbols by appending * to the key letter of weak symbols. Only takes effect with -p and/or -P. | -l |
| Sort symbols by name, in ascending collation order, before they are printed. This is the default. To turn off this option, use -N. | -n |
| Display symbols in the order in which they appear in the symbol table. | -N |
| Display the value and size of a symbol in octal. Equivalent to -t o. | -o |
Display information in a blank-separated output format. Each symbol
name is preceded by its value (blanks if undefined) and one of the letters
absolute bss symbol common symbol data symbol section region tstorage symbol (32-bit mode SOM files only) If the symbol is local (nonexternal), the type letter is in lowercase. If the symbol is a secondary definition, the type letter is followed by the letter S. Note that -p is not compatible with -P. text symbol undefined |
-p |
| Display information in a portable output format as specified below, to standard output. Note that -p is not compatible with -P. | -P |
| 32-bit mode SOM files only: Silence some warning messages. | -q |
| Prefix each output line with the name of the object file or archive, file. Equivalent to -A. | -r |
| 64-bit mode ELF files only: Print the section index instead of the section name. | -s |
Display each numeric value in the specified format. format can
be one of:
Display the value and size of a symbol in decimal. This is the default for the default format or the -p format. Equivalent to -d. Display the value and size of a symbol in octal. Equivalent to -o. Display the value and size of a symbol in hexadecimal. This is the default for the -P format. Equivalent to -x. |
-t format |
| 32-bit mode SOM files only: Truncate every name that would otherwise
overflow its column and place an asterisk as the last character in the
displayed name to mark it as truncated. If -A or
-r is
also specified, the file prefix is truncated first.
By default, nm prints the entire name of the symbols listed. Since object files can have symbol names with an arbitrary number of characters, a name that is longer than the width of the column set aside for names overflows its column, forcing every column after the name to be misaligned. |
-T |
| Display undefined symbols only. | -u |
| Print the usage menu. | -U |
| Sort symbols by value before they are printed. | -v |
| Display the executing version of the nm command on standard error. | -V |
| Displays the value and size of a symbol in hexadecimal. this is the default for the -P format. Equivalent to -t x. | -x |
"nm -rup *.o | grep leap"
Display which object files have a definition for the text symbol "leap":
nm -rp *.o | awk '{ if\ ($3 == " "T" " && $4 == " "leap" ") { print $0 } }'"
To view the symbols defined in an object file, use the nm command. The following 32-bit mode example shows output from running nm -p on the func.o and main.o object files.
$ nm -p func.o
Other symbols created from compiling. 1073741824 d $THIS_DATA$ 1073741824 d $THIS_SHORTDATA$ 1073741824 b $THIS_BSS$ 1073741824 d $THIS_SHORTBSS$ 0000000000 T sum_n Global definitions of sum_n. $ nm -p main.o 0000000000 U $global$ Other symbols created from compiling. 1073741824 d $THIS_DATA$ 1073741872 d $THIS_SHORTDATA$ 1073741872 b $THIS_BSS$ 1073741872 d $THIS_SHORTBSS$ 0000000000 T main Global definition of main. 0000000000 U printf 0000000000 U scanf 0000000000 U sum_nThe first column shows the address of each symbol or reference. The last column shows the symbol name. The second column denotes the symbol's type:
| NOTE | The elfdump command works on 64-bit executables or shared libraries. |
The elfdump(1) command displays information contained in ELF format object files, archives, and shared libraries.
Use the following options to select the information you want to display:
| To view the contents. | Use this option |
|---|---|
| Symbol table entries. | -t |
| Archive headers from an archive library. | -a |
| String table(s). | -c |
| File header. | -f |
| Global symbols from an archive. | -g |
| Section headers. | -h |
| The .dynamic section in shared libraries and dynamically linked program files. | -L |
| Optional headers (program headers). | -o |
| Relocations. | -r |
| Section contents. | -s |
| Unwind table. | -U |
elfdump provides the following additional options to modify
your selections:
| Option | Modifies | Causes elfdump to |
|---|---|---|
| -H | all | Select output format in hexadecimal, octal, or decimal. |
| -p | all | Suppress title printing. |
| -S | -h,-o | Display headers in short format. |
| -C | -c, -r, -s, -t | Demangle C++ symbol names before displaying them.
With -n name, display the symbol whose unmangled name matches name, and prints its symbol name as a demangled name. |
| -D num | -h, -s | Display the section whose index is num. |
| +Dnum2 | -h, -s | Display the sections in the range 1 to
num2.
|
| -D num | -r | Display the relocation whose index is num. |
| +D num2 | -r | Display only the relocations which apply to the section(s) in the range. |
| +s name | -c, -t | Display the section specified by name. |
| -n name | -h, -r, -s | Display information about the section specified by name. |
| -n name | -t | Display information about the symbol entry specified by name. |
| -T num | -t | Display the symbol whose index is num. |
| +T num2 | -t | Display the symbols in the range 0 to
num2.
|
ldd does not list shared libraries explicitly loaded using dlopen(3C) or shl_load(3X).
ldd prints the record of shared library path names to stdout.
It prints the optional list of symbol resolution problems to stderr.
| To do this | Use the option |
|---|---|
| 32-bit mode only. Used in conjuction with -d and/or -r, force dld.sl to bind all dependent libraries and report unsats. By default the smartbind mechanism in dld.sl only binds libraries whose symbols are explicitly referenced. | -b |
| Check reference to data symbols. | -d |
| Check reference to data and code symbols. | -r |
| Displays the search path used to locate the shared libraries. | -s |
| Display all dependency relationships. | -v |
$ldd a.out
./libx.sl => ./libx.sl libc.2 => /lib/pa20_64/libc.2 libdl.1 => /lib/pa20_64/libdl.1The -v option causes ldd to print the dependency relationships along with the dynamic path information.
$ldd -v a.out
find library=./libx.sl; required by a.out ./libx.sl => ./libx.sl find library=libc.2; required by a.out libc.2 => /lib/pa20_64/libc.2 find library=libdl.1; required by /lib/pa20_64/libc.2 libdl.1 => /lib/pa20_64/libdl.1The -r option to causes it to analyze all symbol references and print information about unsatisfied code and data symbols.
$ldd -r a.out
./libx.sl => ./libx.sl libc.2 => /lib/pa20_64/libc.2 libdl.1 => /lib/pa20_64/libdl.1 symbol not found: val1 (./libx.sl) symbol not found: count (./libx.sl) symbol not found: func1 (./libx.sl) symbol not found: func2 (./libx.sl)
Use the following options to display information for your specified
files:
| To display | Use this option |
|---|---|
| Sizes in decimal (default). | -d |
| Sizes in octal. | -o |
| Sizes in hexadecimal. | -x |
| Version information about the size command. | -V |
| Verbose list of the subspaces in the object files. Each subspace is listed on a separate line with its size, physical address, and virtual address. | -v |
| 64-bit mode only: Size of each allocatable section. | -f |
| 64-bit mode only: Size and permission bits of each loadable segment=. | -F |
| 64-bit mode only: Sizes of non loadable segments or non allocatable sections. | -n |
You can control the amount of information stripped from the symbol table
by using the following options:
| To | Use this option |
|---|---|
| Strip line number information only; do not strip any symbol table information. | -l |
| Do not strip static or external symbol information. | -x |
| 32-bit mode only: Reset the relocation indexes into the symbol table. This option allows strip to be run on relocatable files, in which case the effect is also to strip only symbolic debugging information and unloadable data. | -r |
| Print the version of the strip command to stderr. | -V |
| NOTE | The -l and -x options are synonymous because the symbol table contains only static and external symbols. Either option strips only symbolic debugging information and unloadable data. |
If there are any relocation entries in the object file and any symbol table information is to be stripped, strip issues a message and terminates without stripping the specified file unless the -r option is used.
If you execute strip on an archive file (see ar(4)),
it removes the archive symbol table. The archive symbol table must be restored
by executing ar with its s operator (see ar(1))
before the ld command (see ld (1)) can use the archive.
strip
issues appropriate warning messages when this situation occurs.
fastbind performs analysis on the symbols used to bind an executable and all of its dependent shared libraries, and stores this information in the executable file. The next time the executable is run, the dynamic loader (/usr/lib/dld.sl for 32-bit mode or /usr/lib/pa20_64/dld.sl for 64-bit mode) detects that this information is available, and uses it to bind the executable instead of using the standard search method for binding the symbols.
Because fastbind writes the fastbind information in the executable file, you must have write permission on the executable file. If the executable file being analyzed is being run as another process or the file is locked against modifications by the kernel, the fastbind command fails.
If the shared libraries that an executable is dependent on are modified
after the fastbind information is created, the dynamic loader silently
reverts to standard search method for binding the symbols. The fastbind
information can be re-created by running fastbind on the executable
again. fastbind automatically erases the old fastbind information
and generate the new one.
| To do this | Use this option |
|---|---|
| Remove the fastbind information from the executable, returning it to the same state it as was in before you ran fastbind on it. | -n |
| Normally, if fastbind detects any unsatisfied symbols while building the fastbind information, it generates an error message and does not modify the executable file. When you invoke fastbind with the -u option however, it allows unresolved symbols. | -u |
The 32-bit mode fastbind command does not work with EXEC_MAGIC executables.
fastbind effectively enforces the binding modes bind-restricted and bind-immediate. For example, consider an executable linked bind-deferred, which calls a function foo() defined in an implicitly loaded library. Before the actual call is made, if it explicitly loads a shared library (using shl_load(3X) with BIND_FIRST) having a definition for foo() when foo() is finally called, it is resolved from the explicitly-loaded library. But after running fastbind, the symbol foo() is resolved from the implicitly-loaded library.
For more information about fastbind and performance, see Improving
Shared Library Start-Up Time with fastbind .
Example
$fastbind a.out
To later remove the fastbind information from the executable file a.out
$fastbind -n a.out
You can process the output with tsort to find an ordering of a library suitable for one-pass access by ld (see tsort(1) and ld(1)). The linker ld is capable of multiple passes over an archive in the archive format and does not require that you use lorder when building an archive. Using the lorder command may, however, allow for a slightly more efficient access of the archive during the link-edit process.
The symbol table maintained by ar allows ld to randomly access symbols and files in the archive, making the use of lorder unnecessary when building archive libraries (see ar(1)).
lorder overlooks object files whose names do not end with .o,
even when contained in library archives, and attributes their global symbols
and references to some other file.
Examples
$ar cr library `lorder *.o | tsort`
When creating libraries with so many objects that the shell cannot properly handle the *.o expansion, the following technique may prove useful:
$ls |grep '.o$'|lorder|tsort|xargs ar cq library
Example Program Comparing Shared and Archive Libraries
Shared Libraries with Debuggers, Profilers, and Static Analysis
| Comparing | Archive | Shared (or dll) |
|---|---|---|
| file name suffix | Suffix is .a. | Suffix is .sl or .number representing a particular version of the library. |
| object code | Made from relocatable object code. | Made from position-independent object code, created by compiling with the +z or +Z compiler option. Can also be created in assembly language (see Position-Independent Code ). |
| creation | Combine object files with the ar command | Combine PIC object files with the ld command |
| address binding | Addresses of library subroutines and data are resolved at link time. | Addresses of library subroutines are bound at run time. Addresses of data in a.out are bound at link time; addresses of data in shared libraries are bound at run time. |
| a.out files | Contains all library routines or data (external references) referenced in the program. An a.out file that does not use shared libraries is known as a complete executable. | Does not contain library routines; instead, contains a linkage table that is filled in with the addresses of routines and shared library data. An a.out that uses shared libraries is known as an incomplete executable, and is almost always much smaller than a complete executable. |
| run time | Each program has its own copy of archive library routines. | Shared library routines are shared among all processes that use the library. |
Almost all system libraries are available both as a shared library and as an archive library for 32-bit mode in the directory /usr/lib and for 64-bit mode in /usr/lib/pa20_lib. Archive library file names end with .a whereas shared library file names end with .sl. For example, in 32-bit mode, the archive C library libc is /usr/lib/libc.a and the shared version is /usr/lib/libc.sl. In 64-bit mode, the archive C library libc is /usr/lib/pa20_64/libc.a and the shared version is /usr/lib/pa20_64/libc.sl
If both shared and archived versions of a library exist, ld uses the one that it finds first in the default library search path. If both versions exist in the same directory, ld uses the shared version. For example, compiling the C program prog.c causes cc to invoke the linker with a command like this:
For 64-bit mode:
ld /opt/langtools/lib/pa20_64/crt0.o prog.o -lc
In addition to the system libraries provided on HP-UX, you can create your own archive and shared libraries. To create archive libraries, combine object files with the ar command, as described in Overview of Creating an Archive Library . To create shared libraries, use ld to combine object files containing position-independent code (PIC), as described in Creating Shared Libraries.
For more information, see Caution
When Mixing Shared and Archive Libraries .
| NOTE | For 32-bit only:
If the only definition referenced in an object file of an archive library is a common symbol, only that common symbol is copied into the a.out and not the entire object file. This helps reduce the size of the a.out file. |

Consequently, linking with shared libraries generally results in smaller
a.out
files than linking with archive libraries. Therefore, a clear benefit of
using shared libraries is that it can reduce disk space and virtual memory
requirements.
| NOTE | In prior releases, data defined by a shared library was copied into
the program file at link time. All references to this data, both in the
libraries and in the program file, referred to the copy in the executable
file.
With the HP-UX 10.0 release, however, this data copying is eliminated. Data is accessed in the shared library itself. The code in the executable file references the shared library data indirectly through a linkage pointer, in the same way that a shared library would reference the data. |
$ ld /opt/langtools/lib/crt0.o prog.o -lc liblocal.slIn 32-bit mode, the linker records the path name of liblocal.sl in the a.out file as /users/hyperturbo/liblocal.sl. When shipping this application to users, you must ensure that (1) they have a copy of liblocal.sl on their system, and (2) it is in the same location as it was when you linked the final application. Otherwise, when the users of your application run it, the dynamic loader will look for /users/hyperturbo/liblocal.sl, fail to find it, and the program will abort.
In 64-bit mode, the linker records ./liblocal.sl.
This is more of a concern with non-standard libraries-that is, libraries
not found in /usr/lib or /usr/lib/pa20_64. There is little
chance of the standard libraries not being in these directories.

Compare this with the memory requirements if prog1 and prog2
had been linked with the archive version of libc. As shown in
Figure
8: Two Processes with Their Own Copies of libc , 1428Kb of memory are
required (768Kb + 640Kb). The numbers in this example are made up, but
it is true in general that shared libraries reduce memory requirements.

Addition support is provided by the WDB Debugger. Refer to the WDB documentation
at http://www.hp.com/go/wdb.
Use the following steps to profile a shared library:
Link the application which links with the library to be profiled to take one of the following actions:
Export all the symbols explicitly (with the ld +e option).
You can profile either the application or a shared library, however, you cannot profile both the application and the shared library together.
To profile applications, continue to use the existing gprof method, in which you compile with -G option, but do not set LD_PROFILE.
For example, to profile the shared library test.sl:
$LD_PROFILE=test.sl $export LD_PROFILEIf a.out is linked to test.sl, execute a.out.
$a.out
This step creates the file <shared_library>.profile (where shared_library is the name of the shared library specified by the LD_PROFILE environment variable) at run time. This file contains the profile information for the shared library. For this example, the file test.sl.profile is created.
To get the complete profile information about test.sl, run the gprof command:
$gprof test.sl test.sl.profile The following limitations exist for gprof shared library profiling:
Shared libraries built with -B symbolic are not profiled.
Any function calls made from library initializers are not collected.
Combine these object files into a single archive library file with the ar command.
cc -Aa -c length.c volume.c mass.c ar r libunits.a length.o volume.o mass.oThese steps are described in detail in Overview of Creating an Archive Library .
Other topics relevant to archive libraries are:
Example of Creating an Archive Library
Replacing, Adding, and Deleting an Object Module
Combine these object files into a single archive library file with the ar command. Invoke ar with the r key.
("Keys" are like command line options, except that they do not require a preceding -.)

a symbol table, used by the linker and other commands to find the location, size, and other information for each routine or data item contained in the library
an optional string table used by the linker to store file names that are greater than 15 bytes long (only created if a long file name is encountered)
object modules, one for each object file specified on the ar command line
$ ar t /usr/lib/libm.a Run ar with the t key.
cosh.o Object modules are displayed.
erf.o fabs.o floor.o . . . .This indicates that the library was built from object files named cosh.o, erf.o, fabs.o, floor.o, and so forth. In other words, module names are the same as the names of the object files from which they were created.
length.c - Routine to Convert Length Units
float in_to_cm(float in) /* convert inches to centimeters */
{
return (in * 2.54);
}
volume.c - Routine to Convert Volume Units
float gal_to_l(float gal) /* convert gallons to liters */
{
return (gal * 3.79);
}
mass.c - Routine to Convert Mass Units
float oz_to_g(float oz) /* convert ounces to grams */
{
return (oz * 28.35);
}
During development, each routine is stored in a separate file. To make
the routines easily accessible to other programmers, they should be stored
in an archive library. To do this, first compile the source files, either
separately or together on the same command line:
$ cc -Aa -c length.c volume.c mass.c Compile them together. length.c: volume.c: mass.c: $ ls *.o List the .o files. length.o mass.o volume.oThen combine the .o files by running ar with the r key, followed by the library name (say libunits.a), followed by the names of the object files to place in the library:
$ ar r libunits.a length.o volume.o mass.o ar: creating libunits.aTo verify that ar created the library correctly, view its contents:
$ ar t libunits.a Use ar with the t key. length.o volume.o mass.o All the .o modules are included; it worked.Now suppose you've written a program, called convert.c, that calls several of the routines in the libunits.a library. You could compile the main program and link it to libunits.a with the following cc command:
$ cc -Aa convert.c libunits.aNote that the whole library name was given, and the -l option was not specified. This is because the library was in the current directory. If you move libunits.a to /usr/lib before compiling, the following command line will work instead:
$ cc -Aa convert.c -lunitsLinking with archive libraries is covered in detail in Linker Tasks.
$ ar r libunits.a length.o
$ ar d libunits.a volume.o Delete volume.o. $ ar t libunits.a List the contents. length.o mass.o volume.o is gone.
ar [-] keys archive [modules] ...archive is the name of the archive library. modules is an optional list of object modules or files. See ar(1) for the complete list of keys and options.
$ ar tv libunits.a rw-rr 265/ 20 230 Feb 2 17:19 1990 length.o rw-rr 265/ 20 228 Feb 2 16:25 1990 mass.o rw-rr 265/ 20 230 Feb 2 16:24 1990 volume.oThe next example replaces length.o in libunits.a, only if length.o is more recent than the one already contained in libunits.a:
$ ar ru libunits.a length.o
Users who link by letting the compilers such as cc invoke the
linker do not have include crt0.o on the link line.
in the 32-bit /usr/local/lib or /usr/contrib/lib directory
The drawbacks of putting the libraries in /usr/lib or /usr/lib/pa20_64 are:
You could overwrite an HP-UX system library that resides in the directory.
Creating the Shared Library with ld by linking with -b.
$ cc -Aa -c +z length.c volume.c mass.c $ ld -b -o libunits.sl length.o volume.o mass.oOther topics relevant to shared libraries are:
Write assembly language programs that use appropriate addressing modes, described in Position-Independent Code .
$ cc -Aa -c +z length.c The +z option creates PIC.You could then link it with other PIC object files to create a shared library, as discussed in Creating the Shared Library with ld .
In 64-bit mode, +Z is the default and the compilers ignore the options
and generate PIC code.
In 64-bit mode, +Z is the default for the C and C++ compilers.
For example, suppose you have three C source files containing routines to do length, volume, and mass unit conversions. They are named length.c, volume.c, and mass.c, respectively. To make a shared library from these source files, first compile all three files using the +z option, then combine the resulting .o files with ld. Shown below are the commands you would use to create a shared library named libunits.sl:
$ cc -Aa -c +z length.c volume.c mass.c length.c: volume.c: mass.c: $ ld -b -o libunits.sl length.o volume.o mass.oOnce the library is created, be sure it has read and execute permissions for all users who will use the library. For example, the following chmod command allows read/execute permission for all users of the libunits.sl library:
$ chmod +r+x libunits.slThis library can now be linked with other programs. For example, if you have a C program named convert.c that calls routines from libunits.sl, you could compile and link it with the cc command:
$ cc -Aa convert.c libunits.slIn 32-bit mode, once the executable is created, the library should not be moved because the absolute path name of the library is stored in the executable. (In 64-bit mode, ./libunit.sl is stored in the executable.)For details, see Shared Library Location .
For details on linking shared libraries with your programs, see Linker
Tasks.
| NOTE | If you are linking any C++ object files to create an executable or a shared library, you must use the CC command to link. This ensures that c++patch executes and chains together your nonlocal static constructors and destructors. If you use ld, the library or executable may not work correctly and you will probably not get any error messages. For more information see the HP C++ Programmer's Guide. |
$ ld -b -o libdep.sl mod1.o mod2.o -lcurses -lcustomThereafter, any programs that load libdep.sl - either explicitly with shl_load or implicitly with the dynamic loader when the program begins execution - also automatically load the dependent libraries libcurses.sl and libcustom.sl.
There are two additional issues that may be important to some shared library developers:
Where are all the dependent libraries placed in relation to other already loaded libraries? That is, where are they placed in the process's shared library search list used by the dynamic loader?
For example, suppose you create three libraries - libQ, libD, and libP - using the ld commands below. The order in which the libraries are built is important because a library must exist before you can specify it as a dependent library.
$ ld -b -o libQ.sl modq.o -lB $ ld -b -o libD.sl modd.o -lQ -lB $ ld -b -o libP.sl modp.o -lA -lD -lQThe dependency lists for these three libraries are:
libD depends on libQ and libB
libP depends on libA, libD, and libQ
+-->libA.sl | libP.sl-->libD------+ | | | | v v +-->libB.sl-->libQ.sl
if the library has not been visited then
mark the library as visited.
if the library has a dependency
list then
traverse
the list in reverse order.
Place the library at the head of
the load list.
Shown below are the steps taken to form the load graph when libP is loaded:
mark Q, traverse B
mark B, load B
load Q
traverse D
mark D, traverse B
B is already marked, so skip B, traverse Q
Q is already marked, so skip Q
load D
mark A, load A
load P
libP-->libA-->libD--> libQ-->
libB
if the library has not been visited then
mark the library as visited;
append the library at the end of
the list.
if the library has a dependency
list then
traverse
the list in order.
Shown below are the steps taken to form the load graph when libP is loaded:
traverse P
mark A, loadA
mark D, loadD
mark Q, loadQ
traverse D
D is already marked, so skip D
traverse Q
Q is already marked, so skip Q
traverse Q
Q is already marked, so skip Q
traverse B
mark B, loadB
traverse B
B is already marked, so skip B
In standard dld-64 constructs the load graph in the following manner,
<current search list>--> libP--> libA--> libD--> libQ--> libB
The same behavior occurs for libraries that are explicitly loaded with shl_load, but without the BIND_FIRST modifier (see BIND_FIRST Modifier for details). If BIND_FIRSTis specified in the shl_load call, then the libraries in the load graph are inserted before the existing search list. For example, suppose libP is loaded with this call:
lib_handle = shl_load("libP.sl", BIND_IMMEDIATE | BIND_FIRST, 0);
Then the resulting library search list is:
libP--> libA--> libD--> libQ-->
libB--><current
search list>
$ cc -Aa -c +z length.c $ ld -b -o libunits.sl length.o volume.o mass.oAny programs that use this library will now be using the new versions of the routines. That is, you do not have to relink any programs that use this shared library. This is because the routines in the library are attached to the program at run time.
This is one of the advantages of shared libraries over archive libraries:
if you change an archive library, you must relink any programs that use
the archive library. With shared libraries, you need only recreate the
library.
Prior to the HP-UX 9.0 release, moving a shared library caused any programs that were linked with the library to fail when they tried to load the library. Prior to 9.0, you were required to relink all applications that used the library if the library was moved to a different directory.
Beginning with the HP-UX 9.0 release, a program can search a list of directories at run time for any required libraries. Thus, libraries can be moved after an application has been linked with them. To search for libraries at run time, a program must know which directories to search. There are two ways to specify this directory search information:
Link the program with +s, enabling the program to use the path list defined by the SHLIB_PATH environment variable at run time.
For details on the use of these options, refer to Moving
Libraries after Linking with +b and
Moving Libraries
After Linking with +s and SHLIB_PATH .
The LD_PRELOAD environment variable allows you to load additional shared libraries at program startup. LD_PRELOAD provides a colon- separated or space-separated list of shared libraries that the dynamic loader can interpret. The dynamic loader, dld.sl, loads the specified shared libraries as if the program had been linked explicitly with the shared libraries in LD_PRELOAD before any other dependents of the program.
At startup time, the dynamic loader implicitly loads one or more libraries, if found, specified in the LD_PRELOAD environment. It uses the same load order and symbol resolution order as if the library had been explicitly linked as the first library in the link line when building the executable. For example, given an executable built with the following link line:
$ ld ... lib2.sl lib3.sl lib4.sl
If LD_PRELOAD="/var/tmp/lib1.sl", the dynamic loader uses the same load order and symbol resolution order as if lib1.sl had been specified as the first library in the link line:
$ ld ... /var/tmp/lib1.sl lib2.sl lib3.sl lib4.sl
In a typical command line use (with /bin/sh), where LD_PRELOAD is defined as follows:
$ LD_PRELOAD=mysl.sl application
The dynamic loader searches application according to $PATH, but searches mysl.sl according to SHLIB_PATH and/or LD_LIBRARY_PATH, and/or the embedded path (if enabled).
You can use the LD_PRELOAD environment variable to load a shared library that contains thread-local storage to avoid the following error when loading the library dynamically:
/usr/lib/dld.sl: Can't shl_load() a library containing Thread Local Storage: /usr/lib/libpthread.1The load order and symbol resolution order may be different in a PA32 program than in the same PA64 program because the dynamic loader uses depth-first search order in PA32 mode and breadth-first search order in PA64 mode.
The dynamic loader uses the LD_PRELOAD environment variable even if you use the +noenvvar in the link line. This insures that LD_PRELOAD is enabled even in a +compat link. The LD_PRELOAD variable is always enabled except for setuid and setgid programs.
Note: Using LD_PRELOAD can cause a core dump when used with applications which mix shared and archived libraries, especially when both the shared library and the application are built with aC++ or use libc.
You can specify multiple libraries as part of the LD_PRELOAD environment variable. Separate the libraries by spaces or colons as in LD_LIBRARY_PATH. (Multi-byte support is not provided as part of parsing the LD_PRELOAD library list). You can specify LD_PRELOAD libraries with absolute paths or relative paths. The LD_PRELOAD libraries can also consist of just the library names, in which case the dynamic loader uses the directory path list in the environment variables LD_LIBRARY_PATH and/or SHLIB_PATH or the embedded path list (if enabled) to search for the libraries.
The dynamic loader does not issue an error or warning message if it
cannot find a library specified by LD_PRELOAD. However, if it
does not find a dependent of the LD_PRELOAD libraries, the dynamic
loader issues the same error message as if the LD_PRELOAD library
is specified in the link line.
a.out / \ libA.sl libB.sl / \ libC.sl libD.slThat is, a.out was built with commands like these:
$ cc +DA2.0W -c +z ?.c $ ld -b -o libB.sl b.o $ ld -b -o libC.sl c.o $ ld -b -o libD.sl d.o $ ld -b -o libA.sl a.o -L. -lC -lD $ cc foo.c -L. -lA -lB
$ ldd a.out libA.sl => ./libA.sl libB.sl => ./libB.sl libc.2 => /usr/lib/pa20_64/libc.2 libC.sl => ./libC.sl libD.sl => ./libD.sl libdl.1 => /usr/lib/pa20_64/libdl.1Therefore, with LD_PRELOAD unset, the symbol resolution order for the user libraries in 64-bit mode is:
a.out- -> libA.sl --> libB.sl --> libC.sl
-->libD.sl
Case (i): LD_PRELOAD="./libB.sl"
In 64-bit mode, the symbol resolution order is:
$ export LD_PRELOAD="./libB.sl" $ ldd a.out ./libB.sl => ./libB.sl ./libB.sl => ./libB.sl libA.sl => ./libA.sl libB.sl => ./libB.sl libc.2 => /usr/lib/pa20_64/libc.2 libC.sl => ./libC.sl libD.sl = ./libD.sl libdl.1 => /usr/lib/pa20_64/libdl.1a.out --> libD.sl --> libA.sl --> libB.sl --> libC.sl
In 64-bit mode, the symbol resolution order is:
$ export LD_PRELOAD="./libD.sl" $ ldd a.out ./libD.sl => ./libD.sl ./libD.sl => ./libD.sl libA.sl => ./libA.sl libB.sl => ./libB.sl libc.2 => /usr/lib/pa20_64/libc.2 libC.sl => ./libC.sl libD.sl => ./libD.sl libdl.1 => /usr/lib/pa20_64/libdl.1a.out --> libD.sl --> libA.sl --> libB.sl- -> libC.sl
If the same symbol is defined in libA.sl and libD.sl,
the 64-bit linker toolset uses the symbol defined in libD.sl since
this library is loaded and searched before libA.sl when LD_PRELOAD="./libD.sl".
mark A, traverse D, load D
load C
load A
a.out --> libA.sl --> libC.sl --> libD.sl
--> libB.sl
Case (i): LD_PRELOAD="./libB.sl"
In 32-bit mode, the dependents of a.out are treated as: libB.sl, libA.sl, libB.sl. The second libB.sl is ignored by the dynamic loader, since it is a duplicate library. So, the effective dependents of a.out are treated as: libB.sl, libA.sl. The load graph is:
load C
load A
load B
a.out --> libB.sl--> libA.sl --> libC.sl --> libD.sl
Case (ii): LD_PRELOAD = "./libD.sl"
In 32-bit mode, the load graph is:
mark A, traverse D, load D
load C
load A
a.out --> libA.sl--> libC.sl --> libD.sl
--> libB.sl
One possible way to improve shared library performance is to export only those symbols that need exporting from a library. To control which symbols are exported, use either the +e or the -h option to the ld command. When +e options are specified, the linker exports only those symbols specified by +e options. The -h option causes the linker to hide the specified symbols. (For details on using these options, see Hiding Symbols with -h and Exporting Symbols with +e.)
As an example, suppose you've created a shared library that defines the procedures init_prog and quit_prog and the global variable prog_state. To ensure that only these symbols are exported from the library, specify these options when creating the library:
+e init_prog +e quit_prog +e prog_stateIf you have to export many symbols, you may find it convenient to use the -cfile option, which allows you to specify linker options in file. For instance, you could specify the above options in a file named export_opts as:
+e init_prog +e quit_prog +e prog_stateThen you would specify the following option on the linker command line:
-c export_opts(For details on the -c option, see Passing Linker Options in a file with -c .)
A huge module, but contains only error routines that are seldom called.
Contains routines that are called frequently by a.o, and calls routines in a.o frequently.
$ ld -b -o libabc.sl *.oThe potential problem with this ordering is that the routines in a.o and c.o are spaced far apart in the library. Better virtual memory performance could be attained by positioning the modules a.o and c.o together in the shared library, followed by the module b.o. The following command will do this:
$ ld -b -o libabc.sl a.o c.o b.oOne way to help determine the best order to specify the object files is to gather profile data for the object modules; modules that are frequently called should be grouped together on the command line.
Another way is to use the lorder(1) and tsort(1) commands. Used together on a set of object modules, these commands determine how to order the modules so that the linker only needs a single pass to resolve references among the modules. A side-effect of this is that modules that call each other may be positioned closer together than modules that don't. For instance, suppose you have defined the following object modules:
Calls Routines in Module
$ lorder ?.o | tsort Pipe lorder's output to tsort. a.o b.o e.o x.o y.o d.oNotice that d.o is now closer to x.o and y.o, which call it. However, this is still not the best information to use because a.o and b.o are separated from x.o and y.o by the module e.o, which is not called by any modules. The actual optimal order might be more like this:
a.o b.o x.o y.o d.o e.oAgain, the use of lorder and tsort is not perfect, but it may give you leads on how to best order the modules. You may want to experiment to see what ordering gives the best performance.
$ chmod 555 libname
To get around this, use the C compiler's +ESlit option, which
places constant data in the $LIT$ text space (or in 64-bit mode,
in a .text text segment) instead of the data space. This results
in one copy of the constant data being shared among all processes that
use the library.
| NOTE | This option requires that programs not write into constant strings and data. In addition, structures with embedded initialized pointers won't work because the pointers cannot be relocated since they are in read-only $TEXT$ space. In this case, the linker outputs the error message "Invalid loader fixup needed". |
The linker provides the +filter option, used with the -b option, to enable this mechanism.
$ld -b...+filter shared_library_pathname
If you divide a filtered shared library up into independent implementation libraries, the total memory consumption is reduced significantly if the application uses only a small portion of the library. This reduction is most significant when the shared library contains a large amount of static data which is not used by all applications.
It is important, when dividing a shared library into implementation
libraries, that you keep them independent of each other. If there are dependencies
between implementation libraries, the memory reduction benefits cannot
be realized. Filtered shared libraries preserve compatibility because a
filtered shared library appears as a single component to the application.
You need to link only to the filter library, regardless of how many implementation
libraries it is divided into.
Build the filter library using the +filter linker option. Each implementation library which is part of the filter set is specified using the +filter option. For example:
$ld -b impl1.o -o libimpl1.sl $ld -b impl2.o -o libimpl2.sl $ld -b +filter libimpl1.sl +filter libimpl2.sl -o libfilt.slThis builds a filtered shared library libfilt.sl with two implementation libraries libimpl1.sl and libimpl2.sl.
To confirm which implementation libraries are in the filter set, use odump to list the contents of the filter library:
$odump -filtertable libfilt.sl
Filtered Shared Library List Table for libfilt.sl:
Index String Table Offset Name 0 11 ./libimpl1.sl 1 24 ./libimpl2.sl
| NOTE | The +filter option takes effect only when you use the -b option. |
The +filter option can be used in conjunction -L linker option or the LPATH environment variable. For example:
$ld -b -L. +filter impl1 +filter impl2 -o libfilt.sl
or
$export LPATH=. $ld -b -L. +filter impl1 +filter impl2 -o libfilt.slThe filtered shared library itself can contain definitions of some symbols which are not required to be ``lazy loaded'' (typically those symbols that are always used, for example, exit(2) in case of libc). For example:
$ld -b a.o b.o...+filter libimpl1.sl +filter libimpl2.sl -o libfilt.sl
$cc prog.c libfilt.sl -o prog
You can explicitly link your program with an implementation library,
as well as with the parent filtered library, but you lose any advantage
offered by the filter library feature. The implementation library is loaded
as your program is run.
#include <dlfcn.h>
void initializer(void *handle, int loading)
{
...
ptr = dlsym(handle, "symname");
...
}
$ld -b impl1.o -o libimpl1.sl $ld -b +b /path/to/implementation/libs +filter\ ./libimpl1.sl -o libfilt.sl $chatr +b enable libfilt.sl $cc prog.c libfilt.sl -o prog $mv libimpl1.sl /path/to/implementation/libs $./prog
Change symbol types, for example, when an uninitialized data symbol (BSS symbol) is changed to an initialized data symbol (or vice-versa), or a text symbol into a data symbol.
Change the TLS (Thread Local Storage) size of an implementation library Even though the newly-added TLS symbols may not be exported outside the implementation library, you need to rebuild the filter library so the correct amount of TLS space is reserved.
To see the TLS size, use the following command:
$odump -sldlheader shared_library_pathname\ | fgrep tdsize
| NOTE | Beginning with HP-UX Release 11.00, the 64-bit linker toolset supports only library-level versioning. |
Here are some guidelines to keep in mind when making changes to a library:
When creating the first version of a shared library using intra-library versioning, version control is not an issue: The default version number is satisfactory.
When creating future revisions of a library, you must determine when a change represents an incompatible change, and thus deserves a new version. Some examples of incompatible changes are as follows:
For exported data, any change in either the initial value or the size represents an incompatible change.
Any function that is changed to take advantage of an incompatible change in another module should be considered incompatible.
With intra-library versioning, when an incompatible change is made to a module, all the old versions of the module should be retained along with the new version. The new version number should correspond to the date the change was made. If several modules are changed incompatibly in a library, it is a good idea to give all modules the same version date.
ld -b *.o -o libA.0 +h libA.0 Creates the shared library libA.0.Since the linker still looks for libraries ending in .sl with the -l option, create a symbolic link from the usual name of the library ending in .sl to the actual library. For example, libA.sl points to libA.0:
ln -s libA.0 libA.sl libA.sl is a symbolic link to libA.0.Link applications as usual, using the -l option to specify libraries. The linker searches for libA.sl, as usual. However, if the library it finds has an internal name, the linker places the internal name of the library in the executable's shared library dependency list. When you run the application, the dynamic loader loads the library named by this internal name. For example:
ld /opt/langtools/lib/crt0.o prog.o -lA -lc Binds a.out with libA.0.
When you run a program that uses shared libraries and was linked before
HP-UX 10.0, the dynamic loader first attempts to open the shared library
ending in .0. If it cannot find that library, it attempts to open
the library ending in .sl.
+h internal_nameinternal_name is typically the same name as the library file itself, for example libA.1 as in +h libA.1. When you link with a library that has an internal name, the linker puts the internal_name in the shared library dependency list of the executable or shared library being created. When running the resulting executable or shared library, it is the library named by this internal name that the dynamic loader loads.
You can include a relative or absolute path with the internal name, but you must ensure the dynamic loader can find the library from this name using its normal search process.
For 32-bit mode, if internal_name includes a relative path (that is, a path not starting with /), the internal name stored by the linker is the concatenation of the actual path where the library was found and internal_name, including the relative path. When the resulting program is run, if the dynamic loader cannot find the library at this location, the program will not run.
If internal_name includes an absolute path (that is, a path starting with /), the internal name stored by the linker is simply the internal_name, including the absolute path. When the resulting program is run, if the dynamic loader cannot find the library at this location, the program will not run.
For 64-bit mode, see Internal Name Processing
for more information.
$ ld -b -o /X/libapp.sl *.o Create shared library. $ ln -s /X/libapp.sl /tmp/libmine.sl Make the link.

During a link, the linker records the file name of the opened library in the shared library list of the output file. However, if the shared library is a file system link to the actual library, the linker does not record the name of the library the file system link points to. Rather it records the name of the file system link.
For example, if /tmp/libmine.sl is a file system link to /X/libapp.sl, the following command records /tmp/libmine.sl in a.out, not /X/libapp.sl as might be expected:
$ ld /opt/langtools/lib/crt0.o main.o -L /tmp -lmine -lcTo use library-level versioning in this situation, you must set up corresponding file system links to make sure older applications linked with the older libraries run with these libraries. Otherwise, older applications could end up running with newer shared libraries. In addition, you must include the absolute path name in the internal name of the new library.
For example, in 32-bit mode, to make the above example work correctly with library-level versioning, first implement library-level versioning with the actual library /X/libapp.sl and include the absolute path in the internal name of the new library:
$ mv /X/libapp.sl /X/libapp.0 Rename old version. $ ld -b -o /X/libapp.1 +h /X/libapp.1 *.o Create new version. $ ln -s /X/libapp.1 /X/libapp.sl Set up symbolic link.Then set up the corresponding file system links:
$ ln -s /X/libapp.0 /tmp/libmine.0 Link to old version. $ ln -s /X/libapp.1 /tmp/libmine.1 Link to new version. $ rm /tmp/libmine.sl Remove old link. $ ln -s /X/libapp.sl /tmp/libmine.sl Link to the link.

With these links in place, the loader will load /X/libapp.0
when running the a.out file created above. New applications will
link and run with /X/libapp.1.
| NOTE | Renaming the old version of the .0 version library only works for 32-bit mode. |
For 64-bit mode programs, the dynamic loader only loads the library
recorded in the dynamic load table. You should use library-lever versioning
and create your 64-bit shared library with an internal name unless the
library will not be versioned in the future.
shl_load("libA.1", BIND_DEFERRED, 0);
This insures that when the application is migrated to a system that has
a later version of libA available, the actual version desired
is the one that is dynamically loaded.
This section provides information on the following topics:
Shared Library Dependencies and Version Control
This directive should only be used if incompatible changes are made
to a source file. If a version number directive is not present in a source
file, the version number of all symbols defined in the object module defaults
to 1/90.
When a shared library is built with a dependent shared library, the version number of the dependent library used during the link is recorded with the dependency.
When shl_load(3X) is called to load a shared library, the latest version of the library is loaded. If that library has dependencies, the dynamic loader (dld.sl(5)) will load the versions of the dependent libraries that were recorded in the dependency list. Note that these are not necessarily the most recent versions of the dependent libraries. When dld.sl loads a particular version of a shared library, it will load the same version of any dependent libraries.
If a shared library lists a second shared library as a dependency,
dld.sl
will generate an error if the second shared library has a version number
which is older than the version number recorded with the dependency. This
means that the first library was built using a more recent version of the
second library than the version that dld.sl currently finds.
#pragma HP_SHLIB_VERSION "11/93" /* date is November 1993 */
/*
* New version of "in_to_cm" also returns a character string
* "cmstr" with the converted value in ASCII form.
*/
float in_to_cm(float in, char *cmstr) /* convert in to cm */
{
. . . /* build "cmstr" */
return(in * 2.54);
}
. . . /* other length conversion routines */
To update libunits.sl to include the new length.c routines,
copy the old version of length.o to oldlength.o; then
compile length.c and rebuild the library with the new length.o
and oldlength.o:
$ cp length.c oldlength.c Save the old source. $ mv length.o oldlength.o Save old length.o. . . . Make new length.c. $ cc -Aa -c +z length.c Make new length.o. $ ld -b -o libunits.sl oldlength.o \ Relink the library. volume.o mass.o length.oThereafter, any programs linked with libunits.sl use the new versions of length-conversion routines defined in length.o. Programs linked with the old version of the library still use those routines from oldlength.o. For details on linking with shared libraries, see Linker Tasks.
The reason for doing this is best illustrated with an example. Suppose
in the previous example you gave each module a version date corresponding
to the date it was completed: 04/93 for file1.o, 05/93 for file2.o,
and 07/93 for file3.o. You then build the final library on 07/93
and link an application
a.out with the library. Now suppose that
you introduce an incompatible change to function foo found in
file1.o,
set the version date to 05/93, and rebuild the library. If you run
a.out
with the new version of the library, a.out will get the new, incompatible
version of foo because its version date is still earlier than
the date the application was linked with the original library!
For 64-bit naming, see Internal Name Processing
for more information.
For example, suppose several definitions and references of a symbol exist in different object and archive library files. By specifying the files in a particular link order, you could cause the linker to use one definition over another. But doing so requires an understanding of the subtle (and undocumented) symbol resolution rules used by the linker, and these rules are slightly different for shared libraries. So make files or shell scripts that took advantage of such linker behavior prior to the support of shared libraries may not work as expected with shared libraries.
More commonly, programmers may take advantage of undocumented linker behavior to minimize the size of routines copied into the a.out files from archive libraries. This is no longer necessary if all libraries are shared.
Although it is impossible to characterize the new resolution rules exactly, the following rules always apply:
The linker treats shared libraries more like object files.
An example of an implicit address dependency is a function that assumes that two global variables that were defined adjacently in the source code will actually be adjacent in virtual memory. Since 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.
| NOTE | For 32-bit mode only:
Although it is not 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 should link with archive libraries, or at least use immediate binding, 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 when deferred binding of symbols is used.
Conversely, if you move a program that uses shared libraries into a
chrooted
environment, you must have a copy of the dynamic loader,
dld.sl,
and all required shared libraries in the correct locations.
HP-UX library routines are described in detail in sections 2 and 3 of the HP-UX Reference. Routines in section 2 are known as system calls, because they provide low-level system services; they are found in libc. Routines in section 3 are other "higher-level" library routines and are found in several different libraries including libc.
Each library routine, or group of library routines, is documented on a man page. Man pages are sorted alphabetically by routine name and have the general form routine(nL), where:
The major library groups defined in the HP-UX Reference are shown
below:
| NOTE | Certain language-specific libraries are not documented in the HP-UX Reference; instead, they are documented with the appropriate language documentation. For example, all FORTRAN intrinsics (MAX, MOD, and so forth) are documented in the HP FORTRAN/9000 Programmer's Reference. |
For more information on these libraries, see C, A Reference Manual
by Samual P. Harbison and Guy L. Steele Jr., published in 1991 by Prentice-Hall,
or UNIX System V Libraries by Baird Peterson, published in 1992
by Van Nostrand Reinhold, or C Programming for UNIX by John Valley,
published in 1992 by Sams Publishing. For more information on system calls
see Advanced UNIX Programming by Marc J. Rochkind, published in
1985 by Prentice-Hall or Advanced Programming in the UNIX Environment
by W. Richard Stevens, published in 1992 by Addison-Wesley.
Mixing shared and archive libraries can lead to unsatisfied symbols,
hidden definitions, and duplicate definitions and cause an application
to abort or exhibit incorrect behavior at run time. The following examples
illustrate some of these problems.
$ cc -c main.c f1.c f2.c Compile to relocatable object code. $ cc -c +z f3.c Compile to position-independent code

Next suppose you put f3.o into the shared library lib3.sl and f1.o and f2.o into the archive library lib12.a:
$ ld -b -o lib3.sl f3.o Create a shared library. $ ar qvc lib12.a f1.o f2.o Create an archive library.

Now link the main with the libraries and create the executable a.out:
$ cc main.o lib12.a lib3.sl Link the program .

When you run a.out, it runs correctly. Now suppose you need
to modify f3() to call f2():

Compile the new f3() and rebuild the shared library lib3.sl:
$ cc -c +z f3.c Compile to relocatable code. $ ld -b -o lib3.sl f3.o Create a new shared library


$ a.out /usr/lib/dld.sl: Unresolved symbol: f2 (code) from /users/steve/dev/lib3.sl Abort(coredump)
If a library being loaded depends on a definition that does not exist in the application or any of the dependent shared libraries, the application will abort with an unsatisfied definition at run time. This seems obvious enough when an application is first created. However, over time, as the shared libraries evolve, new symbol imports may be introduced that were not originally anticipated. This problem can be avoided by ensuring that shared libraries maintain accurate dependency lists.
Suppose you have a main program, main(), and three functions, f1(), f2(), and f3() each in a separate source file. main() calls f1() and uses shl_load() to call f3(). main() does not call f2():
$ cc -c main.c f1.c f2.c Compile to relocatable object code $ cc -c +z f3.c Compile to position-independent code

Next suppose you put f3.o into the shared library lib3.sl and f1.o and f2.o into the archive library lib12.a:
$ ld -b -o lib3.sl f3.o Create a shared library. $ ar qvc lib12.a f1.o f2.o Create an archive library.

Now link the main with the archive library and create the executable a.out:
$ cc main.o lib12.a -ldld Link the program.

When you run a.out, it runs correctly. Now suppose you need
to modify f3() to call f2():

$ cc -c +z f3.c Compile to position-independent code. $ ld -b -o lib3.sl f3.o Error! Missing library containing f2().

Here's where the problem shows up. If you do not relink the application,
main.o,
and just run a.out with the new version of lib3.sl, the
program will abort since f2() is not available in the program's
address space. The reference to
f2() from f3() remains
unsatisfied, generating the 32-bit error message:

$ a.out Illegal instruction (coredump)
Duplicate definitions can occur when a dependent shared library is updated to refer to a symbol contained in the program file but not visible to the shared library. The new symbol import must be satisfied somehow by either adding the symbol to the library or by updating the shared library dependency list. Otherwise the application must be relinked.
Using an archive version of libc in an application using shared
libraries is the most common cause of duplicate definitions. Remember that
symbols not referenced by a shared library at link time will not be exported
by default.
| NOTE | Duplicate definitions can be avoided if any or all symbols that may be referenced by a shared library are exported from the application at link time. Shared libraries always reference the first occurrence of a definition. In the following example the first definition is in the executable file, a.out. See the -E option and +e symbol option described in ld(1) and Exporting Symbols from main with -E , Exporting Symbols with +ee, and Exporting Symbols with +e. |
The following example illustrates this situation. Suppose you have a main program, main(), and three functions, f1(), f2(), and f3() each in a separate source file. main() calls f1(), f2(), and f3().
$ cc -c main.c Compile to relocatable code. $ cc -c +z f1.c f2.c f3.c Compile to position-independent code.

Next suppose you put f3.o into the shared library lib3.sl and f1.o and f2.o into the archive library lib12.a. Also put f1.o and f2.o into the shared library lib12.sl:
$ ld -b -o lib3.sl f3.o Create a shared library. $ ld -b -o lib12.sl f1.o f2.o Create a shared library. $ ar qvc lib12.a f1.o f2.o Create an archive library.

Now link the main with the archive library lib12.a and the shared library lib3.sl and create the executable a.out:
$ cc main.o lib12.a lib3.sl Link the program.

When you run a.out, it runs correctly. Now suppose you need
to modify f3() to call f2():

Compile the new f3() and rebuild the shared library lib3.sl, including the new dependency on f2() in lib12.sl:
$ cc -c +z f3.c Compile to PIC.
$ ld -b -o lib3.sl f3.o -L . -l12 Create library with dependency.


$ CC -G -c main.c Compile to relocatable object code $ CC -c +z sub.c Compile to position-independent code
$ CC -b sub.o +z -o lib1.sl -lc Create a shared library $ CC -G main.o -L. -l1 Create an executable with profiling
When the program is run, one set of static variables is modified. When the program is exited, the other set of static variables is read. Since the second set of static variables was never modified, no gmon.out file is created.
To avoid this problem, do not build shared libraries with libc.sl as a dependent library. Besides libc.sl, the following libraries should not be included on the link line when building a shared library because these libraries contain thread-local storage: libcl.sl, libcma.sl, libpthread.sl (ie, -lcl -lcma -lpthread). Either include these libraries on the a.out link line (so the symbols needed for various runtime libraries are exported), or link the a.out with the -E linker option.
Providers of shared libraries should make every effort to prevent these
kinds of problems. In particular, if a shared library provider allows unsatisfied
symbols to be satisfied by an archive version of libc, the application
that uses the library may fail if the shared library is later updated and
any new libc dependencies are introduced. New dependencies in
shared libraries can be satisfied by maintaining accurate dependency lists.
However, this can lead to multiple occurrences of the same definition in
an application if the definitions are not explicitly exported.
The 64-bit mode linker toolset introduces different types of shared libraries. (In SVR4 Unix, shared libraries are sometimes called dlls.)
Standard mode shared library: A standard mode shared library is a library built with ld -b or ld -b +std with dependent shared libraries.
| NOTE | If you specify ld -b +compat with no dependent libraries, you create a shared library that has no mode -neither compatibility mode nor standard mode. |
The linker handles these libraries in different way with regard to internal
naming and library search modes.
In an ld +compat link, the linker treats the internal names like it does in 32-bit mode:
If the dependent library's internal name contains a relative path, the internal name is inserted at the end of the path where the shared library is found at link time, replacing the library's filename in the DT_HP_NEEDED entry. If the library is specified with -l, the dynamic path bit is set to TRUE.
If the dependent library's internal name contains no path, it is inserted at the end of the path where the shared library is found at link time, replacing the library's filename. If the library is specified with -l, the dynamic path bit is set to TRUE.
If the dependent shared library does not have an internal name, the path where the library is found and the library filename is recorded in the DT_HP_NEEDED entry. If specified with -l, the dynamic path bit is set to TRUE.
If the shared libraries are specified with a relative or no path in this mode, the linker expands the current working directory when constructing the DT_HP_NEEDED entry. So instead of getting something like ./libdk.sl, you get /home/your_dir/libdk.sl.
All DT_HP_NEEDED entries with the dynamic path bit set are subject to dynamic path lookup.
If the dependent shared library is specified with the -l or -l: option, only the libname.ext is recorded in the DT_NEEDED entry.
The path of the dependent shared library as seen on the link line is recorded in the DT_NEEDED entry.
All DT_NEEDED entries with no "/" in the libname are subject to dynamic path lookup.
In the 32-bit mode of the linker toolset (selected by the +compat option), the linker enables run-time dynamic path searching when you link a program with the -llibrary and +b path_name options. Or, you can use the -llibrary option with the +spath_name option. When programs are linked with +s, the dynamic loader searches directories specified by the SHLIB_PATH environment variable to find shared libraries.
The following example shows dynamic path searching changes for 64-bit mode.
ld /opt/langtools/lib/crt0.o main.o \ Subject to -lfoo -o main dynamic path searching.In 32-bit mode, main aborts at run time if libfoo.sl is moved from its standard location, /usr/lib or /usr/ccs/lib. The linker does not do dynamic path searching unless you specify the +b or +s options to the ld or chatr commands. In 64-bit mode, the dynamic loader searches for libfoo.sl in the directories specified by the LD_LIBRARY_PATH and SHLIB_PATH environment variables.
This section covers the following topics:
Resolution of unsatisfied shared library references
The 32-bit mode linker does not remember the definition of a procedure in a shared library unless it was referenced in previously scanned object files.
If you have function names that are duplicated in a shared and archive library, the 64-bit mode linker may reference a different version of a procedure than is referenced by the 32-bit mode linker. This change can lead to unexpected results.
For example, given these source files:
sharedlib.c
void afunc()
{
printf("\tin SHARED library procedure 'afunc'\n");
}
unsat.c
void bfunc()
{
afunc();
}
archive.c
void afunc()
{
printf ("\tin ARCHIVE library procedure 'afunc'\n");
}
main.c
main()
{
bfunc();
}
If these files are compiled and linked as:
cc -c main.c unsat.c archive.c cc -c +z sharedlib.c ld -b sharedlib.o -o libA.sl ar rv libB.a archive.o cc main.o libA.sl unsat.o libB.a -o test1The 32-bit linker toolset produces:
$ test1 in ARCHIVE library procedure `afunc'At link time, there is an outstanding unsatisfied symbol for afunc() when libB is found. The exported symbol for afunc()is not remembered after libA.sl is scanned. At run time, the afunc() symbol that is called is the one that came from the archive library, which resides in test1.
The 64-bit mode linker toolset produces:
$ test1 in SHARED library procedure `afunc'The 64-bit mode linker remembers the symbol for afunc(), and archive.o will not be pulled out of libB.a. The shared library version of afunc is called during execution. This behavior is consistent with other SVR4 systems.
The 32-bit mode linker toolset accepts unresolved symbols in some cases. For example, if an entry point defined in an object file is never reachable from the main program file, the unresolved symbol is allowed. You can use the +vshlibunsats linker option to find unresolved symbols in shared libraries.
For example, given these source files:
lib1.c
void a()
{
}
lib2.c
extern int unsat;
void b()
{
unsat = 14;
}
main.c
main()
{
a();
}
If these files are compiled and linked as:
cc -c main.c cc -c +z lib1.c lib2.c ld -b lib1.o lib2.o -o liba.sl cc main.o liba.sl -o test2Using the 32-bit mode linker, test2 executes without error. The module in liba.sl created from lib2.o is determined to be unreachable during execution, so the global symbol for unsat (in lib2.o) is not bound.
The 64-bit mode linker toolset reports an unsatisfied symbol error for
unsat
at link time or at run time if the program were made executable.
The result of this change is that load-time symbol resolution for one of these objects stops at the first one encountered, instead of continuing through all loaded libraries to see if an initialized data object exists.
For example, given these source files:
a.c
int object; /* Uninitialized global data symbol */
void a()
{
printf ("\tobject is %d\n", object);
}
b.c
int object =1; /* Initialized global data symbol */
void b()
{
}
main.c
main()
{
a();
}
If these files are compiled and linked as:
cc -c main.c cc -c +z a.c b.c ld -b a.o -o libA.sl ld -b b.o -o libB.sl cc main.o libA.sl libB.sl -o test3The 32-bit mode linker toolset produces:
$ test3 object is 1The 32-bit mode linker toolset defines the object global variable in libA.sl as a storage export symbol. The dynamic loader, when searching for a definition of object to satisfy the import request in libA.sl, does not stop with the storage export in that library. It continues to see if there is a data export symbol for the same symbol definition.
The 64-bit mode linker toolset produces:
$ test 3 object is 0The 64-bit mode linker toolset does not allow storage exports from a shared library. The uninitialized variable called object in a.o turns into a data export in libA.sl, with an initial value of 0. During symbol resolution for the import request from that same library, the dynamic loader stops with the first seen definition.
The 32-bit mode linker toolset searches dependent libraries in a depth-first
order. This means it searches dependent shared library files in the order
in which they are linked to shared libraries. The shl_load library
management routines and executable files (a.out) created by the
linker with the +compat option use the depth-first search
order.
| NOTE | If you have data or function names that are duplicated in different shared libraries, the 64-bit mode linker may link in a different version of a procedure than the current release. This can lead to unexpected results. |
Figure 30: Search Order of Dependent Libraries
shows an example program with shared libraries (the shaded boxes are libA.sl
dependent libraries; and the example does not consider libDK or
libLH)
and compares the two search methods:

The commands to build the libraries and the executable in Figure 30: Search Order of Dependent Libraries are shown below. Note the link order of libraries in steps 2 and 3:
ld -b libD.o -o libD.sl libA dependent shared library ld -b libE.o -o libE.sl libA dependent shared library ld -b libF.o -o libF.sl libA dependent shared library ld -b libB.o -o libB.sl ld -b libC.o -o libC.slNext, libA.o is linked to its dependent libraries and libA.sl is built.
ld -b libA.o -lD -lE -lF -o libA.slFinally, main.o is linked to its shared libraries.
cc main.o -lA -lB -lC -o main
If you use mixed mode shared libraries, the search mechanism may produce unexpected results.
For the following command, libA.sl and its dependent libB.sl are compatibility mode libraries and libC.sl and libD.sl are standard mode libraries.
ld -b libF.o +compat -L.-lA -lC -o LibF.sl
libF.sl is a compatibility mode library, but is dependent libC.sl
is a standard mode library. The linker uses depth-first searching mechanisms
because the highest-level library is in compatability mode.
If you use mixed mode shared libraries, you get behavior based on the first mode encountered. At runtime, the dynamic loader does a depth-first search if the dependent libraries at the highest level are compatibility mode libraries. Otherwise, it does breadth-first searching. This applies to all dependent libraries of the incomplete executable file. The loader cannot toggle back and forth between depth-first and breadth-first at the library level, so the first dependent library it sees determines which search method to use.
For example:
# build standard mode dlls # libfile1.sl is a dependent of libfile2.sl ld -b file1.o -o libfile1.sl +h libfile1.1 ld -b file2.o -o libfile2.sl +h libfile2.1 -L. -lfile1 # build compatibility mode dlls # libfile3.sl is a dependent of libfile4.sl ld -b file3.o -o libfile3.sl +h libfile3.1 ld -b file4.o -o libfile4.sl +h libfile4.1 -L. -lfile3 +compat ln -s libfile1.sl libfile1.1 ln -s libfile3.sl libfile3.1 # build a dll using both standard and compatibility mode dependent dlls # since we didn't specify +compat, the resulting dll is a standard mode dll ld -b file5.o -o libfile5.sl +h libfile5.1 -L. -lfile4 -lfile2 ln -s libfile4.sl libfile4.1 ln -s libfile2.sl libfile2.1 ld main.o -L. -lfile5 -lcThe resulting a.out has standard mode dependents, libfile5.sl and libc.sl. libfile5.sl has two dependents,: libfile4.sl and libfile2.sl. libfile4.sl is a compatibility mode library, and has a dependent, libfile3.sl. libfile2.sl is a standard mode library, and has a dependent, libfile1.sl. The dynamic loader does a breadth-first search of all dependent libraries needed by a.out because the link was done without +compat and libfile5.sl is a standard mode library. The loader uses 64-bit mode search techniques on all libraries except for libfile3.sl, in which case it uses 32-mode search techniques.
| NOTE | Embedded path inheritance is not applied to any mixed mode shared library and its descendents. It is only applied to libraries in an a.out linked with +compat. Embedded path inheritance does not apply to a breadth-first search mechanism. |
ld -b file1.o -o libfile1.sl +h libfile1.1 ld -b file2.o -o libfile2.sl +h ./libfile2.1 ld -b file3.o -o libfile3.sl +h /var/tmp/libfile3.1 ld -b file4.o -o libfile4.sl ld -b +compat file3a.o -o libfile3a.sl -L. -lfile -lfile3 +h libfile3a.1 ld -b +compat file2a.o -o libfile2a.sl libfile2.sl ./libfile4.sl +b /var/tmp elfdump -L libfile3a.sl libfile2a.sl
libfile3a.sl:
*** Dynamic Section ***
Index Tag Value 0 HPNeeded 1:./libfile1.1 1 HPNeeded 1:/var/tmp/libfile3.1 2 Soname libfile3a.1 ...
libfile2a.sl:
*** Dynamic Section ***
Index Tag Value 0 HPNeeded 0:/home/knish/./libfile2.1 1 HPNeeded 0:./libfile4.sl 2 Rpath /var/tmp ...
ld -b file1.o -o libfile1.sl +h libfile1.1 ld -b file2.o -o libfile2.sl +h ./libfile2.1 ld -b file3.o -o libfile3.sl +h /var/tmp/libfile3.1 ld -b file4.o -o libfile4.sl ld -b file3a.o -o libfile3a.sl -L. -lfile1 -lfile3 +h libfile3a.1 ld -b file2a.o -o libfile2a.sl libfile2.sl ./libfile4.sl +b /var/tmp elfdump -L libfile3a.sl libfile2a.sl
libfile3a.sl:
*** Dynamic Section ***
Index Tag Value/Ptr0 Needed libfile1.1 1 Needed /var/tmp/libfile3.1 2 Soname libfile3a.1 3 Rpath . ...
libfile2a.sl:
*** Dynamic Section ***
Index Tag Value/Ptr0 Needed ./libfile2.1 1 Needed ./libfile4.sl 2 Rpath /var/tmp ...The dynamic loader does dynamic path searching for libfile1.sl.. It does not do dynamic path searching for libfile2.sl, libfile3.sl, and libfile4.sl.
# build standard mode shared libraries #libfile1.sl is a dependent of libfile2.sl ld -b file1.o -o libfile1.sl +h libfile1.1 ld -b file2.o -o libfile2.sl +h libfile2.1 -L. -lfile1 ld main.o -L. -lfile2 -lc # move dependent lib so dld can't find it # dld won't find library because we didn't set the environment # variable LD_LIBRARY_PATH and SHLIB_PATH # By default, dld will look at the environment variables # LD_LIBRARY_PATH and # SHLIB_PATH when doing dynamic path searching unless +noenvvar # is specified mv libfile2.sl /var/tmp ln -s /var/tmp/libfile2.sl /var/tmp/libfile2.1 a.out dld.sl: Unable to find library 'libfile2.1' export SHLIB_PATH=/var/tmp a.out in file1 in file2
# build compatibility mode dlls # libfile1.sl is a dependent of libfile2.sl ld -b file1.o -o libfile1.sl +h libfile1.1 ld -b file2.o -o libfile2.sl +h libfile2.1 -L. -lfile1 +compat ln -s libfile1.sl libfile1.1 ld main.o +compat -L. -lfile2 -lc # move dependent lib so dld can't find it. Even when we specify SHLIB_PATH dld won't be # able to find the dependent because we didn't link with +s mv libfile2.sl /var/tmp ln -s /var/tmp/libfile2.sl /var/tmp/libfile2.1 a.out dld.sl: Unable to find library '1:./libfile2.1' export SHLIB_PATH=/var/tmp a.out dld.sl: Unable to find library '1:./libfile2.1'You can use chatr +s to enable a.out in file1 and file2:
chatr +s enable a.out
# build standard mode dlls # libfile1.sl is a dependent of libfile2 ld -b file1.o -o libfile1.sl +h libfile1.1 mkdir TMP ld -b +b $pwd/TMP file2.o -o libfile2.sl +h libfile2.1 -L. -lfile1 # build compatibility mode dlls # libfile3.sl is a dependent of libfile4 ld -b file3.o -o libfile3.sl +h libfile3.1 ld -b file4.o -o libfile4.sl +b $pwd/TMP +h libfile4.1 +compat -L. -lfile3 ln -s libfile1.sl libfile1.1 ln -s libfile3.sl libfile3.1 mv libfile1.sl TMP mv libfile3.sl TMP cd TMP ln -s libfile1.sl libfile1.1 ln -s libfile3.sl libfile3.1 cd ..# link with +b so ld will use RPATH at link time to find # libfile1.sl (standard mode dll) # the linker will not use RPATH to find libfile3.sl # (compatibility mode dll) # Note that this is true in both a standard mode link and a # compatibility mode link. The # linker never uses RPATH to find any compatibility mode dlls ld -b +b pwd/TMP main.o -o libfile5.sl +h libfile5.1 -L. -lfile2 -lfile4 ld: Can't find dependent library "./libfile3.sl" ld -b +b pwd/TMP main.o -o libfile5a.sl +h libfile5.1 -L. -lfile2 -lfile4 +compat ld: Can't find dependent library "./libfile3.sl"
lib1.sl has dependents lib2.sl, lib3.sl, and lib4.sl lib2.sl has dependents lib2a.sl and lib2b.sl lib3.sl has dependents lib3a.sl and lib3b.sl lib3a.sl has dependent lib3aa.sl
+-->lib2a.sl | +-->lib2.sl-->lib2b.sl | lib1.sl-->lib3.sl-->lib3a.sl-->lib3aa.sl | | | +-->lib3b.sl +-->lib4.slIn breadth-first searching, the load order is siblings before children:
lib1.sl->lib2.sl->lib3.sl->lib4.sl->lib2a.sl->lib2b.sl->lib3a.sl->lib3b.sl->lib3aa.slIn depth-first searching, the load order is children before siblings:
lib1.sl->lib2.sl->lib2a.sl->lib2b.sl->lib3.sl->lib3a.sl->lib3aa.sl->lib3b.sl->lib4.sl
ld -b bar.o -o libbar.sl ld -b foo.o -o libfoo.sl -L. -lbar +b /var/tmp # ld should look in /var/tmp to find libbar.sl since libfoo.sl # has an embedded RPATH of # /var/tmp mv libbar.sl /var/tmp ld main.o -L. -lfoo -lc # For compatibility mode dlls, embedded RPATHs are ignored ld -b bar.o -o libbar.sl ld -b foo.o -o libfoo.sl +compat -L. -lbar +b /var/tmp # ld won't find libbar.sl since it does not look at embedded RPATHs mv libbar.sl /var/tmp ld main.o -L. -lfoo +compat -lc ld: Can't find dependent library "libbar.sl" Fatal error.
This example builds two shared libraries (libx.sl and liba.sl) with a dependency on the same third library (libb.sl). If the three shared libraries are moved to a new location, the dynamic loader successfully loads lib/libb.sl as a dependency of lib/libx.sl (rpath includes the lib directory).While loading lib/liba.sl, the dynamic loader unsuccessfully looks for ./libb.sl (rpath does not include the lib directory), then looks for previously-loaded libraries with a basename of libb.sl and successfully resolves libb.sl to lib/libb.sl that was loaded as a result of the load of lib/libx.sl.
$ cat liba.c
extern int funcb();
extern int funca() {
funcb();
return 0;
}
$ cat libb.c
extern int funcb() {
return 0;
}
$ cat libx.c
extern int funca();
extern int funcb();
int funcx() {
funca();
funcb();
return 0;
}
$ cat main.c
extern int funcx();
int main() {
funcx();
}
Dependency structure:
main | +-> libx | +-> liba | | | V +-> libb
$ cc -Ae +DA2.0W -c +z libb.c $ ld -b -o libb.sl libb.o $ cc -Ae +DA2.0W -c +z liba.c $ ld -b -o liba.sl liba.o -L. -lb $ cc -Ae +DA2.0W -c +z libx.c $ ld -b -o libx.sl libx.o -L. -Llib -la -lb $ cc -Ae +DA2.0W -o x main.c -L. -Llib -lx $ export SHLIB_PATH=/tmp $ export LD_LIBRARY_PATH=/var/tmp $ ./x $ ldd -s ./x
find library=libx.sl; required by ./x search path=/var/tmp (LD_LIBRARY_PATH) trying path=/var/tmp/libx.sl search path=/tmp (SHLIB_PATH) trying path=/tmp/libx.sl search path=.:lib:/usr/lib/pa20_64:/opt/langtools/lib/pa20_64: (RPATH) trying path=./libx.sl libx.sl => ./libx.sl
find library=libc.2; required by ./x search path=/var/tmp (LD_LIBRARY_PATH) trying path=/var/tmp/libc.2 search path=/tmp (SHLIB_PATH) trying path=/tmp/libc.2 search path=.:lib:/usr/lib/pa20_64:/opt/langtools/lib/pa20_64: (RPATH) trying path=./libc.2 trying path=lib/libc.2 trying path=/usr/lib/p libc.2 => /usr/lib/pa20_64/libc.2
find library=liba.sl; required by ./libx.sl search path=/var/tmp (LD_LIBRARY_PATH) trying path=/var/tmp/liba.sl search path=/tmp (SHLIB_PATH) trying path=/tmp/liba.sl search path=.:lib (RPATH) trying path=./liba.sl liba.sl => ./liba.sl
find library=libb.sl; required by ./libx.sl search path=/var/tmp (LD_LIBRARY_PATH) trying path=/var/tmp/libb.sl search path=/tmp (SHLIB_PATH) trying path=/tmp/libb.sl search path=.:lib (RPATH) trying path=./libb.sl libb.sl => ./libb.sl
find library=libdl.1; required by /usr/lib/pa20_64/libc.2 search path=/var/tmp (LD_LIBRARY_PATH) trying path=/var/tmp/libdl.1 search path=/tmp (SHLIB_PATH) trying path=/tmp/libdl.1 search path=/usr/lib/pa20_64 (RPATH) trying path=/usr/lib/pa20_64/libdl.1 libdl.1 => /usr/lib/pa20_64/libdl.1
find library=libb.sl; required by ./liba.sl search path=/var/tmp (LD_LIBRARY_PATH) trying path=/var/tmp/libb.sl search path=/tmp (SHLIB_PATH) trying path=/tmp/libb.sl search path=. (RPATH) trying path=./libb.sl libb.sl => ./libb.sl search path=./libb.sl (BASENAME MATCH)
$ mv liba.sl libb.sl libx.sl lib/ $ ./x $ ldd -s ./x
find library=libx.sl; required by ./x search path=/var/tmp (LD_LIBRARY_PATH) trying path=/var/tmp/libx.sl search path=/tmp (SHLIB_PATH) trying path=/tmp/libx.sl search path=.:lib:/usr/lib/pa20_64:/opt/langtools/lib/pa20_64: (RPATH) trying path=./libx.sl trying path=lib/libx.sl libx.sl => lib/libx.sl
find library=libc.2; required by ./x search path=/var/tmp (LD_LIBRARY_PATH) trying path=/var/tmp/libc.2 search path=/tmp (SHLIB_PATH) trying path=/tmp/libc.2 search path=.:lib:/usr/lib/pa20_64:/opt/langtools/lib/pa20_64: (RPATH) trying path=./libc.2 trying path=lib/libc.2 trying path=/usr/lib/pa20_64/libc.2 libc.2 => /usr/lib/pa20_64/libc.2
find library=liba.sl; required by lib/libx.sl search path=/var/tmp (LD_LIBRARY_PATH) trying path=/var/tmp/liba.sl search path=/tmp (SHLIB_PATH) trying path=/tmp/liba.sl search path=.:lib (RPATH) trying path=./liba.sl trying path=lib/liba.sl liba.sl => lib/liba.sl
find library=libb.sl; required by lib/libx.sl search path=/var/tmp (LD_LIBRARY_PATH) trying path=/var/tmp/libb.sl search path=/tmp (SHLIB_PATH) trying path=/tmp/libb.sl search path=.:lib (RPATH) trying path=./libb.sl trying path=lib/libb.sl libb.sl => lib/libb.sl
find library=libdl.1; required by /usr/lib/pa20_64/libc.2 search path=/var/tmp (LD_LIBRARY_PATH) trying path=/var/tmp/libdl.1 search path=/tmp (SHLIB_PATH) trying path=/tmp/libdl.1 search path=/usr/lib/pa20_64 (RPATH) trying path=/usr/lib/pa20_64/libdl.1 libdl.1 => /usr/lib/pa20_64/libdl.1
find library=libb.sl; required by lib/liba.sl search path=/var/tmp (LD_LIBRARY_PATH) trying path=/var/tmp/libb.sl search path=/tmp (SHLIB_PATH) trying path=/tmp/libb.sl search path=. (RPATH) trying path=./libb.sl search path=/lib/pa20_64:/usr/lib/pa20_64 (default) trying path=/lib/pa20_64/libb.sl trying path=/usr/lib/pa20_64/libb.sl search path=lib/libb.sl (BASENAME MATCH)
cc -c +DD64 me.c ld -b me.o -o libme.sl ld -b bar.o -o libbar.sl -L. -lme +b /var/tmp mv libme.sl /var/tmp ld main.o -L. -lbar -lcIn 64-bit mode, the linker finds libme.sl in /var/tmp because the +b /var/tmp option is used when linking libbar.sl. Since -lme was specified when linking libbar.sl, libme.sl is subject to run-time dynamic path searching.
When linking main.o, the link order in the above example is:
./libme.sl not found
/var/tmp/libme.sl found
./libc.sl not found
/usr/lib/pa20_64/libc.sl found
ld main.o -L. -lbar -lc mv libme.sl /var/tmp
instead of:
mv libme.sl /var/tmp ld main.o -L. -lbar -lc
the linker findslibme.sl in ./ at link time, and the dynamic loader finds libme.sl in /var/tmp at run time.
At run time, the dynamic loader searches paths to resolve external references made by main in the following order:
SHLIB_PATH to find libbar.sl not found
./libbar.sl (./libbar.sl) found
LD_LIBRARY_PATH to find libme.sl not found
SHLIB_PATH to find libme.sl not found
/var/tmp/libme.slfound
LD_LIBRARY_PATH to find libc.sl not found
SHLIB_PATH to find libc.sl not found
./libc.slnot found
cc -c +DD32 me.c ld -b me.o -o libme.sl ld -b bar.o -o libbar.sl -L. -lme +b /var/tmp ld main.o -L. -lbar -lc mv libme.sl /var/tmpWhen linking main.o, the link order is:
./libme.sl found
./libc.sl not found
/usr/lib/libc.slfound
mv libme.sl /var/tmp ld main.o -L. -lbar -lcinstead of:
ld main.o -L. -lbar -lc mv libme.sl /var/tmp
the linker issues the following error:
ld: Can't find dependent library ./libme.sl Fatal ErrorThe linker does not look in /var/tmp to find shared libraries because in 32-bit mode the directories specified by +b pathname are only searched at run time.
Because libme.sl is specified with the -l option, it is subject to dynamic path searching.
At run time, the dynamic loader looks for shared libraries used by main in the following order:
/var/tmp/libme.slfound
./libc.sl not found
/usr/lib/libc.slfound
| NOTE | In 32-bit mode, the dynamic loader does not search the default directories to find libme.sl at run time. In 64 bit mode, by default, the dynamic loader looks in the default directories. |
| NOTE | Support for shl_load library management routines may be discontinued in a future 64-bit HP-UX release. You are encouraged to migrate to the dlopen family of routines for shared library management if you use the 64-bit mode linker toolset. |
| NOTE | Do not mix use of the shl_load and dlopen APIs. |
All of the shl_load family routines use the same user interface in 32-bit and 64-bit mode linking.
Use the following shl_load routines for shared library management:
| Routine | Action |
|---|---|
| shl_load and cxxshl_load | Explicitly load a shared library and a C++ (cftont) shared library, respectively. They have the same syntax. shl_load() lets you load a compatibility or standard mode shared library. It does depth-first searching. |
| shl_findsym | Finds the address of a global symbol in a shared library. |
| shl_get and shl_get_r | Get information about currently loaded libraries. shl_get_r is a thread-safe version of shl_get with the same syntax. |
| shl_gethandle and shl_gethandle_r | Get descriptor information about a loaded shared library. shl_gethandle_r is a thread-safe version of shl_gethandle with the same syntax. |
| shl_definesym | Adds a new symbol to the global shared library symbol table. |
| shl_getsymbols | Returns a list of symbols in a shared library. |
| shl_unload and cxxshl_unload | Unload a shared library and a C++ (cfront) shared library, respectively. They have the same syntax. |
Except for shl_get and shl_gethandle, all these routines are thread safe.
These routines are described in the shl_load(3x) man page.
dlclose
dlsym
dlerror
Use the following dl* routines for shared library management:
| Routine | Action |
|---|---|
| dlopen | Loads a shared library. This routine does breadth-first searching. |
| dlerror | Prints the last error message recorded by dld. |
| dlsym | Gets the address of a symbol in a shared library. |
| dlget | Returns information on a loaded module. |
| dlmodinfo | Returns information about a loaded module. |
| dlgetname | Retrieves the name of a loaded module given a load model descriptor. |
| dlclose | Unloads a shared library previously loaded by dlopen(). |
All the dlopen routines are thread-safe.
These routines are described in the dl*(3C) man pages.
| Command/File | Action |
|---|---|
| a.out(4) | Executable file from assembler, compiler, and linker output. |
| cc(1) | Command to invoke the HP-UX C compiler. |
| exec(2) | System loader. |
| ld(1) | Command to invoke the linker. |
#include <dl.h>If you are using HP C++, also include /opt/CC/include/CC/cxxdl.h.
Similarly, if you are using the dlopen family of routines, include /usr/include/dlfcn.h.
#include <dlfcn.h>If an error occurs when calling shared library management routines, the system error variable errno is set to an appropriate error value. Constants are defined for these error values in /usr/include/errno.h (see errno(2)). Thus, if a program checks for these error values, it must include errno.h:
#include <errno.h>Throughout this section, all examples are given in C. To learn how to call these routines from C++, FORTRAN, or Pascal, refer to the inter-language calling conventions described in the compiler documentation.
Some 32-bit mode implementations do not, by default, export all symbols defined by a program (instead exporting only those symbols imported by a shared library seen at link time). Use the -E option to ld to ensure that all symbols defined in the program are available to the loaded libraries. This is the default behavior in 64-bit mode.
To create shared libraries, compile source files with +z or
+Z
and link the resultant object files with the -b with the cc
or ld command.
When a program begins execution, its initializers are called before any other user code is executed.This allows for setup at initialization and cleanup at termination. Also, when a shared library is explicitly loaded using shl_load or dlopen or unloaded using shl_unload or dlclose, its initializers and terminators are called at the appropriate time.
In 64-bit mode, you can specify initializers and terminators even for
archive libraries or nonshared executables.
HP-UX 10.X style.
The C compiler pragma "init" can be used to declare these functions. For example:
#pragma init "my_init"void my_init() { ... do some initializations ... }
The ld command also supports the +init function
option to specify the initializer. Use this option while building a shared
library, an incomplete executable, or fully bound executable.
Use the +init option to specify the initializer functions, to be invoked in reverse order, the order the functions appear right to left on the command line. Initializers are called in depth-first order. For example, when a shared library is loaded, the initializers in all its dependent libraries are called first.
Do not use +init with the -r option. (The linker ignores the +init option.)
You can specify more than one initializer function on the command line
with multiple option-symbol pairs, that is, each function you specify must
be preceded by the +init option.
The C compiler pragma "fini" can be used to create them. For example:
#pragma fini "my_fini"void my_fini() { ... do some clean up ... }
The ld command also supports the +fini function
option to specify the terminator. Use this option while building a shared
library, an incomplete executable, or fully bound executable.
Use the +fini option to specify the terminator (finalizer) functions, to be invoked in forward order, the order the functions appear left to right on the command line. The terminator functions are called in reverse of the depth-first order of initializers.
Do not use +fini with the -r option. (The linker ignores the +fini option.)
You can specify more than one terminator function on the command line
with multiple option-symbol pairs, that is, each function you specify must
be preceded by the +fini option.
$ ld -b foo.o +I my_10x_init -o libfoo.sl
#include <dl.h>
void my_10x_init(shl_t handle, int loading)
{ /* handle is the shl_load API handle for the shared library being initialized. */
/* loading is non-zero at startup and zero at termination. */
if (loading) {
... do some initializations ...
else { ... do some clean up ...
}}
| NOTE | Unlike 32-bit mode, the 64-bit HP-UX 10.X style initializers are called when unloading implicitly loaded shared libraries. |
See Using HP-UX 10.X Style Initializers for
more information on using these initializers.
C source for lib1.sl (file lib1.c):
lib1()
{
printf("lib1\n");
}
void
lib1_init()
{
printf("lib1_init\n");
}
void
lib1_fini()
{
printf("lib1_fini\n");
}
C source for lib2.sl (file lib2.c):
ib2()
{
printf("lib2\n");
}
void
lib2_init()
{
printf("lib2_init\n");
}
void
lib2_fini()
{
printf("lib2_fini\n");
}
C source for lib3.sl (file lib3.c):
ib3()
{
printf("lib3\n");
}
#pragma init "lib3_init"
void
lib3_init()
{
printf("lib3_init\n");
}
#pragma fini "lib3_fini"
void
lib3_fini()
{
printf("lib3_fini\n");}
Commands used to build these libraries:
$ cc +DD64 lib1.c lib2.c lib3.c main.c -c; $ ld -b lib3.o -o lib3.sl; $ ld -b +init lib2_init +fini lib2_fini lib2.o -o lib2.sl; $ ld -b +init lib1_init +fini lib1_fini lib1.o ./lib3.sl -o \ lib1.sl; $ cc -L. +DD64 main.o -l1 -l2 -lc;Output from running a.out:
lib2_init lib3_init lib1_init lib1 lib2 lib3 lib1_fini lib3_fini lib2_fini
Finis in .o or .a files are called in forward order of the link line.
HP-UX 10.X style initializers are called in forward order of the +I options specified on the link line when loading a shared library. They are then called in reverse order when unloading the library.
HP-UX 10.X style initializers are called after inits and before finis.
Any inits or finis in archive (.a) files are called only if the .o which contains it is used during the link. Use the linker -v option to determine which .o files within an archive file were used.
Shared libraries on the link line (dependent libraries) follow the ordering described in Ordering Among Executables and Shared Libraries.
$ ld -b first_64bit.o -l:libfoo.sl second_64bit.o my_64bit.a +I first_10x_init +I second_10x_init -o libbar.slresults in the following order when library is loaded:
inits in second_64bit.o
inits in first_64bit.o
first_10x_init
second_10x_init
first_10x_init
finis in first_64bit.o
finis in second_64bit.o
finis from any .o files used in my_64bit.a
| NOTE | libfoo.sl is ignored in this example. It follows the rules in Ordering Among Executables and Shared Libraries. |
When unloading, the finis and HP-UX 10.X style initializers of any dependent libraries are called after the finis of the current library.
If a shared library is itself a dependent of one of its dependents (a "circular" dependency), no ordering between them is guaranteed.
$ ld -b foo.o -lB -lC -o libA.slOne possible ordering while loading is:
inits in B
inits in A
finis in B
finis in C
When calling initializers for implicitly loaded libraries, the dynamic loader waits until all libraries have been loaded before calling the initializers. It calls the initializers in depth-first order-that is, the initializers are called in the reverse order in which the libraries are searched for symbols. All initializers are called before the main program begins execution.
When calling the initializer for explicitly loaded libraries, the dynamic loader waits until any dependent libraries are loaded before calling the initializers. As with implicitly loaded libraries, initializers are called in depth-first order.
Note that initializers can be disabled for explicitly loaded libraries with the BIND_NOSTART flag to shl_load. For more information, see The shl_load and cxxshl_load Routines .
This section contains the following topics:
+I initializerwhere initializer is the initializer's name.
Multiple initializers may be called by repeating the +Iinitializer option.
For example, to create a shared library named libfoo.sl that uses an initializer named init_foo, use this linker command line:
$ ld -b -o libfoo.sl libfoo.o +I init_foo
| NOTE | Initializers are not executed when unloading shared libraries which were implicitly loaded since the program exits without re-entering the dynamic loader to unload them. Initializers are only called during the explicit unloading of a shared library. |
Initializers behave the same as other symbols; once they are bound they
cannot be overridden with a new symbol through the use of shl_definesym()
or by loading a more visible occurrence of the initializer symbol with
the BIND_FIRST flag. What this means is that once the initializer
is executed upon a load, it is guaranteed to be the same initializer that
is called on an explicit unload.
void initializer( shl_t handle, int loading )
It is strongly recommended that initializers be defined with names which
do not cause name collisions with other user-defined names in order to
avoid overriding behavior of shared library symbol binding.
If only one initializer is specified for a given library, its address
is still available through the initializer field of a shared library descriptor.
If more than one initializer is specified, the initializer field will be
set to NO_INITIALIZER. Access to multiple initializers can then
be accomplished through the use of shl_getsymbols(). (The shl_getsymbols()
routine can also access a single initializer.)
| NOTE | shl_getsymbols() may not return the initializer which was invoked for a given library if a more visible initializer symbol is defined after the library being queried has been loaded. This can occur through the use of shl_definesym() and by explicitly loading a more visible symbol using the BIND_FIRST flag upon loading. |
To access initializers, use the new flag, INITIALIZERS, in the shl_getsymbols() routine. This flag can be ORed with the NO_VALUES and GLOBAL_VALUES flags. For example,
shl_getsymbols(handle, TYPE_PROCEDURE, INITIALIZERS | GLOBAL_VALUES, malloc, &symbol_array);If the GLOBAL_VALUES modifier is not used and the initializer is defined in another shared library or in the program file, shl_getsymbols() does not find the initializer for the requested library because it is not defined within the library.
For more information on the usage of shl_getsymbols(), see
The
shl_getsymbols Routine.
#include <stdio.h>
#include <dl.h>
/*
* This is the local initializer that is called when the libfoo.sl
* is loaded and unloaded:
*/
void init_foo(shl_t hndl, int loading)
{
if (loading)
printf("libfoo loaded\n");
else
printf("libfoo unloaded\n");
}
float in_to_cm(float in) /* convert inches to centimeters */
{
return (in * 2.54);
}
float gal_to_l(float gal) /* convert gallons to litres */
{
return (gal * 3.79);
}
float oz_to_g(float oz) /* convert ounces to grams */
{
return (oz * 28.35);
}
You can use the +I linker option to register a routine as an initializer.
Here are the commands to create libfoo.sl and to register init_foo
as the initializer:
$ cc -Aa -c +z libfoo.c $ ld -b -o libfoo.sl +I init_foo libfoo.oTo use this technique with multiple libraries, each library should have a unique initializer name. The following example program loads and unloads libfoo.sl.
#include <stdio.h>
#include <dl.h>
main()
{
float (*in_to_cm)(float), (*gal_to_l)(float), (*oz_to_g)(float);
shl_t hndl_foo;
/*
* Load libfoo.sl and find the required symbols:
*/
if ((hndl_foo = shl_load("libfoo.sl",
BIND_IMMEDIATE, 0)) == NULL)
perror("shl_load: error loading libfoo.sl"), exit(1);
if (shl_findsym(&hndl_foo, "in_to_cm", TYPE_PROCEDURE,
(void *) &in_to_cm))
perror("shl_findsym: error finding in_to_cm"), exit(1);
if (shl_findsym(&hndl_foo, "gal_to_l", TYPE_PROCEDURE,
(void *) &gal_to_l))
perror("shl_findsym: error finding gal_to_l"), exit(1);
if (shl_findsym(&hndl_foo, "oz_to_g", TYPE_PROCEDURE,
(void *) &oz_to_g))
perror("shl_findsym: errror finding oz_to_g"), exit(1);
/*
* Call routines from libfoo.sl:
*/
printf("1.0in = %5.2fcm\n", (*in_to_cm)(1.0));
printf("1.0gal = %5.2fl\n", (*gal_to_l)(1.0));
printf("1.0oz = %5.2fg\n", (*oz_to_g)(1.0));
/*
* Unload the library:
*/
shl_unload(hndl_foo);
}
The following is the output of running the testlib program:
$ cc -Aa testlib.c -o testlib -ldld $ testlib libfoo loaded 1.0in = 2.54cm 1.0gal = 3.79l 1.0oz = 28.35g libfoo unloaded
This is easily done by defining load and unload functions in each library. When _INITIALIZER is called, it uses shl_findsym to find and call the load or unload function (depending on the value of the loading flag).
The following example shows the source for an _INITIALIZER
function:
C Source for _INITIALIZER (file init.c)
#include <dl.h>
/*
* Global initializer used by shared libraries that have
* registered it:
*/
void _INITIALIZER(shl_t hand, int loading)
{
void (*load_unload)();
if (loading)
shl_findsym(&hand, "load", TYPE_PROCEDURE, (void *) &load_unload);
else
shl_findsym(&hand, "unload", TYPE_PROCEDURE, (void *) &load_unload);
(*load_unload) (); /* call the function */
}
The following two source files show shared libraries that have registered
_INITIALIZER.
#include <stdio.h>
#include <dl.h>
void load() /* called after libunits.sl loaded */
{
printf("libunits.sl loaded\n");
}
void unload() /* called after libunits.sl unloaded */
{
printf("libunits.sl unloaded\n");
}
extern void _INITIALIZER();
float in_to_cm(float in) /* convert inches to centimeters */
{
return (in * 2.54);
}
float gal_to_l(float gal) /* convert gallons to litres */
{
return (gal * 3.79);
}
float oz_to_g(float oz) /* convert ounces to grams */
{
return (oz * 28.35);
}
C Source for libtwo.c
#include <stdio.h>
void load() /* called after libtwo.sl loaded */
{
printf("libtwo.sl loaded\n");
}
void unload() /* called after libtwo.sl unloaded */
{
printf("libtwo.sl unloaded\n");
}
extern void _INITIALIZER();
void (*init_ptr)() = _INITIALIZER;
void foo()
{
printf("foo called\n");
}
void bar()
{
printf("bar called\n");
}
Here are the commands used to build these libraries:
$ cc -Aa -c +z libunits.c $ ld -b -o libunits.sl +I _INITIALIZER libunits.o $ cc -Aa -c +z libtwo.c $ ld -b -o libtwo.sl +I _INITIALIZER libtwo.oThe following is an example program that loads these two libraries:
#include <stdio.h>
#include <dl.h>
main()
{
float (*in_to_cm)(float), (*gal_to_l)(float), (*oz_to_g)(float);
void (*foo)(), (*bar)();
shl_t hndl_units, hndl_two;
/*
* Load libunits.sl and find the required symbols:
*/
if ((hndl_units = shl_load("libunits.sl", BIND_IMMEDIATE, 0)) == NULL)
perror("shl_load: error loading libunits.sl"), exit(1);
if (shl_findsym(&hndl_units, "in_to_cm",
TYPE_PROCEDURE, (void *) &in_to_cm))
perror("shl_findsym: error finding in_to_cm"), exit(1);
if (shl_findsym(&hndl_units, "gal_to_l",
TYPE_PROCEDURE, (void *) &gal_to_l))
perror("shl_findsym: error finding gal_to_l"), exit(1);
if (shl_findsym(&hndl_units, "oz_to_g",
TYPE_PROCEDURE, (void *) &oz_to_g))
perror("shl_findsym: errror finding oz_to_g"), exit(1);
/*
* Load libtwo.sl and find the required symbols:
*/
if ((hndl_two = shl_load("libtwo.sl", BIND_IMMEDIATE, 0)) == NULL)
perror("shl_load: error loading libtwo.sl"), exit(1);
if (shl_findsym(&hndl_two, "foo", TYPE_PROCEDURE, (void *) &foo))
perror("shl_findsym: error finding foo"), exit(1);
if (shl_findsym(&hndl_two, "bar", TYPE_PROCEDURE, (void *) &bar))
perror("shl_findsym: error finding bar"), exit(1);
/*
* Call routines from libunits.sl:
*/
printf("1.0in = %5.2fcm\n", (*in_to_cm)(1.0));
printf("1.0gal = %5.2fl\n", (*gal_to_l)(1.0));
printf("1.0oz = %5.2fg\n", (*oz_to_g)(1.0));
/*
* Call routines from libtwo.sl:
*/
(*foo)();
(*bar)();
/*
* Unload the libraries so we can see messages displayed by initializer:
*/
shl_unload(hndl_units);
shl_unload(hndl_two);
}
Here is the compiler command used to create the executable
testlib2:
$ cc -Aa -Wl,-E -o testlib2 testlib2.c init.c -ldldNote that the -Wl,-E option is required to cause the linker to export all symbols from the main program. This allows the shared libraries to find the _INITIALIZER function in the main executable.
Finally, the output from running testlib2 is shown:
Output of testlib2
$ cc -Aa testlib2.c -o testlib2 -ldld $ testlib2 libunits.sl loaded libtwo.sl loaded 1.0in = 2.54cm 1.0gal = 3.79l 1.0oz = 28.35g foo called bar called libunits.sl unloaded libtwo.sl unloaded
| NOTE | You can use these routines in both 32-bit and 64-bit mode. Support for these routines may be discontinued in a future 64-bit HP-UX release. If you use these routines in 64-bit mode, consider converting your programs to the dl* family of shared library management routines. |
shl_t shl_load( const char * path, int flags, long address )
In addition to the above values, the flags parameter can be ORed with the following values:
However, programs are not restricted to using shared libraries only in that situation. For example, rather than linking with any required libraries, a program could explicitly load libraries as they are needed. One possible reason for doing this is to minimize virtual memory overhead. To keep virtual memory resource usage to a minimum, a program could load libraries with shl_load and unload with shl_unload when the library is no longer needed. However, it is normally not necessary to incur the programming overhead of loading and unloading libraries yourself for the sole reason of managing system resources.
Note that if shared library initializers have been declared for an explicitly loaded library, they are called after the library is loaded. For details, see Initializers for Shared Libraries.
To explicitly load a shared library, use the shl_load routine. If loading a C++ (cfront) library, use the cxxshl_load routine. This ensures that constructors of nonlocal static objects are executed when the library is loaded. The syntax of cxxshl_load is the same as that of shl_load.
In 64-bit mode, shl_load lets you load a compatibility or standard
mode shared libraries. The BIND_BREADTH_FIRST flag overrides the
default depth-first loading mechanism.
Get the library name from an environment variable using the getenv library routine (see getenv(3C)).
Get the library path name from the command line through argv.
Read the library name from a configuration file.
Prompt for the library path name at run time.
Once a library is explicitly loaded, use the shl_findsym routine to get pointers to functions or data contained in the library; then call or reference them through the pointers. This is described in detail inThe shl_findsym Routine .
Use caution when building shared libraries with external library dependencies.
Any library that contains Thread Local Storage (TLS) should not be used
as a dependency. If a dependent library contains TLS, and it is loaded
during program startup (that is, not linked against the executable), the
dynamic loader fails to perform the operation.
The function then attempts to load the specified library. If successful,
it returns the shared library handle, of type shl_t. If an error
occurs, it displays an error message and exits. This function is used later
in The shl_findsym Routine .
load_lib - Function to Load a Shared
Library
#include <stdio.h> /* contains standard I/O defs */
#include <stdlib.h> /* contains getenv definition */
#include <dl.h> /* contains shared library type defs */
shl_t load_lib(int argc,
char * argv[]) /* pass argc and argv from main */
{
shl_t lib_handle; /* temporarily holds library handle */
char lib_path[MAXPATHLEN]; /* holds library path name */
char *env_ptr; /* points to SHLPATH variable value */
/*
* Get the shared library path name:
*/
if (argc > 1) /* library path given on command line */
strcpy(lib_path, argv[1]);
else /* get lib_path from SHLPATH variable */
{
env_ptr = getenv("SHLPATH");
if (env_ptr != NULL)
strcpy(lib_path, env_ptr);
else /* prompt user for shared library path */
{
printf("Shared library to use >> ");
scanf("%s", lib_path);
}
}
/*
* Dynamically load the shared library using BIND_IMMEDIATE binding:
*/
lib_handle = shl_load( lib_path, BIND_IMMEDIATE, 0);
if (lib_handle == NULL)
perror("shl_load: error loading library"), exit(1);
return lib_handle;
}
shl_t libH;
. . .
libH = shl_load("libxyz.sl", BIND_IMMEDIATE | BIND_NONFATAL, 0);
However, data symbol binding cannot be deferred, so using the BIND_NONFATAL
modifier does not allow the binding of unresolved data symbols.
shl_t libH;
. . .
libH = shl_load("libxyz.sl", BIND_IMMEDIATE | BIND_VERBOSE, 0);
shl_t libH;
. . .
libH = shl_load("libpdq.sl", BIND_DEFERRED | BIND_FIRST, 0);
BIND_FIRST is typically used when you want to make the symbols
in a particular library more visible than the symbols of the same name
in other libraries. Compare this with the default behavior, which is to
append
loaded libraries to the link order.
When used with only the BIND_DEFERRED flag, it has this behavior: When a symbol is referenced and needs to be bound, this flag causes the search for the symbol definition to be restricted to those symbols that were visible when the library was loaded. If a symbol definition cannot be found within this restricted set, it results in a run-time symbol-binding error.
When used with BIND_DEFERRED and the BIND_NONFATAL
modifier, it has the same behavior, except that when a symbol definition
cannot be found, the dynamic loader will then look in the global symbol
set. If a definition still cannot be found within the global set, a run-time
symbol-binding error occurs.
BIND_FIRST | BIND_TOGETHER causes the library being loaded
and its dependent libraries to be bound all at the same time, thereby resolving
interdependencies. If you are not using BIND_FIRST, libraries
are bound together by default so this option has no effect.
This flag causes the dependent libraries to be loaded breadth first.
By default, the 64-bit mode shl_load loads dependent libraries
depth-first. This modifier overrides the default load order.
When using BIND_DEFERRED or BIND_IMMEDIATEwithoutBIND_FIRST, these libraries are loaded such that all symbols are visible and the interdependencies are resolved:
shl_t libE;
libE = shl_load("libE.sl", BIND_IMMEDIATE, 0);
shl_load succeeds.
When using BIND_IMMEDIATE | BIND_FIRST, however, libG
is loaded and bound first and since it depends on libF, an error
results because the needed symbols in libF are not yet available:
libE = shl_load("libE.sl", BIND_IMMEDIATE | BIND_FIRST, 0);
shl_load fails.
Using BIND_IMMEDIATE | BIND_FIRST | BIND_TOGETHER loads libE,
libF,
and libG together and correctly resolves all symbols:
libE = shl_load("libE.sl", BIND_IMMEDIATE | BIND_FIRST | BIND_TOGETHER, 0);
shl_load succeeds.
int shl_findsym( shl_t * handle, const char * sym, short type, void * value )
shl_t handle; handle = NULL; shl_findsym(&handle,"_foo",...);
To call a routine in an explicitly loaded library
using shl_findsym with the type parameter set to TYPE_PROCEDURE, find the symbol in the shared library and assign its address to the function pointer declared in Step 1
call the pointer to the function obtained in Step 2, with the correct number and type of arguments
using shl_findsym with the type parameter set to TYPE_DATA, find the symbol in the shared library and assign its address to the pointer declared in Step 1
#include <stdio.h> /* contains standard I/O defs */
#include <stdlib.h> /* contains getenv definition */
#include <dl.h> /* contains shared library type defs */
/*
* Define linker symbols:
*/
#define GOPEN "gopen"
#define GCLOSE "gclose"
#define MOVE2D "move2d"
#define DRAW2D "draw2d"
#define MAXX "maxX"
#define MAXY "maxY"
shl_t load_lib(int argc, char * argv[]);
main(int argc,
char * argv[])
{
shl_t lib_handle; /* handle of shared library */
int (*gopen)(void); /* opens the graphics device */
int (*gclose)(void); /* closes the graphics device */
int (*move2d)(int, int); /* moves to specified x,y location */
int (*draw2d)(int, int); /* draw line to specified x,y location*/
int *maxX; /* maximum X pixel on device */
int *maxY; /* maximum Y pixel on device */
lib_handle = load_lib(argc, argv); /* load required shared library */
/*
* Get addresses of all functions and data that will be used:
*/
if (shl_findsym(&lib_handle, GOPEN, TYPE_PROCEDURE, (void *) &gopen))
perror("shl_findsym: error finding function gopen"), exit(1);
if (shl_findsym(&lib_handle, GCLOSE, TYPE_PROCEDURE, (void *) &gclose))
perror("shl_findsym: error finding function gclose"), exit(1);
if (shl_findsym(&lib_handle, MOVE2D, TYPE_PROCEDURE, (void *) &move2d))
perror("shl_findsym: error finding function move2d"), exit(1);
if (shl_findsym(&lib_handle, DRAW2D, TYPE_PROCEDURE, (void *) &draw2d))
perror("shl_findsym: error finding function draw2d"), exit(1);
if (shl_findsym(&lib_handle, MAXX, TYPE_DATA, (void *) &maxX))
perror("shl_findsym: error finding data maxX"), exit(1);
if (shl_findsym(&lib_handle, MAXY, TYPE_DATA, (void *) &maxY))
perror("shl_findsym: error finding data maxY"), exit(1);
/*
* Using the routines, draw a line from (0,0) to (maxX,maxY):
*/
(*gopen)(); /* open the graphics device */
(*move2d)(0,0); /* move to pixel 0,0 */
(*draw2d)(*maxX,*maxY); /* draw line to maxX,maxY pixel */
(*gclose)(); /* close the graphics device */
}
Shown below is the compile line for this program, along with the commands
to set SHLPATH appropriately before running the program. SHLPATH
is declared and used by load_lib(), defined inThe
shl_load and cxxshl_load Routines example. Notice that load_lib()
is compiled here along with this program. Finally, this example assumes
you have created a graphics library, libgrphdd.sl:
$ cc -Aa -o drawline shl_findsym.c load_lib.c -ldld $ SHLPATH=/usr/lib/libgrphdd.sl $ export SHLPATH $ drawline
int shl_get( int index, struct shl_descriptor **desc )
If the shared library has multiple initializers, this field will also be set to NULL. Multiple initializers can be found with shl_getsymbols, described later in this chapter.
Other than obtaining interesting information, this routine is of little
use to most programmers. A typical use might be to display the names and
starting/ending address of all shared libraries in a process's virtual
memory address space.
#include <stdio.h> /* contains standard I/O defs */
#include <dl.h> /* contains shared library type defs */
void show_loaded_libs(void)
{
int idx;
struct shl_descriptor *desc;
printf("SUMMARY of currently loaded libraries:\n");
printf("%-25s %10s %10s %10s %10s\n",
"___library___", "_tstart_", "__tend__", "_dstart_", "__dend__");
idx = 0;
for (idx = 0; shl_get(idx, &desc) != -1; idx++)
printf("%-25s %#10lx %#10lx %#10lx %#10lx\n",
desc->filename, desc->tstart, desc->tend, desc->dstart, desc->dend);
}
Calling this function from a C program compiled with shared
libc
and libdld produced the following output:
SUMMARY of currently loaded libraries: ___library___ _tstart_ __tend__ _dstart_ __dend__ ./a.out 0x1000 0x1918 0x40000000 0x40000200 /usr/lib/libdld.sl 0x800ac800 0x800ad000 0x6df62800 0x6df63000 /usr/lib/libc.sl 0x80003800 0x80091000 0x6df63000 0x6df85000
int shl_gethandle( shl_t handle, struct shl_descriptor **desc )
#include <stdio.h>
#include <dl.h>
int show_lib_info(shl_t libH)
{
struct shl_descriptor *desc;
if (shl_gethandle(libH, &desc) == -1)
{
fprintf(stderr, "Invalid library handle.\\n");
return -1;
}
printf("library path: %s\\n", desc->filename);
printf("text start: %#10lx\\n", desc->tstart);
printf("text end: %#10lx\\n", desc->tend);
printf("data start: %#10lx\\n", desc->dstart);
printf("data end: %#10lx\\n", desc->dend);
return 0;
}
int shl_definesym( const char *sym, short type, long value, int flags )
There are two main reasons to add or change shared library symbol table entries:
to override a current definition
The incomplete executable only uses a more visible code symbol if the main program itself does not provide a definition.
int shl_getsymbols( shl_t handle, short type, int flags, void * (*memfunc)(), struct shl_symbol **symbols )
struct shl_symbol {
char * name;
short type;
void * value;
shl_t handle;
};
The members of this structure are described in The
shl_symbol Structure.
int show_symbols(shl_t hndl, short type, int flags)
#include <dl.h>
#include <stdio.h>
#include <stdlib.h>
int show_symbols(shl_t hndl,
short type,
int flags)
{
int num_symbols, sym_idx;
struct shl_symbol *symbols, *orig_symbols;
num_symbols = shl_getsymbols(hndl, type, flags, malloc, &symbols);
if (num_symbols < 0) {
printf("shl_getsymbols failed\n");
exit(1);
}
orig_symbols = symbols;
for (sym_idx = 0; sym_idx < num_symbols; sym_idx++)
{
printf(" %-30s", symbols->name); /* display symbol name */
if (type == TYPE_UNDEFINED) /* display type if TYPE_UNDEFINED */
switch (symbols->type) {
case TYPE_PROCEDURE:
printf(" PROCEDURE");
break;
case TYPE_DATA:
printf(" DATA ");
break;
case TYPE_STORAGE:
printf(" STORAGE ");
}
if ((flags & EXPORT_SYMBOLS) /* export symbols requested */
&& (flags & NO_VALUES)==0) /* NO_VALUES was NOT specified */
printf(" 0x%8X", symbols->value); /* so display symbol's address */
printf("\n"); /* terminate output line */
symbols++; /* move to next symbol record */
}
free(orig_symbols); /* free memory allocated by malloc */
return num_symbols; /* return the number of symbols */
}
The following example shows the source for a program named
show_all.c
that calls show_symbols to show all imported and exported symbols
for every loaded shared library. It uses shl_get to get the library
handles of all loaded libraries.
#include <dl.h>
#include <stdio.h>
/* prototype for show_syms */
int show_syms(shl_t hndl, short type, int flags);
main()
{
int idx, num_syms;
struct shl_descriptor * desc;
for (idx=0; shl_get(idx, &desc) != -1; idx++) /* step through libs */
{
printf("[%s]\n", desc->filename); /* show imports & exports for each */
printf(" Imports:\n");
num_syms = show_symbols(desc->handle, TYPE_UNDEFINED, IMPORT_SYMBOLS);
printf(" TOTAL SYMBOLS: %d\n", num_syms);
printf(" Exports:\n");
num_syms = show_symbols(desc->handle, TYPE_UNDEFINED, EXPORT_SYMBOLS);
printf(" TOTAL SYMBOLS: %d\n", num_syms);
}
}
The show_all program shown above was compiled with the command:
$ cc -Aa -o show_all show_all.c show_symbols.c -ldld
| NOTE | The following output for the example will differ in 64-bit mode. For example, STORAGE is not supported. |
The output produced by running this program is shown below:
[show_all] Imports: errno STORAGE _start PROCEDURE malloc PROCEDURE free PROCEDURE exit PROCEDURE printf PROCEDURE shl_get PROCEDURE shl_getsymbols PROCEDURE __d_trap PROCEDURE TOTAL SYMBOLS: 9 Exports: environ DATA 0x40001018 errno STORAGE 0x400011CC _SYSTEM_ID DATA 0x40001008 __dld_loc STORAGE 0x400011C8 _FPU_MODEL DATA 0x4000100C _end DATA 0x400011D0 _environ DATA 0x40001018 __d_trap PROCEDURE 0x7AFFF1A6 main PROCEDURE 0x7AFFF1BE TOTAL SYMBOLS: 9 [/usr/lib/libc.1] Imports: _res_rmutex STORAGE errno STORAGE _regrpc_rmutex STORAGE _yellowup_rmutex STORAGE _FPU_MODEL STORAGE _environ_rmutex STORAGE _iop_rmutex STORAGE _rpcnls_rmutex STORAGE _switch_rmutex STORAGE _mem_rmutex STORAGE _dir_rmutex STORAGE
int shl_unload(shl_t handle)
Another reason for doing this occurs if a program needs to replace a shared library. For example, suppose you implement some sort of shell or interpreter, and you want to load and execute user "programs" which are actually shared libraries. So you load one program, look up its entry point, and call it. Now you want to run a different program. If you do not unload the old one, its symbol definitions might get in the way of the new library. So you should unload it before loading the new library.
Note that if shared library initializers have been declared for a shared library, they will be called when the shared library is explicitly unloaded. For details, see Initializers for Shared Libraries.
If unloading a C++ (cfront) library, use the cxxshl_unload
routine. This ensures that destructors of nonlocal static objects are executed
when the library is unloaded. The syntax of cxxshl_unload is the
same as that of shl_unload.
In 32-bit mode the shl_unload routine unloads a shared library
irrespective of whether other shared libraries depend on it. In 64-bit
mode shl_unload unloads a shared library only if no other shared
library depend on it.
dlclose
dlsym
dlerror
void *dlopen(const char *file, int mode);
| Parm | Definition | |
|---|---|---|
| file | Used to construct a pathname to the shared library file.
If files contain a slash character (/), dlopen uses the file argument itself as the pathname. If not, dlopen searches a series of directories for file.
Any directories specified by the variable SHLIB_PATH. Any directories specified by the RPATH of the calling load module. The directories /usr/lib and usr/ccs/lib in 32-bit mode and /usr/lib/pa20_64 and usr/ccs/lib/pa20_64 in 64-bit mode. |
|
| flags | Mode | Definition |
| RTLD_LAZY | Under this mode, only references to data symbols are relocated when the library t is loaded. References to functions are not relocated until a given function is invoked for the first time. This mode should result in better performance, since a process may not reference all of the functions in any given shared object. | |
| RTLD_NOW | Under this mode, all necessary relocations are performed when the library is first loaded. This can cause some wasted effort, if relocations are performed for functions that are never referenced, but is useful for applications that need to know as soon as an object is loaded that all symbols referenced during execution are available. | |
| RTLD_GLOBAL | The shared library's symbols are made available for the relocation processing of any other object. In addition, symbol lookup using dlopen(0, mode) and an associated dlsym() allows objects loaded with RTLD_GLOBAL to be searched. | |
| RTLD_LOCAL | The shared library's symbols are made available for relocation processing
only to objects loaded in the same dlopen invocation.
If neither RTLD_GLOBAL nor RTLD_LOCAL are specified, the default is RTLD_LOCAL. |
dlopen returns NULL under the following conditions:
file cannot be opened for reading.
file is not a shared object.
An error occurs during the process of loading file or relocating its symbolic references.
If the value of file is 0, dlopen provides a handle on a "global symbol shared library." This shared library provides access to the symbols from an ordered set of shared libraries consisting of the original a.out, all of the shared libraries that were loaded at program startup along with the a.out, and all shared libraries loaded using a dlopen operation along with the RTLD_GLOBAL flag. As the latter set of shared libraries can change during execution, the set identified by handle can also change dynamically.
Only a single copy of a shared library file is brought into the address space, even if dlopen is invoked multiple times in reference to the file, and even if different pathnames are used to reference the file.
When a shared library is brought into the address space of a process, it can contain references to symbols whose addresses are not known until the shared library is loaded. These references must be relocated before the symbols can be accessed. The mode parameter governs when these relocations take place and may have the following values (defined in Parameters): RTLD_LAZY and RTLD_NOW.
Any shared library loaded by dlopen that requires relocations against global symbols can reference the following:
Any shared libraries loaded at program startup, from the shared library itself.
Any shared library included in the same dlopen invocation.
Any shared libraries that were loaded in any dlopen invocation that specified the RTLD_GLOBAL flag.
If neither RTLD_GLOBAL nor RTLD_LOCAL are specified, the default is RTLD_LOCAL.
If a file is specified in multiple dlopen invocations, mode is interpreted at each invocation. Note, however, that once RTLD_NOW has been specified, the linker operation completes all relocations, rendering any further RTLD_NOW operations redundant and any further RTLD_LAZY operations irrelevant. Similarly note that once you specify RTLD_GLOBAL, the shared library maintains the RTLD_GLOBAL status regardless of any previous or future specification of RTLD_LOCAL, as long as the shared library remains in the address space [see dlclose(3C)].
Symbols introduced into a program through calls to dlopen may be used in relocation activities. Symbols so introduced may duplicate symbols already defined by the program or previous dlopen operations. To resolve the ambiguities such a situation might present, the resolution of a symbol reference to a symbol definition is based on a symbol resolution order. Two such resolution orders are defined: load and dependency ordering.
Dependency ordering uses a "breadth-first" order starting with a given shared library, then all of its dependencies, then any dependents of those, iterating until all dependencies are satisfied.
When a dlopen operation first makes it accessible, a shared library and its dependent shared libraries are added in dependency order. Once all shared libraries are added, relocations are performed using load order. Note that if a shared library and its dependencies have been loaded by a previous dlopen invocation or on startup, the load and dependency order may yield different resolutions.
The symbols introduced by dlopen operations and available through
dlsym
are those which are "exported" as symbols of global scope by the shared
library. For shared libraries, such symbols are typically those that were
specified in (for example) C source code as having extern linkage.
For a.out files, only a subset of externally visible symbols are
typically exported: specifically those referenced by the shared libraries
with which the a.out is linked. The exact set of exported symbols
for any shared library or the a.out can be controlled using the
linker [see ld(1)].
| NOTE | The environment variable LD_LIBRARY_PATH should contain a colon-separated list of directories, in the same format as the PATH variable [see sh(1)]. In 64-bit modeLD_LIBRARY_PATH is ignored if the process' real user id is different from its effective user id or its real group id is different from its effective group id [see exec(2)] or if the process has acquired any privileges [see tfadmin(1M)]. |
| NOTE | In 64-bit mode, with the +compat option specified, LD_LIBRARY_PATH and the +b embedded path are ignored when searching for dependent libraries. ]. |
#include <stdio.h> #include <dlfcn.h>
int main(int argc, char **argv)
{
void* handle; handle = dlopen("./lib1.sl", RTLD_GLOBAL | RTLD_LAZY);
if (handle == NULL) {
printf("Cannot load library\n");
}
}
char *dlerror(void);
| NOTE | The messages returned by dlerror may reside in a static buffer that is overwritten on each call to dlerror. Application code should not write to this buffer. Programs wishing to preserve an error message should make their own copies of that message. |
void* handle;
/* Try to load a non-existing library */
handle = dlopen("invalid.sl", RTLD_GLOBAL | RTLD_LAZY);
if (handle == NULL) {
printf("%s\n", dlerror());
}
void *dlsym(void *handle, const char *name);
| Parameter | Definition |
|---|---|
| handle | Either the value returned by a call to dlopen or the special flag RTLD_NEXT. In the former case, the corresponding shared library must not have been closed using dlclose. |
| name | The symbol's name as a character string. |
The dlsym routine searches for the named symbol in all shared
libraries loaded automatically as a result of loading the shared library
referenced by handle [see dlopen(3C)]. If handle
is RTLD_NEXT, the search begins with the "next" shared library
after the shared library from which dlsym was invoked. Shared
libraries are searched using a load order symbol resolution algorithm [see
dlopen(3C)].
The "next" shared library, and all other shared libraries searched, are
either of global scope (because they were loaded at startup or as part
of a dlopen operation with the RTLD_GLOBAL flag) or are
shared libraries loaded by the same dlopen operation that loaded
the caller of dlsym.
void *handle;
int i, *iptr;
int (*fptr)(int);
/* open the needed object */
handle = dlopen("/usr/mydir/mylib.sl", RTLD_LAZY);
/* find address of function and data objects */
fptr = (int (*)(int))dlsym(handle, "some_function");
iptr = (int *)dlsym(handle, "int_object");
/* invoke function, passing value of integer as a parameter */
i = (*fptr)(*iptr);
The next example shows how to use dlsym with RTLD_NEXT
to add functionality to an existing interface. (Error checking has been
omitted.)
extern void record_malloc(void *, size_t);
void *
malloc(size_t sz)
{
void *ptr;
void *(*real_malloc)(size_t);
real_malloc = (void * (*) (size_t))
dlsym(RTLD_NEXT, "malloc");
ptr = (*real_malloc)(sz);
record_malloc(ptr, sz);
return ptr;
}
void *dlget(unsigned int index, struct load_module_desc *desc, size_t desc_size);
| Parameter | Definition |
|---|---|
| index | Specifies the requested shared library by its placement on the dynamic loader's search list. An index of zero requests information about the program file itself. An index of -1 requests info about the dynamic loader. |
| desc | Must be preallocated by the user. The structure members are filled in by the dynamic loader with information about the requested shared library. |
| desc_size | Specifies the size in bytes of the load_module_desc structure sent in by the user. |
A load_module_desc structure has the following members:
struct load_module_desc {
unsigned long text_base;
unsigned long text_size;
unsigned long data_base;
unsigned long data_size;
unsigned long unwind_base;
unsigned long linkage_ptr;
unsigned long phdr_base;
unsigned long tls_size;
unsigned long tls_start_addr;
}
void* handle; int index; struct load_module_desc desc;
for (index = 0; ; i++) { handle = dlget(i, &desc, sizeof(struct load_module_desc));
if (handle = NULL) {
printf("%s\n", dlerror());
break;
}
else {
printf("library %d text base = %lx\n", index,
desc.text_base);
}
}
cc [flag...] file... -ldl [library]...
#include <dlfcn.h> unsigned long dlmodinfo(unsigned long ip_value, struct load_module_desc *desc, size_t desc_size, void *(*read_tgt_mem)(void* buffer, unsigned long ptr, size_t bufsiz, int ident), int ident_parm, uint64_t load_map_parm);
| Parameter | Description | |
|---|---|---|
| ip_value | An address. The instruction pointer value of the requested library. | |
| desc | A buffer of memory allocated by the user program. The dynamic loader fills this in with module information. | |
| desc_size | Size in bytes of the desc buffer. | |
| read_tgm_mem | A pointer to a function used by dlmodinfo to retrieve needed
information.
If the value is NULL, the dynamic loader uses its own internal data structures to find the correct load module and ignore the ident_parm and load_map_parm parameters. |
|
| buffer | A buffer supplied by dlmodinfo to read into. | |
| ptr | The virtual memory address to read from. | |
| bufsiz | Tthe size of buffer in bytes. | |
| ident | The value of the ident_parm parameter to dlmodinfo. | |
| ident_parm | Only used to pass the fourth parameter to read_tgt_mem. | |
| load_map_parm | Only used when calling through read_tgt_mem. Contains the starting address of the load map. |
read_tgm_mem allows dlmodinfo to find a load module in one process on behalf of another. The calling process passes a callback via read_tgt_mem in order to read memory in a different process address space from the one in which dlmodinfo resides. ip_value, load_map_parm, and ptr from read_tgt_mem can be pointers to shared libraries in another process.
If the calling process calls dlmodinfo with a callback registered
via read_tgt_mem, it must supply the starting address of the target
process' load map in the load_map_parm parameter to dlmodinfo.
This can be retrieved from the DT_HP_LOAD_MAP entry in the .dynamic
section in the target executable file.
void foo()
{
printf("foo\n");
}
int retrieve_info()
{
unsigned long handle; struct load_module_desc desc;
handle = dlmodinfo((unsigned long) &foo, &desc, sizeof(struct load_module_desc), NULL, 0, 0);
if (handle != 0) {
printf("text base = %lx\n", desc.text_base);
}
}
char *dlgetname(struct load_module_desc *desc, size_t desc_size, void *(*read_tgt_mem)(void* buffer, unsigned long long ptr, size_t bufsiz, int ident), int ident_parm, unsigned long long load_map_parm);
| Parameter | Description | |
|---|---|---|
| desc | A buffer of memory allocated by the user program. The dynamic loader fills this in with module information. | |
| desc_size | Size in bytes of the desc buffer. | |
| read_tgm_mem | A pointer to a function used by dlmodinfo to retrieve needed
information.
If the value is NULL, the dynamic loader uses its own internal data structures to find the correct load module and ignore the ident_parm and load_map_parm parameters. |
|
| buffer | A buffer supplied by dlmodinfo to read into. | |
| ptr | The virtual memory address to read from. | |
| bufsiz | The size of buffer in bytes. | |
| ident | The value of the ident_parm parameter to dlmodinfo. | |
| ident_parm | Only used to pass the fourth parameter to read_tgt_mem. | |
| load_map_parm | Only used when calling through read_tgt_mem. Contains the starting address of the load map. |
The read_tgt_mem, ident_parm, and load_map_parm parameters are identical to those for dlmodinfo.
The caller of dlgetname must copy the return value to insure
that it is not corrupted.
void* handle; struct load_module_desc desc; char* dll_name;
/* Get load module of the index'th shared library */ handle = dlget(1, &desc, sizeof(struct load_module_desc));
/* Retrieve pathname of the shared library */
dll_name = dlgetname(&desc,
sizeof(struct load_module_desc),
NULL,
0,
NULL);
printf("pathname of 1st shared library : %s\n", dll_name);
int dlclose(void *handle);
| Parm | Definition |
|---|---|
| handle | Value returned by a previous invocation of dlopen. |
A successful invocation of dlclose does not guarantee that
the shared libraries associated with handle have actually been removed
from the address space of the process. shared libraries loaded by one invocation
of dlopen may also be loaded by another invocation of dlopen.
The same shared library may also be opened multiple times. a shared library
is not removed from the address space until all references to that shared
library through an explicit dlopen invocation have been closed
and all other shared libraries implicitly referencing that shared library
have also been closed. Once a shared library has been closed by dlclose,
referencing symbols contained in that shared library can cause undefined
behavior.
void* handle; int ret_value;
handle = dlopen("./lib1.sl", RTLD_GLOBAL | RTLD_LAZY);if (handle == NULL) {
printf("%s\n", dlerror());
}
ret_value = dlclose(handle);
if (ret_value != 0) {
printf("%s\n", dlerror());
}
export _HP_DLDOPTS=-warnings Turn on compatibility warningsThe following sections provide information about the dynamic loader compatibility warnings.
shl_get()
shl_get_r()
shl_gethandle()
shl_gethandle_r()
shl_getsymbols()
BIND_NOSTART
BIND_RESTRICTED
BIND_TOGETHER
BIND_NONFATAL
BIND_VERBOSE
DYNAMIC_PATH
TYPE_DATA
TYPE_STORAGE
| NOTE | The for HP-UX Release 11.00 64-bit mode linker does not support the TYPE_STORAGE flag |
| NOTE | Throughout this chapter, examples of PIC are shown in assembly code.
For the corresponding information for 64-bit mode, see 64-bit Runtime Architecture for PA-RISC 2.0 available from the HP-UX Software Transition Toolkit (STK) at http://www.software.hp.com/STK/. |
For example, if you write a program that references the external variable errno, the object code created by the compiler contains only a symbolic reference to errno since errno is not defined in your program. Only when the linker links this object code does the reference to errno change (relocate) to an absolute address in virtual memory.
If your program defines a global variable, the compiler assigns a relocatable
address to that variable. The compiler also marks all references to that
variable as relocatable. The linker replaces the references to the variable
with the absolute address of the variable.
Note that absolute object code does not containphysical addresses. Physical addresses refer to exact locations in physical memory. Instead, absolute object code contains virtual addresses within a process's address space. These virtual addresses are mapped to physical addresses by the HP-UX virtual memory management system.
Because program files contain absolute virtual addresses, the HP-UX
program loader, exec, must always load the code and data into
the same location within a process's address space. Because this code always
resides at the same location within the address space, and because it contains
virtual addresses, it is not suitable for shared libraries, although it
can be shared by several processes running the same program.
In order for the object code in a shared library to be fully shareable, it must not depend on its position in the virtual address space of any particular process. The object code in a shared library may be attached at different points in different processes, so it must work independent of being located at any particular position, hence the term position-independent code.
Position independence is achieved by two mechanisms: First, PC-relative addressing is used wherever possible for branches within modules. Second, indirect addressing through a per-process linkage table is used for all accesses to global variables, or for inter-module procedure calls and other branches and literal accesses where PC-relative addressing cannot be used. Global variables must be accessed indirectly since they may be allocated in the main program's address space, and even the relative position of the global variables may vary from one process to another.
The HP-UX dynamic loader (see dld.sl(5)) and the virtual memory management system work together to find free space at which to attach position-independent code within a process's address space. The dynamic loader also resolves any virtual addresses that might exist in the library.
Calls to PIC routines are accomplished through a procedure linkage table (PLT), which is built by the linker. Similarly, references to data are accomplished through a data linkage table (DLT). Both tables reside in a process's data segment. The dynamic loader fills in these tables with the absolute virtual addresses of the routines and data in a shared library at run time (known as binding). Because of this, PIC can be loaded and executed anywhere that a process has free space.
On compilers that support PIC generation, the +z and +Z
options cause the compiler to create PIC relocatable object code.
Register 19 (%r19) is the designated pointer to the linkage table. The linker generates stubs that ensure %r19 always points to the correct value for the target routine and that handle the inter-space calls needed to branch between shared libraries.
The linker generates an import stub for each external reference
to a routine. The call to the routine is redirected to branch to the import
stub, which obtains the target routine address and the new linkage table
pointer value from the current linkage table; it then branches to an export
stub for the target routine. In 32-bit mode, the linker generates an export
stub for each externally visible routine in a shared library or program
file. The export stub is responsible for trapping the return from the target
routine in order to handle the inter-space call required between shared
libraries and program files.
| NOTE | The 64-bit mode linker does not require or support export stubs. |
Shown below is the PIC code generated for import and export stubs. Note that this code is generated automatically by the linker; you don't have to generate the stubs yourself.
;Import Stub (Incomplete Executable) X': ADDIL L'lt_ptr+ltoff,%dp ; get procedure entry point LDW R'lt_ptr+ltoff(%r1),%r21 LDW R'lt_ptr+ltoff+4(%r1),%r19 ; get new r19 value. LDSID (%r21),%r1 MTSP %r1,%sr0 BE 0(%sr0,%r21) ; branch to target STW %rp,-24(%sp) ; save rp ;Import Stub (Shared Library) X': ADDIL L'ltoff,%r19 ; get procedure entry point LDW R'ltoff(%r1),%r21 LDW R'ltoff+4(%r1),%r19 ; get new r19 value LDSID (%r21),%r1 MTSP %r1,%sr0 BE 0(%sr0,%r21) ; branch to target STW %rp,-24(%sp) ; save rp ;Export Stub (Shared libs and Incomplete Executables) X': BL,N X,%rp ; trap the return NOP LDW -24(%sp),%rp ; restore the original rp LDSID (%rp),%r1 MTSP %r1,%sr0 BE,N 0(%sr0,%rp) ; inter-space return
Long Branches and Switch Tables
In general, references to code are handled by the linker, and the compilers
act differently only in the few cases where they would have generated long
calls or long branches. References to data, however, need a new fixup request
to identify indirect references through the linkage table, and the code
generated will change slightly.
| NOTE | Any code which is PIC or which makes calls to PIC must follow the standard procedure call mechanism. |
When linking files produced by the assembler, the linker exports only those assembly language routines that have been explicitly exported as entry (that is, symbols of type ST_ENTRY). Compiler generated assembly code does not explicitly export routines with the entry type specified, so the assembly language programmer must ensure that this is done with the .EXPORT pseudo-op.
For example: In assembly language, a symbol is exported using
.EXPORT foo, typewhere type can be code, data, entry, and others. To ensure that foo is exported from a shared library, the assembly statement must be:
.EXPORT foo,entry
LDIL L'target,%r1 BLE R'target(%sr4,%r1) COPY %r1,%rpWhen the PIC option is in effect, the compilers must generate the following instruction sequence, which is PC-relative:
BL .+8,%rp ; get pc into rp ADDIL L'target - $L0 + 4, %rp ; add pc-rel offset to rp LDO R'target - $L1 + 8(%r1), %r1 $L0: LDSID (%r1), %r31 $L1: MTSP %r31, %sr0 BLE 0(%sr0,%r1) COPY %r31,%rp
LDIL L'target,%r1 BE R'target(%sr4,%r1)For PIC, these two instructions must be transformed into four instructions, similar to the long call sequence: