I thought we could take a quick look at how to create a shared library out of our code. This week we’ll create the library and next week we’ll look at the various ways of installing/accessing it on the operating system.
I’m going to re-use the palindrome program I’ve talked about before (a proper version, not the dodgy one).
First of all we want to break the code up into several files instead of having it all in one cpp file. We’ll take the palindrome function out and use that to create a library. Obviously if you were creating your own libraries you’d probably want a lot more functionality, but I’ll leave that part to you 😉
Right then. Let’s create a header file, pal.h:
bool isPalindrome(char* word);And a cpp file, pal.cpp:
#include "pal.h" #include <string.h> bool isPalindrome(char* word) { bool ret = true; char *p = word; int len = strlen(word); char *q = &word[len-1]; for (int i = 0 ; i < len ; ++i, ++p, --q) { if (*p != *q) { ret = false; } } return ret; }And then get down to the business of creating our shared object!
First of all we want to compile the code above into an object file.
To do this we pass gcc the -c option, which tells it NOT to perform the linking stage (if you did try to link, gcc would complain that you have no main function defined – because a program can’t run without a main, but a library doesn’t need one).
g++ -fPIC -c -Wall pal.cppThis will create a pal.o file in the directory that you are working in.
Next, we want to create our actual library with this line, which I’ll explain below:
ld -shared pal.o -o libpal.soThis uses the linker program (ld), usually called by g++ (remember we told g++ with the -c option not to link in the first stage). It says make a shared object (the -shared option), using the input file pal.o and call it libpal.so (the -o option). The .so extension is the usual naming convention for shared libraries.
After running this, you should be able to see the libpal.so file in your working directory.
Cool! You’ve just created a shared library 🙂
Next up we actually want to use that library with some other code. So let’s create a main.cpp file that calls the library function isPalindrome:
#include "pal.h" #include <iostream> using namespace std; int main() { while (1) { char buffer[64] = {0}; cin >> buffer; if (isPalindrome(buffer)) { cout << "Word is a palindrome" << endl; } else { cout << "Word is not a palindrome" << endl; } } return 0; }As with all libraries, we use it in main.cpp by including the library header (pal.h). Then at compile time we have to tell gcc that we want to link to the library.
We would normally link to a library with a line like this:
g++ -Wall main.cpp -lpalHowever, if we try this, we get an error that says:
/usr/bin/ld: cannot find -lpal collect2: error: ld returned 1 exit statusOops.
This is because the linker, ld, looks in a specified set of locations for libraries and our library doesn’t live there (more on this next week).
So what can we do?
A temporary solution is to tell ld where the library currently lives. It’s in our working directory, so we can use the -rpath option to let the linker know about it.
g++ -Wall -L/home/faye -Wl,-rpath=/home/faye/ main.cpp -lpalg++ -Wall is our usual compile command, checking for all warnings.
-L is the path to the shared library, provided so that the linker knows where to look for it.
-Wl means a list of comma separated options for the linker follows, and the only option we pass is:
-rpath – which means that the path the library will be embedded in the executable so that the loader will be able to find it at run time.
Finally we have our main.cpp file, and then we add in our new shared library by filename with libpal.so.
Easy!
Just to make that clear:
-L is used for the linker
-rpath embeds the path (via the linker) for the loader.If you omit either of these, you will have problems either linking or running.
If you run this now, with ./a.out, you’ll get the following:
We’ll look at other ways of accessing shared libraries next week (i.e. without using -rpath).
In the meantime, have fun!