1335 lines
75 KiB
Plaintext
1335 lines
75 KiB
Plaintext
|
Program Library HOWTO
|
|||
|
|
|||
|
David A. Wheeler
|
|||
|
|
|||
|
version 1.20, 11 April 2003
|
|||
|
|
|||
|
|
|||
|
This HOWTO for programmers discusses how to create and use program libraries
|
|||
|
on Linux. This includes static libraries, shared libraries, and dynamically
|
|||
|
loaded libraries.
|
|||
|
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
Table of Contents
|
|||
|
1. Introduction
|
|||
|
2. Static Libraries
|
|||
|
3. Shared Libraries
|
|||
|
3.1. Conventions
|
|||
|
3.2. How Libraries are Used
|
|||
|
3.3. Environment Variables
|
|||
|
3.4. Creating a Shared Library
|
|||
|
3.5. Installing and Using a Shared Library
|
|||
|
3.6. Incompatible Libraries
|
|||
|
|
|||
|
|
|||
|
4. Dynamically Loaded (DL) Libraries
|
|||
|
4.1. dlopen()
|
|||
|
4.2. dlerror()
|
|||
|
4.3. dlsym()
|
|||
|
4.4. dlclose()
|
|||
|
4.5. DL Library Example
|
|||
|
|
|||
|
|
|||
|
5. Miscellaneous
|
|||
|
5.1. nm command
|
|||
|
5.2. Library constructor and destructor functions
|
|||
|
5.3. Shared Libraries Can Be Scripts
|
|||
|
5.4. Symbol Versioning and Version Scripts
|
|||
|
5.5. GNU libtool
|
|||
|
5.6. Removing symbols for space
|
|||
|
5.7. Extremely small executables
|
|||
|
5.8. C++ vs. C
|
|||
|
5.9. Speeding up C++ initialization
|
|||
|
5.10. Linux Standard Base (LSB)
|
|||
|
|
|||
|
|
|||
|
6. More Examples
|
|||
|
6.1. File libhello.c
|
|||
|
6.2. File libhello.h
|
|||
|
6.3. File demo_use.c
|
|||
|
6.4. File script_static
|
|||
|
6.5. File script_shared
|
|||
|
6.6. File demo_dynamic.c
|
|||
|
6.7. File script_dynamic
|
|||
|
|
|||
|
|
|||
|
7. Other Information Sources
|
|||
|
8. Copyright and License
|
|||
|
|
|||
|
1. Introduction
|
|||
|
|
|||
|
This HOWTO for programmers discusses how to create and use program libraries
|
|||
|
on Linux using the GNU toolset. A ``program library'' is simply a file
|
|||
|
containing compiled code (and data) that is to be incorporated later into a
|
|||
|
program; program libraries allow programs to be more modular, faster to
|
|||
|
recompile, and easier to update. Program libraries can be divided into three
|
|||
|
types: static libraries, shared libraries, and dynamically loaded (DL)
|
|||
|
libraries.
|
|||
|
|
|||
|
This paper first discusses static libraries, which are installed into a
|
|||
|
program executable before the program can be run. It then discusses shared
|
|||
|
libraries, which are loaded at program start-up and shared between programs.
|
|||
|
Finally, it discusses dynamically loaded (DL) libraries, which can be loaded
|
|||
|
and used at any time while a program is running. DL libraries aren't really a
|
|||
|
different kind of library format (both static and shared libraries can be
|
|||
|
used as DL libraries); instead, the difference is in how DL libraries are
|
|||
|
used by programmers. The HOWTO wraps up with a section with more examples and
|
|||
|
a section with references to other sources of information.
|
|||
|
|
|||
|
Most developers who are developing libraries should create shared libraries,
|
|||
|
since these allow users to update their libraries separately from the
|
|||
|
applications that use the libraries. Dynamically loaded (DL) libraries are
|
|||
|
useful, but they require a little more work to use and many programs don't
|
|||
|
need the flexibility they offer. Conversely, static libraries make upgrading
|
|||
|
libraries far more troublesome, so for general-purpose use they're hard to
|
|||
|
recommend. Still, each have their advantages, and the advantages of each type
|
|||
|
are described in the section discussing that type. Developers using C++ and
|
|||
|
dynamically loaded (DL) libraries should also consult the ``C++ dlopen
|
|||
|
mini-HOWTO''.
|
|||
|
|
|||
|
It's worth noting that some people use the term dynamically linked libraries
|
|||
|
(DLLs) to refer to shared libraries, some use the term DLL to mean any
|
|||
|
library that is used as a DL library, and some use the term DLL to mean a
|
|||
|
library meeting either condition. No matter which meaning you pick, this
|
|||
|
HOWTO covers DLLs on Linux.
|
|||
|
|
|||
|
This HOWTO discusses only the Executable and Linking Format (ELF) format for
|
|||
|
executables and libraries, the format used by nearly all Linux distributions
|
|||
|
today. The GNU gcc toolset can actually handle library formats other than
|
|||
|
ELF; in particular, most Linux distributions can still use the obsolete a.out
|
|||
|
format. However, these formats are outside the scope of this paper.
|
|||
|
|
|||
|
If you're building an application that should port to many systems, you might
|
|||
|
consider using [http://www.gnu.org/software/libtool/libtool.html] GNU libtool
|
|||
|
to build and install libraries instead of using the Linux tools directly. GNU
|
|||
|
libtool is a generic library support script that hides the complexity of
|
|||
|
using shared libraries (e.g., creating and installing them) behind a
|
|||
|
consistent, portable interface. On Linux, GNU libtool is built on top of the
|
|||
|
tools and conventions described in this HOWTO. For a portable interface to
|
|||
|
dynamically loaded libraries, you can use various portability wrappers. GNU
|
|||
|
libtool includes such a wrapper, called ``libltdl''. Alternatively, you could
|
|||
|
use the glib library (not to be confused with glibc) with its portable
|
|||
|
support for Dynamic Loading of Modules. You can learn more about glib at
|
|||
|
[http://developer.gnome.org/doc/API/glib/
|
|||
|
glib-dynamic-loading-of-modules.html] http://developer.gnome.org/doc/API/glib
|
|||
|
/glib-dynamic-loading-of-modules.html. Again, on Linux this functionality is
|
|||
|
implemented using the constructs described in this HOWTO. If you're actually
|
|||
|
developing or debugging the code on Linux, you'll probably still want the
|
|||
|
information in this HOWTO.
|
|||
|
|
|||
|
This HOWTO's master location is [http://www.dwheeler.com/program-library]
|
|||
|
http://www.dwheeler.com/program-library, and it has been contributed to the
|
|||
|
Linux Documentation Project ([http://www.linuxdoc.org] http://
|
|||
|
www.linuxdoc.org). It is Copyright (C) 2000 David A. Wheeler and is licensed
|
|||
|
through the General Public License (GPL); see the last section for more
|
|||
|
information.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
2. Static Libraries
|
|||
|
|
|||
|
Static libraries are simply a collection of ordinary object files;
|
|||
|
conventionally, static libraries end with the ``.a'' suffix. This collection
|
|||
|
is created using the ar (archiver) program. Static libraries aren't used as
|
|||
|
often as they once were, because of the advantages of shared libraries
|
|||
|
(described below). Still, they're sometimes created, they existed first
|
|||
|
historically, and they're simpler to explain.
|
|||
|
|
|||
|
Static libraries permit users to link to programs without having to recompile
|
|||
|
its code, saving recompilation time. Note that recompilation time is less
|
|||
|
important given today's faster compilers, so this reason is not as strong as
|
|||
|
it once was. Static libraries are often useful for developers if they wish to
|
|||
|
permit programmers to link to their library, but don't want to give the
|
|||
|
library source code (which is an advantage to the library vendor, but
|
|||
|
obviously not an advantage to the programmer trying to use the library). In
|
|||
|
theory, code in static ELF libraries that is linked into an executable should
|
|||
|
run slightly faster (by 1-5%) than a shared library or a dynamically loaded
|
|||
|
library, but in practice this rarely seems to be the case due to other
|
|||
|
confounding factors.
|
|||
|
|
|||
|
To create a static library, or to add additional object files to an existing
|
|||
|
static library, use a command like this:
|
|||
|
ar rcs my_library.a file1.o file2.o
|
|||
|
|
|||
|
This sample command adds the object files file1.o and file2.o to the static
|
|||
|
library my_library.a, creating my_library.a if it doesn't already exist. For
|
|||
|
more information on creating static libraries, see ar(1).
|
|||
|
|
|||
|
Once you've created a static library, you'll want to use it. You can use a
|
|||
|
static library by invoking it as part of the compilation and linking process
|
|||
|
when creating a program executable. If you're using gcc(1) to generate your
|
|||
|
executable, you can use the -l option to specify the library; see info:gcc
|
|||
|
for more information.
|
|||
|
|
|||
|
Be careful about the order of the parameters when using gcc; the -l option is
|
|||
|
a linker option, and thus needs to be placed AFTER the name of the file to be
|
|||
|
compiled. This is quite different from the normal option syntax. If you place
|
|||
|
the -l option before the filename, it may fail to link at all, and you can
|
|||
|
end up with mysterious errors.
|
|||
|
|
|||
|
You can also use the linker ld(1) directly, using its -l and -L options;
|
|||
|
however, in most cases it's better to use gcc(1) since the interface of ld(1)
|
|||
|
is more likely to change.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
3. Shared Libraries
|
|||
|
|
|||
|
Shared libraries are libraries that are loaded by programs when they start.
|
|||
|
When a shared library is installed properly, all programs that start
|
|||
|
afterwards automatically use the new shared library. It's actually much more
|
|||
|
flexible and sophisticated than this, because the approach used by Linux
|
|||
|
permits you to:
|
|||
|
|
|||
|
<EFBFBD><EFBFBD>*<2A>update libraries and still support programs that want to use older,
|
|||
|
non-backward-compatible versions of those libraries;
|
|||
|
|
|||
|
<EFBFBD><EFBFBD>*<2A>override specific libraries or even specific functions in a library when
|
|||
|
executing a particular program.
|
|||
|
|
|||
|
<EFBFBD><EFBFBD>*<2A>do all this while programs are running using existing libraries.
|
|||
|
|
|||
|
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
3.1. Conventions
|
|||
|
|
|||
|
For shared libraries to support all of these desired properties, a number of
|
|||
|
conventions and guidelines must be followed. You need to understand the
|
|||
|
difference between a library's names, in particular its ``soname'' and ``real
|
|||
|
name'' (and how they interact). You also need to understand where they should
|
|||
|
be placed in the filesystem.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
3.1.1. Shared Library Names
|
|||
|
|
|||
|
Every shared library has a special name called the ``soname''. The soname has
|
|||
|
the prefix ``lib'', the name of the library, the phrase ``.so'', followed by
|
|||
|
a period and a version number that is incremented whenever the interface
|
|||
|
changes (as a special exception, the lowest-level C libraries don't start
|
|||
|
with ``lib''). A fully-qualified soname includes as a prefix the directory
|
|||
|
it's in; on a working system a fully-qualified soname is simply a symbolic
|
|||
|
link to the shared library's ``real name''.
|
|||
|
|
|||
|
Every shared library also has a ``real name'', which is the filename
|
|||
|
containing the actual library code. The real name adds to the soname a
|
|||
|
period, a minor number, another period, and the release number. The last
|
|||
|
period and release number are optional. The minor number and release number
|
|||
|
support configuration control by letting you know exactly what version(s) of
|
|||
|
the library are installed. Note that these numbers might not be the same as
|
|||
|
the numbers used to describe the library in documentation, although that does
|
|||
|
make things easier.
|
|||
|
|
|||
|
In addition, there's the name that the compiler uses when requesting a
|
|||
|
library, (I'll call it the ``linker name''), which is simply the soname
|
|||
|
without any version number.
|
|||
|
|
|||
|
The key to managing shared libraries is the separation of these names.
|
|||
|
Programs, when they internally list the shared libraries they need, should
|
|||
|
only list the soname they need. Conversely, when you create a shared library,
|
|||
|
you only create the library with a specific filename (with more detailed
|
|||
|
version information). When you install a new version of a library, you
|
|||
|
install it in one of a few special directories and then run the program
|
|||
|
ldconfig(8). ldconfig examines the existing files and creates the sonames as
|
|||
|
symbolic links to the real names, as well as setting up the cache file /etc/
|
|||
|
ld.so.cache (described in a moment).
|
|||
|
|
|||
|
ldconfig doesn't set up the linker names; typically this is done during
|
|||
|
library installation, and the linker name is simply created as a symbolic
|
|||
|
link to the ``latest'' soname or the latest real name. I would recommend
|
|||
|
having the linker name be a symbolic link to the soname, since in most cases
|
|||
|
if you update the library you'd like to automatically use it when linking. I
|
|||
|
asked H. J. Lu why ldconfig doesn't automatically set up the linker names.
|
|||
|
His explanation was basically that you might want to run code using the
|
|||
|
latest version of a library, but might instead want development to link
|
|||
|
against an old (possibly incompatible) library. Therefore, ldconfig makes no
|
|||
|
assumptions about what you want programs to link to, so installers must
|
|||
|
specifically modify symbolic links to update what the linker will use for a
|
|||
|
library.
|
|||
|
|
|||
|
Thus, /usr/lib/libreadline.so.3 is a fully-qualified soname, which ldconfig
|
|||
|
would set to be a symbolic link to some realname like /usr/lib/
|
|||
|
libreadline.so.3.0. There should also be a linker name, /usr/lib/
|
|||
|
libreadline.so which could be a symbolic link referring to /usr/lib/
|
|||
|
libreadline.so.3.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
3.1.2. Filesystem Placement
|
|||
|
|
|||
|
Shared libraries must be placed somewhere in the filesystem. Most open source
|
|||
|
software tends to follow the GNU standards; for more information see the info
|
|||
|
file documentation at [info:standards#Directory_Variables] info:standards#
|
|||
|
Directory_Variables. The GNU standards recommend installing by default all
|
|||
|
libraries in /usr/local/lib when distributing source code (and all commands
|
|||
|
should go into /usr/local/bin). They also define the convention for
|
|||
|
overriding these defaults and for invoking the installation routines.
|
|||
|
|
|||
|
The Filesystem Hierarchy Standard (FHS) discusses what should go where in a
|
|||
|
distribution (see [http://www.pathname.com/fhs] http://www.pathname.com/fhs).
|
|||
|
According to the FHS, most libraries should be installed in /usr/lib, but
|
|||
|
libraries required for startup should be in /lib and libraries that are not
|
|||
|
part of the system should be in /usr/local/lib.
|
|||
|
|
|||
|
There isn't really a conflict between these two documents; the GNU standards
|
|||
|
recommend the default for developers of source code, while the FHS recommends
|
|||
|
the default for distributors (who selectively override the source code
|
|||
|
defaults, usually via the system's package management system). In practice
|
|||
|
this works nicely: the ``latest'' (possibly buggy!) source code that you
|
|||
|
download automatically installs itself in the ``local'' directory (/usr/
|
|||
|
local), and once that code has matured the package managers can trivially
|
|||
|
override the default to place the code in the standard place for
|
|||
|
distributions. Note that if your library calls programs that can only be
|
|||
|
called via libraries, you should place those programs in /usr/local/libexec
|
|||
|
(which becomes /usr/libexec in a distribution). One complication is that Red
|
|||
|
Hat-derived systems don't include /usr/local/lib by default in their search
|
|||
|
for libraries; see the discussion below about /etc/ld.so.conf. Other standard
|
|||
|
library locations include /usr/X11R6/lib for X-windows. Note that /lib/
|
|||
|
security is used for PAM modules, but those are usually loaded as DL
|
|||
|
libraries (also discussed below).
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
3.2. How Libraries are Used
|
|||
|
|
|||
|
On GNU glibc-based systems, including all Linux systems, starting up an ELF
|
|||
|
binary executable automatically causes the program loader to be loaded and
|
|||
|
run. On Linux systems, this loader is named /lib/ld-linux.so.X (where X is a
|
|||
|
version number). This loader, in turn, finds and loads all other shared
|
|||
|
libraries used by the program.
|
|||
|
|
|||
|
The list of directories to be searched is stored in the file /etc/ld.so.conf.
|
|||
|
Many Red Hat-derived distributions don't normally include /usr/local/lib in
|
|||
|
the file /etc/ld.so.conf. I consider this a bug, and adding /usr/local/lib to
|
|||
|
/etc/ld.so.conf is a common ``fix'' required to run many programs on Red
|
|||
|
Hat-derived systems.
|
|||
|
|
|||
|
If you want to just override a few functions in a library, but keep the rest
|
|||
|
of the library, you can enter the names of overriding libraries (.o files) in
|
|||
|
/etc/ld.so.preload; these ``preloading'' libraries will take precedence over
|
|||
|
the standard set. This preloading file is typically used for emergency
|
|||
|
patches; a distribution usually won't include such a file when delivered.
|
|||
|
|
|||
|
Searching all of these directories at program start-up would be grossly
|
|||
|
inefficient, so a caching arrangement is actually used. The program ldconfig
|
|||
|
(8) by default reads in the file /etc/ld.so.conf, sets up the appropriate
|
|||
|
symbolic links in the dynamic link directories (so they'll follow the
|
|||
|
standard conventions), and then writes a cache to /etc/ld.so.cache that's
|
|||
|
then used by other programs. This greatly speeds up access to libraries. The
|
|||
|
implication is that ldconfig must be run whenever a DLL is added, when a DLL
|
|||
|
is removed, or when the set of DLL directories changes; running ldconfig is
|
|||
|
often one of the steps performed by package managers when installing a
|
|||
|
library. On start-up, then, the dynamic loader actually uses the file /etc/
|
|||
|
ld.so.cache and then loads the libraries it needs.
|
|||
|
|
|||
|
By the way, FreeBSD uses slightly different filenames for this cache. In
|
|||
|
FreeBSD, the ELF cache is /var/run/ld-elf.so.hints and the a.out cache is /
|
|||
|
var/run/ld.so.hints. These are still updated by ldconfig(8), so this
|
|||
|
difference in location should only matter in a few exotic situations.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
3.3. Environment Variables
|
|||
|
|
|||
|
Various environment variables can control this process, and there are
|
|||
|
environment variables that permit you to override this process.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
3.3.1. LD_LIBRARY_PATH
|
|||
|
|
|||
|
You can temporarily substitute a different library for this particular
|
|||
|
execution. In Linux, the environment variable LD_LIBRARY_PATH is a
|
|||
|
colon-separated set of directories where libraries should be searched for
|
|||
|
first, before the standard set of directories; this is useful when debugging
|
|||
|
a new library or using a nonstandard library for special purposes. The
|
|||
|
environment variable LD_PRELOAD lists shared libraries with functions that
|
|||
|
override the standard set, just as /etc/ld.so.preload does. These are
|
|||
|
implemented by the loader /lib/ld-linux.so. I should note that, while
|
|||
|
LD_LIBRARY_PATH works on many Unix-like systems, it doesn't work on all; for
|
|||
|
example, this functionality is available on HP-UX but as the environment
|
|||
|
variable SHLIB_PATH, and on AIX this functionality is through the variable
|
|||
|
LIBPATH (with the same syntax, a colon-separated list).
|
|||
|
|
|||
|
LD_LIBRARY_PATH is handy for development and testing, but shouldn't be
|
|||
|
modified by an installation process for normal use by normal users; see ``Why
|
|||
|
LD_LIBRARY_PATH is Bad'' at [http://www.visi.com/~barr/ldpath.html] http://
|
|||
|
www.visi.com/~barr/ldpath.html for an explanation of why. But it's still
|
|||
|
useful for development or testing, and for working around problems that can't
|
|||
|
be worked around otherwise. If you don't want to set the LD_LIBRARY_PATH
|
|||
|
environment variable, on Linux you can even invoke the program loader
|
|||
|
directly and pass it arguments. For example, the following will use the given
|
|||
|
PATH instead of the content of the environment variable LD_LIBRARY_PATH, and
|
|||
|
run the given executable:
|
|||
|
/lib/ld-linux.so.2 --library-path PATH EXECUTABLE
|
|||
|
Just executing ld-linux.so without arguments will give you more help on using
|
|||
|
this, but again, don't use this for normal use - these are all intended for
|
|||
|
debugging.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
3.3.2. LD_DEBUG
|
|||
|
|
|||
|
Another useful environment variable in the GNU C loader is LD_DEBUG. This
|
|||
|
triggers the dl* functions so that they give quite verbose information on
|
|||
|
what they are doing. For example:
|
|||
|
export LD_DEBUG=files
|
|||
|
command_to_run
|
|||
|
displays the processing of files and libraries when handling libraries,
|
|||
|
telling you what dependencies are detected and which SOs are loaded in what
|
|||
|
order. Setting LD_DEBUG to ``bindings'' displays information about symbol
|
|||
|
binding, setting it to ``libs'' displays the library search paths, and
|
|||
|
setting ti to ``versions'' displays the version depdendencies.
|
|||
|
|
|||
|
Setting LD_DEBUG to ``help'' and then trying to run a program will list the
|
|||
|
possible options. Again, LD_DEBUG isn't intended for normal use, but it can
|
|||
|
be handy when debugging and testing.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
3.3.3. Other Environment Variables
|
|||
|
|
|||
|
There are actually a number of other environment variables that control the
|
|||
|
loading process; their names begin with LD_ or RTLD_. Most of the others are
|
|||
|
for low-level debugging of the loader process or for implementing specialized
|
|||
|
capabilities. Most of them aren't well-documented; if you need to know about
|
|||
|
them, the best way to learn about them is to read the source code of the
|
|||
|
loader (part of gcc).
|
|||
|
|
|||
|
Permitting user control over dynamically linked libraries would be disastrous
|
|||
|
for setuid/setgid programs if special measures weren't taken. Therefore, in
|
|||
|
the GNU loader (which loads the rest of the program on program start-up), if
|
|||
|
the program is setuid or setgid these variables (and other similar variables)
|
|||
|
are ignored or greatly limited in what they can do. The loader determines if
|
|||
|
a program is setuid or setgid by checking the program's credentials; if the
|
|||
|
uid and euid differ, or the gid and the egid differ, the loader presumes the
|
|||
|
program is setuid/setgid (or descended from one) and therefore greatly limits
|
|||
|
its abilities to control linking. If you read the GNU glibc library source
|
|||
|
code, you can see this; see especially the files elf/rtld.c and sysdeps/
|
|||
|
generic/dl-sysdep.c. This means that if you cause the uid and gid to equal
|
|||
|
the euid and egid, and then call a program, these variables will have full
|
|||
|
effect. Other Unix-like systems handle the situation differently but for the
|
|||
|
same reason: a setuid/setgid program should not be unduly affected by the
|
|||
|
environment variables set.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
3.4. Creating a Shared Library
|
|||
|
|
|||
|
Creating a shared library is easy. First, create the object files that will
|
|||
|
go into the shared library using the gcc -fPIC or -fpic flag. The -fPIC and
|
|||
|
-fpic options enable ``position independent code'' generation, a requirement
|
|||
|
for shared libraries; see below for the differences. You pass the soname
|
|||
|
using the -Wl gcc option. The -Wl option passes options along to the linker
|
|||
|
(in this case the -soname linker option) - the commas after -Wl are not a
|
|||
|
typo, and you must not include unescaped whitespace in the option. Then
|
|||
|
create the shared library using this format:
|
|||
|
gcc -shared -Wl,-soname,your_soname \
|
|||
|
-o library_name file_list library_list
|
|||
|
|
|||
|
Here's an example, which creates two object files (a.o and b.o) and then
|
|||
|
creates a shared library that contains both of them. Note that this
|
|||
|
compilation includes debugging information (-g) and will generate warnings
|
|||
|
(-Wall), which aren't required for shared libraries but are recommended. The
|
|||
|
compilation generates object files (using -c), and includes the required
|
|||
|
-fPIC option:
|
|||
|
gcc -fPIC -g -c -Wall a.c
|
|||
|
gcc -fPIC -g -c -Wall b.c
|
|||
|
gcc -shared -Wl,-soname,libmystuff.so.1 \
|
|||
|
-o libmystuff.so.1.0.1 a.o b.o -lc
|
|||
|
|
|||
|
Here are a few points worth noting:
|
|||
|
|
|||
|
<EFBFBD><EFBFBD>*<2A>Don't strip the resulting library, and don't use the compiler option
|
|||
|
-fomit-frame-pointer unless you really have to. The resulting library
|
|||
|
will work, but these actions make debuggers mostly useless.
|
|||
|
|
|||
|
<EFBFBD><EFBFBD>*<2A>Use -fPIC or -fpic to generate code. Whether to use -fPIC or -fpic to
|
|||
|
generate code is target-dependent. The -fPIC choice always works, but may
|
|||
|
produce larger code than -fpic (mnenomic to remember this is that PIC is
|
|||
|
in a larger case, so it may produce larger amounts of code). Using -fpic
|
|||
|
option usually generates smaller and faster code, but will have
|
|||
|
platform-dependent limitations, such as the number of globally visible
|
|||
|
symbols or the size of the code. The linker will tell you whether it fits
|
|||
|
when you create the shared library. When in doubt, I choose -fPIC,
|
|||
|
because it always works.
|
|||
|
|
|||
|
<EFBFBD><EFBFBD>*<2A>In some cases, the call to gcc to create the object file will also need
|
|||
|
to include the option ``-Wl,-export-dynamic''. Normally, the dynamic
|
|||
|
symbol table contains only symbols which are used by a dynamic object.
|
|||
|
This option (when creating an ELF file) adds all symbols to the dynamic
|
|||
|
symbol table (see ld(1) for more information). You need to use this
|
|||
|
option when there are 'reverse dependencies', i.e., a DL library has
|
|||
|
unresolved symbols that by convention must be defined in the programs
|
|||
|
that intend to load these libraries. For ``reverse dependencies'' to
|
|||
|
work, the master program must make its symbols dynamically available.
|
|||
|
Note that you could say ``-rdynamic'' instead of ``-Wl,export-dynamic''
|
|||
|
if you only work with Linux systems, but according to the ELF
|
|||
|
documentation the ``-rdynamic'' flag doesn't always work for gcc on
|
|||
|
non-Linux systems.
|
|||
|
|
|||
|
|
|||
|
During development, there's the potential problem of modifying a library
|
|||
|
that's also used by many other programs -- and you don't want the other
|
|||
|
programs to use the ``developmental''library, only a particular application
|
|||
|
that you're testing against it. One link option you might use is ld's
|
|||
|
``rpath'' option, which specifies the runtime library search path of that
|
|||
|
particular program being compiled. From gcc, you can invoke the rpath option
|
|||
|
by specifying it this way:
|
|||
|
-Wl,-rpath,$(DEFAULT_LIB_INSTALL_PATH)
|
|||
|
If you use this option when building the library client program, you don't
|
|||
|
need to bother with LD_LIBRARY_PATH (described next) other than to ensure
|
|||
|
it's not conflicting, or using other techniques to hide the library.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
3.5. Installing and Using a Shared Library
|
|||
|
|
|||
|
Once you've created a shared library, you'll want to install it. The simple
|
|||
|
approach is simply to copy the library into one of the standard directories
|
|||
|
(e.g., /usr/lib) and run ldconfig(8).
|
|||
|
|
|||
|
First, you'll need to create the shared libraries somewhere. Then, you'll
|
|||
|
need to set up the necessary symbolic links, in particular a link from a
|
|||
|
soname to the real name (as well as from a versionless soname, that is, a
|
|||
|
soname that ends in ``.so'' for users who don't specify a version at all).
|
|||
|
The simplest approach is to run:
|
|||
|
ldconfig -n directory_with_shared_libraries
|
|||
|
|
|||
|
Finally, when you compile your programs, you'll need to tell the linker about
|
|||
|
any static and shared libraries that you're using. Use the -l and -L options
|
|||
|
for this.
|
|||
|
|
|||
|
If you can't or don't want to install a library in a standard place (e.g.,
|
|||
|
you don't have the right to modify /usr/lib), then you'll need to change your
|
|||
|
approach. In that case, you'll need to install it somewhere, and then give
|
|||
|
your program enough information so the program can find the library... and
|
|||
|
there are several ways to do that. You can use gcc's -L flag in simple cases.
|
|||
|
You can use the ``rpath'' approach (described above), particularly if you
|
|||
|
only have a specific program to use the library being placed in a
|
|||
|
``non-standard'' place. You can also use environment variables to control
|
|||
|
things. In particular, you can set LD_LIBRARY_PATH, which is a
|
|||
|
colon-separated list of directories in which to search for shared libraries
|
|||
|
before the usual places. If you're using bash, you could invoke my_program
|
|||
|
this way using:
|
|||
|
LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH my_program
|
|||
|
|
|||
|
If you want to override just a few selected functions, you can do this by
|
|||
|
creating an overriding object file and setting LD_PRELOAD; the functions in
|
|||
|
this object file will override just those functions (leaving others as they
|
|||
|
were).
|
|||
|
|
|||
|
Usually you can update libraries without concern; if there was an API change,
|
|||
|
the library creator is supposed to change the soname. That way, multiple
|
|||
|
libraries can be on a single system, and the right one is selected for each
|
|||
|
program. However, if a program breaks on an update to a library that kept the
|
|||
|
same soname, you can force it to use the older library version by copying the
|
|||
|
old library back somewhere, renaming the program (say to the old name plus
|
|||
|
``.orig''), and then create a small ``wrapper'' script that resets the
|
|||
|
library to use and calls the real (renamed) program. You could place the old
|
|||
|
library in its own special area, if you like, though the numbering
|
|||
|
conventions do permit multiple versions to live in the same directory. The
|
|||
|
wrapper script could look something like this:
|
|||
|
#!/bin/sh
|
|||
|
export LD_LIBRARY_PATH=/usr/local/my_lib:$LD_LIBRARY_PATH
|
|||
|
exec /usr/bin/my_program.orig $*
|
|||
|
Please don't depend on this when you write your own programs; try to make
|
|||
|
sure that your libraries are either backwards-compatible or that you've
|
|||
|
incremented the version number in the soname every time you make an
|
|||
|
incompatible change. This is just an ``emergency'' approach to deal with
|
|||
|
worst-case problems.
|
|||
|
|
|||
|
You can see the list of the shared libraries used by a program using ldd(1).
|
|||
|
So, for example, you can see the shared libraries used by ls by typing:
|
|||
|
ldd /bin/ls
|
|||
|
Generally you'll see a list of the sonames being depended on, along with the
|
|||
|
directory that those names resolve to. In practically all cases you'll have
|
|||
|
at least two dependencies:
|
|||
|
|
|||
|
<EFBFBD><EFBFBD>*<2A>/lib/ld-linux.so.N (where N is 1 or more, usually at least 2). This is
|
|||
|
the library that loads all other libraries.
|
|||
|
|
|||
|
<EFBFBD><EFBFBD>*<2A>libc.so.N (where N is 6 or more). This is the C library. Even other
|
|||
|
languages tend to use the C library (at least to implement their own
|
|||
|
libraries), so most programs at least include this one.
|
|||
|
|
|||
|
|
|||
|
Beware: do not run ldd on a program you don't trust. As is clearly stated in
|
|||
|
the ldd(1) manual, ldd works by (in certain cases) by setting a special
|
|||
|
environment variable (for ELF objects, LD_TRACE_LOADED_OBJECTS) and then
|
|||
|
executing the program. It may be possible for an untrusted program to force
|
|||
|
the ldd user to run arbitrary code (instead of simply showing the ldd
|
|||
|
information). So, for safety's sake, don't use ldd on programs you don't
|
|||
|
trust to execute.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
3.6. Incompatible Libraries
|
|||
|
|
|||
|
When a new version of a library is binary-incompatible with the old one the
|
|||
|
soname needs to change. In C, there are four basic reasons that a library
|
|||
|
would cease to be binary compatible:
|
|||
|
|
|||
|
1. The behavior of a function changes so that it no longer meets its
|
|||
|
original specification,
|
|||
|
|
|||
|
2. Exported data items change (exception: adding optional items to the ends
|
|||
|
of structures is okay, as long as those structures are only allocated
|
|||
|
within the library).
|
|||
|
|
|||
|
3. An exported function is removed.
|
|||
|
|
|||
|
4. The interface of an exported function changes.
|
|||
|
|
|||
|
|
|||
|
If you can avoid these reasons, you can keep your libraries
|
|||
|
binary-compatible. Said another way, you can keep your Application Binary
|
|||
|
Interface (ABI) compatible if you avoid such changes. For example, you might
|
|||
|
want to add new functions but not delete the old ones. You can add items to
|
|||
|
structures but only if you can make sure that old programs won't be sensitive
|
|||
|
to such changes by adding items only to the end of the structure, only
|
|||
|
allowing the library (and not the application) to allocate the structure,
|
|||
|
making the extra items optional (or having the library fill them in), and so
|
|||
|
on. Watch out - you probably can't expand structures if users are using them
|
|||
|
in arrays.
|
|||
|
|
|||
|
For C++ (and other languages supporting compiled-in templates and/or compiled
|
|||
|
dispatched methods), the situation is trickier. All of the above issues
|
|||
|
apply, plus many more issues. The reason is that some information is
|
|||
|
implemented ``under the covers'' in the compiled code, resulting in
|
|||
|
dependencies that may not be obvious if you don't know how C++ is typically
|
|||
|
implemented. Strictly speaking, they aren't ``new'' issues, it's just that
|
|||
|
compiled C++ code invokes them in ways that may be surprising to you. The
|
|||
|
following is a (probably incomplete) list of things that you cannot do in C++
|
|||
|
and retain binary compatibility, as reported by [http://www.trolltech.com/
|
|||
|
developer/faq/tech.html#bincomp] Troll Tech's Technical FAQ:
|
|||
|
|
|||
|
1. add reimplementations of virtual functions (unless it it safe for older
|
|||
|
binaries to call the original implementation), because the compiler
|
|||
|
evaluates SuperClass::virtualFunction() calls at compile-time (not
|
|||
|
link-time).
|
|||
|
|
|||
|
2. add or remove virtual member functions, because this would change the
|
|||
|
size and layout of the vtbl of every subclass.
|
|||
|
|
|||
|
3. change the type of any data members or move any data members that can be
|
|||
|
accessed via inline member functions.
|
|||
|
|
|||
|
4. change the class hierarchy, except to add new leaves.
|
|||
|
|
|||
|
5. add or remove private data members, because this would change the size
|
|||
|
and layout of every subclass.
|
|||
|
|
|||
|
6. remove public or protected member functions unless they are inline.
|
|||
|
|
|||
|
7. make a public or protected member function inline.
|
|||
|
|
|||
|
8. change what an inline function does, unless the old version continues
|
|||
|
working.
|
|||
|
|
|||
|
9. change the access rights (i.e. public, protected or private) of a member
|
|||
|
function in a portable program, because some compilers mangle the access
|
|||
|
rights into the function name.
|
|||
|
|
|||
|
|
|||
|
Given this lengthy list, developers of C++ libraries in particular must plan
|
|||
|
for more than occasional updates that break binary compatibility.
|
|||
|
Fortunately, on Unix-like systems (including Linux) you can have multiple
|
|||
|
versions of a library loaded at the same time, so while there is some disk
|
|||
|
space loss, users can still run ``old'' programs needing old libraries.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
4. Dynamically Loaded (DL) Libraries
|
|||
|
|
|||
|
Dynamically loaded (DL) libraries are libraries that are loaded at times
|
|||
|
other than during the startup of a program. They're particularly useful for
|
|||
|
implementing plugins or modules, because they permit waiting to load the
|
|||
|
plugin until it's needed. For example, the Pluggable Authentication Modules
|
|||
|
(PAM) system uses DL libraries to permit administrators to configure and
|
|||
|
reconfigure authentication. They're also useful for implementing interpreters
|
|||
|
that wish to occasionally compile their code into machine code and use the
|
|||
|
compiled version for efficiency purposes, all without stopping. For example,
|
|||
|
this approach can be useful in implementing a just-in-time compiler or
|
|||
|
multi-user dungeon (MUD).
|
|||
|
|
|||
|
In Linux, DL libraries aren't actually special from the point-of-view of
|
|||
|
their format; they are built as standard object files or standard shared
|
|||
|
libraries as discussed above. The main difference is that the libraries
|
|||
|
aren't automatically loaded at program link time or start-up; instead, there
|
|||
|
is an API for opening a library, looking up symbols, handling errors, and
|
|||
|
closing the library. C users will need to include the header file <dlfcn.h>
|
|||
|
to use this API.
|
|||
|
|
|||
|
The interface used by Linux is essentially the same as that used in Solaris,
|
|||
|
which I'll call the ``dlopen()'' API. However, this same interface is not
|
|||
|
supported by all platforms; HP-UX uses the different shl_load() mechanism,
|
|||
|
and Windows platforms use DLLs with a completely different interface. If your
|
|||
|
goal is wide portability, you probably ought to consider using some wrapping
|
|||
|
library that hides differences between platforms. One approach is the glib
|
|||
|
library with its support for Dynamic Loading of Modules; it uses the
|
|||
|
underlying dynamic loading routines of the platform to implement a portable
|
|||
|
interface to these functions. You can learn more about glib at [http://
|
|||
|
developer.gnome.org/doc/API/glib/glib-dynamic-loading-of-modules.html] http:/
|
|||
|
/developer.gnome.org/doc/API/glib/glib-dynamic-loading-of-modules.html. Since
|
|||
|
the glib interface is well-explained in its documentation, I won't discuss it
|
|||
|
further here. Another approach is to use libltdl, which is part of [http://
|
|||
|
www.gnu.org/software/libtool/libtool.html] GNU libtool. If you want much more
|
|||
|
functionality than this, you might want to look into a CORBA Object Request
|
|||
|
Broker (ORB). If you're still interested in directly using the interface
|
|||
|
supported by Linux and Solaris, read on.
|
|||
|
|
|||
|
Developers using C++ and dynamically loaded (DL) libraries should also
|
|||
|
consult the ``C++ dlopen mini-HOWTO''.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
4.1. dlopen()
|
|||
|
|
|||
|
The dlopen(3) function opens a library and prepares it for use. In C its
|
|||
|
prototype is:
|
|||
|
void * dlopen(const char *filename, int flag);
|
|||
|
If filename begins with ``/'' (i.e., it's an absolute path), dlopen() will
|
|||
|
just try to use it (it won't search for a library). Otherwise, dlopen() will
|
|||
|
search for the library in the following order:
|
|||
|
|
|||
|
1. A colon-separated list of directories in the user's LD_LIBRARY_PATH
|
|||
|
environment variable.
|
|||
|
|
|||
|
2. The list of libraries specified in /etc/ld.so.cache (which is generated
|
|||
|
from /etc/ld.so.conf).
|
|||
|
|
|||
|
3. /lib, followed by /usr/lib. Note the order here; this is the reverse of
|
|||
|
the order used by the old a.out loader. The old a.out loader, when
|
|||
|
loading a program, first searched /usr/lib, then /lib (see the man page
|
|||
|
ld.so(8)). This shouldn't normally matter, since a library should only be
|
|||
|
in one or the other directory (never both), and different libraries with
|
|||
|
the same name are a disaster waiting to happen.
|
|||
|
|
|||
|
|
|||
|
In dlopen(), the value of flag must be either RTLD_LAZY, meaning ``resolve
|
|||
|
undefined symbols as code from the dynamic library is executed'', or
|
|||
|
RTLD_NOW, meaning ``resolve all undefined symbols before dlopen() returns and
|
|||
|
fail if this cannot be done''. RTLD_GLOBAL may be optionally or'ed with
|
|||
|
either value in flag, meaning that the external symbols defined in the
|
|||
|
library will be made available to subsequently loaded libraries. While you're
|
|||
|
debugging, you'll probably want to use RTLD_NOW; using RTLD_LAZY can create
|
|||
|
inscrutable errors if there are unresolved references. Using RTLD_NOW makes
|
|||
|
opening the library take slightly longer (but it speeds up lookups later); if
|
|||
|
this causes a user interface problem you can switch to RTLD_LAZY later.
|
|||
|
|
|||
|
If the libraries depend on each other (e.g., X depends on Y), then you need
|
|||
|
to load the dependees first (in this example, load Y first, and then X).
|
|||
|
|
|||
|
The return value of dlopen() is a ``handle'' that should be considered an
|
|||
|
opaque value to be used by the other DL library routines. dlopen() will
|
|||
|
return NULL if the attempt to load does not succeed, and you need to check
|
|||
|
for this. If the same library is loaded more than once with dlopen(), the
|
|||
|
same file handle is returned.
|
|||
|
|
|||
|
In older systems, if the library exports a routine named _init, then that
|
|||
|
code is executed before dlopen() returns. You can use this fact in your own
|
|||
|
libraries to implement initialization routines. However, libraries should not
|
|||
|
export routines named _init or _fini. Those mechanisms are obsolete, and may
|
|||
|
result in undesired behavior. Instead, libraries should export routines using
|
|||
|
the __attribute__((constructor)) and __attribute__((destructor)) function
|
|||
|
attributes (presuming you're using gcc). See Section 5.2 for more
|
|||
|
information.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
4.2. dlerror()
|
|||
|
|
|||
|
Errors can be reported by calling dlerror(), which returns a string
|
|||
|
describing the error from the last call to dlopen(), dlsym(), or dlclose().
|
|||
|
One oddity is that after calling dlerror(), future calls to dlerror() will
|
|||
|
return NULL until another error has been encountered.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
4.3. dlsym()
|
|||
|
|
|||
|
There's no point in loading a DL library if you can't use it. The main
|
|||
|
routine for using a DL library is dlsym(3), which looks up the value of a
|
|||
|
symbol in a given (opened) library. This function is defined as:
|
|||
|
void * dlsym(void *handle, char *symbol);
|
|||
|
the handle is the value returned from dlopen, and symbol is a NIL-terminated
|
|||
|
string. If you can avoid it, don't store the result of dlsym() into a void*
|
|||
|
pointer, because then you'll have to cast it each time you use it (and you'll
|
|||
|
give less information to other people trying to maintain the program).
|
|||
|
|
|||
|
dlsym() will return a NULL result if the symbol wasn't found. If you know
|
|||
|
that the symbol could never have the value of NULL or zero, that may be fine,
|
|||
|
but there's a potential ambiguity otherwise: if you got a NULL, does that
|
|||
|
mean there is no such symbol, or that NULL is the value of the symbol? The
|
|||
|
standard solution is to call dlerror() first (to clear any error condition
|
|||
|
that may have existed), then call dlsym() to request a symbol, then call
|
|||
|
dlerror() again to see if an error occurred. A code snippet would look like
|
|||
|
this:
|
|||
|
dlerror(); /* clear error code */
|
|||
|
s = (actual_type) dlsym(handle, symbol_being_searched_for);
|
|||
|
if ((err = dlerror()) != NULL) {
|
|||
|
/* handle error, the symbol wasn't found */
|
|||
|
} else {
|
|||
|
/* symbol found, its value is in s */
|
|||
|
}
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
4.4. dlclose()
|
|||
|
|
|||
|
The converse of dlopen() is dlclose(), which closes a DL library. The dl
|
|||
|
library maintains link counts for dynamic file handles, so a dynamic library
|
|||
|
is not actually deallocated until dlclose has been called on it as many times
|
|||
|
as dlopen has succeeded on it. Thus, it's not a problem for the same program
|
|||
|
to load the same library multiple times. If a library is deallocated, its
|
|||
|
function _fini is called (if it exists) in older libraries, but _fini is an
|
|||
|
obsolete mechanism and shouldn't be relied on. Instead, libraries should
|
|||
|
export routines using the __attribute__((constructor)) and __attribute__
|
|||
|
((destructor)) function attributes. See Section 5.2 for more information.
|
|||
|
Note: dlclose() returns 0 on success, and non-zero on error; some Linux
|
|||
|
manual pages don't mention this.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
4.5. DL Library Example
|
|||
|
|
|||
|
Here's an example from the man page of dlopen(3). This example loads the math
|
|||
|
library and prints the cosine of 2.0, and it checks for errors at every step
|
|||
|
(recommended):
|
|||
|
#include <stdlib.h>
|
|||
|
#include <stdio.h>
|
|||
|
#include <dlfcn.h>
|
|||
|
|
|||
|
int main(int argc, char **argv) {
|
|||
|
void *handle;
|
|||
|
double (*cosine)(double);
|
|||
|
char *error;
|
|||
|
|
|||
|
handle = dlopen ("/lib/libm.so.6", RTLD_LAZY);
|
|||
|
if (!handle) {
|
|||
|
fputs (dlerror(), stderr);
|
|||
|
exit(1);
|
|||
|
}
|
|||
|
|
|||
|
cosine = dlsym(handle, "cos");
|
|||
|
if ((error = dlerror()) != NULL) {
|
|||
|
fputs(error, stderr);
|
|||
|
exit(1);
|
|||
|
}
|
|||
|
|
|||
|
printf ("%f\n", (*cosine)(2.0));
|
|||
|
dlclose(handle);
|
|||
|
}
|
|||
|
|
|||
|
If this program were in a file named "foo.c", you would build the program
|
|||
|
with the following command:
|
|||
|
gcc -o foo foo.c -ldl
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
5. Miscellaneous
|
|||
|
|
|||
|
5.1. nm command
|
|||
|
|
|||
|
The nm(1) command can report the list of symbols in a given library. It works
|
|||
|
on both static and shared libraries. For a given library nm(1) can list the
|
|||
|
symbol names defined, each symbol's value, and the symbol's type. It can also
|
|||
|
identify where the symbol was defined in the source code (by filename and
|
|||
|
line number), if that information is available in the library (see the -l
|
|||
|
option).
|
|||
|
|
|||
|
The symbol type requires a little more explanation. The type is displayed as
|
|||
|
a letter; lowercase means that the symbol is local, while uppercase means
|
|||
|
that the symbol is global (external). Typical symbol types include T (a
|
|||
|
normal definition in the code section), D (initialized data section), B
|
|||
|
(uninitialized data section), U (undefined; the symbol is used by the library
|
|||
|
but not defined by the library), and W (weak; if another library also defines
|
|||
|
this symbol, that definition overrides this one).
|
|||
|
|
|||
|
If you know the name of a function, but you truly can't remember what library
|
|||
|
it was defined in, you can use nm's ``-o'' option (which prefixes the
|
|||
|
filename in each line) along with grep to find the library name. From a
|
|||
|
Bourne shell, you can search all the libraries in /lib, /usr/lib, direct
|
|||
|
subdirectories of /usr/lib, and /usr/local/lib for ``cos'' as follows:
|
|||
|
nm -o /lib/* /usr/lib/* /usr/lib/*/* \
|
|||
|
/usr/local/lib/* 2> /dev/null | grep 'cos$'
|
|||
|
|
|||
|
Much more information about nm can be found in the nm ``info'' documentation
|
|||
|
locally installed at [info:binutils#nm] info:binutils#nm.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
5.2. Library constructor and destructor functions
|
|||
|
|
|||
|
Libraries should export initialization and cleanup routines using the gcc
|
|||
|
__attribute__((constructor)) and __attribute__((destructor)) function
|
|||
|
attributes. See the gcc info pages for information on these. Constructor
|
|||
|
routines are executed before dlopen returns (or before main() is started if
|
|||
|
the library is loaded at load time). Destructor routines are executed before
|
|||
|
dlclose returns (or after exit() or completion of main() if the library is
|
|||
|
loaded at load time). The C prototypes for these functions are:
|
|||
|
void __attribute__ ((constructor)) my_init(void);
|
|||
|
void __attribute__ ((destructor)) my_fini(void);
|
|||
|
|
|||
|
Shared libraries must not be compiled with the gcc arguments
|
|||
|
``-nostartfiles'' or ``-nostdlib''. If those arguments are used, the
|
|||
|
constructor/destructor routines will not be executed (unless special measures
|
|||
|
are taken).
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
5.2.1. Special functions _init and _fini (OBSOLETE/DANGEROUS)
|
|||
|
|
|||
|
Historically there have been two special functions, _init and _fini that can
|
|||
|
be used to control constructors and destructors. However, they are obsolete,
|
|||
|
and their use can lead to unpredicatable results. Your libraries should not
|
|||
|
use these; use the function attributes constructor and destructor above
|
|||
|
instead.
|
|||
|
|
|||
|
If you must work with old systems or code that used _init or _fini, here's
|
|||
|
how they worked. Two special functions were defined for initializing and
|
|||
|
finalizing a module: _init and _fini. If a function ``_init'' is exported in
|
|||
|
a library, then it is called when the library is first opened (via dlopen()
|
|||
|
or simply as a shared library). In a C program, this just means that you
|
|||
|
defined some function named _init. There is a corresponding function called
|
|||
|
_fini, which is called whenever a client finishes using the library (via a
|
|||
|
call dlclose() that brings its reference count to zero, or on normal exit of
|
|||
|
the program). The C prototypes for these functions are:
|
|||
|
void _init(void);
|
|||
|
void _fini(void);
|
|||
|
|
|||
|
In this case, when compiling the file into a ``.o'' file in gcc, be sure to
|
|||
|
add the gcc option ``-nostartfiles''. This keeps the C compiler from linking
|
|||
|
the system startup libraries against the .so file. Otherwise, you'll get a
|
|||
|
``multiple-definition'' error. Note that this is completely different than
|
|||
|
compiling modules using the recommended function attributes. My thanks to Jim
|
|||
|
Mischel and Tim Gentry for their suggestion to add this discussion of _init
|
|||
|
and _fini, as well as help in creating it.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
5.3. Shared Libraries Can Be Scripts
|
|||
|
|
|||
|
It's worth noting that the GNU loader permits shared libraries to be text
|
|||
|
files using a specialized scripting language instead of the usual library
|
|||
|
format. This is useful for indirectly combining other libraries. For example,
|
|||
|
here's the listing of /usr/lib/libc.so on one of my systems:
|
|||
|
/* GNU ld script
|
|||
|
Use the shared library, but some functions are only in
|
|||
|
the static library, so try that secondarily. */
|
|||
|
GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a )
|
|||
|
|
|||
|
For more information about this, see the texinfo documentation on ld linker
|
|||
|
scripts (ld command language). General information is at info:ld#Options and
|
|||
|
info:ld#Commands, with likely commands discussed in info:ld#Option Commands.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
5.4. Symbol Versioning and Version Scripts
|
|||
|
|
|||
|
Typically references to external functions are bound on an as-needed basis,
|
|||
|
and are not all bound when the application starts up. If a shared library is
|
|||
|
out of date, a required interface may be missing; when the application tries
|
|||
|
to use that interface, it may suddenly and unexpectedly fail.
|
|||
|
|
|||
|
A solution to this problem are symbol versioning coupled with version
|
|||
|
scripts. With symbol versioning, the user can get a warning when they start
|
|||
|
their program if the libraries being used with the application are too old.
|
|||
|
You can learn more about this from ld manual's descussion of version scripts
|
|||
|
at [http://www.gnu.org/manual/ld-2.9.1/html_node/ld_25.html] http://
|
|||
|
www.gnu.org/manual/ld-2.9.1/html_node/ld_25.html.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
5.5. GNU libtool
|
|||
|
|
|||
|
If you're building an application that should port to many systems, you might
|
|||
|
consider using [http://www.gnu.org/software/libtool/libtool.html] GNU libtool
|
|||
|
to build and install libraries. GNU libtool is a generic library support
|
|||
|
script. Libtool hides the complexity of using shared libraries behind a
|
|||
|
consistent, portable interface. Libtool provides portable interfaces to
|
|||
|
create object files, link libraries (static and shared), link executables,
|
|||
|
debug executables, install libraries, install executables. It also includes
|
|||
|
libltdl, a portability wrapper for dynamically loading programs. For more
|
|||
|
information, see its documentation at [http://www.gnu.org/software/libtool/
|
|||
|
manual.html] http://www.gnu.org/software/libtool/manual.html
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
5.6. Removing symbols for space
|
|||
|
|
|||
|
All the symbols included in generated files are useful for debugging, but
|
|||
|
take up space. If you need space, you can eliminate some of it.
|
|||
|
|
|||
|
The best approach is to first generate the object files normally, and do all
|
|||
|
your debugging and testing first (debugging and testing is much easier with
|
|||
|
them). Afterwards, once you've tested the program thoroughly, use strip(1) to
|
|||
|
remove the symbols. The strip(1) command gives you a good deal of control
|
|||
|
over what symbols to eliminate; see its documentation for details.
|
|||
|
|
|||
|
Another approach is to use the GNU ld options ``-S'' and ``-s''; ``-S'' omits
|
|||
|
debugger symbol information (but not all symbols) from the output file, while
|
|||
|
``-s'' omits all symbol information from the output file. You can invoke
|
|||
|
these options through gcc as ``-Wl,-S'' and ``-Wl,-s''. If you always strip
|
|||
|
the symbols and these options are sufficient, feel free, but this is a less
|
|||
|
flexible approach.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
5.7. Extremely small executables
|
|||
|
|
|||
|
You might find the paper [http://www.muppetlabs.com/~breadbox/software/tiny/
|
|||
|
teensy.html] Whirlwind Tutorial on Creating Really Teensy ELF Executables for
|
|||
|
Linux useful. It describes how to make a truly tiny program executable.
|
|||
|
Frankly, you shouldn't use most of these tricks under normal circumstances,
|
|||
|
but they're quite instructive in showing how ELF really works.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
5.8. C++ vs. C
|
|||
|
|
|||
|
It's worth noting that if you're writing a C++ program, and you're calling a
|
|||
|
C library function, in your C++ code you'll need to define the C function as
|
|||
|
extern "C". Otherwise, the linker won't be able to locate the C function.
|
|||
|
Internally, C++ compilers ``mangle'' the names of C++ functions (e.g., for
|
|||
|
typing purposes), and they need to be told that a given function should be
|
|||
|
called as a C function (and thus, not have its name mangled).
|
|||
|
|
|||
|
If you're writing a program library that could be called from C or C++, it's
|
|||
|
recommended that you include 'extern "C"' commands right in your header files
|
|||
|
so that you do this automatically for your users. When combined with the
|
|||
|
usual #ifndef at the top of a file to skip re-executing header files, this
|
|||
|
means that a typical header file usable by either C or C++ for some header
|
|||
|
file foobar.h would look like this:
|
|||
|
/* Explain here what foobar does */
|
|||
|
|
|||
|
#ifndef FOOBAR_H
|
|||
|
#define FOOBAR_H
|
|||
|
|
|||
|
#ifdef __cplusplus
|
|||
|
extern "C" {
|
|||
|
#endif
|
|||
|
|
|||
|
... header code for foobar goes here ...
|
|||
|
|
|||
|
#ifdef __cplusplus
|
|||
|
}
|
|||
|
#endif
|
|||
|
#endif
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
5.9. Speeding up C++ initialization
|
|||
|
|
|||
|
The KDE developers have noticed that large GUI C++ applications can take a
|
|||
|
long time to start up, in part due to its needing to do many relocations.
|
|||
|
There are several solutions to this. See Making C++ ready for the desktop (by
|
|||
|
Waldo Bastian) for more information.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
5.10. Linux Standard Base (LSB)
|
|||
|
|
|||
|
The goal of the Linux Standard Base (LSB) project is to develop and promote a
|
|||
|
set of standards that will increase compatibility among Linux distributions
|
|||
|
and enable software applications to run on any compliant Linux system. The
|
|||
|
project's home page is at [http://www.linuxbase.org] http://
|
|||
|
www.linuxbase.org.
|
|||
|
|
|||
|
A nice article that summarizes how to develop LSB-compliant applications was
|
|||
|
published in October 2002, Developing LSB-certified applications: Five steps
|
|||
|
to binary-compatible Linux applications by George Kraft IV (Senior software
|
|||
|
engineer, IBM's Linux Technology Center). Of course, you need to write code
|
|||
|
that only accesses the standardized portability layer if you want your code
|
|||
|
to be portable. In addition, the LSB provides some tools so that application
|
|||
|
writers of C/C++ programs can check for LSB compliance; these tools use some
|
|||
|
capabilities of the linker and special libraries to do these checks.
|
|||
|
Obviously, you'll need to install the tools to do these checks; you can get
|
|||
|
them from the LSB website. Then, simply use the "lsbcc" compiler as your C/
|
|||
|
C++ compiler (lsbcc internally creates a linking environment that will
|
|||
|
complain if certain LSB rules aren't followed):
|
|||
|
$ CC=lsbcc make myapplication
|
|||
|
(or)
|
|||
|
$ CC=lsbcc ./configure; make myapplication
|
|||
|
You can then use the lsbappchk program to ensure that the program only uses
|
|||
|
functions standardized by the LSB:
|
|||
|
$ lsbappchk myapplication
|
|||
|
You also need to follow the LSB packaging guidelines (e.g., use RPM v3, use
|
|||
|
LSB-conforming package names, and for add-on software must install in /opt by
|
|||
|
default). See the article and LSB website for more information.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
6. More Examples
|
|||
|
|
|||
|
The following are more examples of all three approaches (static, shared, and
|
|||
|
dynamically loaded libraries). File libhello.c is a trivial library, with
|
|||
|
libhello.h as its header. File demo_use.c is a trivial caller of the library.
|
|||
|
This is followed by commented scripts (script_static and script_dynamic)
|
|||
|
showing how to use the library as a static and shared library. This is
|
|||
|
followed by demo_dynamic.c and script_dynamic, which show how to use the
|
|||
|
shared library as a dynamically loaded library.
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
6.1. File libhello.c
|
|||
|
|
|||
|
/* libhello.c - demonstrate library use. */
|
|||
|
|
|||
|
#include <stdio.h>
|
|||
|
|
|||
|
void hello(void) {
|
|||
|
printf("Hello, library world.\n");
|
|||
|
}
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
6.2. File libhello.h
|
|||
|
|
|||
|
/* libhello.h - demonstrate library use. */
|
|||
|
|
|||
|
|
|||
|
void hello(void);
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
6.3. File demo_use.c
|
|||
|
|
|||
|
/* demo_use.c -- demonstrate direct use of the "hello" routine */
|
|||
|
|
|||
|
#include "libhello.h"
|
|||
|
|
|||
|
int main(void) {
|
|||
|
hello();
|
|||
|
return 0;
|
|||
|
}
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
6.4. File script_static
|
|||
|
|
|||
|
#!/bin/sh
|
|||
|
# Static library demo
|
|||
|
|
|||
|
# Create static library's object file, libhello-static.o.
|
|||
|
# I'm using the name libhello-static to clearly
|
|||
|
# differentiate the static library from the
|
|||
|
# dynamic library examples, but you don't need to use
|
|||
|
# "-static" in the names of your
|
|||
|
# object files or static libraries.
|
|||
|
|
|||
|
gcc -Wall -g -c -o libhello-static.o libhello.c
|
|||
|
|
|||
|
# Create static library.
|
|||
|
|
|||
|
ar rcs libhello-static.a libhello-static.o
|
|||
|
|
|||
|
# At this point we could just copy libhello-static.a
|
|||
|
# somewhere else to use it.
|
|||
|
# For demo purposes, we'll just keep the library
|
|||
|
# in the current directory.
|
|||
|
|
|||
|
# Compile demo_use program file.
|
|||
|
|
|||
|
gcc -Wall -g -c demo_use.c -o demo_use.o
|
|||
|
|
|||
|
# Create demo_use program; -L. causes "." to be searched during
|
|||
|
# creation of the program. Note that this command causes
|
|||
|
# the relevant object file in libhello-static.a to be
|
|||
|
# incorporated into file demo_use_static.
|
|||
|
|
|||
|
gcc -g -o demo_use_static demo_use.o -L. -lhello-static
|
|||
|
|
|||
|
# Execute the program.
|
|||
|
|
|||
|
./demo_use_static
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
6.5. File script_shared
|
|||
|
|
|||
|
#!/bin/sh
|
|||
|
# Shared library demo
|
|||
|
|
|||
|
# Create shared library's object file, libhello.o.
|
|||
|
|
|||
|
gcc -fPIC -Wall -g -c libhello.c
|
|||
|
|
|||
|
# Create shared library.
|
|||
|
# Use -lc to link it against C library, since libhello
|
|||
|
# depends on the C library.
|
|||
|
|
|||
|
gcc -g -shared -Wl,-soname,libhello.so.0 \
|
|||
|
-o libhello.so.0.0 libhello.o -lc
|
|||
|
|
|||
|
# At this point we could just copy libhello.so.0.0 into
|
|||
|
# some directory, say /usr/local/lib.
|
|||
|
|
|||
|
# Now we need to call ldconfig to fix up the symbolic links.
|
|||
|
|
|||
|
# Set up the soname. We could just execute:
|
|||
|
# ln -sf libhello.so.0.0 libhello.so.0
|
|||
|
# but let's let ldconfig figure it out.
|
|||
|
|
|||
|
/sbin/ldconfig -n .
|
|||
|
|
|||
|
# Set up the linker name.
|
|||
|
# In a more sophisticated setting, we'd need to make
|
|||
|
# sure that if there was an existing linker name,
|
|||
|
# and if so, check if it should stay or not.
|
|||
|
|
|||
|
ln -sf libhello.so.0 libhello.so
|
|||
|
|
|||
|
# Compile demo_use program file.
|
|||
|
|
|||
|
gcc -Wall -g -c demo_use.c -o demo_use.o
|
|||
|
|
|||
|
# Create program demo_use.
|
|||
|
# The -L. causes "." to be searched during creation
|
|||
|
# of the program; note that this does NOT mean that "."
|
|||
|
# will be searched when the program is executed.
|
|||
|
|
|||
|
gcc -g -o demo_use demo_use.o -L. -lhello
|
|||
|
|
|||
|
# Execute the program. Note that we need to tell the program
|
|||
|
# where the shared library is, using LD_LIBRARY_PATH.
|
|||
|
|
|||
|
LD_LIBRARY_PATH="." ./demo_use
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
6.6. File demo_dynamic.c
|
|||
|
|
|||
|
/* demo_dynamic.c -- demonstrate dynamic loading and
|
|||
|
use of the "hello" routine */
|
|||
|
|
|||
|
|
|||
|
/* Need dlfcn.h for the routines to
|
|||
|
dynamically load libraries */
|
|||
|
#include <dlfcn.h>
|
|||
|
|
|||
|
#include <stdlib.h>
|
|||
|
#include <stdio.h>
|
|||
|
|
|||
|
/* Note that we don't have to include "libhello.h".
|
|||
|
However, we do need to specify something related;
|
|||
|
we need to specify a type that will hold the value
|
|||
|
we're going to get from dlsym(). */
|
|||
|
|
|||
|
/* The type "simple_demo_function" describes a function that
|
|||
|
takes no arguments, and returns no value: */
|
|||
|
|
|||
|
typedef void (*simple_demo_function)(void);
|
|||
|
|
|||
|
|
|||
|
int main(void) {
|
|||
|
const char *error;
|
|||
|
void *module;
|
|||
|
simple_demo_function demo_function;
|
|||
|
|
|||
|
/* Load dynamically loaded library */
|
|||
|
module = dlopen("libhello.so", RTLD_LAZY);
|
|||
|
if (!module) {
|
|||
|
fprintf(stderr, "Couldn't open libhello.so: %s\n",
|
|||
|
dlerror());
|
|||
|
exit(1);
|
|||
|
}
|
|||
|
|
|||
|
/* Get symbol */
|
|||
|
dlerror();
|
|||
|
demo_function = dlsym(module, "hello");
|
|||
|
if ((error = dlerror())) {
|
|||
|
fprintf(stderr, "Couldn't find hello: %s\n", error);
|
|||
|
exit(1);
|
|||
|
}
|
|||
|
|
|||
|
/* Now call the function in the DL library */
|
|||
|
(*demo_function)();
|
|||
|
|
|||
|
/* All done, close things cleanly */
|
|||
|
dlclose(module);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
6.7. File script_dynamic
|
|||
|
|
|||
|
#!/bin/sh
|
|||
|
# Dynamically loaded library demo
|
|||
|
|
|||
|
# Presume that libhello.so and friends have
|
|||
|
# been created (see dynamic example).
|
|||
|
|
|||
|
# Compile demo_dynamic program file into an object file.
|
|||
|
|
|||
|
gcc -Wall -g -c demo_dynamic.c
|
|||
|
|
|||
|
# Create program demo_use.
|
|||
|
# Note that we don't have to tell it where to search for DL libraries,
|
|||
|
# since the only special library this program uses won't be
|
|||
|
# loaded until after the program starts up.
|
|||
|
# However, we DO need the option -ldl to include the library
|
|||
|
# that loads the DL libraries.
|
|||
|
|
|||
|
gcc -g -o demo_dynamic demo_dynamic.o -ldl
|
|||
|
|
|||
|
# Execute the program. Note that we need to tell the
|
|||
|
# program where get the dynamically loaded library,
|
|||
|
# using LD_LIBRARY_PATH.
|
|||
|
|
|||
|
LD_LIBRARY_PATH="." ./demo_dynamic
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
|
|||
|
7. Other Information Sources
|
|||
|
|
|||
|
Particularly useful sources of information about libraries include the
|
|||
|
following:
|
|||
|
|
|||
|
<EFBFBD><EFBFBD>*<2A>``The GCC HOWTO'' by Daniel Barlow. In particular, this HOWTO discusses
|
|||
|
compiler options for creating libraries and how to query libraries. It
|
|||
|
covers information not covered here, and vice versa. This HOWTO is
|
|||
|
available through the Linux Documentation Project at [http://
|
|||
|
www.linuxdoc.org] http://www.linuxdoc.org.
|
|||
|
|
|||
|
<EFBFBD><EFBFBD>*<2A>``Executable and Linkable Format (ELF)'' by the Tool Interface Standards
|
|||
|
(TIS) committee (this is actually one chapter of the Portable Formats
|
|||
|
Specification Version 1.1 by the same committee). This provides
|
|||
|
information about the ELF format (it isn't specific to Linux or GNU gcc),
|
|||
|
and provides a great deal of detail on the ELF format. See [ftp://
|
|||
|
tsx-11.mit.edu/pub/linux/packages/GCC/ELF.doc.tar.gz] ftp://
|
|||
|
tsx-11.mit.edu/pub/linux/packages/GCC/ELF.doc.tar.gz If you get the file
|
|||
|
from MIT, note that the format is unusual; after gunzipping and
|
|||
|
untarring, you'll get an ``hps'' file; just strip off the top and bottom
|
|||
|
lines, rename it to a ``ps'' file, and you'll get a printable Postscript
|
|||
|
file with the usual filename.
|
|||
|
|
|||
|
<EFBFBD><EFBFBD>*<2A>``ELF: From the Programmer's Perspective'' by Hongjui Lu. This gives
|
|||
|
Linux and GNU gcc-specific information on ELF, and is available at [ftp:/
|
|||
|
/tsx-11.mit.edu/pub/linux/packages/GCC/elf.ps.gz] ftp://tsx-11.mit.edu/
|
|||
|
pub/linux/packages/GCC/elf.ps.gz.
|
|||
|
|
|||
|
<EFBFBD><EFBFBD>*<2A>The ld documentation ``Using LD, the GNU Linker'' describes ld in far
|
|||
|
more detail. It is available at [http://www.gnu.org/manual/ld-2.9.1]
|
|||
|
http://www.gnu.org/manual/ld-2.9.1.
|
|||
|
|
|||
|
|
|||
|
-----------------------------------------------------------------------------
|
|||
|
8. Copyright and License
|
|||
|
|
|||
|
This document is Copyright (C) 2000 David A. Wheeler. It is covered by the
|
|||
|
GNU General Public License (GPL). You may redistribute it without cost.
|
|||
|
Interpret the document's source text as the ``program'' and adhere to the
|
|||
|
following terms:
|
|||
|
|
|||
|
|
|||
|
This program is free software; you can redistribute it and/or modify it
|
|||
|
under the terms of the GNU General Public License as published by the
|
|||
|
Free Software Foundation; either version 2 of the License, or (at your
|
|||
|
option) any later version.
|
|||
|
|
|||
|
This program is distributed in the hope that it will be useful, but
|
|||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|||
|
Public License for more details.
|
|||
|
|
|||
|
You should have received a copy of the GNU General Public License along
|
|||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||
|
|
|||
|
These terms do permit mirroring by other web sites, but please:
|
|||
|
|
|||
|
<EFBFBD><EFBFBD>*<2A>make sure your mirrors automatically get upgrades from the master site,
|
|||
|
|
|||
|
<EFBFBD><EFBFBD>*<2A>clearly show the location of the master site, [http://www.dwheeler.com/
|
|||
|
program-library] http://www.dwheeler.com/program-library, with a
|
|||
|
hypertext link to the master site, and
|
|||
|
|
|||
|
<EFBFBD><EFBFBD>*<2A>give me (David A. Wheeler) credit as the author.
|
|||
|
|
|||
|
|
|||
|
The first two points primarily protect me from repeatedly hearing about
|
|||
|
obsolete bugs. I do not want to hear about bugs I fixed a year ago, just
|
|||
|
because you are not properly mirroring the document. By linking to the master
|
|||
|
site, users can check and see if your mirror is up-to-date. I'm sensitive to
|
|||
|
the problems of sites which have very strong security requirements and
|
|||
|
therefore cannot risk normal connections to the Internet; if that describes
|
|||
|
your situation, at least try to meet the other points and try to occasionally
|
|||
|
sneakernet updates into your environment.
|
|||
|
|
|||
|
By this license, you may modify the document, but you can't claim that what
|
|||
|
you didn't write is yours (i.e., plagiarism) nor can you pretend that a
|
|||
|
modified version is identical to the original work. Modifying the work does
|
|||
|
not transfer copyright of the entire work to you; this is not a ``public
|
|||
|
domain'' work in terms of copyright law. See the license for details, in
|
|||
|
particular noting that ``You must cause the modified files to carry prominent
|
|||
|
notices stating that you changed the files and the date of any change.'' If
|
|||
|
you have questions about what the license allows, please contact me. In most
|
|||
|
cases, it's better if you send your changes to the master integrator
|
|||
|
(currently David A. Wheeler), so that your changes will be integrated with
|
|||
|
everyone else's changes into the master copy.
|