How to build a Shared Library |
Michael Walker Thursday July 22, 2004
How to build a Shared Library
It's very easy to build a Shared Library on Solaris (and *nix in general). However, the Solaris link-editor (/usr/ccs/bin/ld) has quite a few options. It can sometimes be very confusing to determine which options to use. I'll try to lay out some general rules below.
Let the compiler do the work
First - do not invoke the link-editor (/usr/ccs/bin/ld) directly, instead run it via your compiler driver (cc/gcc/CC/g++). The compiler driver will include many magic files (crt*.o, values-xa.o, ...) which are important to the construction of your shared library. If you invoke the link-editor directly you will likely miss those files and the benefits they give you. The first thing that will fail if you drop the comiler supplied files is that your _init and _fini routines will not run when the shared library is loaded.
Use PIC code
Compile code destined for the shared library as Position Independent Code. You do this by using the cc -Kpic or gcc -fpic options, depending upon which compiler you are using. PIC code is the most flexible and efficient code for a shared library because it permits the final library to be loaded at any address in memory without having to be modified at run-time. This permits the sharing of the library among all processes which have loaded it on a given system - hence the name Shared Library. If you include non-PIC code in a shared library, it will still run but you loose the share ability of the library. You can enforce that all code in the shared object is PIC by adding the -z text option to your link line.
List your dependencies
Always list all of your libraries dependencies (what you link against) when building the shared library. By listing them at link-edit time the built library will have recorded in it what libraries it needs at run-time. You can enforce this at build time by adding the -z defs option to your link line.
Include the proper RunPaths
By default the run-time linker (ld.so.1) will always search /lib & /usr/lib (or /lib/64 & /usr/lib/64 for 64bit processes) to find a shared library at run-time. If your library is referencing objects not in those directories you must also include a RunPath to find the dependencies. This is done by adding a -R {path} for each directory containing a shared libraries being referenced by the current object being built. You should *not* depend upon LD_LIBRARY_PATH being set at run-time to find shared libraries - Rod has done a very nice post explaining why this is so.
Example
Following is a example link demonstrating all of the options I recommended above. It's much simpler then it sounds :)
% cc -Kpic bar.c -G -o bar.so -z text -z defs -L /tmp/play/lib \ -R /tmp/play/lib -lfoo -lc % ldd -r bar.so libfoo.so.1 => /tmp/play/lib/libfoo.so.1 libc.so.1 => /lib/libc.so.1 /platform/SUNW,Sun-Blade-1000/lib/libc_psr.so.1 libm.so.2 => /lib/libm.so.2 %
Note that you can use ldd to verify that your shared library is self contained and that it has the proper RunPath's defined. If either of those hadn't been true then ldd would have given appropriate error messages.
The above is just the basics on building a shared library, if you just do the above you're off to a good start. In future posts I'll follow up with additional options you can apply, these may include topics on Symbol Scoping, Library Versioning, Lazy Loading, Direct Bindings, Relocation Optimization, ....
You can find out more details about off of this in The Linker and Libraries Guide. This is a great resource with additional details about all of the above and much more then you ever wanted to know about the Linkers.
Cheers,
_Mike_
[1] Hello World | [3] Library Bindings |