C H A P T E R  16

Building Libraries

This chapter explains how to build your own libraries.

16.1 Understanding Libraries

Libraries provide two benefits. First, they provide a way to share code among several applications. If you have such code, you can create a library with it and link the library with any application that needs it. Second, libraries provide a way to reduce the complexity of very large applications. Such applications can build and maintain relatively independent portions as libraries and so reduce the burden on programmers working on other portions.

Building a library simply means creating .o files (by compiling your code with the -c option) and combining the .o files into a library using the CC command. You can build two kinds of libraries, static (archive) libraries and dynamic (shared) libraries.

With static (archive) libraries, objects within the library are linked into the program's executable file at link time. Only those .o files from the library that are needed by the application are linked into the executable. The name of a static (archive) library generally ends with a .a suffix.

With dynamic (shared) libraries, objects within the library are not linked into the program's executable file, but rather the linker notes in the executable that the program depends on the library. When the program is executed, the system loads the dynamic libraries that the program requires. If two programs that use the same dynamic library execute at the same time, the operating system shares the library among the programs. The name of a dynamic (shared) library ends with a .so suffix.

Linking dynamically with shared libraries has several advantages over linking statically with archive libraries:

However, dynamic libraries have some disadvantages:

16.2 Building Static (Archive) Libraries

The mechanism for building static (archive) libraries is similar to that of building an executable. A collection of object (.o) files can be combined into a single library using the -xar option of CC.

You should build static (archive) libraries using CC -xar instead of using the ar command directly. The C++ language generally requires that the compiler maintain more information than can be accommodated with traditional .o files, particularly template instances. The -xar option ensures that all necessary information, including template instances, is included in the library. You might not be able to accomplish this in a normal programming environment since make might not know which template files are actually created and referenced. Without CC -xar, referenced template instances might not be included in the library, as required. For example:

% CC -c foo.cc # Compile main file, templates objects are created.
% CC -xar -o foo.a foo.o # Gather all objects into a library.

The -xar flag causes CC to create a static (archive) library. The -o directive is required to name the newly created library. The compiler examines the object files on the command line, cross-references the object files with those known to the template repository, and adds those templates required by the user's object files (along with the main object files themselves) to the archive.

Note - Use the -xar flag for creating or updating an existing archive only. Do not use it to maintain an archive. The -xar option is equivalent to ar -cr.

It is a good idea to have only one function in each .o file. If you are linking with an archive, an entire .o file from the archive is linked into your application when a symbol is needed from that particular .o file. Having one function in each .o file ensures that only those symbols needed by the application will be linked from the archive.

16.3 Building Dynamic (Shared) Libraries

Dynamic (shared) libraries are built the same way as static (archive) libraries, except that you use -G instead of -xar on the command line.

You should not use ld directly. As with static libraries, the CC command ensures that all the necessary template instances from the template repository are included in the library if you are using templates. All static constructors in a dynamic library that is linked to an application are called before main() is executed and all static destructors are called after main() exits. If a shared library is opened using dlopen(), all static constructors are executed at dlopen() and all static destructors are executed at dlclose().

You should use CC -G to build a dynamic library. When you use ld (the link-editor) or cc (the C compiler) to build a dynamic library, exceptions might not work and the global variables that are defined in the library are not initialized.

To build a dynamic (shared) library, you must create relocatable object files by compiling each object with the -Kpic or -KPIC option of CC. You can then build a dynamic library with these relocatable object files. If you get any bizarre link failures, you might have forgotten to compile some objects with -Kpic or -KPIC.

To build a C++ dynamic library named libfoo.so that contains objects from source files lsrc1.cc and lsrc2.cc, type:

% CC -G -o libfoo.so -h libfoo.so -Kpic lsrc1.cc lsrc2.cc

The -G option specifies the construction of a dynamic library. The -o option specifies the file name for the library. The -h option specifies a name for the shared library. The -Kpic option specifies that the object files are to be position-independent.

Note - The CC -G command does not pass any -l options to ld. If you want the shared library to have a dependency on another shared library, you must pass the necessary -l option on the command line. For example, if you want the shared library to be dependent upon libCrun.so, you must pass -lCrun on the command line.

16.4 Building Shared Libraries That Contain Exceptions

Never use -Bsymbolic with programs containing C++ code, use linker map files instead. With -Bsymbolic, references in different modules can bind to different copies of what is supposed to be one global object.

The exception mechanism relies on comparing addresses. If you have two copies of something, their addresses won't compare equal, and the exception mechanism can fail because the exception mechanism relies on comparing what are supposed to be unique addresses.

When shared libraries are opened using dlopen(), you must use RTLD_GLOBAL for exceptions to work.

16.5 Building Libraries for Private Use

When an organization builds a library for internal use only, the library can be built with options that are not advised for more general use. In particular, the library need not comply with the system's application binary interface (ABI). For example, the library can be compiled with the -fast option to improve its performance on a known architecture. Likewise, it can be compiled with the -xregs=float option to improve performance.

16.6 Building Libraries for Public Use

When an organization builds a library for use by other organizations, the management of the libraries, platform generality, and other issues become significant. A simple test for whether or not a library is public is to ask if the application programmer can recompile the library easily. Public libraries should be built in conformance with the system's application binary interface (ABI). In general, this means that any processor-specific options should be avoided. (For example, do not use -fast or -xtarget.)

The SPARC ABI reserves some registers exclusively for applications. For V7 and V8, these registers are %g2, %g3, and %g4. For V9, these registers are %g2 and %g3. Since most compilations are for applications, the C++ compiler, by default, uses these registers for scratch registers, improving program performance. However, use of these registers in a public library is generally not compliant with the SPARC ABI. When building a library for public use, compile all objects with the -xregs=no%appl option to ensure that the application registers are not used.

16.7 Building a Library That Has a C API

If you want to build a library that is written in C++ but that can be used with a C program, you must create a C API (application programming interface). To do this, make all the exported functions extern "C". Note that this can be done only for global functions and not for member functions.

If a C-interface library needs C++ run-time support and you are linking with cc, then you must also link your application with either libC (compatibility mode) or libCrun (standard mode) when you use the C-interface library. (If the C-interface library does not need C++ run-time support, then you do not have to link with libC or libCrun.) The steps for linking differ for archived and shared libraries.

When providing an archived C-interface library, you must provide instructions on how to use the library.

When providing a shared C-interface library you must create a dependency on libC or libCrun at the time that you build the library. When the shared library has the correct dependency, you do not need to add -lC or -lCrun to the command line when you use the library.

If you want to remove any dependency on the C++ runtime libraries, you should enforce the following coding rules in your library sources:

16.8 Using dlopen to Access a C++ Library From a C Program

If you want to use dlopen() to open a C++ shared library from a C program, make sure that the shared library has a dependency on the appropriate C++ runtime (libC.so.5 for -compat=4, or libCrun.so.1 for -compat=5).

To do this, add -lC for -compat=4 or add -lCrun for -compat=5 to the command line when building the shared library. For example:

example% CC -G -compat=4... -lC
example% CC -G -compat=5... -lCrun

If the shared library uses exceptions and does not have a dependency on the C++ runtime library, your C program might behave erratically.

Note - When shared libraries are opened with dlopen(), RTLD_GLOBAL must be used for exceptions to work.