Chapter 17. Shared Libraries

Table of Contents

17.1. Building Shared Libraries
17.2. Shared Library Example

In most cases, development efforts consisting of large amounts of dynamic and evolving code will be best served by sticking with static libraries. Sometimes, however, it may be desirable or necessary to create shared library object files. Abuild provides support for creating shared libraries on UNIX-based systems and DLLs on Windows systems. Note that there are many ancillary concerns one must keep in mind when using shared libraries such as binary interface compatibility. We touch lightly on these topics here, but a full discussion is out of scope for this document.

17.1. Building Shared Libraries

Building shared libraries with abuild is essentially identical to building static libraries. You still set up your shared library targets using TARGETS_lib in Abuild.mk just as you would for static libraries. In order to tell abuild that a library should be created as a shared library, you must set the additional variable SHLIB_libname where libname is the name of the library target. The value of this variable consists of up to three numbers: major version, minor version, and revision. These values tell potential users of your library when the library has changed. In general, you should only modify these values when you are releasing versions of your library. During development, it's best to just leave them alone or else your version numbers will get very large and you will lose all the advantages of using shared libraries because of the need to relink everything all the time. Before a release, the major version number should be incremented if the shared library has had interfaces removed or modified since the last release as those operations would make old binaries that linked with the shared library fail to work with the new version. The minor version should be incremented if no interfaces were changed or removed but new interfaces were added. This indicates that old binaries would work with new libraries but new binaries may not work with old libraries. The revision number should be incremented if any changes were made to the shared library code that did not affect the interfaces. This just tells the user that the library has changed relative to another version that may be installed. On UNIX platforms, abuild will build executables that link against shared libraries in such a way that they will fail to locate shared libraries whose major version numbers do not match what they linked against. The unversioned .so file and the .so file with only the major version will be symbolic links to the fully versioned file name. (For example, if the actual shared library file were libmoo.so.1.2.3, both libmoo.so and libmoo.so.1 would be symbolic links to it.) On Windows systems, the version number parameters are ignored.

Note that all the version number parameters are optional. Although they should always be used when creating actual shared libraries that you intend to link programs against, they may be omitted in some other cases. For example, if you are building a shared library object file that will be loaded at runtime or used as a plugin (such as with Java native code), then it may be appropriate to omit the version numbers altogether. Even if the SHLIB_libname variable is set to an empty string, abuild will still make a shared library instead of a static library. There is no way to create both a shared and a static version of the same library at the same time, but it is possible to create a shared library that links against a static library, which can be used to achieve the same effect.

By default, when a shared library depends on a static library, abuild will not link the static library into the shared library. On some systems, you must link static libraries into shared libraries in order for the build to succeed, and on other systems, it is optional. When mixing shared libraries and static libraries, you should make sure that you don't include two copies of the same symbols in more than one place (two shared libraries or a shared library and an executable). Some systems handle this case acceptably, and others don't. Even in the best case, doing this is wasteful and potentially confusing. In order to tell abuild to link a static library into a shared library, set the variable LINK_SHLIBS to 1 in your Abuild.mk. This applies to all shared libraries built in a build item.

Abuild allows you to mix executables, shared libraries, and static libraries in the same build item. If you do this, all executable targets will link with all shared and all static library targets. If LINK_SHLIBS is set, then all shared libraries will also link with all static libraries.

In order to allow static libraries to be linked into shared libraries, abuild compiles all library object files as position-independent code. In some extremely rare cases, you may wish to avoid doing this as there is a very minor performance cost to do it. If you wish to prevent a specific source file from being compiled as position-independent code, set the variable NOPIC_filename to 1 where filename is the name of the source file. For example, the code NOPIC_File.cc := 1 in your Abuild.mk file would prevent File.cc from being compiled as position-independent code. Note that abuild does not check to make sure that code compiled in this way is not eventually linked into a shared library. If you try to link non-position-independent code into a shared library, it may not link at all, or it may cause undefined and hard-to-trace behavior. Use of this feature is not recommended unless absolutely needed to fix some specific problem.

In order to run a program that is linked with shared libraries, the operating system will have to know where to find the shared library. Abuild does not include library run path information in the executables as doing so is inherently dangerous and non-portable. Even if abuild were to ignore this danger and include run path information, doing so would potentially preclude the ability to swap out shared libraries at runtime, which is often the main reason for wanting to use them in the first place. [31] Instead, you will need to make sure that, one way or another, the shared libraries you need are located in a directory that is in your shared library path. On most UNIX systems, you can set the LD_LIBRARY_PATH environment variable or install the shared libraries into certain system-defined locations. On some systems (like Linux), you can also add directories to /etc/ld.so.conf. On Windows, you can colocate the DLL files with the executables, or you can add the directories containing the DLL files to your path.



[31] The way the runtime loader behaves when shared library location information is compiled into an executable (as run path data) varies from system to system. In most systems, if the shared library doesn't exist at the compiled-in location, the system will fall back to its standard rules for locating shared libraries. In some systems, if the shared library does exist in the compiled-in location, that copy of the shared library will be used with no way to override it. This may have undesired implications. For example, suppose you were to create an executable that linked with a shared library and included run path information to the development version of the shared library. If you installed that executable and shared library in standard locations on a system without a copy of the development environment, everything would work fine. Then suppose you put a development environment on that system and built a newer version of the same shared library. Your installed executable would actually use the new development copy of the library because it still has that path compiled into it! This is almost certainly not what would be intended. Abuild avoids this issue entirely but not including support for specifying run path data.

For another approach to using shared libraries, look at libtool. The libtool program gets around this problem by creating wrappers around executables and shared libraries in the development tree. Although abuild is not integrated with libtool, such an integration would be possible. The possibility of including support for libtool is actually one of the motivations behind allowing library object files and non-library object files to have different extensions.