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.

UML model

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 library foo, means look for shared object files with the name libfoo.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 file qux instead of a.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.

See also

Share on: TwitterHacker NewsFacebookLinkedInRedditEmail


Related Posts


Published

Category

Programming

Tags

Connect with me on...