How to Know If One Shared Library Depends on Another Shared Library or Not

How to know if one shared library depends on another shared library or not?

Some distributions would require you to list linked libraries after object files that reference them (to mimic how static libs work). So try to build like:

g++ -v -shared -Wl,-soname,libmyssl.so.1,-o libmyssl.so.1.0 myssl.o -lz -lssl -lcrypto

Using a shared library in another shared library

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.

Managing secondary dependencies of shared libraries

By setting DT_RUNPATH you are telling the loader that each of your binaries is linked with all of its dependencies.

But that isn't true for libC.so -- it (apparently) doesn't have DT_RUNPATH of its own.

I can link the libraries C and D statically, ... But is there a better way?

Yes: link libC.so with correct DT_RUNPATH (if libC.so and libD.so are in the same directory, then -Wl,-rpath,\$ORIGIN will work for libC.so as well).

Update:

The problem is that I may not have the source or the properly compiled object files of libC

In that cause you should use RPATH instead of RUNPATH. Unlike the latter, the former applies to the object itself and to all dependencies of that object.

In other words, using --enable-new-dtags is wrong in this case -- you want the opposite.

In this case, there is no solution (besides static linking); correct?

The other solution (besides using RPATH) is to set LD_LIBRARY_PATH in the environment.

Update 2:

Difference between RPATH and RUNPATH

The difference is explained in the ld.so man page:

       If a shared object dependency does not contain a slash, then it
is searched for in the following order:

o Using the directories specified in the DT_RPATH dynamic
section attribute of the binary if present and DT_RUNPATH
attribute does not exist. Use of DT_RPATH is deprecated.
...
o Using the directories specified in the DT_RUNPATH dynamic
section attribute of the binary if present. Such directories
are searched only to find those objects required by DT_NEEDED
(direct dependencies) entries and do not apply to those
objects' children, which must themselves have their own
DT_RUNPATH entries. This is unlike DT_RPATH, which is applied
to searches for all children in the dependency tree.

On linking of shared libraries, are they really final, and if so, why?

Once you have a shared library libfoo.so the only ways you can use it
in the linkage of anything else are:-

Link a program that dynamically depends on it, e.g.

$ gcc -o prog bar.o ... -lfoo

Or, link another shared library that dynamically depends on it, e.g.

$ gcc -shared -o libbar.so bar.o ... -lfoo

In either case the product of the linkage, prog or libbar.so
acquires a dynamic dependency on libfoo.so. This means that prog|libfoo.so
has information inscribed in it by the linker that instructs the
OS loader, at runtime, to find libfoo.so, load it into the
address space of the current process and bind the program's references to libfoo's exported symbols to
the addresses of their definitions.

So libfoo.so must continue to exist as well as prog|libbar.so.
It is not possible to link libfoo.so with prog|libbar.so in
such a way that libfoo.so is physically merged into prog|libbar.so
and is no longer a runtime dependency.

It doesn't matter whether or not you have the source code of the
other linkage input files - bar.o ... - that depend on libfoo.so. The
only kind of linkage you can do with a shared library is dynamic linkage.

This is in complete contrast with the linkage of a static library

You wonder about the statement in this this answer where it says:

If you have access to either source or object files for both libraries, it is straightforward to compile/link a combined SO from them.

The author is just observing that if I have source files

foo_a.c foo_b.c... bar_a.c bar_b.c

which I compile to the corresponding object files:

foo_a.o foo_b.o... bar_a.o bar_b.o...

or if I simply have those object files. Then as well as - or instead of - linking them into two shared libraries:

$ gcc -shared -o libfoo.so foo_a.o foo_b.o...
$ gcc -shared -o libbar.so bar_a.o bar_b.o...

I could link them into one:

$ gcc -shared -o libfoobar.so foo_a.o foo_b.o... bar_a.o bar_b.o...

which would have no dependency on libfoo.so or libbar.so even if they exist.

And although that could be straightforward it could also be false. If there is
any symbol name that is globally defined in any of foo_a.o foo_b.o... and
also globally defined in any of bar_a.o bar_b.o... then it will not matter
to the linkage of either libfoo.so or libbar.so (and it need not be dynamically
exported by either of them). But the linkage of libfoobar.so will fail for
multiple definition of name.

If we build a shared library libbar.so that depends on libfoo.so and has
itself been linked with libfoo.so:

$ gcc -shared -o libbar.so bar.o ... -lfoo

and we then want to link a program with libbar.so, we can do that in such a way
that we don't need to mention its dependency libfoo.so:

$ gcc -o prog main.o ... -lbar -Wl,-rpath=<path/to/libfoo.so>

See this answer to follow that up. But
this doesn't change the fact that libbar.so has a runtime dependency on libfoo.so.

If that's not possible, what is technically preventing it?

What technically prevents linking a shared library with some program
or shared library targ in a way that physically merges it into targ is that a
shared library (like a program) is not the sort of thing that a linker knows
how to physically merge into its output file.

Input files that the linker can physically merge into targ need to
have structural properties that guide the linker in doing that merging. That is the structure of object files.
They consist of named input sections of object code or data that are tagged with various attributes.
Roughly speaking, the linker cuts up the object files into their sections and distributes them into
output sections of the output file according to their attributes, and makes
binary modifications to the merged result to resolve static symbol references
or enable the OS loader to resolve dynamic ones at runtime.

This is not a reversible process. The linker can't consume a program or
shared library and reconstruct the object files from which it was made to
merge them again into something else.

But that's really beside the point. When input files are physically
merged into targ, that is called static linkage.
When input files are just externally referenced in targ to
make the OS loader map them into a process it has launched for targ,
that is called dynamic linkage. Technical development has given us
a file-format solution to each of these needs: object files for static linkage, shared libraries
for dynamic linkage. Neither can be used for the purpose of the other.

Linking a shared library with another shared lib in linux

Suppose that libabc.so is obtained from posiition independent object code files abc1.pic.o and abc2.pic.o ; then you have built them with e.g.

 gcc -Wall -fPIC -O -g abc1.c -c -o abc1.pic.o
gcc -Wall -fPIC -O -g abc2.c -c -o abc2.pic.o

and you build libabc.so with

gcc -shared  abc1.pic.o  abc2.pic.o -L/usr/local/lib -l123 -o libabc.so

I added -L/usr/local/lib before -l123 because I am assuming you have a /usr/local/lib/lib123.so shared library.

Read also the Program Library HowTo.

As you see, you may link a shared library lib123.so into your own shared library libabc.so

Then check with ldd libabc.so

You may want to set up some rpath in your libabc.so by adding -Wl,-rpath and -Wl,$RPATHDIR to the linking command.

For much more details, read Drepper's paper How to write shared libraries

PS. Don't use a static library for lib123.a (it should be PIC). If you link non-PIC code into a shared object, you lose most of the advantages of shared objects, and the dynamic linker ld.so has to do zillions of relocations.

How to link shared library to another shared library

Let's decode your symbol:

$ c++filt _ZN5vatps6PosAPI8sendDataB5cxx11Ev
vatps::PosAPI::sendData[abi:cxx11]()

So, your code expects sendData with std::string with C++11 ABI, whereas libPosAPI.so provides sendData with pre-C++11 ABI std::string.

abi:cxx11 hints to GCC5 and the C++11 ABI:

Users that depend on third-party libraries or plugin interfaces that still use the old ABI can build their code with -D_GLIBCXX_USE_CXX11_ABI=0 and everything should work fine. In most cases, it will be obvious when this flag is needed because of errors from the linker complaining about unresolved symbols involving __cxx11.

Creating a shared object which depends on other shared objects

This is probably a dup of how-to-know-if-one-shared-library-depends-on-another-shared-library-or-not

TLDR is that you should put linked libs after object/source files.



Related Topics



Leave a reply



Submit