The linker and dynamic loader provide linking and loading behaviors found widely across the Unix industry, considered, with the SVR4 standards, to define the de facto industry standards. The following linker behavior is compliant with de facto industry standard:
ELF object file format and libelf(3E) routines
Dynamic path searching
Library-level versioning
dl* family of dynamic
loading routines
Breadth-first symbol searching
The HP-UX 11i version 1.5 release maintains certain compatibility behaviors to make transition from PA-32 to IPF mode easier:
Creation of default run-time path
environment variable (RPATH) if
no ld +b is seen on the link line,
to improve transition from the PA-32 linker.
ld +compat option
for compatibility with PA-32 linking and loading behavior.
Starting with the HP-UX 11.00 release, the linker toolset supports the ELF (executable and linking format) object file format. The linker toolset provides new tools to display and manipulate ELF files. The libelf(3E) library routines provide access to ELF files. The command elfdump(1) displays contents of an ELF file.
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.
This section introduces new features of the IPF linker for HP-UX 11i version 1.5.
The linker toolset provides the following new features for developing IPF programs:
Option |
Action |
|---|---|
|
Routines for manipulating shared libraries. |
|
Routines for manipulating the ELF object
file format. Includes the |
|
A tool that displays information about an ELF object file. |
|
A tool that shows shared libraries used by a program or shared library. |
New options to |
Command line options to assist in the development of IPF applications. |
Standard SVR4 dynamic loading features |
Includes SVR4 dynamic path searching and breadth-first symbol searching. |
Mapfile support |
A linker option that lets you control the organization of segments in executable files. This feature is intended for embedded systems development. |
The IPF linker toolset does not support the following PA-32 mode features.
Option or Behavior |
Description |
|---|---|
|
Specifies incremental loading. IPF applications must use shared libraries instead. |
|
Does parameter type checking. This option is unsupported. |
|
Generates an initial program loader header file. This option is unsupported. |
|
Save data and relocation information in temporary files to reduce virtual memory requirements during linking. This option is unsupported. |
|
Generates an executable with file type
|
|
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. It can be use in 32-bit and IPF applications. If this option is used because your program is used in an
embedded system or other specialized application, consider using
mapfile support with the |
|
Specifies pathname for compiling I-SOMs to SOMs. This option is unsupported. |
| Share library suffix | In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit), the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix. |
+objdebug mode
|
In IPF (32-bit and 64-bit), the compiler option +objdebug
is the default. When used with -g, the +objdebug option
leaves debug information in the object files instead of copying it to the
executable file at link time, resulting in shorter link times and smaller
executables. The +noobjdebug option can be used to override the +objdebug
option and copy all debug information to the executable file.
|
+nosrcpos
|
In IPF (32-bit and 64-bit), the +srcpos option is
the default. The +srcpos option causes the compiler to generate
part of the debug information even when the -g compiler option is
not specified. The +srcpos option also causes part of the
debug information to be always copied over to the executable file resulting in
larger executables. The +srcpos option enables users to profile
programs using tools like CXperf and HP Caliper, or compiler options like +I and +P, even in the absence of -g
compilation. The linker option +nosrcpos can be used to
override the +srcpos option and strip the associated debug
information during link time. The +nosrcpos option can also be
used with -g +objdebug to fully enforce the +objdebug mode (i.e.,
leaving the debug information in the object files).
|
odump |
The |
Intra-library versioning |
Specified by using the In PA-32 mode, the linker lets you version your library by object files. IPF or PA-64 applications must use SVR4 library-level versioning instead. |
Duplicate code and data symbols |
Code and data cannot share the same namespace in IPF mode. You should rename the conflicting symbols. |
All internal and undocumented linker options |
These options are unsupported. |
IPF applications (32-bit and 64-bit) and PA-64 applications use a run-time dynamic loading model similar to other SVR4 systems. There are two main areas where the IPF/PA-64 program startup differs from PA 32-bit mode:
Dynamic path searching for shared libraries.
Symbol searching in dependent libraries.
It is recommended that you use the standard SVR4 linking option
(+std), which is on by default
when linking IPF (32-bit and 64-bit) and PA-64 applications.
There may be circumstances while you transition, that you need PA-32-compatible
linking behavior. The IPF and PA-64 linkers provides the mode +compat
option to force the linker to use PA-32 linking and dynamic loading
behavior.
The following table summarizes the dynamic loader differences between PA-32 and IPF/PA-64 bit modes (IPF (64-bit and 32-bit) and PA-64):
Linker and Loader Functions |
PA-32 Mode Behavior |
IPF (32-bit and 64-bit) and PA-64 Mode Behavior |
|---|---|---|
|
Ordering is significant. |
Ordering is insignificant by default. Use |
Symbol searching in dependent libraries |
Depth-first search order. |
Breadth-first search order. Use
|
Run time path environment variables |
No run time environment variables by default. If |
Use
|
|
|
Add all directories
specified in |
This section provides a description and examples of dynamic path searching for shared libraries.
Dynamic path searching is the process that allows you to specify the location of shared libraries at run time. In PA-32 mode, you can enable run-time dynamic path searching of shared libraries in two ways:
By linking the program with +s,
enabling the program to use the path list defined by the SHLIB_PATH
environment variable at run time.
By storing a directory path list in the program
with the linker option +b path_list.
If +s or +b
path_list is enabled, all shared libraries
specified with the -l library
or -l:library
linker options are subject to a dynamic path lookup at run time.
In IPF, the dynamic path searching behavior has changed (same as PA-64 mode):
The +s
dynamic path searching option is enabled by default. It is not enabled
by default in PA-32 mode.
The LD_LIBRARY_PATH
environment variable is available in addition to the SHLIB_PATH
environment variable.
An embedded run-time path list called RUNPATH
may be stored in the executable. If +b
path_list is specified at link time,
these directories are added to RUNPATH.
If +b path_list
is not specified, the linker creates a default RUNPATH
consisting of:
directories in the -L
option (if specified), followed by
directories in the LPATH
environment variable (if specified).
By default, the linker ignores the ordering of the
+b path_list
and +s options.
At run time, the dynamic loader searches directory paths in the following order:
LD_LIBRARY_PATH
(if set)
SHLIB_PATH (if
set)
RUNPATH
the default location /usr/lib/hpux32
for 32-bit programs and /usr/lib/hpux64
for 64-bit programs.
The following are examples of specifying library paths in PA-32 mode and IPF/PA-64 mode (IPF (32-bit and 64-bit) and PA-64):
Linking to libraries by fully qualifying
paths and the library does not contain SONAME, which is specified
by the linker +h option when building
the shared library:
In this example, the program is linked with /opt/myapp/mylib.s:
$ cc main.o /opt/myapp/mylib.s Perform 32-bit link.
$ cc +DD64 main.o /opt/myapp/mylib.s Perform 64-bit link.
At run-time, in both 32-bit and 64-bit mode, the dynamic loader
only looks in /opt/myapp to find
mylib.s.
Linking to libraries using the -l
library or -l:library options:
In this example, the +s option
is not explicitly enabled at link time. Both 32-bit and 64-bit versions
of a shared library called libfoo.s
exist in the default location.
PA-32, IPF 32-bit mode example:
$ cc main.o -lfoo -o main Perform 32-bit link.
With the +DD32 default, when
linked in PA-32 mode, main
aborts at run time if libfoo.sl
is moved from /usr/lib. This is
because the absolute path name of the shared library /usr/lib/libfoo.sl
is stored in the executable.
When linked in IPF 32-bit mode, main
does not abort at run time if libfoo.s
is moved from /usr/lib/hpux32 as
long as LD_LIBRARY_PATH or SHLIB_PATH
is set and point to libfoo.s.
PA-64, IPF 64-bit mode example:
$ cc +DD64 main.o -lfoo -o main Perform 64-bit link.
When linked in IPF or PA-64 mode, main
will not abort at run time if libfoo.s
is moved, as long as LD_LIBRARY_PATH
or SHLIB_PATH is set and point
to libfoo.s.
Linking to libraries using -L
and +b path_list:
The -L option is used by
the linker to locate libraries at link time. The +b
option is used to embed a library path list in the executable for
use at run time.
PA-32, IPF 32-bit mode example:
$ cc main.o -L. -Wl,+b/var/tmp -lme Link the program.
$ mv libme.s /var/tmp/libme.sl Move libme.sl.
$ a.out Run the program.
In PA-32 mode, the dynamic loader searches paths to resolve external references in the following order:
/var/tmp
to find libme.sl found
/var/tmp to find
libc.sl not found
/usr/lib/libc.sl
found
In IPF 32-bit mode, the dynamic loader searches paths to resolve external references in the following order:
LD_LIBRARY_PATH
(if set) to find libme.s not found
SHLIB_PATH (if
set) to find libme.s not found
/var/tmp to find
libme.s found
LD_LIBRARY_PATH
(if set) to find libc.s not found
SHLIB_PATH (if
set) to find libc.s not found
/var/tmp to find libc.s
not found
/usr/lib/hpux32/libc.so
found
LD_LIBRARY_PATH
(if set) to find libc.s not found
SHLIB_PATH (if
set) to find libc.s not found
/var/tmp to find
libc.s not found
/usr/lib/hpux32/libc.so found
PA-64, IPF 64-bit mode example:
$ cc +DD64 main.o -L. -Wl,+b/var/tmp -lme Link the program.
$ mv libme.sl /var/tmp/libme.s Move libme.s.
$ a.out Run the program.
The dynamic loader searching order is the same in PA-64 and IPF (32-bit and 64-bit). The dynamic loader searches paths to resolve external references in the following order:
LD_LIBRARY_PATH
(if set) to find libme.s not found
SHLIB_PATH (if
set) to find libme.s not found
/var/tmp to find
libme.s found
LD_LIBRARY_PATH
(if set) to find libc.s not found
SHLIB_PATH (if
set) to find libc.s not found
/var/tmp to find
libc.s not found
/usr/lib/pa20_64/libc.sl
(PA-64) or /usr/lib/hpux32/libc.so
(IPF 32-bit) or /usr/lib/hpux64/libc.so
(IPF 64-bit) found
In IPF (32-bit and 64-bit) and PA-64 mode, the dynamic loader searches shared libraries using a breadth-first search order. Breadth-first symbol searching is used on all SVR4 platforms.
In PA-32 mode, the dynamic loader searches shared libraries using a depth-first search order.
The diagram below shows an example program with shared libraries and compares the two search methods:
a.out
|
|-----------------|
| |
lib1 lib2
|
lib3
Breadth-first search list: a.out -> lib1 -> lib2 -> lib3
Depth-first search list: a.out -> lib1 -> lib3 -> lib2
The commands to build the libraries and the executable in "Search Order of Dependent Libraries" are shown:
ld -b lib2.o -o lib2.s
ld -b lib3.o -o lib3.s
ld -b lib1.o
-L. -l3 -o lib1.s
cc main.o -Wl,-L. -l1 -l2 -o main
In PA-32 mode, if a procedure called same_name() is defined in both lib3 and lib2, main calls the procedure defined in lib3. In PA-64 and IPF (32-bit and 64-bit), main calls the procedure in lib2.
This section describes the system library locations and changes to the library name extension.
IPF HP-UX systems provide two new subdirectories called
hpux32 and hpux64
under /usr/lib directory for IPF
system and HP product libraries.
The diagram below shows the new directory structure:
/(root)
|
|-------------------------------------|
| |
opt usr
| |
application lib
| |
lib |----------|---------|
| | |
|----|-----| hpux32 hpux64
hpux32 hpux64
The linker automatically finds the correct set of system libraries depending on whether the application is compiled as a 32-bit or 64-bit application.
Library providers are encouraged to supply both 32-bit and 64-bit versions of application libraries. Be sure to develop a strategy for library naming conventions, directory structures, link-time options, and run-time environment variables.
IPF (32-bit and 64-bit) shared libraries are suffixed with .so,
which is different from the PA-RISC shared libraries, which are
suffixed with .sl. The IPF linker
supports shared libraries with both .so
and .sl extension.
For example, /usr/lib/hpux32/libc.so
is the IPF system 32-bit C library whereas /usr/lib/libc.sl
is the PA-RISC system 32-bit C library.
Use the compiler -complete
option or the linker -noshared option to create a statically-bound program. The default is -dynamic.
The following changes are planned in future releases.
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.
The Linker and Libraries Online User Guide
is available as online help with HP-UX Operating System. You can
access online help through your HTML browser, as defined in the
BROWSER environment variable.
To access the Linker and Libraries Online User
Guide from the ld
command line:
ld +help
This chapter describes the process of compiling and linking a program.
Compiling Programs on HP-UX: An Example provides an overview of compiling on HP-UX.
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 Features describes the thread-safe features.
To create an executable program, you compile a source file
containing a main program. For example, to compile an ANSI C program
named sumnum.c,
shown below, use this command (-Aa
says to compile in ANSI mode):
$ cc -Aa sumnum.c
The 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: 10
The process is essentially the same for all HP-UX compilers.
For instance, to compile and run a similar FORTRAN program named
sumnum.f:
$ f90 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 .
| NOTE | If multiple definitions of a symbol occur in the specified
libraries, |
An executable file is created after the program has been compiled and linked. The next step is to run or load the program.
When you run an executable file created by ld,
the program is loaded into memory by the HP-UX program loader, exec.
This routine is actually a system call and can be called by other
programs to load a new program into the current process space. The
exec function
performs many tasks; some of the more important ones are:
Determine how to load the executable file by looking at its magic number. (See Also The a.out File .)
Determine where to begin execution of the program
- that is, the entry point - For examples in share-bound
executables in dld.so, in archive-bound executables 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.so),
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?.)
For details on exec,
see the exec(2) page in the HP-UX
Reference.
Since shared library routines and data are not actually contained
in the a.out
file, the dynamic loader must attach the
routines and data to the program at run time. Attaching a shared
library entails mapping the shared library code and data into the
process's address space, relocating any pointers in the shared library
data that depend on actual virtual addresses, allocating the bss
segment, and binding routines
and data in the shared library to the program.
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.
To accelerate program startup time, routines in a shared library are not bound until referenced. (Data items are always bound at program startup.) This deferred binding of shared library routines distributes the overhead of binding across the execution time of the program and is especially expedient for programs that contain many references that are not likely to be executed. In essence, deferred binding is similar to demand-loading.
The dynamic loader (dld.so)
and its application interface library (libdld.so)
are thread-safe.
Also, the linker toolset provides thread local storage support in:
ld
- the link editor
crt0.o
- the program startup file
Thread local storage (also called thread-specific data) is data specific to a thread. Each thread has its own copy of the data item.
| NOTE | Use of the |
See your HP compiler documentation
to learn how to create thread local storage data items with the
_thread compiler
directive.
See Programming with Threads on HP-UX for information on threads.
You have a great deal of control over how the
linker links your program or library by using ld
command-line options.
Using the Compiler Command
Using the Linker Command
In many cases, you use your compiler command to compile and link programs. Your compiler uses options that directly affect the linker.
By default, the linker searches the directories /usr/lib/hpux32
and /usr/ccs/lib/hpux32 for 32-bit
mode libraries, and /usr/lib/hpux64
and /usr/ccs/lib/hpux64
for 64-bit mode libraries specified with the -l
compiler option. (If the -p
or -G compiler
option is specified, then the linker also searches the profiling
library directory /usr/lib/libp.)
The -L
libpath 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 aC++ compiler (CC),
and the HP Fortran 90 compiler (f90)
recognize the -L
option and pass it directly to ld.
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 -llocal
The LPATH
environment variable provides another way to override the default
search path. For details, see Changing the
Default Library Search Path with -L, LPATH, and $ORIGIN .
The -v
option makes a compiler display verbose information. This is useful
for seeing how the compiler calls ld.
For example, using the -v
option with the C compiler shows that it automatically links
with libsin,
and libc.
$ cc -v himom.c cc: informational note 404: NLSPATH is /opt/ansic/lib/nls/msg/%L/%N.cat:/opt/ansic/lib/nls/msg/C/%N.cat: cc: informational note 404: INCLUDIR is INCLUDIR=3D/usr/include cc: informational note 404: CCOPTS is not set. main.c: /opt/langtools/lbin/cpp.ansi main.c /var/tmp/ctmAAAa21620\\ -D__hpux -D__unix -D_FLT_EVAL_METHOD=3D0 -D__ia64\\ -D_INLINE_ASM -D_ILP32 -D_BIND_LIBCALLS\\ -D_Math_errhandling=3D(MATH_ERREXCEPT|MATH_ERRNO) cc: informational note 411: Entering Preprocessor. /opt/ansic/lbin/ccom /var/tmp/ctmAAAa21620 main.o -n main.c\\ -dynamic -DSblended -DD32 -ESdefaultstack\\ -Oq01,al,ag,cn,sz,Ic,vo,mf,Po,es,rs,sp,in,cl,om,vc,pi,\\ fa,Pe,Rr,pa,nf,cp,lx,Pg,ug,lu,lb,uj,dn,ap,rp,Fs,bp,wp,pc,mp,\\ LR,cx,cr,pi,so,Rc,fa,fe,pt,st,lc,Bl! -Aa func.c: /opt/langtools/lbin/cpp.ansi func.c /var/tmp/ctmAAAa21620\\ -D__hpux -D__unix -D_FLT_EVAL_METHOD=3D0 -D__ia64\\ -D_INLINE_ASM -D_ILP32 -D_BIND_LIBCALLS\\ -D_Math_errhandling=3D(MATH_ERREXCEPT|MATH_ERRNO) cc: informational note 411: Entering Preprocessor. /opt/ansic/lbin/ccom /var/tmp/ctmAAAa21620 func.o -n func.c\\ -dynamic -DSblended -DD32 -ESdefaultstack\\ -Oq01,al,ag,cn,sz,Ic,vo,mf,Po,es,rs,sp,in,cl,om,vc,pi,fa,\\ Pe,Rr,pa,nf,cp,lx,Pg,ug,lu,lb,uj,dn,ap,rp,Fs,bp,wp,pc,mp,LR,\\ cx,cr,pi,so,Rc,fa,fe,pt,st,lc,Bl! -Aa cc: informational note 404: LPATH is /usr/lib/hpux32:/opt/langtools/lib/hpux32: /usr/ccs/bin/ld -u__sin_core__ -u__sin_init -h__sin_core__\\ -h__sin_lookup_ibt -lsin -u main main.o func.o -lm -dynamic\\ +Oprocelim -lc cc: informational note 413: Entering Link editor.
The -Wl
option passes options and arguments to ld
directly, without the compiler interpreting the options. Its syntax
is:
-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.
The command for telling the linker to use an archive version
of libm from the C command line
is:
$ cc -Aa mathprog.c -Wl,-a,archive,-lm,-a,default
The 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
The -o
name option causes ld
to name the output file name instead
of a.out. For
example, to compile a C program prog.c
and name the resulting file sum_num:
$ 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
Sometimes programs call routines not contained in the default
libraries. In such cases you must explicitly specify the necessary
libraries on the compile line with the -l
option. The compilers pass -l
options directly to the linker before the C libraries, but after the
SIN library.
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 /usr/ccs/lib/hpux32/crt0.o -u main \
cursesprog.o -lcurses -lc
the POSIX
cc: informational note 413: Entering Link editor.
The -c
compiler option suppresses the link-edit phase. That is, the compiler
generates only the .o
files and not the a.out
file. This is useful when compiling source files that contain only
subprograms and data. These may be linked later with other object
files, or placed in an archive or shared library. The resulting
object files can then be specified on the compiler command line,
just like source files. For example:
$ f90 -c func.f Produce .o for func.f. $ ls func.o func.o Verify that func.o was created. $ f90 main.f func.o Compile main.f with func.o $ a.out Run it to verify it worked.
This section describes linker commands for the 32-bit and 64-bit linker.
| NOTE | Unless otherwise noted, all examples show 32-bit behavior. |
In default mode, you do not need to include crt0.o
on the link line. However, 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.The dynamic loader, dld.so,
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.
You can change or override the default linker search path
by using the LPATH
environment variable, the -L
linker option, or the +origin linker
option.
The LPATH
environment variable allows you to specify which directories ld
should search. If LPATH
is not set, ld
searches the default directory /usr/lib.
If LPATH is
set, ld searches
only the directories specified in LPATH;
the default directories are not searched unless they are specified
in LPATH.
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
The -L
option to ld
also allows you to add additional directories to the search path.
If -L libpath
is specified, ld
searches the libpath directory before
the default places.
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 prog.o -L /usr/local/lib -lc
Multiple -L
options can be specified. For example, to search /usr/contrib/lib
and /usr/local/lib
before the default places:
$ ld prog.o -L /usr/contrib/lib \ -L /usr/local/lib -lc
If LPATH
is set, then the -L
option specifies the directories to search before the directories
specified in LPATH.
The +origin
option to ld
instructs the linker to search for the library in the directory
from which the object module originated.
The +origin option only apples
to the shared library specified directly afterwards, for example,
libc.so or -lc.
At runtime, if the dynamic loader cannot find the library
found in the path specified by $ORIGIN,
it attempts to search paths according to the search path algorithm
described above.
The syntax is as follows:
$ ld main.o +origin -lc
or
$ld main.o +origin /usr/lib/hpux32/libc.so
You can use the $ORIGIN string
in LD_LIBRARY_PATH, SHLIB_PATH,
RUNPATH (the embedded path or RPATH),
or in the path of a shared library in the shared library list. If
the DF_ORIGIN flag is set, the
loader determines the path of the current load module when the load
module is first loaded. If the DF_ORIGIN
flag is not set, the loader determines the path of the current load
module when the loader first encounters $ORIGIN,
whether it is in LD_LIBRARY_PATH,
SHLIB_PATH, RUNPATH,
or the shared library name in the shared library list.
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 -lx
or
+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.so -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, |
You might want to force immediate binding - that is, force all routines and data to be bound at startup time. With immediate binding, the overhead of binding occurs only at program startup, rather than across the program's execution. One possibly useful characteristic of immediate binding is that it causes any possible unresolved symbols to be detected at startup time, rather than during program execution. Another use of immediate binding is to get better interactive performance, if you don't mind program startup taking a little longer.
To force immediate binding, link an application with the -B immediate
linker option. For example, to force immediate binding of all symbols
in the main program and in all shared libraries linked with it,
you could use this ld
command:
$ ld -B prog.o -lc
The linker also supports nonfatal binding,
which is useful with the -B immediate
option. Like immediate binding, nonfatal immediate binding causes
all required symbols to be bound at program startup. The main difference
from immediate binding is that program execution continues even
if the dynamic loader cannot resolve symbols. Compare
this with immediate binding, where unresolved symbols cause the
program to abort.
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 prog.o -B nonfatal -B immediate -lc
Note 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 linker also supports restricted binding,
which is useful with the -B deferred
and -B nonfatal
options. The -B restricted
option causes the dynamic loader to restrict the search for symbols
to those that were visible when the library was loaded. If the dynamic
loader cannot find a symbol within the restricted set, a run-time
symbol binding error occurs and the program aborts.
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 uses shl_load
with the BIND_FIRST
flag to load a library that contains symbol definitions that are
already defined in a library that was loaded at program startup.
The program calls shl_definesym
to define a symbol that is already defined in a library that was
loaded at program startup.
If such a program is linked with -B immediate,
references to symbols will be bound at program startup, regardless
of whether duplicate symbols are created later by shl_load
or shl_definesym.
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.
The linker supports the -B symbolic
option which optimizes call paths between procedures when building
shared libraries. It does this by building direct internal call
paths inside a shared library. In linker terms, import
and export stubs are bypassed for calls within
the library.
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 |
For example, to optimize the call path between procedures
when building a shared library called lib1.s,
use -B symbolic
as follows:
$ ld -B symbolic -b func1.o func2.o -o lib1.s
| NOTE | The 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 |
Similar to the -h (hide symbol) and +e
(export symbol) linker options, -B symbolic optimizes call paths
in a shared library. However, unlike -h
and +e, all functions in a shared
library linked with -B symbolic
are also visible outside of the shared library.
Suppose you have two functions to place in a shared library. The convert_rtn() calls gal_to_liter().
Build the shared library with -b. Optimize the call path inside the shared library with -B symbolic.
$ ld -B symbolic -b convert.o volume.o -o libunits.s
Two main programs link to the shared library. main1 calls convert_rtn() and main2 calls gal_to_liter().
$ cc -Aa main1.c libunits.s -o main1 $ cc -Aa main1.c libunits.s -o main2
Figure 4: Symbols inside a Shared Library Visible with -B symbolic shows that a direct call path is established between convert_rtn() and gal_to_liter() inside the shared library. Both symbols are visible to outside callers.

The -h (hide symbol) and +e
(export symbol) options can also optimize the call path in a shared
library for symbols that are explicitly hidden. However, only the
exported symbols are visible outside of the shared library.
For example, you could hide the gal_to_liter
symbol as shown:
$ ld -b convert.o -h gal_to_liter volume.o -o libunits.s
or export the convert_rtn symbol:
$ ld -b +e convert_rtn convert.o volume.o -o libunits.s
In 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:

If both an archive and shared version of a particular library
reside in the same directory, ld
links with the shared version. Occasionally, you might want to override
this behavior.
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 .
The -a
option tells the linker what kind of library to link with. It applies
to all libraries (-l
options) until the end of the command line or until the next -a
option. Its syntax is:
-a {archive | shared | default | archive_shared | shared_archive}
The different option settings are:
-a archiveSelect archive libraries. If the archive library
does not exist, ld
generates an error message and does not generate the output file.
-a sharedSelect shared libraries. If the shared library does
not exist, ld
generates an error message and does not generate the output file.
-a defaultThis is the same as -a shared_archive.
-a archive_sharedSelect the archive library if it exists; otherwise,
select the shared library. If the library cannot be found in either
version, ld generates
an error message and does not generate the output file.
-a shared_archiveSelect the shared library if it exists; otherwise,
select the archive library. If the library cannot be found in either
version, ld generates
an error message and does not generate the output file.
The -a shared
and -a archive
options specify only one type of library to use. An error results
if that type is not found. The other three options specify a preferred
type of library and an alternate type of library if the preferred
type is not found.
| 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 . |
The following command links with the archive versions of libcurses,
libm and libc:
$ ld /usr/ccs/lib/hpux32/crt0.o prog.o -a archive -lcurses -lm -lc
Use the -dynamic option to
instruct the linker to look for shared libraries first and then
archive libraries. The linker outputs a share-bound executable.
This option is on by default .
For example:
$ld main.o -dynamic -L. -lbar -lc
is the same as:
ld main.o -L. -lbar -k
If you specified an archive library, the linker links it in, but the resulting executable is still a share-bound executable. This is true even if the linker finds no shared libraries at link time.
Use the -noshared option
if you need to link with all archive libraries. The linker outputs
an archive-bound executable.
| NOTE | You cannot link in shared libraries if you specify this option. |
In the following example, the linker only looks for
/usr/lib/hpux32/libfoo.a and /user/lib/hpux32/libc.a:
ld /usr/ccs/hpux32/crt0.o main.o -noshared -L. -lfoo -lc
If you specify a shared library libbar.so with this option, the linker emits an error message.
ld: The shared library "libbar.so" cannot be processed in a static link. Fatal error.
The +e
option allow you to hide and export symbols. Exporting
a symbol makes the symbol a global definition, which can be accessed
by any other object modules or libraries. The +e
option exports symbol and hides from
export all other global symbols not specified with +e.
In essence, -h
and +e provide
two different ways to do the same thing.
The syntax of the +e
option is:
+e symbol
Suppose you want to build a shared library from an object
file that contains the following symbol definitions as displayed
by the nm command:
$ 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 sem
In 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 -o libsem.so One -h option. $ ld -b +e foo +e bar +e sem sem.o -o libsem.so 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 -o libsem.so Three -h options. $ ld -b +e check_sem_val sem.o -o libsem.so One +e option.
How do you decide whether to use -h
or +e? In general,
use -h if you
simply want to hide a few symbols. And use +e
if you want to export a few symbols and hide a large number of symbols.
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), combining .o files ( with
-r), and when linking to create an a.out
file.
Like the +e option, the +ee
option allows you to export symbols. Unlike the +e
option, the option does not alter the visibility of any other symbols
in the file. It exports the specified symbol, and does not hide
any of the symbols exported by default.
In PA-32 mode, the linker exports from a program only those symbols
that were imported by a shared library. For example, if a shared
executable's libraries do not reference the
program's main
routine, the linker does not include the main
symbol in the a.out
file's export list. Normally, this is a problem only when a program
calls shared library management routines (described in Shared Library Management
Routines ). To
make the linker export all symbols from a program,
invoke ld with
the -E option.
In IPF/PA-64 mode, the behavior is specified by -E is
the default behavior. 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.
Use the +hideallsymbols option
to hide all symbols to prevent the linker from exporting them in
a shared link.
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
The -h
option allows you to hide symbols. Hiding
a symbol makes the symbol a local definition, accessible only from
the object module or library in which it is defined. Use -h
if you simply want to hide a few symbols.
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.
Suppose you want to build a shared library from an object
file that contains the following symbol definitions as displayed
by the nm command:
$ 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 sem
In 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
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.
When building a shared library, you might want to hide a symbol in the library for several reasons:
It can improve performance because the dynamic loader does not have to bind hidden symbols. Since most symbols need not be exported from a shared library, hiding selected symbols can have a significant impact on performance.
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.
Exporting a symbol is necessary if the symbol must be accessible
outside the shared library. But remember that, by default, most
symbols are global definitions anyway, so it is seldom necessary
to explicitly export symbols. In C, all functions and global variables
that are not explicitly declared as static
have global definitions, while static
functions and variables have local definitions. In FORTRAN, global
definitions are generated for all subroutines, functions, and initialized
common blocks.
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.
The -r
option combines multiple .o
files, creating a single .o
file. The reasons for hiding symbols in a .o
file are the same as the reasons listed above for shared libraries.
However, a performance improvement will occur only if the resulting
.o file is later
linked into a shared library.
In PA-32 mode, the linker exports all of a program's global definitions
that are imported by shared libraries specified on the linker command
line. For example, given the following linker command, all global
symbols in crt0.o
and prog.o that
are referenced by libm
or libc are automatically
exported:
$ ld /usr/ccs/lib/crt0.o prog.o -lm -lc
With 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 -ldld
When 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.
In IPF/PA-64 mode, the behavior specified by -E
is the default behavior.
A library can be moved even after an application has been
linked with it. This is done by providing the executable with a
list of directories to search at run time for any required libraries.
One way you can store a directory path list in the program is by
using the +b
path_list linker option.
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.s).
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) ).
The syntax of the +b
option is
+b path_list
where 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.s,
libm.s, and
libc.s 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 -lc
If 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
Whether specified as a parameter to +b
or set as the value of the SHLIB_PATH
environment variable, the path list is simply one or more path names
separated by colons (:), just like the syntax of the PATH
environment variable. An optional colon can appear at the start
and end of the list.
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.s,
and the directory path list contains /apps/lib:xyz,
the dynamic loader searches for /apps/lib/libfoo.sl,
then ./xyz/libfoo.s.
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.s,
/usr/local/lib/libfoo.s,
then ./xyz/libfoo.s.
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.s).
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) ).
A library can be moved even after an application has been
linked with it. Linking the program with +s,
enables the program to use the path list defined by the SHLIB_PATH
environment variable at run time.
When a program is linked with +s,
the dynamic loader will get the library path list from the SHLIB_PATH
environment variable at run time. This is especially useful for
application developers who don't know where the libraries will reside
at run time. In such cases, they can have the user or an install
script set SHLIB_PATH
to the correct value.
The Path List provides additional details about
the path list to SHLIB_PATH.
Moving Libraries after Linking with +b provides another way to move libraries.
Use the +noenvvar to instruct
the dynamic loader not to look at the environment variables relating
to dynamic path searching at runtime. It ignores LD_LIBRARY_PATH
and SHLIB_PATH environment variables.
This option is on by default in with ld +compat.
It is off by default with ld +std.
For example, if libbar.so
has dependent library libfee.so
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.so /tmp a.out called bar() called fee() mv /tmp/libbar.so ./ ld main.o +noenvvar -L. -lbar -lc mv libbar.so /tmp a.out dld.so: Unable to find library "libbar.so"
Use the +[no]forceload option
to control how the linker loads object files from an archived library.
+forceload instructs the linker
to load all object files from an archive library. +noforceload
tells the linker to only load those modules from an archive library
that is needed. The mode you select, either by default or explicitly,
remains on until you change it.
+noforceload is the default.
In the following example, main()
references foo(), which is a module
in mylib.a. The function 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 /usr/ccs/lib/hpux32/crt0.o main.o +vtype libraries mylib.a -lc
... Selecting mylib.a[foo.o] to resolve foo
ld /usr/ccs/lib/hpux32/crt0.o main.o +forceload mylib.a -lc\ +vtype libraries
... Selecting mylib.a[foo.o] to forcibly load Selecting mylib.a[bar.o] to forcibly load
The -c
file option causes the linker to read
command line options from the specified file.
This is useful if you have many -h
or +e options
to include on the ld
command line, or if you have to link with numerous object files.
For example, suppose you have over a hundred +e
options that you need when building a shared library. You could
place them in a file named eopts
and force the linker to read options from the file as follows:
$ ld -o libmods.s -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
If you use certain linker options all the time, you may find
it useful to specify them in the LDOPTS
environment variable. The linker inserts the value of this variable
before all other arguments on the linker command line. For instance,
if you always want the linker to display verbose information (-v)
and a trace of each input file (-t),
set LDOPTS as
follows:
$ LDOPTS="-v -t" Korn and Bourne shell syntax. $ export LDOPTS
Thereafter, the following commands would be equivalent:
$ ld -u main prog.o -lc $ ld -v -t -u main prog.o -lc
To direct the linker to search a particular library, use the
-lname
option. For example, to specify libc,
use -lc; to specify
libm, use -lm;
to specify libXm,
use -lXm.
When writing programs that call routines not found in the
default libraries linked at compile time, you must specify the libraries
on the compiler command line with the -lx
option. For example, if you write a C program that calls POSIX math
functions, you must link with libm.
The x argument corresponds to the
identifying portion of the library path name - the part
following lib
and preceding the suffix .a
or .s. For example,
for the libm.s
or libm.a library,
x is the letter m:
$ cc -Aa mathprog.c -lm
The 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 -l:
option works just like the -l
option with one major difference: -l:
allows you to specify the full basename of the library to link with.
For instance, -l:libm.a
causes the linker to link with the archive library /usr/lib/hpux32/libm.a,
regardless of whether -a shared
was specified previously on the linker command line.
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.
For example, even if LDOPTS
were set to -a shared,
the following command would link with the archive libfoo.a
in the directory /usr/mylibs,
the archive libm.a
and libc.a:
$ ld /usr/ccs/lib/hpux32/crt0.o -u main prog.o -L/usr/mylibs \
-l:libfoo.a -l:libc.a -l:libm.a
Use the +allowunsats option
to instruct the linker to not flag unsatisfied symbols at link time.
This is the default for relocatable (-r)
and shared library builds (-b),
and is the default behavior in PA-32 mode.
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 PA-32 mode).
For example, where main()
references functions foo() and bar().
bar() resides in libbar.so
and foo() resides in libfoo.so
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 a non-executable a.out.
ld main.o -L. -lbar -lc
ld: Unsatisfied symbol "foo". 1 error.
The a.out
file created by the linker contains symbol table, relocation, and
(if debug options were specified) information used by the debugger.
Such information can be used by other commands that work on a.out
files, but is not actually necessary to make the file run. ld
provides two command line options for removing such information
and, thus, reducing the size of executables:
-sStrips all such information from the file. The executable
becomes smaller, but difficult or impossible to use with a symbolic
debugger. You can get much the same results by running the strip
command on an executable (see strip(1)).
In some cases, however, -s
rearranges the file to save more space than strip.
-xStrips only local symbols from the symbol table. It reduces executable file size with only a minimal affect on commands that work with executables. However, using this option may still make the file unusable by a symbolic debugger.
These options can reduce the size of executables dramatically. Note, also, that these options can also be used when generating shared libraries without affecting shareability.
Use the +stripunwind option
to suppress output of the unwind table.
ld -b foo.o -o libfoo.s +stripunwind elfdump -U libfoo.s
libfoo.s:
In the HP-UX 11.0 release, the linker toolset supports extended features for linking in PA-64 mode. Since compatibility with the previous linker toolset is a high priority, the IPF linker uses much of the old behavior in the new toolset. The IPF linker includes two options to allow you to instruct the linker to link in one of two modes:
Compatibility mode, with the +compat
option, to create a link and operation in PA-32 style. Because of
some object file format restrictions, the mode is not completely
compatible with the style of the PA-32 linker.
Standard mode, with the +std
option, set by default in the IPF linker, to create a link and
load operation in PA-64 style. This mode uses the new behaviors
and features of the PA-64 linker.
The +compat option instructs the linker to do a PA-32 link.
When you use the +compat
option, the linker:
Uses PA-32 shared library internal name processing.
Lists all dependent shared libraries in a DT_HP_NEEDED
entry the dynamic table using the PA-32 shared library naming conventions.
These dependent libraries are recorded as compatibility mode libraries
even if they are really created as standard mode dependent libraries.
Does not use embedded paths at link time to find dependent libraries.
Considers the order of ld +b
and +s.
+b
first means dld looks at the RPATH
first when searching for dependent shared libraries.
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.
At runtime, the dynamic loader does a PA-32 load for all compatibility mode dependent shared libraries. The dynamic loader:
Does dynamic path searching for compatibility-mode
dependent shared libraries that have the dynamic path selected (set
in the DT_HP_NEEDED entry if the
shared library was specified with -l).
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.
The +std option instructs
the linker to do a standard mode PA-64 link. This is currently the
default.
This default may change in future releases.
When you use +std, the linker:
Assumes -dynamic
was passed to ld. The linker looks
for shared libraries first. The output executable is a shared executable.
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.
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/hpux32:/usr/ccs/lib/hpux32
for 32-bit applications and /usr/lib/hpux64:/usr/ccs/lib/hpux64
for 64-bit applications.
At runtime, the dynamic loader does a PA-64 load for all standard mode dependent shared libraries. The dynamic loader:
Does dynamic path searching only for
standard-mode shared libraries in the DT_NEEDED
entry of the dynamic table which do not contain a path. For those
standard-mode dynamic libraries that contain paths, dld
looks for the library as specified.
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 dependent
shared libraries.
Use the +std option to instructs
the linker to do a PA-64 link. This is the default mode. For more
information, see Using
the IPF Linker with +compat or +std.
Use the +compat option to
instruct the linker to do a PA-32 link. For more information, see
Using
the IPF Linker with +compat or +std.
The linker automatically maps sections from input object files
onto output segments in executable files. These options to the ld
command allow you to change the linker's default mapping.
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.
Use the +vtype option to
get verbose output about specified elements of the link operation.
The following values specify the type:
Description
files Dump information about each object file loaded.
ld main.o +vtype files -L. -lfile1 -lfile2 -lc Loading main.o: Loading ./libfile1.so: Loading ./libfile2.so: Loading /usr/lib/hpux32/libc.so: Loading /usr/lib/hpux32/libdl.so.1:
libraries Dump information about libraries searched.
ld main.o +vtype libraries -L. -lfile1 -lfile2 -lc Searching /usr/lib/hpux32/libc.a: Selecting /usr/lib/hpux32/libc.a
sections Dump information about each input section added to the output file.
ld main.o +vtype sections -L. -lfile1 -lfile2 -lc
main.o:
section .note NOTE 240 4 added to note segment
section .note NOTE 48 4 added to note segment
section .IA_64.unwind_info PROGBITS A 28 4
added to text segment
section .text PROGBITS AX 112 16 added to text
segment
section .IA_64.unwind UNWIND A 12 4 added to
text segment
section .rodata PROGBITS A 9 8 added to text
segment
section .HP.opt_annot PROGBITS A 25 8 added to
text segment
symbols 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.so: printf is UNDEF GLOBAL FUNC _DYNAMIC is DEFINED GLOBAL OBJECT lib1_func is DEFINED GLOBAL FUNC ...
allDump 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 .note NOTE 240 4 added to note segment
section .note NOTE 48 4 added to note segment
section .IA_64.unwind_info PROGBITS A 28 4
added to text segment
section .text PROGBITS AX 112 16 added to text
segment
section .IA_64.unwind UNWIND A 12 4 added to
text segment
section .rodata PROGBITS A 9 8 added to text
segment
section .HP.opt_annot PROGBITS A 25 8 added to
text segment
Loading ./libfile1.so:
./libfile1.so:
...
procelim Dump information about the sections that have been
rejected by the +oprocelim option.
ld main.o +Oprocelim +vtype procelim -L. -lfile1 -lfile2 Deleting 236 bytes in section file.note Eliminated 0K of dead code and data, 0 global, 0 local and 0 hidden symbols
This chapter describes the linker toolset, which provides several tools to help you find symbols, display and modify object files, and determine link order. These tools are specific to ELF object file type.
The following table lists the linker toolset.
| Tool | Description |
|---|---|
|
Displays or modifies the internal attributes of an object file. See Changing a Program's Attributes with chatr(1) . |
|
Displays the contents of an ELF object file. See Viewing the Contents of an Object File with elfdump(1). |
|
Improves startup time of programs that use shared libraries. See Improving Program Start-up with fastbind(1). |
|
Lists dynamic dependencies of executable files and shared libraries. Viewing library dependencies with ldd(1). |
|
Finds ordering relationship for an object library. SeeFinding Object Library Ordering Relationships with lorder(1). |
|
Displays the symbol table of an object file. See Viewing Symbols in an Object file with nm(1) . |
|
Prints sizes of object file elements. See Viewing the Size of Object File Elements with size(1). |
|
Strips symbol and debugging information from an object file, executable, or archive library. See Reducing Storage Space with strip(1). |
The chatr
command (see chatr(1)) allows you to
change various program attributes that were determined at link time.
When run without any options, chatr
displays the attributes of the specified file.
The chatr command supports
two different command syntaxes. One is provided for the easy manipulation
of simple files. Use it to modify files that have only a single
text segment and data segment. The second command syntax allows
you specify selected segments to modify. The following sections
list the additional IPF/PA-64 mode options for the chatr
command. See the chatr(1) manpage for more information about formats.
The following table summarizes the options you can use to change various attributes:
The nm(1) command displays the symbol table of each specified object. file can be a relocatable object file or an executable object file, or an archive of relocatable or executable object files.
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 |
|
Demangle C++ names before printing them. |
|
Display the value and size of a symbol
in decimal. This is the default for the default format or the -p
format. Equivalent to |
|
Display only external
and static symbols. This option is ignored
(see |
|
Display full output. This option is in force by default. |
|
Display only external (global) symbol information. |
|
Do not display the output header data. |
|
Distinguish between weak and global symbols
by appending |
|
Sort symbols by name, in ascending collation
order, before they are printed. This is the default. To turn off
this option, use |
|
Display symbols in the order in which they appear in the symbol table. |
|
Display the value and size of a symbol
in octal. Equivalent to |
|
Display information in a blank-separated output format. Each symbol name is preceded by its value (blanks if undefined) and one of the letters. Lower case letters indicate local values. Upper case letters indicate global values.
|
|
Display information in a portable output
format to standard output. See the nm(1)
manpage for format information. Note that |
|
Prefix each output line with the name
of the object file or archive file. Equivalent to |
|
| Print the section index instead of the section name (ELF only). | -s
|
Display each numeric value in the specified format. format can be one of:
|
-t format |
Display undefined symbols only. |
|
Print the usage menu. |
|
Sort symbols by value before they are printed. |
|
Display the executing version of the
|
|
Displays the value and size of a symbol
in hexadecimal. this is the default for the |
|
Display which object files have undefined
references for the symbol "leap":
"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. 0000000000 u 0000000000 r .HP.opt_annot 0000000000 r .IA_64.unwind_info 0000000000 r .text 0000000000 a func.c 0000000000 T sum_n Global definitions of sum_n. $ nm -p main.o 0000000000 U $global$ Other symbols created from compiling. 0000000000 u 0000000000 r .HP.opt_annot 0000000000 r .IA_64.unwind_info 0000000000 r .data 0000000000 r .sdata 0000000000 r .text 0000000000 a main.c 0000000000 T main Global definition of main. 0000000000 U printf 0000000000 U scanf 0000000000 U sum_n
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 |
|---|---|
Archive headers from an archive library. |
|
String table(s). |
|
The .note section which contains the
compilation unit dictionary and linker footprint. This option has
the same effect as |
|
The compilation unit dictionary of the .notes section. |
|
The linker footprint of the .notes section. The linker footprint has information on the linker used to generate the file as well as the link time. |
|
File header. |
|
Global symbols from an archive. |
|
Section headers. |
|
The run-time interpreter path name for
|
|
The object dictionary for one or more executable files, if the source file was compiled with the +objdebug option. The object dictionary entry contains the name of the object file that contributed to a particular section, the relative offset within the section, size of the object file's contribution, and attributes of the entry. |
|
The CTTI section headers according to the directory member relationship. |
|
The .dynamic section in shared libraries and dynamically linked program files. |
|
The .linkmap section, which is only created
when the incremental linker is used (with the |
|
The .linkmap_bss section, which is only
created when the incremental linker is used (with the |
|
The .linkmap_file section, which is only
created when the incremental linker is used (with the |
|
Optional headers (program headers). |
|
Relocations. |
|
Sections beginning with |
|
Section contents. |
|
Symbol table entries. |
|
Usage message. |
|
Unwind table. |
|
Version number for |
|
elfdump provides the following
additional options to modify your selections:
Option |
Modifies |
Causes elfdump to |
|---|---|---|
|
|
Demangle C++ symbol names before displaying them.
|
|
|
Display the section whose index is num. |
|
|
Display the sections in the range 1 to num2.
|
|
|
Display the relocation whose index is num. |
|
|
Display only the relocations which apply to the section(s) in the range. |
|
all | Select output format in hexadecimal, octal, or decimal. |
|
|
Display information about the section specified by name. |
|
|
Display information about the symbol entry specified by name. |
|
all | Suppress title printing. |
|
|
Suppress printing CTTI section headers. |
|
|
Display the section specified by name. |
|
|
Display headers in short format. |
|
|
Display the symbol whose index is num. |
|
|
Display the symbols in the range 0 to num2.
|
|
|
Verify the CTTI section headers before printing. |
The ldd(1) command lists the dynamic
dependencies of executable files or shared libraries. ldd
displays verbose information about dynamic dependencies and symbol
references:
All shared libraries that would be loaded as a result of executing the file.
All shared libraries that would be loaded as a result of loading the library.
ldd uses the same algorithm
as the dynamic loader (/usr/lib/hpux32/dld.so
and /usr/lib/hpux64/dld.so) to
locate the shared libraries.
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 |
|---|---|
Check reference to data symbols. |
|
Check reference to data and code symbols. |
|
Displays the search path used to locate the shared libraries. |
|
Display all dependency relationships. |
|
By default, ldd
prints simple dynamic path information, including the dependencies
recorded in the executable (or the shared library) followed by the
physical location where the dynamic loader finds these libraries.
$ldd a.out
./libx.so => ./libx.so libc.so.1 => /usr/lib/hpux32/libc.so.1 libdl.so.1 => /usr/lib/hpux32/libdl.so.1
The -v option causes
ldd to print the dependency relationships
along with the dynamic path information.
$ldd -v a.out
find library=./libx.so; required by a.out ./libx.so => ./libx.so find library=libc.so.1; required by a.out libc.so.1 => /usr/lib/hpux32/libc.so.1 find library=libdl.so.1; required by usr/lib/hpux32/libc.so.1 libdl.so.1 => /usr/lib/hpux32/libdl.so.1
The -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.so.1 => /usr/lib/hpux32/libc.so.1 libdl.1 => /usr/lib/hpux32/libdl.so.1 symbol not found: val1 (./libx.so) symbol not found: count (./libx.so) symbol not found: func1 (./libx.so) symbol not found: func2 (./libx.so)
The size(1) command produces section size information for each section in your specified object files. It displays the size of the text, data and bss (uninitialized data) sections with the total size of the object file. If you specify an archive file, the information for all archive members is displayed.
Use the following options to display information for your specified files:
| To display | Use this option |
|---|---|
Sizes in decimal (default). |
|
Size of each allocatable section. |
|
Size and permission bits of each loadable segment. |
|
Sizes of non loadable segments or non allocatable sections. |
|
Sizes in octal. |
|
Display the usage message. |
|
Version information about the |
|
Verbose list of the subspaces in the object files. Each subspace is listed on a separate line with its name, size, and virtual address. |
|
Sizes in hexadecimal. |
|
The strip(1) command removes the
symbol table and line number information from object files, including
archives. Thereafter, no symbolic debugging access is available
for that file. The purpose of this command is to reduce file storage
overhead consumed by the object file. Use this command on production
modules that have been debugged and tested. The effect is nearly
identical to using the -s option
of ld.
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. |
|
Same as the |
|
Strip the unwind information and annotations. |
|
Print the usage message. |
|
Print the version of the |
|
Strip the debug information and line number table. |
|
| NOTE | The |
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.
The fastbind(1) command prepare an incomplete executable for faster program start-up. It can improve the start-up time of programs that use shared libraries (incomplete executables) by storing information about needed shared library symbols in the executable file.
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/hpux32/dld.so
for 32-bit or /usr/lib/hpux64/dld.so
for 64-bit) 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 |
|
Normally, if |
|
The PA-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 .
ExampleTo run fastbind
on the executable file a.out:
$fastbind a.out
To later remove the fastbind information from the
executable file a.out
$fastbind -n a.out
The lorder command finds the ordering relation for an object library. You can specify one or more object or archive library files (see ar(1)) on the command line or read those files from standard input. The standard output is a list of pairs of object file names, meaning that the first file of the pair refers to external identifiers defined in the second.
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.
Build a new library from existing
.o files:
$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
Many libraries come with HP-UX. You can also create and use your own libraries on HP-UX. This chapter provides information on the following topics:
General Information about Shared and Archive Libraries
Creating Libraries on HP-UX
Using Libraries on HP-UX
Using Shared Libraries in IPF Mode
HP-UX supports two kinds of libraries: archive and shared. A shared library is also called a dll (dynamically linked library). Archive libraries are the more traditional of the two, but use of shared libraries has increased dramatically, and is the preferred method. The following table summarizes differences between archive and shared libraries.
Comparing |
Archive |
Shared (or dll) |
|---|---|---|
file name suffix |
Suffix is |
Suffix is |
creation |
Combine object files with the |
Combine object files with the |
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 |
|
Contains all library routines or data
(external references) referenced in the program. An |
Does not contain library routines; instead,
contains a linkage table that is filled in
with the addresses of routines and shared library data. An |
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 executables in the directory
/usr/lib/hpux32/
and for 64-bit executables in /usr/lib/hpux64/.
Archive library file names end with .a
whereas shared library file names end with .so.
For example, for 32-bit executables, the archive C library libc
is /usr/lib/hpux32/libc.a
and the shared version is /usr/lib/hpux32/libc.so. For 64-bit
executables, the archive C library libc
is /usr/lib/hpux64/libc.a
and the shared version is /usr/lib/hpux64/libc.so
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 32-bit mode: ld prog.o -lc
For 64-bit mode:
ld prog.o -lc
The -lc
option instructs the linker to search the C library, hpux32/libc
or hpux64/libc, to resolve unsatisfied
references from prog.o.
If a shared libc
exists (/usr/lib/hpux32/libc.so
or /usr/lib/hpux64/libc.so),
ld uses it instead
of the archive libc
(/usr/lib/hpux32/libc.a
or /usr/lib/hpux64/libc.a).
You can, however, override this behavior and select the archive
version of a library with the -a
option or the -l:
option. These are described in Choosing Archive
or Shared Libraries with -a and Specifying Libraries
with -l and l: .
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 as described
in Creating Shared Libraries.
For more information, see Caution When Mixing Shared and Archive Libraries .
An archive library contains one or
more object files and is created with the ar
command. When linking an object file with an archive library, ld
searches the library for global definitions that match up with external
references in the object file. If a match is found, ld
copies the object file containing the global definition from the
library into the a.out
file. In short, any routines or data a program needs from the library
are copied into the resulting a.out
file.
For example, suppose you write a C program
that calls printf
from the libc
library. Figure 6: Linking with an Archive
Library shows
how the resulting a.out
file would look if you linked the program with the archive version
of libc.

Like an archive library, a shared library
contains object code. However, ld
treats shared libraries quite differently from archive libraries.
When linking an object file with a shared library, ld
does not copy object code from the library into the a.out
file; instead, the linker simply notes in the a.out
file that the code calls a routine in the shared library. An a.out
file that calls routines in a shared library is known as an incomplete
executable.
When an incomplete executable begins execution, the HP-UX
dynamic loader (see dld.so(5))
looks at the a.out
file to see what libraries the a.out
file needs during execution.
The kernel activates the dynamic loader for an a.out.The
dynamic loader then loads and maps any required shared libraries
into the process's address space - known as attaching
the libraries. A program calls shared library routines indirectly
through a linkage table that the dynamic
loader fills in with the addresses of the routines. By default,
the dynamic loader places the addresses of shared library routines
in the linkage table as the routines are called - known
as deferred binding. Immediate
binding is also possible - that is, binding
all required symbols in the shared library at program startup. In
either case, any routines that are already loaded are shared.
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. |
By default, if the dynamic loader cannot find a shared library
from the list, it generates a run-time error and the program aborts.
In PA-32 compatibility mode (with +compat), for example, suppose
that during development, a program is linked with the shared library
liblocal.so in
your current working directory (say, /users/hyperturbo):
$ ld /usr/lib/hpux32/crt0.o prog.o -lc liblocal.so
In PA-32 mode, the linker records the path name of liblocal.so
in the a.out
file as /users/hyperturbo/liblocal.so.
When shipping this application to users, you must ensure that (1)
they have a copy of liblocal.so
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.so,
fail to find it, and the program will abort.
In default mode, the linker records ./liblocal.so.
This is more of a concern with non-standard libraries-that
is, libraries not found in /usr/lib/hpux32
or /usr/lib/hpux64. There is little
chance of the standard libraries not being in these directories.
If different versions of a library exist on your system, be
aware that the dynamic loader may get the wrong version of the library
when dynamic library searching is enabled with SHLIB_PATH
or +b. For instance,
you may want a program to use the PA1.1 libraries found in the /usr/lib/pa1.1
directory; but through a combination of SHLIB_PATH
settings and +b
options, the dynamic loader ends up loading versions found in /usr/lib
instead. If this happens, make sure that SHLIB_PATH
and +b are set
to avoid such conflicts.
As an example, suppose two separate programs, prog1
and prog2, use
shared libc routines
heavily. Suppose that the a.out
portion of prog1
is 256Kb in size, while the a.out
portion of prog.2 is 128Kb. Assume
also that the shared libc
is 512Kb in size. Figure 7: Two Processes
Sharing libc
shows how physical memory might look when both processes run simultaneously.
Notice that one copy of libc
is shared by both processes. The total memory requirement for these
two processes running simultaneously is 896Kb (256Kb + 128Kb + 512Kb).

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.

Debugging of shared libraries is supported by the by the WDB Debugger. Refer to the WDB documentation at http://www.hp.com/go/wdb.
Profiling with prof and gprof
and static analysis are not allowed on shared libraries in this
release.
Two steps are required to create an archive library:
Compile one or more source files to create object files containing relocatable object code.
Combine these object files into a single archive
library file with the ar
command.
Shown below are the commands you would use to create an archive
library called libunits.a:
cc -Aa -c length.c volume.c mass.c ar r libunits.a length.o volume.o mass.o
These steps are described in detail in Overview of Creating an Archive Library .
Other topics relevant to archive libraries are:
To create an archive library:
Create one or more object files containing relocatable object code. Typically, each object file contains one function, procedure, or data structure, but an object file could have multiple routines and data.
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 -.)
Figure 9: Creating an
Archive Library summarizes
the procedure for creating archive libraries from three C source
files (file1.c,
file2.c, and
file3.c). The
process is identical for other languages, except that you would
use a different compiler.

An archive library file consists of four main pieces:
a header string, "!<arch>\n",
identifying the file as an archive file created by ar
(\n represents
the newline character)
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
To see what object modules a library contains, run ar
with the t key,
which displays a table
of contents. For example, to view the "table of contents"
for libm.a:
$ ar t /usr/lib/hpux32/libm.a Run ar with the t key.
acosh.o Object modules are displayed.
erf.o fabs.o . . . .
This indicates that the library was built from object files
named acosh.o,
erf.o, fabs.o,
and so forth. In other words, module names are the same as the names
of the object files from which they were created.
Suppose you are working on a program that does several conversions between English and Metric units. The routines that do the conversions are contained in three C-language files shown:
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.o
Then 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.a
To 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.a
Note 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/hpux before
compiling, the following command line will work instead:
$ cc -Aa convert.c -lunits
Linking with archive libraries is covered in detail in Linker Tasks.
Occasionally you may want to replace an object module in a
library, add an object module to a library, or delete a module completely.
For instance, suppose you add some new conversion routines to length.c
(defined in the previous section) and want to include the new routines
in the library libunits.a.
You would then have to replace the length.o
module in libunits.a.
To replace or add an object module, use the r
key (the same key you use to create a library). For example, to
replace the length.o
object module in libunits.a:
$ ar r libunits.a length.o
To delete an object module from a library, use the d
key. For example, to delete volume.o
from libunits.a:
$ ar d libunits.a volume.o Delete volume.o. $ ar t libunits.a List the contents. length.o mass.o volume.o is gone.
When used to create and manage archive libraries, ar's
syntax is:
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.
Here are some useful ar
keys and their modifiers:
Description
dDelete
the modules from the archive.
rReplace
or add the modules to the archive.
If archive exists, ar
replaces modules specified on the command
line. If archive does not exist, ar
creates a new archive containing the
modules.
tDisplay a table
of contents for the archive.
uUsed with the r,
this modifier tells ar
to replace only those modules with creation
dates later than those in the archive.
vDisplay verbose
output.
xExtracts
object modules from the library. Extracted modules are placed in
.o files in the
current directory. Once an object module is extracted, you can use
nm to view the
symbols in the module.
For example, when used with the v
flag, the t flag
creates a verbose table of contents - including such information
as module creation date and file size:
$ ar tv libunits.a rw-rw-rw- 265/ 20 230 Feb 2 17:19 1990 length.o rw-rw-rw- 265/ 20 228 Feb 2 16:25 1990 mass.o rw-rw-rw- 265/ 20 230 Feb 2 16:24 1990 volume.o
The 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
The crt0.o startup file is
not needed for shared bound links because dld.so
does some of the startup duties previously done by crt0.o.
However, you still need to include crt0.o
on the link line for all fully archive links (ld -noshared).
In PA-32 mode (with +compat), crt0.o
must always be included on the link line.
Users who link by letting the compilers such as cc
invoke the linker do not have include crt0.o
on the link line.
After creating an archive library, you will probably want
to save it in a location that is easily accessible to other programmers
who might want to use it. The main choices for places to put the
library are in the 32-bit /usr/lib/hpux32
or 64-bit /user/lib/hpux64 directory
Since the linker searches /usr/lib/hpux32
or /usr/lib/hpux64 by default,
you might want to put your archive libraries there. You would eliminate
the task of entering the entire library path name each time you
compile or link.
The drawbacks of putting the libraries in /usr/lib/hpux32
or /usr/lib/hpux64 are:
You usually need super-user (system administrator) privileges to write to the directory.
You could overwrite an HP-UX system library that resides in the directory.
Check with your system administrator before attempting to
use /usr/lib/pux32
or /usr/lib/hpux64.
Two steps are required to create a shared library:
Compile one or more source files to create object files. In PA-32 mode, it is necessary to use the +Z compiler option to create position-independent code.
Creating the Shared Library
with ld
by linking with -b.
Shown below are the commands you would use to create a shared
library called libunits.so:
$ cc -Aa -c length.c volume.c mass.c $ ld -b -o libunits.so length.o volume.o mass.o
Other topics relevant to shared libraries are:
In PA-32 mode, the first step in creating a shared library is to create object files containing position-independent code (PIC). There are two ways to create PIC object files:
Compile source files with the +z
or +Z compiler
option, described below.
Write assembly language programs that use appropriate addressing modes, described in Position-Independent Code .
In PA-32 mode, the +z
and +Z options
force the compiler to generate PIC object files. In PA-64 and IPF
mode, the +Z option is the default.
Suppose you have some C functions, stored in length.c,
that convert between English and Metric length units. To compile
these routines and create PIC object files with the C compiler,
you could use this command:
$ 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 PA-32 mode, the +z
and +Z options
are essentially the same. Normally, you compile with +z.
However, in some instances - when the number of referenced
symbols per shared library exceeds a predetermined limit -
you must recompile with the +Z
option instead. In this situation, the linker displays an error
message and tells you to recompile the library with +Z.
In PA-64 and IPF mode, +Z is the default and the compilers ignore the options and generate PIC code.
In PA-32 mode, the C, C++, FORTRAN, and Pascal compilers support
the +z and +Z
options.
In PA-64 and IPF mode, +Z
is the default for the C and C++ compilers.
To create a shared library from one or more PIC object files,
use the linker, ld,
with the -b option.
By default, ld
will name the library a.out.
You can change the name with the -o
option.
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, then combine the resulting .o
files with ld.
Shown below are the commands you would use to create a shared library
named libunits.so:
$ cc -Aa -c length.c volume.c mass.c length.c: volume.c: mass.c: $ ld -b -o libunits.so length.o volume.o mass.o
Once 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.so
library:
$ chmod +r+x libunits.so
This library can now be linked with other programs. For example,
if you have a C program named convert.c
that calls routines from libunits.so,
you could compile and link it with the cc
command:
$ cc -Aa convert.c libunits.so
In PA-32 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 PA-64 and IPF mode, ./libunit.so
is stored in the executable.) For details, see Shared Library Location .
For details on linking shared libraries with your programs, see Linker Tasks.
You can specify additional shared libraries on the ld
command line when creating a shared library. The created shared
library is said to have a dependency on the
specified libraries, and these libraries are known as dependent
libraries or supporting libraries.
When you load a library with dependencies, all its dependent libraries
are loaded too. For example, suppose you create a library named
libdep.so using
the command:
$ ld -b -o libdep.so mod1.o mod2.o -lcurses -lcustom
Thereafter, any programs that load libdep.so
- either explicitly with dlopen
or shl_load or
implicitly with the dynamic loader when the program begins execution
- also automatically load the dependent libraries libcurses.so
and libcustom.so.
There are two additional issues that may be important to some shared library developers:
When a shared library with dependencies is loaded, in what order are the dependent libraries loaded?
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?
When a shared library with dependencies is loaded, the dynamic loader builds a load graph to determine the order in which the dependent libraries are loaded.
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.so modq.o -lB $ ld -b -o libD.so modd.o -lQ -lB $ ld -b -o libP.so modp.o -lA -lD -lQ
The dependency lists for these three libraries are:
libQ
depends on libB
libD
depends on libQ
and libB
libP
depends on libA,
libD, and libQ
+-->libA.so
|
libP.so-->libD------+
| | |
| v v
+-->libB.so-->libQ.so
The loader uses the following algorithm in PA-32 mode:
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 P,
traverse Q
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
The resulting load graph is:
libP-->libA-->libD--> libQ--> libB
The dynamic loader uses the following algorithm in PA-64 and IPF mode:
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:
mark P,
load P
traverse P
mark A, load
A
mark D, load
D
mark Q, load
Q
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, load
B
traverse B
B is already marked,
so skip B
The resulting load graph is:
libP-->libA-->libD--> libQ--> libB
Once a load graph is formed, the libraries must be added to
the shared library search list, thus binding their symbols to the
program. If the initial library is an implicitly loaded library
(that is, a library that is automatically loaded when the program
begins execution), the libraries in the load graph are appended
to the library search list. For example, if libP
is implicitly loaded, the library search list is:
<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_FIRST
is 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.so", BIND_IMMEDIATE | BIND_FIRST, 0);
Then the resulting library search list is:
libP--> libA--> libD--> libQ--> libB--><current search list>
The ld
command cannot replace or delete object modules in a shared library.
Therefore, to update a shared library, you must relink the library
with all the object files you want the library
to include. For example, suppose you fix some routines in length.c
(from the previous section) that were giving incorrect results.
To update the libunits.so
library to include these changes, you would use this series of commands:
$ cc -Aa -c length.c $ ld -b -o libunits.so length.o volume.o mass.o
Any 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.
If you make incompatible changes to a shared library, you can use library versioning to provide both the old and the new routines to ensure that programs linked with the old routines continue to work. See Version Control with Shared Libraries for more information on version control of shared libraries.
You can place shared libraries in the same locations as archive
libraries (see Archive Library Location ).
Again, this is typically /usr/lib/hpux32
and /usr/lib/hpux64
for application libraries, and system libraries. However, these
are just suggestions.
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 three ways to specify this directory search information:
Store a directory path list in the
program with the linker option +b
path_list.
Define the SHLIB_PATH
environment variable for use at run time.
Use the LD_LIBRARY_PATH
environment variable, and the +s
option is enabled by default.
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 .
This section describes methods you can use to improve the run-time performance of shared libraries. If, after using the methods described here, you are still not satisfied with the performance of your program with shared libraries, try linking with archive libraries instead to see if it improves performance. In general, though, archive libraries will not provide great performance improvements over shared libraries.
| NOTE | The |
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.so, 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.so lib3.so lib4.so
If LD_PRELOAD="/var/tmp/lib1.so",
the dynamic loader uses the
same load order and symbol
resolution order as if lib1.so
had been
specified as the first library in the link line:
$ ld ... /var/tmp/lib1.so lib2.so lib3.so lib4.so
In a typical command line use (with /bin/sh),
where LD_PRELOAD
is defined
as follows:
$ LD_PRELOAD=mysl.so application
The dynamic loader searches application
according to $PATH, but
searches
mysl.so 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/hpux32/dld.so: Cannot dlopen load module /usr/lib/hpux32/libpthread.so.1
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 |
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.
Consider a case where a.out has the following dependents:
a.out
/ \
libA.so libB.so
That is, a.out was built with commands like these:
$ cc -c ?.c $ ld -b -o libB.so b.o $ ld -b -o libA.so a.o $ cc foo.c -L. -lA -lB
ldd(1) shows the order in which the shared libraries are loaded:
$ ldd a.out
libA.so => ./libA.so
libB.so => ./libB.so
libc.so.1 => /usr/lib/hpux32/libc.so.1
libdl.so.1 => /usr/lib/hpux32/libdl.so.1
The symbol resolution order for the user libraries is:
a.out- ->libA.so --> libB.so
If the LD_PRELOAD environment
variable is set to "./libC.so",
the symbol resolution order is:
$ export LD_PRELOAD=./libC.so
$ ldd a.out
./libC.so => ./libC.so
libA.so => ./libA.so
libB.so => ./libB.so
libc.so.1 => /usr/lib/hpux32/libc.so.1
libdl.so.1 => /usr/lib/hpux32/libdl.so.1
a.out -->libC.so -->libA.so -->libB.so
You can perform profile-based optimization on your shared libraries to improve their performance. See Profile-Based Optimization for more information.
Normally, all global variables and procedure definitions are exported from a shared library. In other words, any procedure or variable defined in a shared library is made visible to any code that uses this library. In addition, the compilers generate "internal" symbols that are exported. You may be surprised to find that a shared library exports many more symbols than necessary for code that uses the library. These extra symbols add to the size of the library's symbol table and can even degrade performance (since the dynamic loader has to search a larger-than-necessary number of symbols).
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_state
If you have to export many symbols, you may find it convenient
to use the -c
file 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_state
Then 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 .)
When the linker creates a shared library, it places the object modules into the library in the order in which they are specified on the linker command line. The order in which the modules appear can greatly affect performance. For instance, consider the following modules:
a.oCalls routines in c.o
heavily, and its routines are called frequently by c.o.
b.oA huge module, but contains only error routines that are seldom called.
c.oContains routines that are called frequently by
a.o, and calls
routines in a.o
frequently.
If you create a shared library using the following command line, the modules will be inserted into the library in alphabetical order:
$ ld -b -o libabc.so *.o
The 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.so a.o c.o b.o
One 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
a.ox.o
y.o
b.ox.o
y.o
d.onone
e.onone
x.od.o
y.od.o
Then the following command determines the one-pass link order:
$ lorder ?.o | tsort Pipe lorder's output to tsort. a.o b.o e.o x.o y.o d.o
Notice 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.o
Again, 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.
You may get an additional performance gain by ensuring that
no shared libraries have write permissions. Programs that use more
than one writable library can experience significantly degraded
loading time. The following chmod
command gives shared libraries the correct permissions for best
load-time performance:
$ chmod 555 libname
Normally, the C compiler places constant data in the data space. If such data is used in a shared library, each process will get its own copy of the data, in spite of the fact that the data is constant and should not change. This can result in some performance degradation.
Use the C compiler's +ESlit
option, the default, which places constant data in the .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 |
HP-UX provides a method to support incompatible versions of shared library routines. Library-Level Versioning describes how you create multiple versions of a shared library.
| NOTE | Beginning with the HP-UX 11.00 release, the linker toolset supports only library-level versioning. Previous releases supported Inter-library version control. |
For the most part, updates to a shared library should be completely upward-compatible; that is, updating a shared library won't usually cause problems for programs that use the library. But sometimes - for example, if you add a new parameter to a routine - updates cause undesirable side-effects in programs that call the old version of the routine. In such cases, it is desirable to retain the old version as well as the new. This way, old programs will continue to run and new programs can use the new version of the routine.
Here are some guidelines to keep in mind when making changes to a library:
When creating the first version of a shared library, carefully consider whether or not you will need versioning. It is easier to use library-level versioning from the start.
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:
As a general rule, when an exported function is changed such that calls to the function from previously compiled object files should not resolve to the new version, the change is incompatible. If the new version can be used as a wholesale replacement for the old version, the change is compatible.
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.
When using shared library versioning, you need to save the old versions of modules in the library:
With library-level versioning, when an incompatible change is made to a module, the entire old version of the library must be retained along with the new version.
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.
HP-UX 10.0 adds a new library-level versioning scheme that allows you to maintain multiple versions of shared libraries when you make incompatible changes to the library. By maintaining multiple versions, applications linked with the older versions continue to run with the older libraries, while new applications link and run with the newest version of the library. Library-level versioning is very similar to the library versioning on UNIX System V Release 4.
To use library-level versioning, follow these steps:
Name the first version of your shared library with an extension
of .0 (that's
the number zero), for example libA.0.
Use the +h option
to designate the internal name of the library, for example, libA.0:
ld -b *.o -o libA.0 +h libA.0 Creates the shared library libA.0.
Since the linker still looks for libraries ending
in .so with the
-l option, create
a symbolic link from the usual name of the library ending in .so
to the actual library. For example, libA.so
points to libA.0:
ln -s libA.0 libA.so libA.so is a symbolic link to libA.0.
Link applications as usual, using the -l
option to specify libraries. The linker searches for libA.so,
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 prog.o -lA -lc Binds a.out with libA.0.
When you create a new version of the library with incompatible
changes, repeat the above steps except increment the number in the
suffix of the shared library file name. That is, create libA.1
rather than libA.0
and set the symbolic link libA.so
to point to libA.1.
Applications linked with libA.0
continue to run with that library while new applications link and
run with libA.1.
Continue to increment the suffix number for subsequent incompatible
versions, for example libA.2,
libA.3, and so
forth.
If you have an existing library you can start using library-level
versioning. First rename the existing library to have the extension
.0. Then create
the new version of the library with the extension .1
and an internal name using the .1
extension. Create a symbolic link with the extension .so
to point to the .1
library.
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 .so.
The +h
option gives a library an internal name for library-level versioning.
Use +h to create
versioned libraries:
+h internal_name
internal_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 PA-32 compatibility mode (with +compat),
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.
See Internal Name Processing for more information.
This section discusses the situation where you have used the ln(1) command to create file system links to shared library files. For example:
$ ld -b -o /X/libapp.so *.o Create shared library. $ ln -s /X/libapp.so /tmp/libmine.so 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.so
is a file system link to /X/libapp.so,
the following command records /tmp/libmine.so
in a.out, not
/X/libapp.so
as might be expected:
$ ld main.o -L /tmp -lmine -lc
To 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 PA-32 mode, to make the above example work
correctly with library-level versioning, first implement library-level
versioning with the actual library /X/libapp.so
and include the absolute path in the internal name of the new library:
$ mv /X/libapp.so /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.so 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.so Remove old link. $ ln -s /X/libapp.so /tmp/libmine.so 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.
For IPF/PA-64 mode programs, the dynamic loader only loads the library recorded in the dynamic load table. You should use library-level versioning and create your PA-64 and IPF shared library with an internal name unless the library will not be versioned in the future.
Once library level versioning is used, calls to shl_load(3X)
should specify the actual version of the library to be loaded. For
example, if libA.so
is now a symbolic link to libA.1,
then calls to dynamically load this library should specify the latest
version available when the application is compiled as shown below:
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.
There are cases where a program may behave differently when linked with shared libraries than when linked with archive libraries. These are the result of subtle differences in the algorithms the linker uses to resolve symbols and combine object modules. This section covers these considerations. (See also Caution When Mixing Shared and Archive Libraries .)
Occasionally, programmers may take advantage of linker behavior
that is undocumented but has traditionally worked. With shared libraries,
such programming practices might not work or may produce different
results. If the old behavior is absolutely necessary, linking with
archive libraries only (-a archive)
produces the old behavior.
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:
If a symbol is defined in two shared libraries, the definition used at run time is the one that appeared first, regardless of where the reference was.
The linker treats shared libraries more like object files.
As a consequence of the second rule, programs that call wrapper
libraries may become larger. (A wrapper library
is a library that contains alternate versions of C library functions,
each of which performs some bookkeeping and then calls the actual
C function. For example, each function in the wrapper library might
update a counter of how many times the actual C routine is called.)
With archive libraries, if the program references only one routine
in the wrapper library, then only the wrapper routine and the corresponding
routine from the C library are copied into the a.out
file. If, on the other hand, a shared wrapper library and archive
C library are specified, in that order, then all routines
that can be referenced by any routine in the wrapper library are
copied from the C library. To avoid this, link with archive
or shared versions for both the wrapper library and C library, or
use an archive version of the wrapper library and a shared version
of the C library.
Writing code that relies on the linker to locate a symbol in a particular location or in a particular order in relation to other symbols is known as making an implicit address dependency. Because of the nature of shared libraries, the linker cannot always preserve the exact ordering of symbols declared in shared libraries. In particular, variables declared in a shared library may be located far from the main program's virtual address space, and they may not reside in the same relative order within the library as they were linked. Therefore, code that has implicit address dependencies may not work as expected with shared libraries.
An example of an implicit address dependency is a function
that assumes that two global variables that were defined adjacently
in the source code 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.
To load shared libraries, a program must have a copy of the
dynamic loader (dld.so)
mapped into its address space. This copy of the dynamic loader shares
the stack with the program. The dynamic loader uses the stack during
startup and whenever a program calls a shared library routine for
the first time. If you specify -B immediate,
the dynamic loader uses the stack at startup only.
| NOTE | For PA-32 compatibility mode (with 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.
You can maintain multiple versions of a shared library using library-level versioning. This allows you to make incompatible changes to shared libraries and ensure programs linked with the older versions continue to run. (See Library-Level Versioning for more information.)
You can debug shared libraries just like archive libraries
with few exceptions. Support is provided by the WDB Debugger. Refer
to the WDB documentation at http://www.hp.com/go/wdb.
Some users may use the chroot
super-user command when developing and using shared libraries. This
affects the path name that the linker stores in the executable file.
For example, if you chroot
to the directory /users/hyperturbo
and develop an application there that uses the shared library libhype.so
in the same directory, ld
records the path name of the library as /libhype.so.
If you then exit from the chrooted
directory and attempt to run the application, the dynamic loader
won't find the shared library because it is actually stored in /users/hyperturbo/libhype.so,
not in /libhype.so.
Conversely, if you move a program that uses shared libraries
into a chrooted
environment, you must have a copy of the dynamic loader, dld.so,
and all required shared libraries in the correct locations.
Profiling with the prof(1)
and gprof(1)
commands and the monitor
library function is only possible on a contiguous chunk of the main
program (a.out).
Since shared libraries are not contiguous with the main program
in virtual memory, they cannot be profiled. You can still profile
the main program, though. If profiling of libraries is required,
relink the application with the archive version of the library,
using the -a archive
option.
What libraries your system has depends on what components were purchased. For example, if you didn't purchase Starbase Display List, you won't have the Starbase Display List library on your system.
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:
is the name of the routine, or group of closely related routines, being documented.
is the HP-UX Reference section number: 2 for system calls, 3 for other library routines.
is a letter designating the library in which the routine is stored.
For example, the printf(3S) man
page describes the standard input/output libc
routines printf,
nl_printf, fprintf,
nl_fprintf, sprintf,
and nl_sprintf.
And the pipe(2) man page describes the
pipe system call.
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 ( |
Description
These functions are known as system calls. They
provide low-level access to operating system services, such as opening
files, setting up signal handlers, and process control. These routines
are located in libc.
These are standard C library
routines located in libc.
These functions constitute the ELF access library
(libelf) which lets a program manipulate
ELF (Executable and Linking Format) object
files, archive files, and archive members. The linker searches this
library if the -lelf option is
specified. The header file <libelf.h>
provides type and function declarations for all library services
(described in elf(3E).
These functions comprise the Standard
input/output routines (see stdio(3S)). They
are located in libc.
These functions comprise the Math
library. The linker searches this library under the -lm
option (for the SVID math library) or the -lM
option (for the POSIX math library).
These functions comprise the Graphics library.
These functions comprise the Instrument support library.
Various specialized libraries. The names of the libraries in which these routines reside are documented on the man page.
The routines marked by (2), (3C), and (3S) comprise the standard
C library libc.
The C, C++, and FORTRAN compilers automatically link with this library
when creating an executable program.
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 in an application is not recommended and should be avoided. That is, an application should use only shared libraries or only archive libraries.
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.
| NOTE | The examples in this section apply to PA-32 compatibility mode. |
This example (in PA-32 and PA-64/IPF(+compat
mode) shows how mixing shared and archive libraries can cause a
program to abort. Suppose you have a main program, main(),
and three functions, f1(),
f2(), and f3()
each in a separate source file. main()
calls f1() and
f3() but not
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.so
and f1.o and
f2.o into the
archive library lib12.a:
$ ld -b -o lib3.so 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.so 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.so:
$ cc -c +z f3.c Compile to relocatable code. $ ld -b -o lib3.so f3.o Create a new shared library

Here's where the problem can occur. If you do not
relink the application, main.o,
and just run a.out
with the new version of lib3.so,
the program will abort since f2()
is not available in the application. The reference to f2()
from f3() remains
unsatisfied, producing an error in PA-32 mode:

$ a.out
/usr/lib/dld.so: Unresolved symbol: f2 (code) from
/users/steve/dev/lib3.so
Abort(coredump)
This example (in PA-32 and PA-64/IPF+compat
mode shows how mixing archive libraries and shared libraries using
shl_load(3X) can lead to unsatisfied symbols
and cause a program to abort.
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.so
and f1.o and
f2.o into the
archive library lib12.a:
$ ld -b -o lib3.so 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():

Here is where a problem can be introduced. If you compile
the new f3()
and rebuild the shared library lib3.so
without specifying the dependency on a shared library containing
f2(), calls to
f3() will abort.
$ cc -c +z f3.c Compile to position-independent code. $ ld -b -o lib3.so 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.so,
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 PA-32 error message:

$ a.out Illegal instruction (coredump)
This example shows how mixing archive libraries and shared libraries can lead to multiple definitions in the application and unexpected results. If one of the definitions happens to be a data symbol, the results can be catastrophic. If any of the definitions are code symbols, different versions of the same routine could end up being used in the application. This could lead to incompatibilities.
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, |
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.so
and f1.o and
f2.o into the
archive library lib12.a.
Also put f1.o
and f2.o into
the shared library lib12.so:
$ ld -b -o lib3.so f3.o Create a shared library. $ ld -b -o lib12.so 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.so
and create the executable a.out:
$ cc main.o lib12.a lib3.so 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.so,
including the new dependency on f2()
in lib12.so:
$ cc -c +z f3.c Compile to PIC.
$ ld -b -o lib3.so f3.o -L . -l12 Create library with dependency.

Here's where the problem can occur in PA-32 +compat
modes. If you do not relink the application,
main.o, and just
run a.out with
the new version of lib3.so,
the program will execute successfully, but it will execute two different versions
of f2(). main()
calls f2() in
the program file a.out
and f3() calls
f2() in lib12.so.
Even though f2()
is contained within the application, it is not visible to the shared
library, lib3.so.

Applications that depend on shared libraries should not use
archive libraries to satisfy symbol imports in a shared library.
This suggests that only a shared version of libc
should be used in applications using shared libraries. If an archive
version of a dependent library must be used, all of its definitions
should be explicitly exported with the -E
or +e options
to the linker to avoid multiple definitions.
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.
In the HP-UX 11i version 1.5, HP provides an industry-standard linker toolset for programs linked in IPF mode. The new toolset consists of a linker, dynamic loader, object file class library, and an object file tool collection. Although compatibility between the current and previous toolset is an important goal, some differences exist between these toolsets.
The IPF linker toolset introduces different types of shared libraries. (In SVR4 Unix, shared libraries are sometimes called dlls.)
Compatibility mode shared
library: Using the IPF linker, a compatibility mode shared
library is basically a library built with ld -b +compat
that has dependent shared libraries. The +compat
option affects the way the linker and loader search for dependent
libraries of a shared library and records their names.
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 |
The linker handles these libraries in different way with regard to internal naming and library search modes.
For both PA-32 mode and IPF/PA-64 mode, you specify
shared library internal names using ld +h name
However, their dependent libraries' internal names may
not be recorded the same way in a standard mode link.
The linker treats shared library names as follows:
If the dependent shared library has
an internal name, it is recorded in the DT_NEEDED
entry.
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 an ld +compat compatibility-mode
link, the linker treats the internal names like it does in PA-32
mode:
If the dependent library's
internal name is rooted (starts with "/"), it
is inserted as is in the DT_HP_NEEDED
entry. If it was specified with -l,
the dynamic path bit is set to TRUE
in the DT_HP_NEEDED entry.
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.so,
you get /home/your_dir/libdk.so.
All DT_HP_NEEDED
entries with the dynamic path bit set are subject to dynamic path
lookup.
Any library whose name has no "/"
character in it becomes a candidate for dynamic path searching.
Also, the linker always uses the LD_LIBRARY_PATH
and the SHLIB_PATH
environment variable to add directories to the run time search path
for shared libraries, unless the ld +noenvvar
option is set.
In PA-32 compatibility 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 +s
path_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 default mode.
ld main.o \ Subject to -lfoo -o main dynamic path searching.
The dynamic loader searches for libfoo.so
in the directories specified by the LD_LIBRARY_PATH
and SHLIB_PATH
environment variables.
Symbol binding resolution, both at link time and run time, changes slightly in the IPF and PA-64 HP-UX linker toolset. The symbol binding policy is more compatible with other open systems.
This section covers the following topics:
Link-time symbol resolution in shared libraries
Resolution of unsatisfied shared library references
Promotion of uninitialized global variables
Symbol searching in dependent libraries
In the IPF and PA-64 linker toolset, the linker remembers all symbols in a shared library for the duration of the link. Global definitions satisfy trailing references, and unsatisfied symbols remaining in shared libraries are reported.
The PA-32 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 IPF and PA-64 linker may reference a different version of a procedure than is referenced by the PA-32 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.so ar rv libB.a archive.o cc main.o libA.so unsat.o libB.a -o test1
The PA-32 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.so
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 IPF and PA-64 linker toolset produces:
$ test1
in SHARED library procedure `afunc'
The IPF and PA-64 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.
In the IPF and PA-64 linker toolset, the dynamic loader requires that all symbols referenced by all loaded libraries be satisfied at the appropriate time. This is consistent with other SVR4 systems.
The PA-32 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.so cc main.o liba.so -o test2
Using the PA-32 linker, test2
executes without error. The module in liba.so
created from lib2.o
is determined to be unreachable during execution, so the global
symbol for unsat
(in lib2.o) is
not bound.
The IPF and PA-64 linker toolset reports an unsatisfied
symbol error for unsat
at link time or at run time if the program were made executable.
In the IPF and PA-64 linker toolset, the linker expands and promotes uninitialized global data symbols in object files linked into a shared library to global data objects, exactly like executable files. This is standard with other SVR4 systems.
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.so ld -b b.o -o libB.so cc main.o libA.so libB.so -o test3
The PA-32 linker toolset produces:
$ test3
object is 1
The PA-32 linker toolset defines the object
global variable in libA.so
as a storage export symbol. The dynamic loader,
when searching for a definition of object
to satisfy the import request in libA.so,
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 IPF linker and PA-64 toolset produces:
$ test 3
object is 0
The IPF and PA-64 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.so,
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.
In the IPF and PA-64 linker toolset, the linker searches
dependent libraries in a breadth-first order for
symbol resolution. This means it searches libraries linked to the
main program file before libraries linked to shared libraries. This
behavior change is consistent with other SVR4 systems. The linker
also searches siblings of a dependent shared library before its
children. For example, using the structure described in Figure 30: Search Order of Dependent Libraries, if libD had dependent
libraries libDK and libLH, libD, libE, libF, then libDK, libLH would
be searched in that order. The dlopen
library management routines and executable files (a.out)
created by the linker with the +std
option (default) use the breadth-first search order.
The PA-32 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 IPF and PA-64 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.so
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:
First, the dependent shared libraries for libA
are built. (Other libraries are also built.)
ld -b libDK.o -o libDK.so ld -b libLH.o -o libLH.so ld -b libD.o -IDK -ILH -o libD.so libA dependent shared library ld -b libE.o -o libE.so libA dependent shared library ld -b libF.o -o libF.so libA dependent shared library ld -b libB.o -o libB.so ld -b libC.o -o libC.so
Next, libA.o
is linked to its dependent libraries and libA.so
is built.
ld -b libA.o -lD -lE -lF -o libA.so
Finally, main.o
is linked to its shared libraries.
cc main.o -lA -lB -lC -o main
If a procedure called same_name()
is defined in libD.so
and libB.so,
main calls same_name()
in libB.so.
If you use mixed mode shared libraries, the search mechanism may produce unexpected results.
For the following command, libA.so
and its dependent libB.so are compatibility
mode libraries and libC.so and
libD.so are standard mode libraries.
ld -b libF.o +compat -L.-lA -lC -o LibF.so
libF.so is a compatibility
mode library, but is dependent libC.so
is a standard mode library. The linker uses depth-first searching
mechanisms because the highest-level library is in compatibility
mode.
A mixed mode shared library is a library whose children are all of one type (for example, +compat), but whose grandchildren may be of the other mode. This poses some problems when linking and loading the libraries. To help resolve this, the linker treats each library as having any mode. Therefore, when it sees a compatibility mode library, it searches for it using the PA-32-style algorithms. For any standard mode library, it uses the IPF/PA-64-style algorithms. Only the load order of the libraries themselves is fixed between depth-first or breadth-first.
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.so is a dependent of libfile2.so ld -b file1.o -o libfile1.so +h libfile1.1 ld -b file2.o -o libfile2.so +h libfile2.1 -L. -lfile1 # build compatibility mode dlls # libfile3.so is a dependent of libfile4.so ld -b file3.o -o libfile3.so +h libfile3.1 ld -b file4.o -o libfile4.so +h libfile4.1 -L. -lfile3 +compat ln -s libfile1.so libfile1.1 ln -s libfile3.so 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.so +h libfile5.1 -L. -lfile4 -lfile2 ln -s libfile4.so libfile4.1 ln -s libfile2.so libfile2.1 ld main.o -L. -lfile5 -lc
The resulting a.out has standard mode
dependents, libfile5.so and libc.so.
libfile5.so has two dependents,: libfile4.so
and libfile2.so. libfile4.so
is a compatibility mode library, and has a dependent, libfile3.so.
libfile2.so is a standard mode library, and
has a dependent, libfile1.so. 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.so is a standard mode library.
The loader uses IPF/PA-64 search techniques on all libraries
except for libfile3.so, in which case it uses
PA-32 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 |
The examples demonstrate the behavior of compatibility and standard mode shared libraries created by the IPF linker toolset:
The following example creates a compatibility mode shared library.
ld -b file1.o -o libfile1.so +h libfile1.1 ld -b file2.o -o libfile2.so +h ./libfile2.1 ld -b file3.o -o libfile3.so +h /var/tmp/libfile3.1 ld -b file4.o -o libfile4.so ld -b +compat file3a.o -o libfile3a.so -L. -lfile -lfile3 +h libfile3a.1 ld -b +compat file2a.o -o libfile2a.so libfile2.so ./libfile4.so +b /var/tmp elfdump -L libfile3a.so libfile2a.so
libfile3a.so:
*** Dynamic Section ***
Index Tag Value 0 HPNeeded 1:./libfile1.1 1 HPNeeded 1:/var/tmp/libfile3.1 2 Soname libfile3a.1 ...
libfile2a.so:
*** Dynamic Section ***
Index Tag Value 0 HPNeeded 0:/home/knish/./libfile2.1 1 HPNeeded 0:./libfile4.so 2 Rpath /var/tmp ...
The following example builds a standard mode library.
ld -b file1.o -o libfile1.so +h libfile1.1 ld -b file2.o -o libfile2.so +h ./libfile2.1 ld -b file3.o -o libfile3.so +h /var/tmp/libfile3.1 ld -b file4.o -o libfile4.so ld -b file3a.o -o libfile3a.so -L. -lfile1 -lfile3 +h libfile3a.1 ld -b file2a.o -o libfile2a.so libfile2.so ./libfile4.so +b /var/tmp elfdump -L libfile3a.so libfile2a.so
libfile3a.so:
*** Dynamic Section ***
Index Tag Value/Ptr0 Needed libfile1.1 1 Needed /var/tmp/libfile3.1 2 Soname libfile3a.1 3 Rpath . ...
libfile2a.so:
*** Dynamic Section ***
Index Tag Value/Ptr0 Needed ./libfile2.1 1 Needed ./libfile4.so 2 Rpath /var/tmp ...
The dynamic loader does dynamic path searching for libfile1.so..
It does not do dynamic path searching for libfile2.so,
libfile3.so, and libfile4.so.
This example of dynamic path searching demonstrates differences
between compatibility mode and standard mode dependent shared libraries.
The example builds standard mode libraries and does a standard mode
link. By default, the dynamic loader looks at the environment variables
LD_LIBRARY_PATH and SHLIB_PATH
to find the shared libraries.
# build standard mode shared libraries #libfile1.so is a dependent of libfile2.so ld -b file1.o -o libfile1.so +h libfile1.1 ld -b file2.o -o libfile2.so +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.so /var/tmp ln -s /var/tmp/libfile2.so /var/tmp/libfile2.1 a.out dld.so: Unable to find library 'libfile2.1' export SHLIB_PATH=/var/tmp a.out in file1 in file2
This example builds a compatibility mode library and does
a compatibility mode link. The +s
option is not specified at link time, so the dynamic loader does
not look at any environment variables to do dynamic path searching.
# build compatibility mode dlls # libfile1.so is a dependent of libfile2.so ld -b file1.o -o libfile1.so +h libfile1.1 ld -b file2.o -o libfile2.so +h libfile2.1 -L. -lfile1 +compat ln -s libfile1.so 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.so /var/tmp ln -s /var/tmp/libfile2.so /var/tmp/libfile2.1 a.out dld.so: Unable to find library '1:./libfile2.1' export SHLIB_PATH=/var/tmp a.out dld.so: 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
This example mixes compatibility and standard mode shared libraries. It uses PA-32-style linking and loading for the compatibility mode libraries and IPF/PA-64-style linking and loading for standard mode libraries.
# build standard mode dlls # libfile1.so is a dependent of libfile2 ld -b file1.o -o libfile1.so +h libfile1.1 mkdir TMP ld -b +b $pwd/TMP file2.o -o libfile2.so +h libfile2.1 -L. -lfile1 # build compatibility mode dlls # libfile3.so is a dependent of libfile4 ld -b file3.o -o libfile3.so +h libfile3.1 ld -b file4.o -o libfile4.so +b $pwd/TMP +h libfile4.1 +compat -L. -lfile3 ln -s libfile1.so libfile1.1 ln -s libfile3.so libfile3.1 mv libfile1.so TMP mv libfile3.so TMP cd TMP ln -s libfile1.so libfile1.1 ln -s libfile3.so libfile3.1 cd ..# link with +b so ld will use RPATH at link time to find # libfile1.so (standard mode dll) # the linker will not use RPATH to find libfile3.so # (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.so +h libfile5.1 -L. -lfile2 -lfile4 ld: Can't find dependent library "./libfile3.so" ld -b +b pwd/TMP main.o -o libfile5a.so +h libfile5.1 -L. -lfile2 -lfile4 +compat ld: Can't find dependent library "./libfile3.so"
For the following libraries, with the dependencies:
lib1.so has dependents lib2.so, lib3.so, and lib4.so lib2.so has dependents lib2a.so and lib2b.so lib3.so has dependents lib3a.so and lib3b.so lib3a.so has dependent lib3aa.so
+-->lib2a.so
|
+-->lib2.so-->lib2b.so
|
lib1.so-->lib3.so.so-->lib3a.so-->lib3aa.so
| |
| +-->lib3b.so
+-->lib4.so
In breadth-first searching, the load order is siblings before children:
lib1.so->lib2.so->lib3.so->lib4.so->lib2a.so->lib2b.so->lib3a.so->lib3b.so->lib3aa.so
In depth-first searching, the load order is children before siblings:
lib1.so->lib2.so->lib2a.so->lib2b.so->lib3.so->lib3a.so->lib3aa.so->lib3b.so->lib4.so
RPATH with Standard Mode Shared LibraryIn the following example, the linker uses the embedded RPATH at link time to find the dependent library. For compatibility mode shared libraries, embedded RPATHs are ignored.
ld -b bar.o -o libbar.so ld -b foo.o -o libfoo.so -L. -lbar +b /var/tmp # ld should look in /var/tmp to find libbar.so since libfoo.so # has an embedded RPATH of # /var/tmp mv libbar.so /var/tmp ld main.o -L. -lfoo -lc # For compatibility mode dlls, embedded RPATHs are ignored ld -b bar.o -o libbar.so ld -b foo.o -o libfoo.so +compat -L. -lbar +b /var/tmp # ld won't find libbar.so since it does not look at embedded RPATHs mv libbar.so /var/tmp ld main.o -L. -lfoo +compat -lc ld: Can't find dependent library "libbar.so" Fatal error.
+b pathlistThe following examples compare PA-32 and IPF/PA-64 linking
with the ld +b pathlist
option. The dynamic loader uses the directory specified by the -L
option at link time for dynamic library lookup at run time, if you
do not use the +b option.
+b path_list in IPF/PA-64 ModeIn this example, the program main
calls a shared library routine in libbar.so.
The routine in libbar.so in turn
calls a routine in the shared library libme.so.
The +b linker option indicates
the search path for libme.so when
linking libbar.so. (You use +b path_list
with libraries specified with the -l library
or -l:library
options.)
cc -c me.c ld -b me.o -o libme.so ld -b bar.o -o libbar.so -L. -lme +b /var/tmp mv libme.so /var/tmp ld main.o -L. -lbar -lc
In IPF/PA-64 mode, the linker finds libme.so
in /var/tmp because the +b /var/tmp
option is used when linking libbar.so.
Since -lme was specified when linking
libbar.so, libme.so
is subject to run-time dynamic path searching.
When linking main.o, the
link order in the above example is:
./libbar.so
found
./libme.so not
found
/var/tmp/libme.so found
./libc.so not
found
/usr/lib/hpux32/libc.so found
In the above example, if you type:
ld main.o -L. -lbar -lc mv libme.so /var/tmp
instead of:
mv libme.so /var/tmp ld main.o -L. -lbar -lc
the linker findslibme.so
in ./ at link time, and the dynamic
loader finds libme.so 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:
LD_LIBRARY_PATH
to find libbar.so not
found
SHLIB_PATH to find
libbar.so not found
./libbar.so (./libbar.so)
found
LD_LIBRARY_PATH
to find libme.so not
found
SHLIB_PATH to find
libme.so not found
/var/tmp/libme.so
found
LD_LIBRARY_PATH
to find libc.so not found
SHLIB_PATH to find
libc.so not found
./libc.sonot found
/usr/lib/hpux32/libc.so
found
+b path_list in PA-32 ModeThis example is the same as Library Example: Linking to Libraries
with +b path_list
in IPF/PA-64 Mode, but this time the program is compiled
in PA-32 mode.
cc -c +DD32 me.c bar.c main.c
ld -b me.o -o libme.so
ld +compat -b bar.o -o libbar.so -L. -lme +b /var/tmp \PA-32 mode library
created
ld main.o -L. -lbar -lc mv libme.so /var/tmp
When linking main.o, the
link order is:
./libbar.so
found
./libme.so found
./libc.so not
found
/usr/lib/libc.so
found
In the above example, if you type:
mv libme.so /var/tmp ld main.o -L. -lbar -lc
instead of:
ld main.o -L. -lbar -lc mv libme.so /var/tmp
the linker issues the following error:
ld: Can't find dependent library ./libme.so Fatal Error
The linker does not look in /var/tmp
to find shared libraries because in PA-32 mode the directories specified
by +b pathname
are only searched at run time.
Because libme.so 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:
./libbar.so
found
/var/tmp/libme.so
found
./libc.so not
found
/usr/lib/libc.so
found
You can explicitly load and use shared libraries
from your program. The linker toolset provides two families of load
routines, dlopen and shl_load.
The dlopen routines (primarily
for IPF/PA-64 mode ) use Unix SVR4 compatible mechanism for
library management. The shl_load
routines support the shared library mechanisms provided in previous
versions of HP-UX.
| NOTE | Support for |
| NOTE | Do not mix use of the |
The following sections introduce the shared library management routines available for the HP-UX 11.00 release.
The dlopen family of shared library management routines is available for the IPF and PA-64 linker. The following routines are also supported by the PA-32 linker:
dlopen
dlclose
dlsym
dlerror
The dlopen family of routines
use Unix SVR4 shared library mechanisms.
Use the following dl* routines
for shared library management:
| Routine | Action |
|---|---|
|
Loads a shared library. This routine does breadth-first searching. |
|
Prints the last error message recorded
by |
|
Gets the address of a symbol in a shared library. |
|
Returns information on a loaded module. |
|
Returns information about a loaded module. |
|
Retrieves the name of a loaded module given a load model descriptor. |
|
Unloads a shared library previously loaded
by |
All the dlopen routines are
thread-safe.
These routines are described in the dl*(3C) man pages.
The shl_load family of shared
library management routines are available for both the PA-32 (compatibility
mode) and IPF/PA-64 linker.
Use the following shl_load
routines for shared library management:
| Routine | Action |
|---|---|
|
Explicitly load a shared library. It lets you load a compatibility or standard mode shared library. It does depth-first searching. |
|
Finds the address of a global symbol in a shared library. |
|
Get information about currently loaded
libraries. |
|
Get descriptor information about a loaded
shared library. |
|
Adds a new symbol to the global shared library symbol table. |
|
Returns a list of symbols in a shared library. |
|
Unload a shared library. |
Except for shl_get and shl_gethandle,
all these routines are thread safe.
These routines are described in the shl_load(3x) man page.
These commands and files provide more information about using shared library management routines.
| Command/File | Action |
|---|---|
|
Executable file from assembler, compiler, and linker output. |
|
Command to invoke the HP-UX C compiler. |
|
System loader. |
|
Command to invoke the linker. |
The shl_load family of shared
library management routines use some special data types (structures)
and constants defined in the C-language header file /usr/include/dl.h.
When using these functions from C programs, be sure to include dl.h:
#include <dl.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 aC++ or Fortran, refer to the inter-language calling conventions described in the compiler documentation.
In IPF/PA-64 mode, you can access the shl_load
and dlopen routines by specifying
either -ldld or -ldl
on the command line. In PA-32 mode, you can access the shl_load
family of routines by specifying the -ldld
option on the cc(1) or ld(1)
command line.
The default behavior of the PA-64 and IPF linker is to export
all symbols defined by a program. However, some PA-32 implementations
do not, by default, export all symbols, instead exporting only those
symbols imported by a shared library seen at link time. In PA-32
compatibility mode, use the -E
option to ld to ensure that all
symbols defined in the program are available to the loaded libraries.
To create shared libraries, compile source files with +z
or +Z and link the resultant object
files. In PA-32 mode, to create share libraries, compile source files with +Z
or +Z and link the resultant object
files with -b.
A shared library can have an initialization routine-known as an initializer-that is called when the load module (a shared library or executable) is loaded (initializer) or explicitly unloaded (finalizer or terminator). Typically, an initializer is used to initialize a shared library's data when the library is loaded.
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 IPF/PA-64 mode, you can specify initializers and terminators even for archive libraries or nonshared executables.
The linker supports two different types of initializers and terminators:
Init/fini style. (Not supported in HP-UX 10.X)
HP-UX 10.X style.
This style uses init and fini functions to handle initialization and finalization (terminator) operations.
Initializers (inits) are called before the user's
code starts or when a shared library is loaded. Functions specified
with this option should take no arguments and return nothing (void
functions). (Terminators are invoked only if main()
calls exit(). They are not called
if main() calls return()).
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.
Finalizers (finis) are called after the user's code
terminates by either calling the libc exit
function, returning from the main
or _start functions, or when the
shared library which contains the fini is unloaded from memory.
Terminators are invoked only if main()
calls exit(). They are not called
if main() calls return().Like
init functions, functions specified with this option should take
no arguments and return nothing (void functions).
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.
HP-UX 10.X style initializers are the same type supported
in all HP-UX 10.X releases. These are called both before the user's
code is started or a shared library is loaded (using shl_load
or dlopen) as well as when the
shared library is unloaded (using shl_unload
or dlclose). The linker option
+I is used to declare this type
of initializer. The function returns nothing but takes two arguments.
The first is a handle to the shared library being initialized. This
handle can be used in calling shl_load
routines. The second is set to non-zero at startup and zero at program
termination or library unload.
$ ld -b foo.o +I my_10x_init -o libfoo.so
#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 PA-32 mode, the PA-64 and IPF 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.
This section describes use of init/fini initializer and provides examples:
This example consists of three shared libraries lib1.so,
lib2.so and lib3.so.
The lib1.so depends on lib3.so.
The main program (a.out) depends
on lib1.so and lib2.so.
Each shared library has an init
style initializer and a fini style
terminator. The lib1.so and lib2.so
uses linker options (+init and
+fini) to specify the initializers
and terminators and lib3.so uses
compiler pragmas.
lib1()
{
printf("lib1\n");
}
void
lib1_init()
{
printf("lib1_init\n");
}
void
lib1_fini()
{
printf("lib1_fini\n");
}
C source for lib2.so (file lib2.c):
lib2()
{
printf("lib2\n");
}
void
lib2_init()
{
printf("lib2_init\n");
}
void
lib2_fini()
{
printf("lib2_fini\n");
}
C source for lib3.so (file lib3.c):
lib3()
{
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 lib1.c lib2.c lib3.c main.c -c; $ ld -b lib3.o -o lib3.so; $ ld -b +init lib2_init +fini lib2_fini lib2.o -o lib2.so; $ ld -b +init lib1_init +fini lib1_fini lib1.o ./lib3.so -o \ lib1.so; $ cc -L. 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
Multiple initializers/terminators within the same load module (an executable or shared library) are called in an order following these rules:
Inits in .o
(object) files or .a (archive)
files are called in the reverse order of the link line.
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.
For example, the linker command:
$ ld -b first_64bit.o -l:libfoo.so second_64bit.o my_64bit.a +I first_10x_init +I second_10x_init -o libbar.so
results in the following order when library is loaded:
inits from any .o
files used in my_64bit.a
inits in second_64bit.o
inits in first_64bit.o
first_10x_init
second_10x_init
and the following order when library is unloaded:
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 |
|
When multiple load modules have initializers/terminators, the following rules apply to ordering:
When loading, the inits and HP-UX 10.X style initializers of any dependent libraries are called before the ones in the current library.
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.
For example, given three libraries: libA.so,
libB.so, libC.so.
If libA.so were linked as (libB.so
and libC.so are "dependent"
libraries of libA.so):
$ ld -b foo.o -lB -lC -o libA.so
One possible ordering while loading is:
inits in C
inits in B
inits in A
and while unloading is:
finis in A
finis in B
finis in C
The initializer is called for libraries that are loaded implicitly
at program startup, or explicitly with shl_load
or dlopen.
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 Summary .
This section contains the following topics:
To declare the name of the initializer, use the +I
linker option when creating the shared library. The syntax of the
+I option is:
+I initializer
where initializer is the initializer's name.
Multiple initializers may be called by repeating the +I
initializer option.
For example, to create a shared library named libfoo.so
that uses an initializer named init_foo,
use this linker command line:
$ ld -b -o libfoo.so libfoo.o +I init_foo
Multiple initializers are executed in the same order that they appear on the command line; they are unloaded in reverse order. (This applies only to the calling order within a shared library, not across multiple shared libraries.)
| NOTE | In PA-32 compatibility mode, 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 )
The name of the initializer as specified with the
+I linker option.
The initializer is called with this parameter set to the handle of the shared library for which it was invoked.
The initializer is called with this parameter set to -1 (true) when the shared library is loaded and 0 (false) when the library is unloaded.
The initializers cannot be defined as local definitions. Initializers
cannot be hidden through the use of the -h
option when building a shared library.
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.
Prior to the HP-UX 10.0 release, initializer's addresses could
be accessed through the initializer field of the shared library
descriptor which is returned from a call to shl_get().
To support multiple initializers, the shl_getsymbols()
routine has been enhanced to support the return of the initializer's
address.
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 |
|
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.
One way to use initializers is to define a unique initializer
for each library. For instance, the following example shows the
source code for a library named libfoo.so
that contains an initializer named init_foo:
#include <stdio.h>
#include <dl.h>
/*
* This is the local initializer that is called when the libfoo.so
* 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.so
and to register init_foo
as the initializer:
$ cc -Aa -c libfoo.c $ ld -b -o libfoo.so +I init_foo libfoo.o
To use this technique with multiple libraries, each library
should have a unique initializer name. The following example program
loads and unloads libfoo.so.
#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.so and find the required symbols:
*/
if ((hndl_foo = shl_load("libfoo.so",
BIND_IMMEDIATE, 0)) == NULL)
perror("shl_load: error loading libfoo.so"), 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.so:
*/
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
Rather than have a unique initializer for each library, libraries
could have one initializer that calls the actual initialization
code for each library. To use this technique, each library declares
and references the same initializer (for example, _INITIALIZER),
which calls the appropriate initialization code for each library.
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:
#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.so loaded */
{
printf("libunits.so loaded\n");
}
void unload() /* called after libunits.so unloaded */
{
printf("libunits.so 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.so loaded */
{
printf("libtwo.so loaded\n");
}
void unload() /* called after libtwo.so unloaded */
{
printf("libtwo.so 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 libunits.c $ ld -b -o libunits.so +I _INITIALIZER libunits.o $ cc -Aa -c libtwo.c $ ld -b -o libtwo.so +I _INITIALIZER libtwo.o
The following is an example program that loads these two libraries:
C Source for testlib2.c#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.so and find the required symbols:
*/
if ((hndl_units = shl_load("libunits.so", BIND_IMMEDIATE, 0)) == NULL)
perror("shl_load: error loading libunits.so"), 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.so and find the required symbols:
*/
if ((hndl_two = shl_load("libtwo.so", BIND_IMMEDIATE, 0)) == NULL)
perror("shl_load: error loading libtwo.so"), 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.so:
*/
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.so:
*/
(*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 -ldld
Note 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.so loaded libtwo.so loaded 1.0in = 2.54cm 1.0gal = 3.79l 1.0oz = 28.35g foo called bar called libunits.so unloaded libtwo.so unloaded
This section describes the dl*
family of shared library management routines. All these routines
are available in IPF/PA-64 mode. Support for the following
routines is available in PA-32 mode:
dlopen
dlclose
dlsym
dlerror
Opens a shared library.
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 (
|
|
flags |
Mode | Definition |
|
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. |
|
|
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. |
|
|
The shared library's symbols are made
available for the relocation processing of any other object. In
addition, symbol lookup using |
|
|
The shared library's symbols
are made available for relocation processing only to objects loaded
in the same If
neither |
A successful dlopen call
returns to the process a handle which
the process can use on subsequent calls to dlsym
and dlclose. This value should
not be interpreted in any way by the process.
dlopen returns NULL under
the following conditions:
file cannot be found.
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.
More detailed diagnostic information is available through
dlerror.
dlopen is one of a family
of routines that give the user direct access to the dynamic linking
facilities. dlopen makes a shared
library specified by a file available
to a running process. A shared library may specify other objects
that it "needs" in order to execute properly.
These dependencies are specified by DT_NEEDED
entries in the.dynamic section of the original shared library. Each
needed shared library may, in turn, specify other needed shared
libraries. All such shared libraries are loaded along with the original
shared library as a result of the call to dlopen.
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:
Symbols in the original a.out.
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.
To determine the scope of visibility for the symbols loaded
with a dlopen invocation, bitwise
OR the mode parameter with one of the following values: RTLD_GLOBAL
or RTLD_LOCAL.
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.
Load order establishes an ordering among symbol definitions using the temporal order in which the shared libraries containing the definitions were loaded, such that the definition first loaded has priority over definitions added later. Load ordering is used in relocation processing.
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.
The dlsym function uses dependency
ordering, except when the global symbol shared library is obtained
via a dlopen operation on file
with a value 0. The dlsym function
uses load ordering on the global symbol shared library.
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.
In PA-32 mode, 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 |
| NOTE | In IPF/PA-64 mode, with the |
The following example shows how to use dlopen
to load a shared library. The RTLD_GLOBAL
flag enables global visibility to symbols in lib1.so.
The RTLD_LAZY flag indicates that
only references to data symbols are to be relocated and all function
symbol references are to be delayed until their first invocation.
#include <stdio.h> #include <dlfcn.h>
int main(int argc, char **argv)
{
void* handle; handle = dlopen("./lib1.so", RTLD_GLOBAL | RTLD_LAZY);
if (handle == NULL) {
printf("Cannot load library\n");
}
}
Gets diagnostic information.
char *dlerror(void);
dlerror returns a null-terminated
character string (with no trailing newline character) that describes
the last error that occurred during dynamic linking processing.
If no dynamic linking errors have occurred since the last invocation
of dlerror, it returns NULL. Thus,
invoking dlerror a second time,
immediately following a prior invocation, results in NULL being
returned.
| NOTE | The messages returned by |
The following code sequence shows how to use dlerror
to get diagnostic information.
void* handle;
/* Try to load a non-existing library */
handle = dlopen("invalid.so", RTLD_GLOBAL | RTLD_LAZY);
if (handle == NULL) {
printf("%s\n", dlerror());
}
Gets the address of a symbol in shared library.
void *dlsym(void *handle, const char *name);
| Parameter | Definition |
|---|---|
handle |
Either the value returned by a call to
|
name |
The symbol's name as a character string. |
If handle does not refer to a valid
shared library opened by dlopen,
or if the named symbol cannot be found within any of the shared
libraries associated with handle, dlsym
returns NULL. The dlerror routine
provides more detailed diagnostic information.
dlsym allows a process to
obtain the address of a symbol defined within a shared library previously
opened by dlopen.
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.
RTLD_NEXT can be used to
navigate an intentionally created hierarchy of multiply defined
symbols created through interposition. For example, if a program
wished to create an implementation of malloc
that embedded some statistics gathering about memory allocations,
such an implementation could define its own malloc
which would gather the necessary information, and use dlsym
with RTLD_NEXT to find the "real"
malloc, which would perform the
actual memory allocation. Of course, this "real"
malloc could be another user-defined
interface that added its own value and then used RTLD_NEXT
to find the system malloc.
The following example shows how to use dlopen
and dlsym to access either function
or data objects. (For simplicity, error checking has been omitted.)
void *handle;
int i, *iptr;
int (*fptr)(int);
/* open the needed object */
handle = dlopen("/usr/mydir/mylib.so", 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;
}
Retrieves information about a loaded module (program or shared library).
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 |
If successful, dlget returns
a handle for the shared library as defined by the return value from
dlopen(). If a call to dlget
is unsuccessful, a NULL pointer is returned and desc
remains unchanged.
dlget is one of a family
of routines that give the user direct access to the dynamic linking
facilities. dlget retrieves information
about a load module from an index specifying the placement of a
load module in the dynamic loader's search list.
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;
}
The following code sequence shows how to use dlget
to retrieve information about loaded modules. The following code
sequence prints the text base of all loaded modules:
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);
}
}
Retrieves information about a loaded module (program or shared library).
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 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 |
|
ptr |
The virtual memory address to read from. |
|
bufsiz |
Tthe size of buffer in bytes. |
|
ident |
The value of the ident_parm
parameter to |
|
| 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. |
If successful, dlmodinfo
returns a handle for the shared library
as defined by the return value from dlopen().
NULL is returned otherwise. The return values are type-converted
to unsigned long
dlmodinfo is one of a family
of routines that give the user direct access to the dynamic linking
facilities. The dlmodinfo routine
retrieves information about a load module from a given address value.
dlmodinfo searches all currently
loaded load modules looking for a load module whose address range
(address range of all loaded segments) holds the given address value.
The dlmodinfo routine fills the
load_module_desc with information from
the matching load module.
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.
The following code sequence shows how to use dlmodinfo
to retrieve information about a load module. In this example the
dlmodinfo is provided with the
address of a function foo. The
address of foo is matched with
the address range (the address range of all loaded segments) of
all load modules. The dlmodinfo
fills in the load_module_desc with information
form the matching load module.
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);
}
}
Retrieves the name of a load module given a load module descriptor.
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 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 |
|
ptr |
The virtual memory address to read from. |
|
bufsiz |
The size of buffer in bytes. |
|
ident |
The value of the ident_parm
parameter to |
|
| 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. |
dlgetname returns the pathname
of a load module represented by desc.
If desc does not describe a loaded module,
dlgetname returns NULL.
dlgetname is one of a family
of routines that give the user direct access to the dynamic linking
facilities.
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.
The following code sequence shows how to use dlgetname
to retrieve the pathname of a load module. This example uses dlget
to get a load_module_desc of the required
load module and passes that load_module_desc
to dlgetname to retrieve the pathname.
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);
Closes a shared library.
int dlclose(void *handle);
| Parm | Definition |
|---|---|
handle |
Value returned by a previous invocation
of |
If the referenced shared library was successfully closed,
dlclose returns 0. If the shared
library could not be closed, or if handle
does not refer to an open shared library, dlclose
returns a non-0 value. More detailed diagnostic information is available
through dlerror.
dlclose disassociates a shared
library previously opened by dlopen from the current process. Once
a shared library has been closed using dlclose,
dlsym no longer has access to its
symbols. All shared libraries loaded automatically as a result
of invoking dlopen on the referenced
shared library [see dlopen(3C)]
are also closed.
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.
The following example shows how to use dlclose
to unload a shared library:
void* handle; int ret_value;
handle = dlopen("./lib1.so", RTLD_GLOBAL | RTLD_LAZY);if (handle == NULL) {
printf("%s\n", dlerror());
}
ret_value = dlclose(handle);
if (ret_value != 0) {
printf("%s\n", dlerror());
}
This section describes the shl_load
family of shared library management routines.
| NOTE | You can use these routines in both PA-32 and
IPF/PA-64 mode. Support for these routines may be discontinued in a
future Itanium or PA-64 HP-UX release. If you use these routines
in Itanium or PA-64 mode, consider converting your programs to the
|
Explicitly loads a library.
shl_t shl_load( const char * path,
int flags,
long address )
A null-terminated character string containing the path name of the shared library to load.
Specifies when the symbols in the library should
be bound to addresses. It must be one of these values, defined in
<dl.h>:
BIND_IMMEDIATEBind the addresses of all symbols immediately upon loading the library.
BIND_DEFERREDBind the addresses when they are first referenced.
Be aware that BIND_IMMEDIATE
causes the binding of all symbols, and the resolution of all imports,
even from older versioned modules in the shared library. If symbols
are not accessible because they come from old modules, they are
unresolved and shl_load
may fail.
In addition to the above values, the flags parameter can be ORed with the following values:
BIND_NONFATALAllow binding of unresolved symbols.
BIND_VERBOSEMake dynamic loader display verbose messages when binding symbols.
BIND_FIRSTInsert the loaded library before all others in the current link order.
DYNAMIC_PATHCauses the dynamic loader to perform dynamic library
searching when loading the library. The +s
and +b options to the ld
command determine the directories the linker searches. This is the
default mode if +compat linker
option is not specified.
BIND_NOSTARTCauses the dynamic loader to not
call the initializer, even if one is declared for the library, when
the library is loaded or on a future call to shl_load
or dlopen. This also inhibits a
call to the initializer when the library is unloaded.
BIND_RESTRICTEDCauses the search for a symbol definition to be restricted to those symbols that were visible when the library was loaded.
BIND_TOGETHERCauses the library being loaded and all its dependent
libraries to be bound together rather than each independently. Use
this when you have interdependent libraries and you are using BIND_FIRST.
BIND_BREADTH_FIRSTCauses the dependent libraries to be loaded breadth
first. By default, shl_load loads
dependent libraries depth-first.
These flags are discussed in detail inshl_load Example .
Specifies the virtual address at which to attach
the library. Set this parameter to 0
(zero) to tell the system to choose the best location. This argument
is currently ignored; mapping a library at a user-defined address
is not currently supported.
If successful, shl_load
returns a shared library handle of type shl_t.
This address can be used in subsequent calls to shl_close,
shl_findsym, shl_gethandle,
and shl_gethandle_r. Otherwise,
shl_load returns
a shared library handle of NULL
and sets errno
to one of these error codes (from <errno.h>):
ENOEXECThe specified path is not a shared library, or a format error was detected in this or another library.
ENOSYMA symbol needed by this library or another library which this library depends on could not be found.
ENOMEMThere is insufficient room in the address space to load the shared library.
EINVALThe requested shared library address was invalid.
ENOENTThe specified path does not exist.
EACCESSRead or execute permission is denied for the specified path.
A program needs to explicitly load a library only if the library was not linked with the program. This typically occurs only when the library cannot be known at link time - for example, when writing programs that must support future graphics devices.
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. This ensures that constructors of nonlocal static objects
are executed when the library is loaded.
shl_load lets you load a
compatibility or standard mode shared libraries. The BIND_BREADTH_FIRST
flag overrides the default depth-first loading mechanism.
Since the library was not specified at link time, the program must get the library name at run time. Here are some practical ways to do this:
Hard-code the library name into the program (the easiest method).
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.
If successful, shl_load
returns a shared library handle (of type shl_t),
which uniquely identifies the library. This handle can then be passed
to the shl_findsym
or shl_unload
routine.
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 following example shows the source for a function named
load_lib that
explicitly loads a library specified by the user. The user can specify
the library in the environment variable SHLPATH
or as the only argument on the command line. If the user chooses
neither of these methods, the function prompts for the library path
name.
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 .
#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;
}
If you load a shared library with the BIND_IMMEDIATE
flag and the library contains unresolved symbols, the load fails
and sets errno
to ENOSYM. ORing
BIND_NONFATAL
with BIND_IMMEDIATE
causes shl_load
to allow the binding of unresolved symbols to be deferred if their
later use can be detected - for example:
shl_t libH;
. . .
libH = shl_load("libxyz.so", 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.
If BIND_VERBOSE
is ORed with the flags parameter, the
dynamic loader displays messages for all unresolved symbols. This
option is useful to see exactly which symbols cannot be bound. Typically,
you would use this with BIND_IMMEDIATE
to debug unresolved symbols - for example:
shl_t libH;
. . .
libH = shl_load("libxyz.so", BIND_IMMEDIATE | BIND_VERBOSE, 0);
If BIND_FIRST
is ORed with the flags parameter, the
loaded library is inserted before all other loaded shared libraries
in the symbol resolution search order. This has the same effect
as placing the library first in the link order - that is,
the library is searched before other libraries when resolving symbols.
This is used with either BIND_IMMEDIATE
or BIND_DEFERRED
- for example:
shl_t libH;
. . .
libH = shl_load("libpdq.so", 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.
The flag DYNAMIC_PATH
can also be ORed with the flags parameter,
causing the dynamic loader to search for the library using a path
list specified by the +b
option at link time or the SHLIB_PATH
environment variable at run time.
The flag BIND_NOSTART
inhibits execution of initializers for the library.
This flag is most useful with the BIND_DEFERRED
flag; it has no effect with BIND_IMMEDIATE.
It is also useful with the BIND_NONFATAL
flag.
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_TOGETHER
modifies the behavior of BIND_FIRST.
When the library being loaded has dependencies, BIND_FIRST
causes each dependent library to be loaded and bound separately.
If the libraries have interdependencies, the load may fail because
the needed symbols are not available when needed.
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 PA-64 and IPF mode shl_load
loads dependent libraries depth-first. This modifier overrides the
default load order.
Suppose you have the libraries libE.so,
libF.so, and
libG.so. The
libE library
depends on libF
and libF depends
on libG. In addition,
libG depends
on libF-libF
and libG are
interdependent. Your program loads libE.so
with shl_load().
When using BIND_DEFERRED
or BIND_IMMEDIATE
without BIND_FIRST,
these libraries are loaded such that all symbols are visible and
the interdependencies are resolved:
shl_t libE;
libE = shl_load("libE.so", 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.so", 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.so", BIND_IMMEDIATE | BIND_FIRST | BIND_TOGETHER, 0);
shl_load succeeds.
Obtains the address of an exported symbol from a shared library.
To call a routine or access data in an explicitly loaded library,
first get the address of the routine or data with shl_findsym.
int shl_findsym( shl_t * handle,
const char * sym,
short type,
void * value )
A pointer to a shared library
handle of the library to search for the symbol name sym.
This handle could be obtained from the shl_get
routine (described in the The shl_get and shl_get_r Routines). handle
can also point to:
NULLIf a pointer to NULL
is specified, shl_findsym
searches all loaded libraries for sym.
If sym is found, shl_findsym
sets handle to a pointer to the handle
of the shared library containing sym.
This is useful for determining which library a symbol resides in.
For example, the following code sets handle
to a pointer to the handle of the library containing symbol _foo:
shl_t handle; handle = NULL; shl_findsym(&handle,"_foo",...);
PROG_HANDLEThis constant, defined in dl.h,
tells shl_findsym
to search for the symbol in the program itself. This way, any symbols
exported from the program can be accessed explicitly.
A null-terminated character string containing the name of the symbol to search for.
The type of symbol to look for. It must be one of
the following values, defined in <dl.h>:
TYPE_PROCEDURELook for a function or procedure.
TYPE_DATALook for a symbol in the data segment (for example, variables).
TYPE_UNDEFINEDLook for any symbol.
TYPE_STORAGE same as TYPE_DATA
A pointer in which shl_findsym
stores the address of sym, if found.
If successful, shl_findsym
returns an integer (int)
value zero. If shl_findsym
cannot find sym, it returns -1
and sets errno
to zero. If any other errors occur, shl_findsym
returns -1 and sets errno
to one of these values (defined in <errno.h>):
ENOEXECA format error was detected in the specified library.
ENOSYMA symbol on which sym depends could not be found.
EINVALThe specified handle is invalid.
To call a routine or access data in an explicitly loaded library,
first get the address of the routine or data with shl_findsym.
To call a routine in an explicitly loaded library
declare a pointer to a function of the same type as the function in the shared 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
To access data in an explicitly loaded library
declare a pointer to a data structure of the same type as the data structure to access in the library
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
access the data through the pointer obtained in Step 2
Suppose you have a set of libraries that output to various graphics devices. Each graphics device has its own library. Although the actual code in each library varies, the routines in these shared libraries have the same name and parameters, and the global data is the same. For instance, they all have these routines and data:
gopen()opens the graphics device for output
gclose()closes the graphics device
move2d(x,y)moves to pixel location x,y
draw2d(x,y)draws to pixel location x,y from current x,y
maxXcontains the maximum X pixel location on the output device
maxYcontains the maximum Y pixel location on the output device
The following example shows a C program that can load any
supported graphics library at run time, and call the routines and
access data in the library. The program calls load_lib
(see load_lib - Function
to Load a Shared Library ) to load
the library.
#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.so:
$ cc -Aa -o drawline shl_findsym.c load_lib.c -ldld $ SHLPATH=/usr/lib/libgrphdd.so $ export SHLPATH $ drawline
Obtains information on the currently loaded libraries.
int shl_get( int index,
struct shl_descriptor **desc )
Specifies an ordinal number of the shared library
in the process. For libraries loaded implicitly
(at startup time), index is the ordinal
number of the library as it appeared on the command line. For example,
if libc was the
first library specified on the ld
command line, then libc
has an index of 1. For explicitly loaded
libraries, index corresponds to the order
in which the libraries were loaded, starting after the ordinal number
of the last implicitly loaded library. Two index
values have special meaning:
Refers to the main program itself
Refers to the dynamic loader (dld.so).
A shared library's index can be modified during program execution by either of the following events:
The program loads a shared library
with the BIND_FIRST
modifier to shl_load.
This increments all the shared library indexes by one.
The program unloads a shared library with shl_unload.
Any libraries following the unloaded library have their index decremented
by one.
Returns a pointer to a statically allocated buffer
(struct shl_descriptor **)
containing a shared library descriptor. The structure contains these
important fields:
tstartThe start address (unsigned long)
of the shared library text segment.
tendThe end address (unsigned long)
of the shared library text segment.
dstartThe start address (unsigned long)
of the shared library data segment.
dendThe end address (unsigned long)
of the shared library bss segment. The data and bss segments together
form a contiguous memory block starting at dstart
and ending at dend.
handleThe shared library's handle (type shl_t).
filenameA character array containing the library's path name as specified at link time or at explicit load time.
initializerA pointer to the shared library's initializer routine
(see Initializers for Shared Libraries. It is NULL
if there is no initializer. This field is useful for calling the
initializer if it was disabled by the BIND_NOSTART
flag to shl_load.
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.
This buffer is statically allocated. Therefore, if a program
intends to use any of the members of the structure, the program
should make a copy of the structure before the next call to shl_get.
Otherwise, shl_get
will overwrite the static buffer when called again.
If successful, shl_get
returns an integer value 0. If the index
value exceeds the number of currently loaded libraries, shl_get
returns -1 and sets errno
to EINVAL.
To obtain information on currently loaded libraries, use the
shl_get function.
If you are programming in a threaded environment, use the thread-safe
version shl_get_r
which is the same as shl_get
in all other respects. (See Programming with Threads
on HP-UX for more information about threads.)
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.
The function show_loaded_libs
shown below displays the name and start and end address of the text
and data/bss segments the library occupies in a process's virtual
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.so 0x800ac800 0x800ad000 0x6df62800 0x6df63000 /usr/lib/libc.so 0x80003800 0x80091000 0x6df63000 0x6df85000
Returns descriptor information about a loaded shared library.
int shl_gethandle( shl_t handle,
struct shl_descriptor **desc )
The handle of the shared library you want information
about. This handle is the same as that
returned by shl_load.
Points to shared library descriptor information
- the same information returned by the shl_get
routine. The buffer used to store this desc
information is static, meaning that subsequent calls to shl_gethandle
will overwrite the same area with new data. Therefore, if you need
to save the desc information, copy it
elsewhere before calling shl_gethandle
again.
If handle is not valid, the routine
returns -1 and sets errno
to EINVAL. Otherwise,
shl_gethandle
returns 0.
The shl_gethandle
routine returns descriptor information about a loaded shared library.
If you are programming in a threaded environment, use the thread-safe
version shl_gethandle_r
which is the same as shl_gethandle
in all other respects. (See Programming with Threads
on HP-UX for more information about threads.)
The following function named show_lib_info
displays information about a shared library, given the library's
handle.
#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;
}
Adds new symbols to the global shared library symbol table.
int shl_definesym( const char *sym,
short type,
long value,
int flags )
A null-terminated string containing the name of the symbol to change or to add to the process's shared library symbol table.
The type of symbol - either TYPE_PROCEDURE
or TYPE_DATA.
If value falls in the address range of a currently loaded library, an association will be made and the symbol is undefined when the library is unloaded. (Note that memory dynamically allocated with malloc(3C) does not fall in the range of any library.) The defined symbol may be overridden by a subsequent call to this routine or by loading a more visible library that provides a definition for the symbol.
Must be set to zero.
If successful, shl_definesym
returns 0. Otherwise, it returns -1 and sets errno
accordingly. See shl_definesym(3X) for details.
The shl_definesym
function allows you to add a new symbol to the global shared library
symbol table. Use of this routine will be unnecessary for most programmers.
There are two main reasons to add or change shared library symbol table entries:
to generate symbol definitions as the program runs - for example, aliasing one symbol with another
to override a current definition
Symbol definitions in the incomplete executable may also be redefined with certain restrictions:
The incomplete executable always uses its own definition for any data (storage) symbol, even if a more visible one is provided.
The incomplete executable only uses a more visible code symbol if the main program itself does not provide a definition.
The shl_getsymbols
function retrieves symbols that are imported (referenced) or exported
(defined) by a shared library. This information is returned in an
allocated array of records, one for each symbol. Most programmers
do not need to use this routine.
int shl_getsymbols( shl_t handle,
short type,
int flags,
void * (*memfunc)(),
struct shl_symbol **symbols )
The handle of the shared library whose symbols you
want to retrieve. If handle is NULL,
shl_getsymbols
returns symbols that were defined with the shl_definesym
routine.
Defines the type of symbol to retrieve. It must
be one of the following values, which are defined as constants in
<dl.h>:
TYPE_PROCEDURERetrieve only function or procedure symbols.
TYPE_DATARetrieve only symbols from the data segment (for example, variables).
TYPE_UNDEFINEDRetrieve all symbols, regardless of type.
TYPE_STORAGE same as TYPE_DATA
Defines whether to retrieve import or export symbols
from the library. An import symbol is an
external reference made from a library. An export symbol
is a symbol definition that is referenced outside the library. In
addition, any symbol defined by shl_definesym
is an export symbol. Set this argument to one of the following values
(defined in <dl.h>):
IMPORT_SYMBOLSTo return import symbols.
EXPORT_SYMBOLSTo return export symbols.
INITIALIZERSTo return initializer symbols.
One of the following modifiers can be ORed with both the EXPORT_SYMBOLS
and the INITIALIZERS
flags:
NO_VALUESDo not calculate the value
field of the shl_symbol
structure for symbols. The value
field has an undefined value.
GLOBAL_VALUESFor symbols that are defined in multiple libraries,
this flag causes shl_getsymbols
to return the most-visible occurrence, and to set the value
and handle fields
of the shl_symbol
structure (defined in the description of the symbols
parameter).
Points to a function that has the same interface
(calling conventions and return value) as malloc(3C).
The shl_getsymbols
function uses this function to allocate memory to store the array
of symbol records, symbols.
This points to an array of symbol records for all
symbols that match the criteria determined by the type
and value parameters. The type of these
records is struct shl_symbol,
defined in <dl.h>
as:
struct shl_symbol {
char * name;
short type;
void * value;
shl_t handle;
};
The members of this structure are described in The shl_symbol Structure.
If successful, shl_getsymbols
returns the number of symbols found; otherwise, -1 is returned
and shl_getsymbols
sets errno to
one of these values:
ENOEXECA format error was detected in the specified library.
ENOSYMSome symbol required by the shared library could not be found.
EINVALThe specified handle is invalid.
ENOMEMmemfunc failed to allocate the requested memory.
The members of the shl_symbol
structure are defined as follows:
nameContains the name of a symbol.
typeContains the symbol's type: TYPE_PROCEDURE
or TYPE_DATA
is a data symbol used for C uninitialized global variables or Fortran
common blocks.
valueContains the symbol's address. It is valid only
if EXPORT_SYMBOLS
is specified without the NO_VALUES
modifier.
handleContains the handle of the shared library in which
the symbol is found, or NULL
in the case of symbols defined by shl_definesym.
It is valid only if EXPORT_SYMBOLS
or INITIALIZERS
were requested without the NO_VALUES
modifier. It is especially useful when used with the GLOBAL_VALUES
modifier, allowing you to determine the library in which the most-visible
definition of a symbol occurs.
show_symbols - Display
Shared Library Symbols shows
the source for a function named show_symbols
that displays shared library symbols. The syntax of this routine
is defined as:
int show_symbols(shl_t hndl,
short type,
int flags)
The handle of the shared library whose symbols you want to display.
The type of symbol you want to display. This is
the same as the type parameter to shl_getsymbols
and can have these values: TYPE_PROCEDURE,
TYPE_DATA, or
TYPE_UNDEFINED.
If it is TYPE_UNDEFINED,
show_symbols
displays the type of each symbol.
This is the same as the flags
parameter. It can have the value EXPORT_SYMBOLS
or IMPORT_SYMBOLS.
In addition, it can be ORed with NO_VALUES
or GLOBAL_VALUES.
If EXPORT_SYMBOLS
is specified without being ORed with NO_VALUES,
show_symbols
displays the address of each symbol.
#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 Itanium/PA-64 mode. For example, |
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
Unloads or frees up space for a shared library.
int shl_unload(shl_t handle)
The handle of the shared library you wish to unload.
The handle value is obtained from a previous
call to shl_load,
shl_findsym,
or shl_get.
If successful, shl_unload
returns 0. Otherwise, shl_unload
returns -1 and sets errno
to an appropriate value:
To unload a shared library, use the shl_unload
function. One reason to do this is to free up the private copy of
shared library data and swap space allocated when the library was
loaded with shl_load.
(This is done automatically when a process exits.)
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++ 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.
When a library is unloaded, existing linkages to symbols in an unloaded library are not invalidated. Therefore, the programmer must ensure that the program does not reference symbols in an unloaded library as undefined behavior will result. In general, this routine is recommended only for experienced programmers.
Starting with the HP-UX 10.20 release, the dynamic loader
generates compatibility warnings. These warnings include linker
toolset features that may change over time. To display run-time
compatibility warnings, set the _HP_DLDOPTS
environment variable as follows:
export _HP_DLDOPTS=-warnings Turn on compatibility warnings
The following sections provide information about the dynamic loader compatibility warnings.
The following shared library management shl_load(3X) routines may become unsupported in a future HP-UX release:
shl_definesym()
shl_get()
shl_get_r()
shl_gethandle()
shl_gethandle_r()
shl_getsymbols()
When these routines become unsupported, the SVR4 dlopen (3C) family of routines will be the only dynamic loading routines supported.
The following shared library management shl_load(3X) flags may become unsupported in a future HP-UX release:
BIND_FIRST
BIND_NOSTART
BIND_RESTRICTED
BIND_TOGETHER
BIND_NONFATAL
BIND_VERBOSE
DYNAMIC_PATH
The following shl_findsym()
flags may become unsupported in a future release:
TYPE_PROCEDURE
TYPE_DATA
TYPE_STORAGE
| NOTE | The HP-UX 11i version 1.5 Itanium/PA-64 mode linker
does not support the |
The linker provides several ways you can improve your application performance.
Linker Optimizations describes how the linker -O
option removes unnecessary ADDIL
instructions and "dead" or unused procedures.
Options to Improve TLB Hit Rates describes performance improvements in Translation Lookaside Buffer (TLB) hit rates.
Profile-Based Optimization describes how the linker can position your code in the object file or shared library to improve performance.
Incremental Linking describes how to improve compile performance using incremental linking.
Improving Performance with the Global Symbol Table describes how to improve performance by using the global symbol table to speed up symbol searching.
Improving Performance with Function Symbol Aliasing describes how to improve performance by using function symbol aliasing.
Improving Shared Library Start-Up Time with fastbind describes how to improve shared library performance by saving startup information and bypassing the lookup process when running an application.
The linker supports the -O
option which performs the following optimizations at link time:
optimizes references to data by removing
unnecessary ADDIL
instructions from the object code.
removes procedures that can never be reached.
These optimizations can be separately enabled or disabled
with the +O[no]fastaccess
and +O[no]procelim
options respectively. The -O
linker option simply combines enabling of these into one option.
For example, the following ld
command enables linker optimizations and results in a smaller, faster
executable:
$ ld -O -o prog /usr/ccs/lib/crt0.o prog.o -lm -lc
To enable one or the other optimization only, use the appropriate
+O option:
$ ld +Ofastaccess -o prog /usr/ccs/lib/crt0.o prog.o -lm -lc $ ld +Oprocelim -o prog /usr/ccs/lib/crt0.o prog.o -lm -lc
The compilers automatically call the linker with the +Oprocelim
options if compiler at all optimization levels.
For example, the following cc
command invokes full compiler optimization as well as linker optimization:
$ cc -o prog +O4 prog.c O4 invokes +Ofastaccess and +Oprocelim
If invoked with +O4,
the compilers generate object code in such a way that code optimization
is done at link time. Thus, the linker does a better job of optimizing
code that was compiled with +O4.
When the compile and link phases are invoked by separate commands,
specify +O4 on
both command lines. For example:
$ cc -c +O4 prog.c invokes compiler optimizations $ cc -o prog +O4 prog.o invokes linker optimizations
| NOTE | With the HP-UX 10.0 release, you can also invoke linker
optimizations at levels 2 and 3 by using the |
For a brief description of compiler optimization options see Selecting an Optimization Level with PBO . For a complete description, see your compiler manuals or online help.
The -O,
+Ofastaccess,
and +Oprocelim
options are incompatible with these linker options:
-bThese options have no effect on position-independent
code, so they are not useful when building shared libraries with
ld -b.
-ADynamic linking is incompatible with link-time optimization.
-rRelocatable linking is incompatible with link-time optimization.
-DSetting the offset of the data space is incompatible with link-time optimization.
The linker issues a warning when such conflicts occur. If you require any of these features, do not use the linker optimization options.
Unused or "dead" procedure elimination is the process of removing unreferenced procedures from the $TEXT$ space of an executable or shared library to reduce the size of the program or library.
Dead procedure elimination is performed after all symbols
have been resolved prior to any relocation. It works on a per subspace
basis. That is, only entire subspaces are removed and only if all
procedures in the subspace are unreferenced. Typically, if a relocatable
link (ld -r)
has not been performed and the code is not written in assembly,
every procedure is in its own subspace. Relocatable links may merge
subspaces. Merged subspaces can prevent the removal of dead procedures.
Therefore, it is optimal to have each procedure in its own subspace.
For complete executables, dead procedure elimination removes any text subspaces that are not referenced from another subspace. Self references, such as recursive procedures or subspaces with multiple procedures that call each other, are not considered outside references and are therefore candidates for removal.
If the address of a procedure is taken, the subspace within which it resides is not removed. If a subspace is referenced in any way by a fixup representing a reference other than a PC-relative call or an absolute call it is not removed.
For incomplete executables, dead procedure elimination works the same as for complete executables except that no exported symbols or their dependencies are removed. If an incomplete executable contains a symbol that is to be referenced by a shared library and is not exported, it is removed if the other conditions discussed above hold.
In shared libraries only symbols that are not referenced and not exported are removed. In shared libraries all symbols that are not of local scope are exported. Therefore only locally scoped symbols not referenced are removed.
When performing a relocatable link with the -r
option, dead procedure elimination is disabled since the only possible
gain would be the removal of unreferenced local procedures. Objects
resulting from a relocatable link are subject to dead procedure
elimination upon a final link.
Any procedure that has symbolic debug information associated with it is not removed. Procedures that do not have symbolic debug information associated with them but are included in a debug link are removed if they are not referenced.
To improve Translation Lookaside Buffer (TLB) hit rates in
an application running on an Itanium-based or a PA 8000-based system, use the following
linker or chatr
virtual memory page setting options:
+pd
size - requests a specified
data page size of 4K bytes, 16K, 64K,
256K, 1M, 4M, 16M, 64M, 256M, or L. Use L
to specify the largest page size available. The actual page size
may vary if the requested size can not be fulfilled.
+pi
size - requests a specified
instruction page size. (See +pd
size for size
values.)
The default data and instruction page size is 4K bytes on Itanium and PA-RISC systems.
The Itanium architecture supports multiple page sizes from 4K to 4G (4K, 8K, 16K, 64K, 256K, 1M, 4M, 16M, 64M, 256M and 4G). The PA-RISC 2.0 architecture supports multiple page sizes, from 4K bytes to 64M bytes, in multiples of four. This enables large contiguous regions to be mapped into a single TLB entry. For example, if a contiguous 4MB of memory is actively used, 1000 TLB entries are created if the page size is 4K bytes, but only 64 TLB entries are created if the page size is 64K bytes.
Applications and benchmarks have larger and larger working-set
sizes. Therefore, the linker and chatr
TLB page setting options can help boost performance by improving
TLB hit rates.
Some scientific applications benefit from large data pages. Alternatively, some commercial applications benefit from large instruction page sizes.
Examples:To set the virtual memory page size by using the linker:
ld +pd 64K +pi 16K /opt/langtools/lib/crt0.o myprog.o -lc
To set the page size from HP C and HP Fortran:
cc -Wl,+pd,64K,+pi,16K myprog.c f90 -Wl,+pd,64K,+pi,16K myprog.f
To set the page size by using chatr:
chatr +pd 64K +pi 16K a.out
In profile-based optimization (PBO),
the compiler and linker work together to optimize an application
based on profile data obtained from running the application on a
typical input data set. For instance, if certain procedures call
each other frequently, the linker can place them close together
in the a.out
file, resulting in fewer instruction cache misses, TLB misses, and
memory page faults when the program runs. Similar optimizations
can be done at the basic block levels of
a procedure. Profile data is also used by the compiler for other
general tasks, such as code scheduling and register allocation.
| NOTE | The compiler interface to PBO is currently supported only by the C, C++, and FORTRAN compilers. |
PBO should be the last level of optimization you use when building an application. As with other optimizations, it should be performed after an application has been completely debugged.
Most applications will benefit from PBO. The two types of applications that will benefit the most from PBO are:
Applications that exhibit poor instruction memory locality. These are usually large applications in which the most common paths of execution are spread across multiple compilation units. The loops in these applications typically contain large numbers of statements, procedure calls, or both.
Applications that are branch-intensive. The operations performed in such applications are highly dependent on the input data. User interface managers, database managers, editors, and compilers are examples of such applications.
Of course, the best way to determine whether PBO will improve an application's performance is to try it.
| NOTE | Under some conditions, PBO is incompatible with programs
that explicitly load shared libraries. Specifically, PBO will not
function properly if the |
Profile-based optimization involves these steps:
Instrument the application - prepare the application so that it will generate profile data.
Profile the application - create profile data that can be used to optimize the application.
Optimize the application - generate optimized code based on the profile data.
Suppose you want to apply PBO to an application called sample.
The application is built from a C source file sample.c.
Discussed below are the steps involved in optimizing the application.
Step 1 Instrumentation
First, compile the application for instrumentation and level 2 optimization:
$ cc -v -c +I -O sample.c /opt/langtools/lbin/cpp sample.c /var/tmp/ctm123 /opt/ansic/lbin/ccom /var/tmp/ctm123 sample.o -O2 -I $ cc -v -o sample.inst +I -O sample.o /usr/ccs/bin/ld /opt/langtools/lib/icrt0.o -u main \ -o sample.inst sample.o -I -lc
At this point, you have an instrumented program called sample.inst.
Step 2 Profile
Assume you have two representative input files to use for
profiling, input.file1
and input.file2.
Now execute the following three commands:
$ sample.inst < input.file1 $ sample.inst < input.file2 $ mv flow.data sample.data
The first invocation of sample.inst
creates the flow.data
file and places an entry for that executable file in the data file.
The second invocation increments the counters for sample.inst
in the flow.data
file. The third command moves the flow.data
file to a file named sample.data.
Step 3 Optimize
To perform profile based optimizations on this application, relink the program as follows:
$ cc -v -o sample.opt +P +pgm sample.inst \ +df sample.data sample.o /usr/ccs/bin/ld /usr/ccs/lib/crt0.o -u main -o sample.opt \ +pgm sample.inst +df sample.data sample.o -P -lc
Note that it was not necessary to recompile the source file.
The +pgm option
was used because the executable name used during instrumentation,
sample.inst,
does not match the current output file name, sample.opt.
The +df option
is necessary because the profile database file for the program has
been moved from flow.data
to sample.data.
Although you can use the linker alone to perform PBO, the
best optimizations result if you use the compiler as well; this
section describes this approach. To instrument an application, compile
the source with the +I compiler
option.
After creating an instrumented object file for each source file, the compiler invokes the linker as follows:
In PA-32 mode, instead of using the startup file /usr/ccs/lib/crt0.o,
the compiler specifies a special startup file named /opt/langtools/lib/icrt0.o.
When building a shared library, the compiler uses /usr/ccs/lib/scrt0.o.
In PA-64 mode, the linker automatically adds /usr/css/lib/pa20_64/fdp_init.o
or /usr/css/lib/pa20_64/fdp_init_sl.o
to the link when detects that -I crt0.o
is not changed.
In Itanium mode, the linker does not use crt0.o, but it does
add:-u__sin_core__
-u__sin_init
-h__sin_core__
-h__sin_lookup_ibt
-l sin
-u
The compiler passes the -I
option to the linker, causing it to place instrumentation code in
the resulting executable.
You can see how the compiler invokes the linker by specifying
the -v option.
For example, to instrument the file sample.c,
to name the executable sample.inst,
to perform level 2 optimizations (the compiler option -O
is equivalent to +O2),
and to see verbose output (-v):
/opt/ansic/bin/cc -v -o sample.inst +I sample.c cc: informational note 404: NLSPATH is /opt/ansic/lib/nls/msg/%L/%N.cat:/opt/ansic/lib/nls/msg/C/%N.cat: cc: informational note 404: INCLUDIR is INCLUDIR=3D/usr/include cc: informational note 404: CCOPTS is not set. /opt/langtools/lbin/cpp.ansi sample.c /var/tmp/ctmAAAa14034 -$ -D__hpux -D__unix -D_FLT_EVAL_METHOD=3D0 -D__ia64 \ -D_INLINE_ASM -D_ILP32 -e -D_BIND_LIBCALLS \ -D_Math_errhandling=3DMATH_ERREXCEPT -D_HPUX_SOURCE \ -D__STDC_EXT__ cc: informational note 411: Entering Preprocessor. /opt/ansic/lbin/ccom /var/tmp/ctmAAAa14034 sample.o -n sample.c \ -g -dynamic -DSblended -DD32 -ESdefaultstack -Oq01,al,ag,cn,sz,Ic,vo,mf,Po,es,rs,sp,in,cl,om,vc,\ pi,fa,Pe,Rr,pa,nf,cp,lx,Pg,ug,lu,lb,uj,dn,ap,rp,Fs,bp,wp,pc,mp,\ lr,cx,cr,pi,so,Rc ,fa,fe,pt,st,lc,Bl! -Ol18collect !\ -driver_command=3D /opt/ansic/bin/cc -v +I -Ae cc: informational note 404: LPATH is /usr/lib/hpux32:/opt/langtools/lib/hpux32: /usr/ccs/bin/ld -u__sin_core__ -u__sin_init -h__sin_core__ \ -h__sin_lookup_ibt -l sin -u main -o sample.inst sample.o \ -dynamic -I +noobjdebug -lc cc: informational note 413: Entering Link editor.
The compiler adds -g and
-Ol18collect ! -driver_command=3D /opt/ansic/bin/cc -v +I.
Notice in the linker command line (starting with /usr/ccs/bin/ld),
the application is linked the -I
optionand +noobjdebug options.
The +oprocelim is also removed.
To save the profile data to a file other than flow.data
in the current working directory, use the FLOW_DATA
environment variable as described in Specifying a Different flow.data
with FLOW_DATA .
-I Linker Option When invoked with the -I
option, the linker instruments all the specified object files.
For instance, suppose you have a regular object file named
foo.o created
by compiling without the +I
option, and you compile a source file bar.c
with the +I option
and specify foo.o
on the compile line:
/opt/ansic/bin/cc -c foo.c /opt/ansic/bin/cc -v -o foobar +I bar.c foo.c cc: informational note 404: NLSPATH is /opt/ansic/lib/nls/msg/%L/%N.cat:/opt/ansic/lib/nls/msg/C/%N.cat: cc: informational note 404: INCLUDIR is INCLUDIR=3D/usr/include cc: informational note 404: CCOPTS is not set. /opt/langtools/lbin/cpp.ansi bar.c /var/tmp/ctmAAAa14102 \ -$ -D__hpux -D__unix -D_FLT_EVAL_METHOD=3D0 -D__ia64 \ -D_INLINE_ASM -D_ILP32 -e -D_BIND_LIBCALLS \ -D_Math_errhandling=3DMATH_ERREXCEPT -D_HPUX_SOURCE -D__STDC_EXT__ cc: informational note 411: Entering Preprocessor. /opt/ansic/lbin/ccom /var/tmp/ctmAAAa14102 bar.o -n bar.c\ -g -dynamic -DSblended -DD32 -ESdefaultstack \ -Oq01,al,ag,cn,sz,Ic,vo,mf,Po,es,rs,sp,in,cl,om,vc,pi,fa,\ Pe,Rr,pa,nf,cp,lx,Pg,ug,lu,lb,uj,dn,ap,rp,Fs,bp,wp,pc,\mp,lr,cx,cr,pi,so,Rc,fa,fe,pt,st,lc,Bl! \ -Ol18collect! -driver_command=3D /opt/ansic/bin/cc \ -v +I foo.o -Ae cc: informational note 404: LPATH is /usr/lib/hpux32:/opt/langtools/lib/hpux32: /usr/ccs/bin/ld -u__sin_core__ -u__sin_init \ -h__sin_core__ -h__sin_lookup_ibt -l sin -u \ main -o foobar bar.o foo.o -dynamic -I +noobjdebug -lc cc: informational note 413: Entering Link editor.
In this case, the linker instruments both bar.o
and foo.o. However,
since foo.o is
not an object file, only its procedure calls
are instrumented; basic blocks within procedures are not instrumented.
To instrument foo.c
to the same extent, you must compile it with the +I
option - for example:
cc: informational note 404: NLSPATH is /opt/ansic/lib/nls/msg/%L/%N.cat:/opt/ansic/lib/nls/msg/C/%N.cat: cc: informational note 404: INCLUDIR is INCLUDIR=3D/usr/include cc: informational note 404: CCOPTS is not set. /opt/langtools/lbin/cpp.ansi foo.c /var/tmp/ctmAAAa14108 \ -$ -D__hpux -D__unix -D_FLT_EVAL_METHOD=3D0 -D__ia64 \ -D_INLINE_ASM -D_ILP32 -e -D_BIND_LIBCALLS \ -D_Math_errhandling=3DMATH_ERREXCEPT -D_HPUX_SOURCE -D__STDC_EXT__ cc: informational note 411: Entering Preprocessor. /opt/ansic/lbin/ccom /var/tmp/ctmAAAa14108 foo.o -n foo.c \ -g -dynamic -DSblended -DD32 -ESdefaultstack \ -Oq01,al,ag,cn,sz,Ic,vo,mf,Po,es,rs,sp,in,cl,om,vc,\ pi,fa,Pe,Rr,pa,nf,cp,lx,Pg,ug,lu,lb,uj,dn,ap,rp,Fs,\ bp,wp,pc,mp,lr,cx,cr,pi,so,Rc,fa,fe ,pt,st,lc,Bl! \ -Ol18collect! -driver_command=3D /opt/ansic/bin/cc \ -v +I -Ae
/opt/ansic/bin/cc -v -o foobar +I bar.c foo.o cc: informational note 404: NLSPATH is /opt/ansic/lib/nls/msg/%L/%N.cat:/opt/ansic/lib/nls/msg/C/%N.cat: cc: informational note 404: INCLUDIR is INCLUDIR=3D/usr/include cc: informational note 404: CCOPTS is not set. /opt/langtools/lbin/cpp.ansi bar.c /var/tmp/ctmAAAa14111 \ -$ -D__hpux = -D__unix -D_FLT_EVAL_METHOD=3D0 -D__ia64\ -D_INLINE_ASM -D_ILP32 -e -D_BIND_LIBCALLS \ -D_Math_errhandling=3DMATH_ERREXCEPT -D_HPUX_SOURCE -D__STDC_EXT__ cc: informational note 411: Entering Preprocessor. /opt/ansic/lbin/ccom /var/tmp/ctmAAAa14111 bar.o -n bar.c \ -g -dynamic -DSblended -DD32 -ESdefaultstack \ -Oq01,al,ag,cn,sz,Ic,vo,mf,Po,es,rs,sp,in,cl,om,vc,\ pi,fa,Pe,Rr,pa,nf,cp,lx,Pg,ug,lu,lb,uj,dn,ap,rp,\Fs,bp,wp,pc,mp,lr,cx,cr,pi,so,Rc,fa,fe ,pt,st,lc,Bl! \ -Ol18collect! -driver_command=3D /opt/ansic/bin/cc\ -v +I foo.o -Ae cc: informational note 404: LPATH is /usr/lib/hpux32:/opt/langtools/lib/hpux32: /usr/ccs/bin/ld -u__sin_core__ -u__sin_init -h__sin_core__ \ -h__sin_lookup_ibt -l sin -u main -o foobar bar.o foo.o \ -dynamic -I +noobjdebug -lc cc: informational note 413: Entering Link editor.
A simpler approach would be to compile foo.c
and bar.c with
a single cc command:
/opt/ansic/bin/cc -v +I -o foobar bar.c foo.c cc: informational note 404: NLSPATH is /opt/ansic/lib/nls/msg/%L/%N.cat:/opt/ansic/lib/nls/msg/C/%N.cat: cc: informational note 404: INCLUDIR is INCLUDIR=3D/usr/include cc: informational note 404: CCOPTS is not set. bar.c: /opt/langtools/lbin/cpp.ansi bar.c /var/tmp/ctmAAAa14128 \ -$ -D__hpux -D__unix -D_FLT_EVAL_METHOD=3D0 -D__ia64 \ -D_INLINE_ASM -D_ILP32 -e -D_BIND_LIBCALLS \ -D_Math_errhandling=3DMATH_ERREXCEPT \ -D_HPUX_SOURCE -D__STDC_EXT__ cc: informational note 411: Entering Preprocessor. /opt/ansic/lbin/ccom /var/tmp/ctmAAAa14128 \ bar.o -n bar.c -g -dynamic -DSblended \ -DD32 -ESdefaultstack \ -Oq01,al,ag,cn,sz,Ic,vo,mf,Po,es,rs,sp,in,cl,om,vc,\ pi,fa,Pe,Rr,pa,nf,cp,lx,Pg,ug,lu,lb,uj,dn,ap,rp,Fs,\ bp,wp,pc,mp,lr,cx,cr,pi,so,Rc,fa,fe,pt,st,lc,Bl! \ -Ol18collect! -driver_command=3D /opt/ansic/bin/cc \ -v +I -Ae foo.c: /opt/langtools/lbin/cpp.ansi foo.c /var/tmp/ctmAAAa14128 -$ -D__hpux -D__unix -D_FLT_EVAL_METHOD=3D0 -D__ia64 \ -D_INLINE_ASM -D_ILP32 -e -D_BIND_LIBCALLS \ -D_Math_errhandling=3DMATH_ERREXCEPT \ -D_HPUX_SOURCE -D__STDC_EXT__ cc: informational note 411: Entering Preprocessor. /opt/ansic/lbin/ccom /var/tmp/ctmAAAa14128 \ foo.o -n foo.c -g -dynamic -DSblended -DD32 -ESdefaultstack \ -Oq01,al,ag,cn,sz,Ic,vo,mf,Po,es,rs,sp,in,cl,om,vc,\ pi,fa,Pe,Rr,pa,nf,cp,lx,Pg,ug,lu,lb,uj,dn,ap,rp,Fs,bp,\ wp,pc,mp,lr,cx,cr,pi,so,Rc,fa,fe,pt,st,lc,Bl! \ -Ol18collect! -driver_command=3D /opt/ansic/bin/cc \ -v +I -Ae cc: informational note 404: LPATH is /usr/lib/hpux32:/opt/langtools/lib/hpux32: /usr/ccs/bin/ld -u__sin_core__ -u__sin_init -h__sin_core__ -h__sin_lookup_ibt -l sin -u main \ -o foobar bar.o foo.o -dynamic -I +noobjdebug -lc cc: informational note 413: Entering Link editor.
After instrumenting a program, you can run it one or more times to generate profile data, which is ultimately used to perform the optimizations in the final step of PBO.
For best results from PBO, use representative input data when running an instrumented program. Input data that represents rare cases or error conditions is usually not effective for profiling. Run the instrumented program with input data that closely resembles the data in a typical user's environment. Then, the optimizer will focus its efforts on the parts of the program that are critical to performance in the user's environment. You should not have to do a large number of profiling runs before the optimization phase. Usually it is adequate to select a small number of representative input data sets.
The final step in PBO is optimizing a program using profile
data created in the profiling phase. To do this, rebuild the program
with the +P compiler
option.
Note that it is not really necessary to recompile the source
files; you could, instead, specify the object .o
files that were created during the instrumentation phase. For instance,
suppose you have already created an object file named foo.o
from foo.c using
the +I compiler
option; then the following commands are equivalent in effect:
cc +P foo.c cc +P foo.o
Both commands invoke the linker, but the second command doesn't compile before invoking the linker.
After creating an instrumented object file for each source
file, the compiler driver invokes the linker with the -P
option, causing the linker to optimize all the .o
files. As with the +I
option, the driver uses /opt/langtools/lbin/u2comp
to generate code and perform various optimizations.
To see how the compiler invokes the linker, specify the -v
option when compiling. For instance, suppose you have instrumented
prog.c and gathered
profile data into flow.data.
The following example shows how the compiler driver invokes the
linker when +P
is specified:
/opt/ansic/bin/cc -o prog -v +P prog.o cc: informational note 404: NLSPATH is /opt/ansic/lib/nls/msg/%L/%N.cat:/opt/ansic/lib/nls/msg/C/%N.cat: cc: informational note 404: INCLUDIR is INCLUDIR=3D/usr/include cc: informational note 404: LPATH is /usr/lib/hpux32:/opt/langtools/lib/hpux32: /usr/ccs/bin/ld -u__sin_core__ -u__sin_init \ -h__sin_core__ = -h__sin_lookup_ibt -l sin -u main \ -o prog prog.o -dynamic -P +df flow.data +Oprocelim -lc cc: informational note 413: Entering Link editor.
By default, the code generator and linker look for the flow.data
file in the current working directory. In other words, the flow.data
file created during the profiling phase should be located in the
directory where you relink the program.
What if you want to use a flow.data
file from a different directory than where you are linking? Or what
if you have renamed the flow.data
file - for example, if you have multiple flow.data
files created for different input sets? The +df
option allows you to override the default +P
behavior of using the file flow.data
in the current directory. The compiler passes this option directly
to the linker.
For example, suppose after collecting profile data, you decide
to rename flow.data
to prog.prf.
You could then use the +df
option as follows:
/opt/ansic/bin/cc -v -o prog +P +df prog.prf prog.o cc: informational note 404: NLSPATH is /opt/ansic/lib/nls/msg/%L/%N.cat:/opt/ansic/lib/nls/msg/C/%N.cat: cc: informational note 404: INCLUDIR is INCLUDIR=3D/usr/include cc: informational note 404: LPATH is /usr/lib/hpux32:/opt/langtools/lib/hpux32: /usr/ccs/bin/ld -u__sin_core__ -u__sin_init \ -h__sin_core__ -h__sin_lookup_ibt -l sin -u main \ -o prog prog.o -dynamic -P +df prog.prf +Oprocelim -lc cc: informational note 413: Entering Link editor.
The +df
option overrides the effects of the FLOW_DATA
environment variable.
The FLOW_DATA
environment variable provides another way to override the default
flow.data file
name and location. If set, this variable defines an alternate file
name for the profile data file.
For example, to use the file /home/adam/projX/prog.data
instead of flow.data,
set FLOW_DATA:
$ FLOW_DATA=/home/adam/projX/prog.data $ export FLOW_DATA Bourne and Korn shell $ setenv FLOW_DATA /home/adam/projX/prog.data C shell
If an application is linked with +df
and -P, the FLOW_DATA
environment variable is ignored. In other words, +df
overrides the effects of FLOW_DATA.
When -P
is specified, the code generator and linker perform profile-based
optimizations on any instrumented or regular object files found
on the linker command line. PBO works on the +O1
level when using object files created at higher optimizations. Higher
level optimization is ignored.
+O0Minimal optimization. This is the default.
+O1Basic block level optimization.
+O2Full optimization within each procedure in a file.
(Can also be invoked as -O.)
+O3Full optimization across all procedures in an object file. Includes subprogram inlining.
+O4Full optimization across entire application, performed
at link time. (Invokes ld +Ofastaccess.)
Includes inlining across multiple files.
| NOTE | For more detailed information on compiler optimization levels, see your compiler documentation. |
Currently PBO only supports +O1
level of optimization. When you specify the +I option, the optimization
level is set to +O1.:
The optimizations are performed along with instrumentation.
However, profile-based optimizations are not performed until you
compile later with +P:
/opt/ansic/bin/cc -o prog -v +P prog.o cc: informational note 404: NLSPATH is /opt/ansic/lib/nls/msg/%L/%N.cat:/opt/ansic/lib/nls/msg/C/%N.cat: cc: informational note 404: INCLUDIR is INCLUDIR=3D/usr/include cc: informational note 404: LPATH is /usr/lib/hpux32:/opt/langtools/lib/hpux32: /usr/ccs/bin/ld -u__sin_core__ -u__sin_init \ -h__sin_core__ = -h__sin_lookup_ibt -l sin -u main \ -o prog prog.o -dynamic -P +df flow.data +Oprocelim -lc cc: informational note 413: Entering Link editor.
The -I
linker option can be used with -b
to build a shared library with instrumented code. Also, the -P
and +df command-line
options are compatible with the -b
option.
To profile shared libraries, you must set the environment
variable SHLIB_FLOW_DATA
to the file that receives profile data. Unlike FLOW_DATA,
SHLIB_FLOW_DATA
has no default output file. If SHLIB_FLOW_DATA
is not set, profile data is not collected. This allows you to activate
or suspend the profiling of instrumented shared libraries.
You must also link with the SIN library using the -lsin option. Additional linker options are necessary to ensure that the necessary modules are linked from the millicode library.
Note that you could set SHLIB_FLOW_DATA
to flow.data
which is the same file as the default setting for FLOW_DATA.
But, again, profile data will not be collected from shared libraries
unless you explicitly set SHLIB_FLOW_DATA
to some output file.
The following is a simple example for instrumenting, profiling, and optimizing a shared library:
/opt/ansic/bin/cc -v -o prog +P +df prog.prf prog.o cc: informational note 404: NLSPATH is /opt/ansic/lib/nls/msg/%L/%N.cat:/opt/ansic/lib/nls/msg/C/%N.cat: cc: informational note 404: INCLUDIR is INCLUDIR=3D/usr/include cc: informational note 404: LPATH is /usr/lib/hpux32:/opt/langtools/lib/hpux32: /usr/ccs/bin/ld -u__sin_core__ -u__sin_init \ -h__sin_core__ -h__sin_lookup_ibt -l sin -u main \ -o prog prog.o -dynamic -P +df prog.prf +Oprocelim -lc cc: informational note 413: Entering Link editor.
You can take greater advantage of PBO on merged object files
created with the -r
linker option.
Briefly, ld -r
combines multiple .o
files into a single .o
file. It is often used in large product builds to combine objects
into more manageable units. It is also often used in combination
with the linker -h
option to hide symbols that may conflict with other subsystems in
a large application. (See Hiding Symbols with -h for more information on ld -h.)
The subspaces in the merged .o
file produced by ld -r
are relocatable which allows for greater optimization.
The following is a simple example of using PBO with ld -r:
$ cc +I -c file1.c file2.c Create individual object files $ ld -r -I -o reloc.o file1.o file2.o Build relocatable, merged file $ cc +I -o a.out reloc.o Create instrumented executable file. $ a.out < input_file Run instrumented executable with representative input data.
$ ld -r -P +pgm a.out -o reloc.o \ file1.o file2.o Rebuild relocatable file for PBO. $ cc +P -o a.out reloc.o Perform PBO on the final executable file.
Notice in the example above, that the +pgm
option was necessary because the output file name differs from the
instrumented program file name.
| NOTE | If you are using |
This section describes restrictions and limitations you should be aware of when using Profile-Based Optimization.
| NOTE | PBO calls |
The linker does not modify object files. Rather, it compiles,
instruments, and optimizes the code, placing the resulting temporary
object file in a directory specified by the TMPDIR
environment variable. If PBO fails due to inadequate disk space,
try freeing up space on the disk that contains the $TMPDIR
directory. Or, set TMPDIR
to a directory on a disk with more free space.
To avoid the potential problems described below, PBO should only be used during the final stages of application development and performance tuning, when source code changes are the least likely to be made. Whenever possible, an application should be re-profiled after source code changes have been made.
What happens if you attempt to optimize a program using profile
data that is older than the source files? For example, this could
occur if you change source code and recompile with +P,
but don't gather new profile data by re-instrumenting the code.
In that sequence of events, optimizations will still be performed. However, full profile-based optimizations will be performed only on those procedures whose internal structure has not changed since the profile data was gathered. For procedures whose structure has changed, the following warning message is generated:
ucomp warning: Code for name changed since profile database file flow.data built. Profile data for name ignored. Consider rebuilding flow.data.
Note that it is possible to make a source code change that does not affect the control flow structure of a procedure, but which does significantly affect the profiling data generated for the program. In other words, a very small source code change can dramatically affect the paths through the program that are most likely to be taken. For example, changing the value of a program constant that is used as a parameter or loop limit value might have this effect. If the user does not re-profile the application after making source code changes, the profile data in the database will not reflect the effects of those changes. Consequently, the transformations made by the optimizer could degrade the performance of the application.
All options to ld
should work normally with instrumented object files with the following
exceptions:
-rThe -r
option works with both -I
and -P. However,
it produces an object file. In Itanium/PA-64 mode, use -I,
-P, or the +nosectionmerge
option on a -r linker command to
allow procedures to be positioned independently. Without these options,
a -r link merges procedures into
a single section.
-sDo not use this option with -I.
However, there is no problem using this option with -P.
-GDo not use this option with -I.
There is no problem using this option with -P.
-AObsolete.
The nm
command works on object files.
Object files can be manipulated with ar
in exactly the same way that ordinary relocatable files can be.
You can not run strip
on files compiled with +I
or +P. This generates
an error.
Except as noted below, all cc,
CC, and f90
compiler options work as expected when specified with +I
or +P:
-GThis option is incompatible with +I,
but compatible with +P
(as long as the insertion of the gprof
library calls does not affect the control flow graph structure of
the procedures.)
-pThis option is incompatible with +I
option, but is compatible with +P
(as long as the insertion of the prof
code does not affect the control flow graph structure of the procedures.)
-sThis option is incompatible with -g,
which is automatically called with +I.
-SThis option is incompatible with +I
option because assembly code is not generated from the compiler
in these situations. Currently, it is not possible to get assembly
code listings of code generated by +I.
-y/+yUnavailable in IPF compilers.
+oUnavailable in IPF compilers.
In the edit-compile-link-debug development cycle, link time is a significant component. The incremental linker can reduce the link time by taking advantage of the fact that you can reuse most of the previous version of the program and that the unchanged object files do not need to be processed. The incremental linker allows you to insert object code into an output file (executable or shared library) that you created earlier, without relinking the unmodified object files. The time required to relink after the initial incremental link depends on the number of modules you modify.
You can debug the resulting executable or shared library produced by the incremental linker using the gdb debugger with incremental-linking support.
The linker performs the following different modes of linking:
Normal link: the default operation mode in which the linker links all modules.
Initial incremental link: the mode entered when you request an incremental link, but the output module created by the incremental linker does not exist, or it exists but the incremental linker is unable to perform an incremental update.
Incremental link: the mode entered when you request an incremental link, an output module created by the incremental linker exists, and the incremental linker does not require an initial incremental link.
Incremental links are usually much faster than regular links.
On the initial link, the incremental linker requires about the same
amount of time that a normal link process requires, but subsequent
incremental links can be much faster than a normal link. A change
in one object file in a moderate size link (tens of files, several
megabytes total) normally is about 10 times faster than a regular
ld link. The incremental linker
performs as many incremental links as allocated padding space and
other constraints permit. The cost of the reduced link time is an
increase in the size of the executable or shared library.
The incremental linker allocates padding space for all components
of theprogram. Padding makes modules larger than those modules linked
by ld. As object files increase
in size during successive incremental links, the incremental linker
can exhaust the available padding. If this occurs, it displays a
warning message and does a complete initial incremental link of
the module. When an object file changes, the incremental linker
not only replaces the content of that file in the executable or
shared library being linked, but also adjusts references to all
symbols defined in the object file and referenced by other objects.
This is done by looking at relocation records saved in the incrementally
linked executable or shared library.
On the initial incremental link, the linker processes the input object files and libraries in the same way as the normal link. In addition to the normal linking process, the incremental linker performs the additional actions:
Saves information about all the object files processed during the linking process.
Saves information about all global symbol for recreating the linker symbol table.
Saves relocations to keep track of symbolic references.
Pads text, data, bss and other sections in the output file with additional space for future expansion.
On subsequent incremental links, the linker uses timestamps and file sizes to determine which object files have changed. It then performs the following actions:
Remove and vacate the effect of old versions of the modified object files from the output file.
Add contents of the modified object files into the space vacated, or into the padding space when needed.
Use the saved relocation information to patch the symbolic references in the rest of the output file.
Under certain conditions, the incremental linker cannot perform incremental links. When this occurs, the incremental linker automatically performs an initial incremental link to restore the process. In the following situations, the linker automatically performs an initial incremental link of the output file:
Changed linker command line, where the linker command line does not match the command line stored in the output file. (With the exceptions of the verbose and tracing options)
Any of the padding spaces have been exhausted.
Modules have been modified by the ld -s
or ld -x options or tools (for
example, strip(1)). The incremental
linking requires the parts of the output load module which are stripped
out with these options.
Incompatible incremental linker version, when you run a new version of the incremental linker on an executable created by an older version.
New working directory, where the incremental linker performs an initial incremental link if current directory changes.
Archive or shared libraries are added/removed to/from the linker command line.
Objects are added/removed to/from the linker command line.
To use incremental linking from your HP C (cc) or HP aC++
(aCC) compiler, specify the +ild
from your compiler command line.
If the output file does not already exist or if it was created
without the +ild option, the linker
performs an initial incremental link. The output file produced is
suitable for subsequent incremental links. The incremental link
option is valid for both executable and shared library links. The
+ild option is not valid for relocatable
links, options (or tools) that strip the output module, and certain
optimization options.
The incremental linker support the
+ildrelink option to allow you
to instruct the incremental linker to ignore the output load module
and perform an initial incremental relink. In certain situations
(for example, when internal padding space is exhausted), the incremental
linker is forced to perform an initial incremental link. You can
avoid such unexpected initial incremental links by periodically
rebuilding the output file with the +ildrelink
option.
The ld command supports additional
options with +ild. The +ildnowarn
option suppresses all incremental-linking related warning messages.
The +ildpad percentage
controls the amount of padding (percentage) the incremental linker
allocates. You can use these options with the -Wl, arg1...
compiler option.
See ld(1) for more information.
The incremental linker searches an archive library if there are unsatisfied symbols. It extracts all archive members satisfying unsats and processes them as new object files. If an archive library is modified, the linker replaces the modified archive library.
An object file extracted from an archive library in the previous link remains in the output load module even if all references to symbols defined in the object file have been removed. The linker removes these object files when it performs the next initial incremental link.
In an initial incremental link, the linker scans shared library symbol tables and resolves unsats the same way it would in a regular link. In incremental links, the linker does not process shared libraries and their symbol tables at all and does not report shared library unsats. The dynamic loader detects them at run time. If any of the shared libraries on the command line was modified, the linker reverts to an initial incremental link.
Performance of the incremental linker may suffer greatly if you change a high percentage of object files.
The incremental linker may not link small programs much faster, and the relative increase in size of the executable is greater than that for larger programs.
Generally, the linker needs to scan through all shared libraries on a link line in order to determine all the unsats, even in incremental links. This process may slow down incremental links. The incremental linker does not scan shared libraries and leaves detection of shared library unsats to the dynamic loader.
It is not recommended that you use the incremental linker to create final production modules. Because it reserves additional padding space, modules created by the incremental linker are considerably larger than those created in regular links.
Any program that modifies an executable (for example, strip strip(1)), may affect the ability of ld to perform an incremental link. When this happens, the incremental linker issues a message and performs an initial incremental link.
Third-party tools that work on object files may have unexpected results on modules produced by the incremental linker.
The global symbol table mechanism is designed as a performance enhancement option. Enabling this mechanism causes the creation of a global symbol table which speeds up symbol lookup, by eliminating the need to scan all loaded libraries in order to find a symbol. This is particularly effective for applications with large numbers of shared libraries. This mechanism is off by default.
The global symbol table is implemented using a hash table.
Under this mechanism, whenever a library is loaded (either implicitly
or by using dlopen() or shl_load()),
the mechanism hashes the library's exports and places them into
this table. When a library is unloaded, the mechanism looks up the
library's exports in the table and removes them.
The hash table does not contain entries for symbols defined
by shl_definesym(). User-defined
symbols must therefore be handled separately. Enabling the mechanism
causesthe dynamic loader to use more memory and impacts the performance
of the dlopen(), dlclose(), shl_load(),
and shl_unload() API calls.
With the global symbol table, the dynamic loader may need to perform a large number of hashing operations to locate symbols. Performing this hash function may cost considerable time, especially when symbol names are very long (C++ programs). To speed up dld, computing hash values can be off-loaded to the linker.
Use the +gst options, +gst,
+gstbuckets (PA-32 only), +gstsize, +nodynhash
(PA-64 and IPF only), and +plabel_cache,
(PA-32 only), to control the behavior of the global symbol table
hash mechanism. See the ld(1) and chatr(1)
manpages for information on these options.
With these options, you can tune the size of the hash table and number of buckets per entry to reach a balance of performance and memory use. To maximize for performance, tune the table size for an average chain length of one. For maximum memory use, at the expense of performance, tune the size of the table to minimize the number of empty entries. In general, use prime numbers for the table size. The mechanism provides default values of table size, 1103, and number of buckets, 3.
To get statistical information about hash table performance,
set the environment variable _HP_DLDOPTS
to contain the -symtab_stat option.
This option provides a message for each library that contains the
following information:
Operation (load/unload)
Name of library
Number of exports
Number of entries in table with no stored symbols
Average length of non-zero chains
Calculated performance of the hash table
Amount of memory used by the hash table
The +afs option supports
function symbol aliasing. Often user programs
have functions that exactly match the functionality of optimized
library functions with a different name. These user- defined functions
are usually called frequently in the program. With the +afs
option, you can make significant gains in performance by replacing
all references to a user-defined function with references to a tuned
library function during link time, thus optimizing these functions
with just a relink.
The +afs func_sym_x=func_sym_y ...
instructs the linker to replace the function symbol with an alternate
function symbol in shared library and executable file links.
Both functions must define the same number and type of parameters, and return a value of the same type. If they do not match, the results are unpredictable, and the linker does not generate a warning message.
Example:
$ ld ... +afs func_sym1=func_sym2 ...
In the example, the linker replaces all references to the
function symbol func_sym1 with
references to func_sym2. The func_sym2
symbol should be an normal unaliased symbol. It cannot appear on
the left-hand side of "=" on another +afs
option.
You can specify more than one function symbol alias on the
command line with multiple option-symbol pairs, that is, each symbol
pair you specify must be preceded by the +afs
option.
The fastbind
tool improves the start-up time of programs that use shared libraries.
When fastbind
is invoked, it caches relocation information
inside the executable file. The next time the executable file runs,
the dynamic loader uses this cached information to bind the executable
instead of searching for symbols.
The syntax for fastbind is:
fastbind[-n][-u]incomplete executable...
where:
Removes fastbind
data from the executable.
Performs fastbind
even when unresolved symbols are found. (By default, fastbind stops
when it cannot resolve symbols.)
You can create and delete fastbind
information for an executable file after it has been linked with
shared libraries. You can invoke fastbind
from the linker or use the fastbind
tool directly. You can set the _HP_DLDOPTS
environment variable to find out if fastbind
information is out-of-date and to turn off fastbind
at run time.
To invoke fastbind
on an incomplete executable file, verify
that your executable has write access (because fastbind writes to
the file) and then run fastbind.
$ ls -l main -rwxrwxrwx 1 janet 191 28722 Feb 20 09:11 main $ fastbind main
The fastbind
tool generates fastbind
information for main
and rewrites main
to contain this information.
To invoke fastbind from ld,
pass the request to the linker from your compiler by using the -Wl,+fb
options. For example:
$ ld -b convert.o volume.o -o libunits.s Build the shared library. $ cc -Aa -Wl,+fb main.c -o main \ Link main to the shared libunits.s -lc library. Perform fastbind.
The linker performs fastbind
after it creates the executable file.
By default, when the dynamic loader finds that fastbind
information is out-of-date, it silently reverts back to the standard
method for binding symbols. To find out if an executable file has
out-of-date fastbind information, set the _HP_DLDOPTS
environment variable as follows:
$ export _HP_DLDOPTS=-fbverbose $ main /usr/lib/dld.s: Fastbind data is out of date
The dynamic loader provides a warning when the fastbind
information is out-of-date.
To remove fastbind
information from a file, use the fastbind
tool with the -n
option. For example:
$ fastbind -n main Remove fastbind information from main.
To use the standard search method for binding symbols, instead
of the fastbind
information in an executable file, set the _HP_DLDOPTS
environment variable as follows:
export _HP_DLDOPTS=-nofastbind Turns off fastbind at run time.
See the fastbind(1) man page.
The ld command automatically
maps sections from input object files onto output segments in executable
files. The mapfile option allows you to change the default mapping
provided by the linker.
| 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 system programming use, not for application programming use. When using the mapfile option, you can easily create executable files that do not execute. |
The -k option to ld
specifies a text file containing mapfile directives:
ld -k mapfile [flags] files ...
The ld command automatically
maps sections from input object files onto output segments in executable
files. The mapfile option allows you to change the default mapping
provided by the linker.
Use the -k mapfile
option to specify a text file that contains mapfile directives.
The linker appends the specified mapfile to the default mapfile
unless you specify the +nodefaultmap
option.
The +nodefaultmap option
used with -k option prevents the
linker from concatenating the default memory map to the map provided
by mapfile. 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
default to the output file.
-k mapfile and +nodefaultmapcat mapfile
text = LOAD ?RX V0x1000; text : .rodata; text : .PARISC.milli; text : .dynamic; text : .dynsym; text : .dynstr; text : .hash; text : $PROGBITS ?AX; text : .PARISC.unwind; text : $UNWIND; data = LOAD ?RW V0x4000000040001000; data : .opd; data : .plt; data : .dlt; data : .data; data : $PROGBITS ?AW!S; data : .sdata; data : $PROGBITS ?AWS; data : .sbss; data : $NOBITS ?AWS; data : .bss; data : $NOBITS ?AW!S; note = NOTE; note : $NOTE;
# text segment text = LOAD ?RX V0x04000000; text : .dynamic; text : .dynsym; text : .dynstr; text : .hash; text : .rela.dlt; text : .rela.plt; text : .rela.opd; text : $RELA ?A; text : $REL ?A; text : .IA_64.unwind_hdr; text : $UNWIND ?A; text : .IA_64.unwind_info; text : $PROGBITS ?A!X!W; text : .IA_64.milli; text : .text;text : $PROGBITS ?AX!W; # data segment data = LOAD ?RW V0x40000000; data : .data; data : $PROGBITS ?AW!S; data : .preinit_array; data : .init_array; data : .fini_array; data : .opd; data : .plt; data : .dlt; data : .sdata; data : $PROGBITS ?AWS; data : .sbss; data : $NOBITS ?AWS; data : .bss; data : $NOBITS ?AW!S;
cc -c main.c ld main.o +nodefaultmap -k mapfile main.o -lc
elfdump -h -S a.out
a.out:
*** Section Header *** Index Type Vaddr Offset Size Name 1 DYNM 04000138 00000138 000000d0 .dynamic 2 DYNS 04000208 00000208 00002ec0 .dynsym 3 STRT 040030c8 000030c8 000022be .dynstr 4 HASH 04005388 00005388 00001190 .hash 5 RELA 04006518 00006518 000000cc .rela.dlt 6 RELA 040065e4 000065e4 00000018 .rela.plt 7 RELA 040065fc 000065fc 0000000c .rela.HP.preinit 8 RELA 04006608 00006608 000000b4 .rela.data 9 PBIT 040066c0 000066c0 00000018 .IA_64.unwind_hdr 10 UNWI 040066d8 000066d8 00002214 .IA_64.unwind 11 PBIT 040088ec 000088ec 0000621c .IA_64.unwind_info 12 HP_O 0400eb08 0000eb08 000044f8 .HP.opt_annot 13 PBIT 04013000 00013000 00003c08 .rodata 14 PBIT 04016c08 00016c08 000000a0 .interp 15 PBIT 04016ca8 00016ca8 00000bb0 .dynhash 16 PBIT 04017858 00017858 000000d0 .opd 17 PBIT 04017980 00017980 00065460 .text 18 PBIT 0407cde0 0007cde0 00000050 .bortext 19 PBIT 40000000 0007d000 000018c8 .data 20 PBIT 400018c8 0007e8c8 00000008 .HP.preinit 21 PBIT 400018d0 0007e8d0 00000020 .plt 22 PBIT 400018f0 0007e8f0 00000638 .dlt 23 PBIT 40001f28 0007ef28 00000000 .HP.init 24 PREI 40001f28 0007ef28 00000000 .preinit_array 25 INIT 40001f28 0007ef28 00000000 .init_array 26 FINI 40001f28 0007ef28 00000000 .fini_array 27 PBIT 40001f28 0007ef28 000002cc .sdata 28 NOBI 400021f8 0007f1f4 000000e0 .sbss 29 NOBI 400022e0 0007f1f4 00005420 .bss 30 NOBI 40007700 0007f1f4 00000028 .tbss 31 NOBI 40007728 0007f1f4 00000000 .hbss 32 PBIT 00000000 0007f1f4 00000000 .fastbind 33 NOTE 00000000 0007f1f8 00006fb8 .note 34 STRT 00000000 000861b0 0000600a .strtab 35 SYMT 00000000 0008c1c0 000068e0 .symtab 36 STRT 00000000 00092aa0 00000152 .shstrtab
The following directives show how a simple mapfile would appear:
# text segment text = LOAD ?RX; text : .rodata ?A; text : $PROGBITS ?AX; # data segment data = LOAD ?RW; data : $PROGBITS ?AW!S; data : $PROGBITS ?AWS; data : $NOBITS ?AWS; data : $NOBITS ?AW!S; # note segment note = NOTE; note : $NOTE; # non-segment nonsegment = NONSEGMENT;
The HP-UX 11i version 1.5 32-bit linker uses the following default mapfile:
# text segment text = LOAD ?RXlc V0x04000000; text : .dynamic; text : .dynsym; text : .dynstr; text : .hash; text : .rela.plt; text : .rela.dlt; text : .rela.opd; text : .rela.preinit_array; text : .rela.init_array; text : .rela.fini_array; text : $RELA ?A; text : $REL ?A; text : .IA_64.unwind_hdr; text : $UNWIND ?A; text : .IA_64.unwind_info; text : .HP.opt_annot; text : $PROGBITS ?A!X!W; text : .IA_64.milli; text : .text; text : $PROGBITS ?AX!W; text : $OVLBITS ?AX!W; # data segment data = LOAD ?RWm V0x40000000; data : .zeropage; data : .hdata; data : .data; data : $PROGBITS ?AW!S; data : .preinit_array; data : .init_array; data : .fini_array; data : .opd; data : .plt; data : .dlt; data : .sdata; data : $PROGBITS ?AWS; data : .sbss; data : $NOBITS ?AWS; data : .bss; data : $NOBITS ?AW!S; data : .hbss; # thread specific storage segment thread_specific = HP_TLS ?RW; thread_specific : .tbss; thread_specific : $NOBITS ?AWT; # note segment note = NOTE; note : $NOTE; # non-segment nonsegment = NONSEGMENT; nonsegment : .debug_abbrev; nonsegment : .debug_info; nonsegment : .debug_loc; nonsegment : .debug_line; nonsegment : .debug_str;
The HP-UX 11i version 1.5 64-bit linker uses the following default mapfile:
# text segment text = LOAD ?RXlc V0x0x4000000000000000; text : .dynamic; text : .dynsym; text : .dynstr; text : .hash; text : .rela.plt; text : .rela.dlt; text : .rela.opd; text : .rela.preinit_array; text : .rela.init_array; text : .rela.fini_array; text : $RELA ?A; text : $REL ?A; text : .IA_64.unwind_hdr; text : $UNWIND ?A; text : .IA_64.unwind_info; text : .HP.opt_annot; text : $PROGBITS ?A!X!W; text : .IA_64.milli; text : .text; text : $PROGBITS ?AX!W; text : $OVLBITS ?AX!W; # data segment data = LOAD ?RWmo V0x6000000000000000; data : .zeropage; data : .hdata; data : .data; data : $PROGBITS ?AW!S; data : .preinit_array; data : .init_array; data : .fini_array; data : .opd; data : .plt; data : .dlt; data : .sdata; data : $PROGBITS ?AWS; data : .sbss; data : $NOBITS ?AWS; data : .bss; data : $NOBITS ?AW!S;data : .hbss; # thread specific storage segment thread_specific = HP_TLS ?RW; thread_specific : .tbss; thread_specific : $NOBITS ?AWT; # note segment note = NOTE; note : $NOTE; # non-segment nonsegment = NONSEGMENT; nonsegment : .debug_abbrev; nonsegment : .debug_info; nonsegment : .debug_loc; nonsegment : .debug_line; nonsegment : .debug_str;
A mapfile can have zero or more mapfile directives. There are two types of mapfile directives: segment declarations and section mapping directives. The directives can span across lines and are terminated by a semicolon.
The following syntax conventions are used to describe the directives:
[...]*
means zero or more
[...]+ means one
or more
[...] means optional
The section_names and
segment_names are the same as a C identifier
except that a period (.) is treated
as a letter.
A number can be hexadecimal, following the same syntax as the C language.
The section, segment, file, and symbol names are case-sensitive.
A string of characters following #
and ending at a new-line is considered a comment.
A segment declaration can create a new segment with a set of attributes or change the attributes of an existing segment.
segment_name = {segment_attribute_value}* ;
The segment attributes and their valid values are as follows:
Attribute |
Value |
|---|---|
segment_type |
|
segment_flags |
|
virtual_address |
Vnumber |
physical_address |
Pnumber |
alignment |
Anumber |
NOTE
segments cannot be assigned any segment attribute other than a segment_type.
If you do not specify virtual_address, physical_address and alignment, the linker calculates these values as it builds the executable. If you specify both a virtual_address and an alignment for a segment, the virtual_address value takes priority.
An alignment value greater than the file system block size (4K) also specifies the page size. In that case, the value of the alignment is also the size of the page. The operating system uses the largest page size available that is no greater than the value of the alignment when mapping a segment.
The segment_type NONSEGMENT
describes sections placed at the end of the executable file. The
linker does not create a program header entry for this segment.
Segment declarations support the following segment flags:
| Flag | Action |
|---|---|
|
Readable |
|
Writable |
|
Executable |
The default segment_flags for a
LOADable segment is ?RWX.
Segment declarations support the following special flags:
| Flag | Action |
|---|---|
|
Enables lazy swap allocation for a segment. This flag is not set by default. (The lazy swap is disabled by default.) |
|
Sets the "modification" hint for a segment. When this flag is set, it indicates that the program expects to modify the pages in the segment. If not set, the program does not expect to modify any pages in the segment, even though it may have permission to do so. This flag is not set by default. (The modification hint is off by default.) |
|
Sets the "code" hint for a segment. When this flag is set, it indicates that the segment mostly contains code that may be executed. When not set, it indicates that it is unlikely that the segment contains code. This flag is not set by default. (The code hint is off by default.) |
|
Groups segments together. A segment declared
with |
|
Tells the linker that all the segment attributes declared for this segment can be changed or modified to achieve space and/or time optimization. When this flag is set, the linker considers all other segment attribute specifications (for this segment) as hints and change or modify them as it thinks fit for space and/or time optimization. |
The following example declares a segment with segment_type LOAD and segment_flags readable and executable.
text = LOAD ?RX;
The following example declares a LOADable segment
(default) with segment_flags readable
and writable. The virtual_address and
alignment values are set to V0x80000000
and A0x1000 respectively.
mydata = ?RW V0x80000000 A0x1000;
A section mapping directive specifies how the linker should map the input section onto output segments. This directive tells the linker what attributes of a section must be matched in order to map that section into the named segment. The set of attribute values that a section must have to map into a specific segment is called the entrance criteria.
segment_name : {section_attribute_value}*
;
The section attributes and their valid values are as follows:
Section Attribute |
Value |
|---|---|
section_name |
Any valid section name |
section_type |
|
section_flags |
|
| Flag | Value |
|---|---|
|
Allocatable (takes up virtual memory) |
|
Writable |
|
Executable |
|
Short data |
|
|
At most one section_type can be specified in a mapping directive.
If a section flag is preceded by an exclamation
mark (!), it indicates that the
flag should not be set to meet the entrance criteria.
If you do not specify section_flags, the flag can have any value to meet the entrance criteria.
S1 : ?X;
The linker maps all executable sections onto segment S1.
The section_name attribute indicates that the linker should map the input sections with the specified name onto the named segment.
text : .rodata;
An input section can satisfy more than one entrance criteria.
S1 : $PROGBITS;
S2 : $PROGBITS;
In this case, all sections with section type $PROGBITS
are mapped onto segment S1 as the
first rule takes precedence.
An AND relationship exits between attributes specified on the same line. An OR relationship exits between attributes specified for the same segment that span more than one line.
Example 1:
All sections with section_type $PROGBITS
and section_flags AX (allocatable
and executable) are mapped onto the text segment.
text : $PROGBITS ?AX;
Example 2
text : $PROGBITS ?AX;
text : .rodata;
In this case, the linker maps a section onto the text segment if:
Its section_type is $PROGBITS
and section_flags is AX.
(or)
Its section_name is .rodata.
The linker use a default map structure corresponding to the
default mapfile. When you use the mapfile option with the ld
command, the linker appends the default mapfile to the end of your
user-specified mapfile. (You can override the default mapfile by
using the +nodefaultmap option.)
As it processes each segment declaration in the mapfile, the linker compares it with the existing list of segment declarations as follows:
If the segment does not exist already, but another with the same segment_type exists, the linker adds the segment after all of the existing segments with the same segment_type.
If no segment with the same segment_type exists, the linker adds the new segment to the list to maintain the following order based on segment_type:
LOAD
HP_TLS
NOTE
NONSEGMENT
If segments of same type already exists, the linker adds the new segment after the last segment with the same type.
As each section mapping directive in a mapfile is read in, the linker creates a new entrance criteria and appends it to the existing list of entrance criteria. It applies the entrance criteria in the order in which they are specified in the mapfile. The linker maps out the input sections in the same order as their matching entrance criteria.

Figure 31: shows the map structure. The entrance criteria boxes correspond to the information from the section mapping directives and the segment attribute descriptors correspond to the information from the segment declarations. The output section descriptors boxes group the sections that fall under each segment based on their section attributes. The linker associates each entrance criteria with a list of "output section descriptors". In Figure 31: , the entrance criteria are labeled with numbers to illustrate their associated output section descriptors.
The linker performs the following steps when mapping sections to segments:
When a section is read in, the liner checks the list of entrance criteria looking for a match. All specified criteria must be matched. When an entrance criteria matches, the linker traverses its associated "output section descriptor" list.
If the section attribute values match those of an existing output section descriptor exactly, the linker places the section at the end of the list of sections associated with that output section descriptor.
If no matching output section descriptor is found, but output section descriptors of the same section_type exists, the linker creates a new output section descriptor with the same attribute values as the section and adds that section to the new output section descriptor. It places the new output section descriptor after the last output section descriptor with the same section type.
If no other output section descriptor of the indicated section_type exists, the linker creates a new output section descriptor and associates the section with the new output section descriptor. It places the new output section descriptor after the last output section descriptor associated with that entrance criteria.
If no entrance criteria match is found, the linker places the section at the end of the "nonsegment". It does not create a program header entry for the nonsegment.
The following rules apply when the linker adds a new output section descriptor to a list of output section descriptors associated with an entrance criteria:
If an entrance criteria selects both
$PROGBITS and $NOBITS
sections, the linker enforces an order such that the $PROGBITS
sections precede $NOBITS sections.
If an entrance criteria selects both S
(short data) and !S (non-short
data) sections, the layout of the sections depends on section_type
and S flag status.The linker maintains
the following order:
$PROGBITS and !S
$PROGBITS and S
$NOBITS and S
$NOBITS and !S
The linker always tries to group all $NOBITS
sections at the end of the data segment. If it does not place a
$NOBITS section at the end of the
data segment because of user-specified mapping directives, the linker
converts that section to a $PROGBITS
section and zero-fills the section contents. The linker issues a
warning message when it converts a $NOBITS
section into a $PROGBITS section.
The linker adds the section mapping directives from the default mapfile after the user-specified mapping directives. The following rules apply if the you declare a built-in segment (a segment defined in the default mapfile):
If the segment_type and "segment_flags" differ from the default mapfile declarations, the linker issues a warning and uses the user-specified segment_type and/or segment_flags for that segment.
If your segment declaration does not specify a segment_attribute_value, the linker takes it from the default mapfile's segment declaration.
The linker completely ignores the default mapfile
if you use the option +nodefaultmap
on the ld command line.
The following conditions can result in a fatal error:
Specifying more than one -k
option on the command line
Mapfile cannot be opened or read
The linker finds a syntax error in the mapfile
More than one segment_type, segment_flags, virtual_address, physical_address or alignment value appears on a single declaration line
More than one section_name, section_type, or section_flags value appears on a single directive line
A user-defined virtual address causes a segment to overlap the previous segment
The following conditions can produce a warning message:
A physical_address or a virtual_address value is specified for any segment other than a LOADable segment. The directive is ignored.
A second declaration for the same segment changes an attribute value. The second declaration overrides the original.
An attribute value for a built-in segment is changed.
Machine code that contains absolute virtual addresses. Created by the linker when it combines relocatable object files.
A library, created by the ar
command, which contains one or more object modules. By convention,
archive library file names end with .a.
Compare with "shared library."
The process the dynamic loader goes through of mapping the shared library code and data into a process's address space, relocating any pointers in the shared library data that depend on actual virtual addresses, allocating the bss segment, and binding routines and data in the shared library to the program.
A contiguous section of assembly code, produced by compilation, that has no branches in except at the top, and no branches out except at the bottom.
The process the dynamic loader goes through of filling in a process's procedure linkage tables and data linkage tables with the addresses of shared library routines and data. When a symbol is bound, it is accessible to the program.
The dependent library search algorithm used when linking and loading 64-bit applications.
A segment of memory in which uninitialized data is stored. Compare with "text segment" and "data segment." For details, refer to a.out(4).
A temporary holding area for data. Buffers are used to perform input and output more efficiently.
A process that is spawned by a process (a sub-process).
A phase of compilation in which object code is created.
A particular step performed during compilation - for example, pre-processing, lexical analysis, parsing, code generation, linking.
An executable (a.out)
file that does not use shared libraries. It
is "complete" because all of its library code is contained within
it. Compare with "incomplete executable."
See startup file.
An initialized global variable that may be referenced outside of the library.
A linkage table that stores the addresses of data items.
A segment of memory containing a program's initialized data. Compare with "bss segment" and "text segment." For details, refer to a.out(4).
The process of waiting to bind a procedure until a program references it. Deferred binding can save program startup time. Compare with "immediate binding."
When a process is "demand-loadable," its pages are brought into physical memory only when they are accessed.
Occurs when a shared library depends on other libraries
- that is, when the shared library was built (with ld -b),
other libraries were specified on the command line. See also "dependent
library."
A library that was specified on the command line
when building a shared library (with ld -b).
See "dependency."
The dependent library search alogrithm used when linking and loading in 32-bit mode. Searching a list starting at the end of the list and moving toward the head. Shared library initialization routines are invoked by traversing the list of loaded shared libraries depth-first.
See "dynamic loading library."
See "data linkage table."
A program that calls other programs.
The process of linking an object module with a running program and loading the module into the program's address space.
Code that attaches a shared library to a program. See dld.sl(5).
An SVR4 term for a shared library.
The process that allows the location of shared libraries to be specified at runtime.
The location at which a program starts running after
HP-UX loads it into memory. The entry point is defined by the symbol
$START$ in crt0.o.
The process of using the shl_load(3X) function to load a shared library into a running program.
A short code segment generated by the linker for a global definition in a shared library. External calls to shared library procedures go through the export stub. See also import stub.
A symbol definition that may be referenced outside the library.
Making a symbol visible to code outside the module
in which the symbol was defined. This is usually done with the +e
or -E option.
A reference to a symbol defined outside an object file.
An optimization technique wherein procedures are relocated in a program, based on profiling data obtained from running the program. Feedback-directed positioning is one of the optimizations performed during profile-based optimization.
A file descriptor is returned by the open(2), creat(2), and dup(2) system calls. The file descriptor is used by other system calls (for example, read(2), write(2), and close(2)) to refer to a the file.
Programs that accept input data and modify it in
some way before passing it on. For example, the pr
command is a filter.
The process of emptying a buffer's contents and resetting its internal data structures.
A definition of a procedure, function, or data item that can be accessed by code in another object file.
A string, "!<arch>\n",
which identifies a file as an archive created by ar
(\n represents
the newline character).
Making a symbol invisible to code outside the module
in which the symbol was defined. Accomplished with the -h
linker option.
By default, the dynamic loader attempts to bind all symbols in a shared library when a program starts up - known as "immediate binding." Compare with "deferred binding."
Writing code that relies on the linker to locate a symbol in a particular location or in a particular order in relation to other symbols.
Occurs when the dynamic loader automatically loads any required libraries when a program starts execution. Compare with "explicit" loading.
A short code segment generated by the linker for external references to shared library routines. See also export stub.
An external reference made from a library.
An executable (a.out)
file that uses shared libraries. It is "incomplete" because it does
not actually contain the shared library code that it uses; instead,
the shared library code is attached when the program runs. Compare
with "complete executable."
The process of accessing a memory location through a memory address that is stored in memory or a register.
An initialization routine that is called when a shared library is loaded or unloaded.
A representation of object code that is at a lower level than the source code, but at a higher level than the object code.
Intermediate code-System Object Module. Used during profile-based optimizations and level 4 optimization.
A file containing object code for subroutines and data that can be used by programs.