Building and using shared libraries
On this page I will use a simple example on how to use shared libraries on Linux in C++ and using the GNU g++
compiler.
You will be able to compile your library and application separately.
Note
In Windows we have DLLs, in Linux/Unix we have shared libraries. It's the same!
However, you can't specify your library on runtime easily.
It is possible though, using dlopen()
, but that's not really easy.
Design
A very simple UML model about the design in this example.
Our file structure will look like this:
foo/
foo.cpp
foo.h
libfoo.so
qux/
main.cpp
qux
Building a shared library libfoo
foo/foo.h
:
#ifndef LIBFOO_H
#define LIBFOO_H
class Foo {
public:
Foo();
int bar() const;
};
#endif
foo/foo.cpp
:
#include "foo.h"
Foo::Foo(){}
int Foo::bar() const {
return 10;
}
Now, in the folder foo/
run the following command:
g++ -shared -fPIC foo.cpp -o libfoo.so
The options explained:
-shared
—to create a shared object file (shared library)-fPIC
—about memory management. In short: memory addresses are mapped independent of the location.foo.cpp
—the C++ file(s) to compile-o libfoo.so
—instead of outputting to ''a.out'', use a sensible name.
Now check if you can see if all methods are included:
$ nm foo/libfoo.so
0000000000200e20 a _DYNAMIC
[...]
00000000000005ec T _ZN3FooC1Ev
00000000000005ec T _ZN3FooC2Ev
00000000000005f6 T _ZNK3Foo3barEv
[...]
00000000000005c0 t frame_dummy
These are all very cryptic names, called symbols.
To make it human readable, use c++filt
:
$ echo _ZN3FooC1Ev | c++filt
Foo::Foo()
$ echo _ZNK3Foo3barEv | c++filt
Foo::bar() const
Tip
In case you don't have the utilities c++filt
or nm
then install the package binutils
on Debian/Ubuntu and -derivatives.
Obviously, our shared library is created successfully. Now let's move on to how to use it.
Using shared libraries
qux/main.cpp
:
#include <iostream>
#include "../foo/foo.h"
int main(){
Foo myfoo;
std::cout << "Output of Foo.bar()=" << myfoo.bar() << std::endl;
}
Note that we use the foo.h
file from the foo/
folder as we need it to compile the qux
application.
Now we can compile the qux
application as follows:
g++ -lfoo -L ../foo/ main.cpp -o qux
The options explained:
-lfoo
—use libraryfoo
, means look for shared object files with the namelibfoo.so.*
-L ../foo/
—when trying to link object files look in the directory../foo/
.main.cpp
—the source file to compile.-o qux
—output to filequx
instead ofa.out
.
Just when you think you're done and you try to run the program...
$ ./qux
./qux: error while loading shared libraries: libfoo.so: cannot open shared object file: No such file or directory
Apparently it is not able to find our libfoo.so
file on runtime!
To debug it, we can use ldd
to find out what's going on.
$ ldd qux
linux-vdso.so.1 => (0x00007fffa9dff000)
libfoo.so => not found
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f007578d000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f0075507000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f00752f1000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0074f5d000)
/lib64/ld-linux-x86-64.so.2 (0x00007f0075abd000)
Apparently it doesn't know where it can find libfoo.so
.
This can be solved by specifying the correct path for the linker as follows:
$ LD_LIBRARY_PATH=../foo/ ./qux
Output of Foo.bar()=10
Party! We did it! :-D
Why doesn't it know the correct path?
We specified it on compile time, right?
Yes, that was on compile time, not runtime.
In real installations we put the shared library in system paths like /usr/lib
and in that case we don't need to specify the LD_LIBRARY_PATH
variable.
Things to keep in mind
- In order to be able to build any application in Ubuntu/Debian, make sure you have installed the
build-essential
package. - When you want to use a shared library you'll need the corresponding header files
foo.h
to be able to use it. - Also, when distributing or sharing shared libraries with others, you'll need to share the header files as well.
- If you want to compile against a system library, you will need to install the header files.
Usually you'll find them in the
-dev
packages on Debian/Ubuntu distrubutions. - With the method described on this page you can't specify the
.so
file in runtime. - None of the commands given need to be run as root.