Chapter 21. Shared Libraries

Table of Contents

21.1. Building Shared Libraries
21.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.

21.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. 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. On UNIX platforms, 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, the DLL will contain the major version in its name (e.g., moo1.dll) while the companion static library remains unversioned.

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 in many circumstances.

When abuild builds shared libraries, it links them with any libraries in the LIBS variable. Although this is generally the correct behavior for systems that support linking of shared libraries, it can cause unpleasant side effects if you mix shared libraries with static libraries. 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. If you need to prevent abuild from linking certain static libraries into shared libraries, you may manually manipulate the contents of the LIBS variable in your Abuild.mk file. However, if you find that you need to do this, you should probably rethink how you have your build configured. If you have static libraries that are intended to be linked into shared libraries and then not used again for other purposes, you should reset the value of LIBS in an after-build file that is included by your shared library build item's Abuild.interface file. This is discussed in Section 21.2, “Shared Library Example”. [42]

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, and all shared library targets will link with all static library targets. The shared library targets will not link with each other. There are few, if any, good reasons to mix shared and static library targets in the same build item, as doing so can only lead to confusion. When they are mixed, it is probably appropriate to avoid adding the static libraries to LIBS in the Abuild.interface file.

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. [43] 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. When building DLLs and executables with MSVC versions greater than or equal to .NET 2005, abuild automatically embeds the manifest file in the DLL or executable with the mt command.



[42] Versions of abuild prior to 1.0.3 behaved somewhat differently with respect to linking of shared libraries. See the changes for version 1.0.3 in the release notes for details.

[43] 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.