Creating and Using Shared-object Libraries on LinuxRussell Bateman |
This article explains how to create and consume a small shared-object library on Linux. It also demonstrates a makefile (and doesn't demonstrate using autotools to create it—that's a different and very complex topic in itself).
This isn't an overly complicated thing to do, however, most of what's written about it that you can find via Google seems either to go beyond the mark or stop well short of it. If you're faced today with needing to throw a shared object together and haven't had the pleasure before, this document should bail a lot of water out of your boat.
See useful links I stumbled upon while researching for this article.
Usually I multiply words to attempt to explain much, however, this time I'm going to let the code (mostly the makefile) speak for me.
This isn't the best makefile possible, but it illustrates, as step-by-step as possible:
Here is Makefile. The commands for building a) the individual objects and b) the shared object are really the meat of this whole article. Nothing in the rest of it will be of so much interest except to flesh out the context of the discussion.
.PHONY: clean printstring.c printinteger.c libprint.so: init.o printstring.o printinteger.o ld -shared -soname [email protected] -o [email protected] init.o printstring.o printinteger.o -lc ln -s ./libprint.so.1.0 libprint.so.1 ln -s ./libprint.so.1 libprint.so init.o: init.c gcc -fPIC -c $< printstring.o: printstring.c print.h gcc -fPIC -c $< printinteger.o: printinteger.c print.h gcc -fPIC -c $< client: gcc -o client client.c -L. -lprint clean: rm -f *.so* *.o client
The command I use to build the whole enchilada each time is:
russ@rhel-32:~/dev/shared-library> make clean ; make ; make client
Supplementary note: I have found that CFLAGS (and CPPFLAGS) including -I, -D definitions, etc., will not be observed by the gcc/g++ command when explicitly coded as is done here. Thus, if I were specifying, for example, additional include paths, I would add $(CFLAGS) into the gcc command line for init.o and the other object targets.
print.h specifies the library's consumable interfaces.
#ifndef __print_h__ #define __print_h__ void printinteger( int ); void printstring( const char * ); #endif
printinteger.c is an interface that takes an int argument and prints it to the console.
#include <stdio.h> #include "print.h" void printinteger( int integer ) { printf( "libprint::printinteger(): %d\n", integer ); }
printstring.c prints a string to the console. It's in a separate file precisely in order to illustrate how to link more than one object into a shared-object library. Some tutorials or examples only showed one file. I had to putter a bit with the ld command before figuring out how to make it work for two files; I kept getting a run-time error complaining that the second library interface didn't exist even though nm seemed to list it as being there.
#include <stdio.h> #include "print.h" void printstring( const char *string ) { printf( "libprint::printstring(): %s\n", string ); }
init.c contains the shared-object equivalent of Windows DllMain(). It's the start-up and shut-down code for the library. Actually, there are default ones supplied, but I wanted to show using my own and I wanted them to illustrate when they were being called for purely pedagogical reasons.
#include <stdio.h> void _init( void ) { printf( "libprint::_init()\n" ); } void _fini( void ) { printf( "libprint::_fini()\n" ); }
client.c contains the code that consumes the two library interfaces and demonstrates that this whole thing works.
#include <stdio.h> #include "print.h" int main( void ) { printf( "In main()...\n" ); printstring( "We're calling the shared library from main()..." ); printinteger( 99 ); return 0; }
Finally, the execution output:
russ@rhel-32:~/dev/shared-library> ./client libprint::_init() In main()... libprint::printstring(): We're calling the shared library from main()... libprint::printinteger(): 99 libprint::_fini()
Here's a directory listing of what you should find in your development subdirectory when you're done. The library versioning scheme consists of (or so I imagine it does) the formal shared-object being fully named as libprint.so.1.0, meaning "version 1.0 of the print library." A link is set up such that libprint.so.1, or version 1 of the library, points to it. Finally, the basic name of the library object, libprint.so is a link to that version.
russ@rhel-32:~/dev/shared-library> ll total 100 -rwxrwxr-x 1 russ russ 5159 Jan 29 14:26 client -rw-r--r-- 1 russ russ 190 Jan 29 13:55 client.c -rw-rw-r-- 1 russ russ 388 Jan 29 13:46 init.c -rw-rw-r-- 1 russ russ 1236 Jan 29 14:26 init.o lrwxrwxrwx 1 russ russ 15 Jan 29 14:26 libprint.so -> ./libprint.so.1 lrwxrwxrwx 1 russ russ 17 Jan 29 14:26 libprint.so.1 -> ./libprint.so.1.0 -rwxrwxr-x 1 russ russ 2630 Jan 29 14:26 libprint.so.1.0 -rw-rw-r-- 1 russ russ 894 Jan 29 14:26 Makefile -rw-rw-r-- 1 russ russ 344 Jan 29 13:43 print.h -rw-rw-r-- 1 russ russ 383 Jan 29 13:47 printinteger.c -rw-rw-r-- 1 russ russ 1168 Jan 29 14:26 printinteger.o -rw-rw-r-- 1 russ russ 387 Jan 29 13:47 printstring.c -rw-rw-r-- 1 russ russ 1160 Jan 29 14:26 printstring.o
Were I to distribute an updated version of my library, say with some new functionality, I might redo a few things link-wise in my installation procedure...
... lrwxrwxrwx 1 russ russ 15 Jan 29 14:26 libprint.so -> ./libprint.so.1 lrwxrwxrwx 1 russ russ 17 Jan 29 14:26 libprint.so.1 -> ./libprint.so.1.1 -rwxrwxr-x 1 russ russ 2630 Jan 29 14:26 libprint.so.1.0 -rwxrwxr-x 1 russ russ 2790 Feb 28 10:23 libprint.so.1.1 ...
Don't quote me on this, but I imagine that if I wrote an application that depended crucially on a bug in the first version of my library, I could relink it to use libprint.so.1.0. However, in the usual situation, any application would like to consume the latest version as indicated by simply libprint.so which I would continually update with new releases as time went on.
What I haven't covered here is installation in a likely, Linux-ish place like the big boys. You might find a better explanation on versioning than the one I gave above by reading through one or more of the articles to which links are provided below. It transcends my immediate purpose to look into this for now.