Using a shared library in another shared library

Unah Henry picture Unah Henry · Aug 14, 2018 · Viewed 9.6k times · Source

I am creating a shared library from a class from an example I got here C++ Dynamic Shared Library on Linux. I would like to call another shared library from the shared library created and then use it in the main program. So I have the myclass.so library and I want to call another library say anotherclass.so from the myclass.so library and then use this myclass.so library in the main program. Any idea on how I can do this please.

Answer

Mike Kinghan picture Mike Kinghan · Aug 14, 2018

There is more than one way in which multiple shared libraries may be added to the linkage of a program, if you are building all the libraries, and the program, yourself.

The elementary way is simply to explicitly add all of the libraries to the the linkage of the program, and this is the usual way if you are building only the program and linking libraries built by some other party.

If an object file foo.o in your linkage depends on a library libA.so, then foo.o should precede libA.so in the linkage sequence. Likewise if libA.so depends on libB.so then libA.so should precede libB.so. Here's an illustration.

We'll make a shared library libsquare.so from the files:

square.h

#ifndef SQUARE_H
#define SQUARE_H

double square(double d);

#endif

and

square.cpp

#include <square.h>
#include <cmath>

double square(double d)
{
    return pow(d,2);
}

Notice that the function square calls pow, which is declared in the Standard header <cmath> and defined in the math library, libm.

Compile the source file square.cpp to a position-independent object file square.o:

$ g++ -Wall -fPIC -I. -c square.cpp

Then link square.o into a shared library libsquare.so:

$ g++ -shared -o libsquare.so square.o

Next we'll make another shared library libcube.so from these files:

cube.h

#ifndef CUBE_H
#define CUBE_H

double cube(double d);

#endif

and

cube.cpp

#include <cube.h>
#include <square.h>

double cube(double d)
{
    return square(d) * d;
}

See that the function cube calls square, so libcube.so is going to depend on libsquare.so. Build the library as before:

$ g++ -Wall -fPIC -I. -c cube.cpp
$ g++ -shared -o libcube.so cube.o

We haven't bothered to link libsquare with libcube, even though libcube depends on libsquare, and even though we could have, since we're building libcube. For that matter, we didn't bother to link libm with libsquare. By default the linker will let us link a shared library containing undefined references, and it is perfectly normal. It won't let us link a program with undefined references.

Finally let's make a program, using these libraries, from this file:

main.cpp

#include <cube.h>
#include <iostream>

int main()
{
    std::cout << cube(3) << std::endl;
    return 0;
}

First, compile that source file to main.o:

$ g++ -Wall -I. -c main.cpp

Then link main.o with all three required libraries, making sure to list the linker inputs in dependency order: main.o, libcube.so, libsquare.so, libm.so:

$ g++ -o prog main.o -L. -lcube -lsquare -lm

libm is a system library so there's no need to tell the linker where to look for it. But libcube and libsquare aren't, so we need to tell the linker to look for them in the current directory (.), because that's where they are. -L. does that.

We've successfully linked ./prog, but:

$ ./prog
./prog: error while loading shared libraries: libcube.so: cannot open shared object file: No such file or directory

It doesn't run. That's because the runtime loader doesn't know where to find libcube.so (or libsquare.so, though it didn't get that far).

Normally, when we build shared libraries we then install them in one of the loader's default search directories (the same ones as the linker's default search directories), where they're available to any program, so this wouldn't happen. But I'm not going to install these toy libraries on my system, so as a workaround I'll prompt the loader where to look for them by setting the LD_LIBRARY_PATH in my shell.

$ export LD_LIBRARY_PATH=.
$ ./prog
27

Good. 3 cubed = 27.

Another and better way to link a program with shared libraries that aren't located in standard system library directories is to link the program using the linker's -rpath=DIR option. This will write some information into the executable to tell the loader that it should search for required shared libraries in DIR before it tries the default places.

Let's relink ./prog that way (first deleting the LD_LIBRARY_PATH from the shell so that it's not effective any more):

$ unset LD_LIBRARY_PATH
$ g++ -o prog main.o -L. -lcube -lsquare -lm -Wl,-rpath=.

And rerun:

$ ./prog
27

To use -rpath with g++, prefix it with -Wl, because it's an option for linker, ld, that the g++ frontend doesn't recognise: -Wl tells g++ just to pass the option straight through to ld.