Understanding how dynamic linking works on UNIX
Asked Answered
K

5

26

Consider we have the following situation:

  • a program named program which depends dynamically on libfoo.so
  • libfoo.so that depends on nothing (well, it depends on libstdc++ and stuff but I guess we can omit that)

program runs perfectly.

Suddenly, libfoo codes changes, and some function now uses internally func_bar() a function that is provided by another library libbar.so.

libfoo.so is recompiled and now depends on libbar.so. program remains unchanged, it still depends only on libfoo.so.

Now when I execute program it complains that he can't find func_bar().

Here are my questions:

  • libfoo.so interface didn't change, only its implementation. Why does program have to explicitely link with libbar.so ?
  • Isn't the dependency tree recursive ? I would have think that since libfoo.so depends on libbar.so, libbar.so would have been automatically added to the dependency list of program, without recompilation. However, ldd program shows that it is not the case.

It seems weird that one has to recompile (relink) every binary that depends on some library everytime that library's dependencies change. What solutions do I have here to prevent this ?

Kendry answered 5/11, 2010 at 16:7 Comment(1)
Can you post a minimal configuration that reproduces the problem? vitaut's comments following his post seem to describe the same process and does not arrive at the same problem. Maybe if you put simple steps, we could help with answering the question?Digastric
C
18

The problem arises when you have not linked libfoo.so against libbar. When you are compiling an executable, by default the linker will not let you leave undefined references. However, when you're compiling a shared library, it will - and it will expect them to be satisfied at link time. This is so that libfoo can use functions exported by program itself - when you try to run it, the dynamic linker is expecting func_bar() to be supplied by program. The problem is illustrated like so:

(foo.c is selfcontained)

export LD_RUN_PATH=`pwd`
gcc -Wall -shared foo.c -o libfoo.so
gcc -Wall -L. p.c -lfoo -o p

At this point, ./p runs correctly, as you would expect. We then create libbar.so and modify foo.c to use it:

gcc -Wall -shared bar.c -o libbar.so
gcc -Wall -shared foo.c -o libfoo.so

At this point, ./p gives the error you describe. If we check ldd libfoo.so, we notice that it does not have a dependency on libbar.so - this is the error. To correct the error, we must link libfoo.so correctly:

gcc -Wall -L. -lbar -shared foo.c -o libfoo.so

At this point, ./p again runs correctly, and ldd libfoo.so shows a dependency on libbar.so.

Chesnut answered 8/11, 2010 at 6:16 Comment(1)
You were right: I wasn't linking with libbar.so. Works like a charm now ! Many thanks !Kendry
Z
9

On Fedora dynamic linking is performed by ld-linux.so.2. The dynamic linker use /etc/ld.so.cache and /etc/ld.so.preload to find library files.

Run ldconfig to tell the system where libfoo should look for libbar.

ldconfig looks in /lib, /usr/lib and any directory listed in /etc/ld.so.conf. You can check which libraries a program uses with ldd.

More details are available on the manual pages for each command.

Here is an example of an application using shared libraries.
Program.cc

#include "foo.h"
#include <iostream>

int main(int argc, char *argv[])
{
    for (int i = 0; i < argc; ++i) {
        std::cout << func_foo(argv[i]) << std::endl;
    }
}

foo.h

#ifndef FOO_H
#define FOO_H
#include <string>
std::string func_foo(std::string const &);
#endif

foo.cc

#include "foo.h"

std::string func_foo(std::string const &arg)
{
    return arg + "|" + __func__;
}

bar.h

#ifndef BAR_H
#define BAR_H
#include <string>
std::string func_bar();
#endif

bar.cc

#include "bar.h"

std::string func_bar()
{
    return __func__;
}

Build with libfoo.so as a shared library.
g++ -Wall -Wextra -fPIC -shared foo.cc -o libfoo.so
g++ -lfoo -L./ -Wall -Wextra program.cc foo.h -o program
ldd program
...
libfoo.so => not found

Update /etc/ld.so.cache
sudo ldconfig /home/tobias/projects/stubs/so/

ldd shows that the dynamic linker finds libfoo.so
ldd program
...
libfoo.so => /home/tobias/projects/stubs/so/libfoo.so (0x00007f0bb9f15000)

Add a call to libbar.so in libfoo.so
New foo.cc

#include "foo.h"
#include "bar.h"

std::string func_foo(std::string const &arg)
{
    return arg + "|" + __func__ + "|" + func_bar();
}

Build libbar.so and rebuild libfoo.so
g++ -Wall -Wextra -fPIC -shared bar.cc -o libbar.so
g++ -Wall -Wextra -fPIC -shared libbar.so foo.cc -o libfoo.so
ldd libfoo.so
...
libbar.so => not found

ldd program
...
libfoo.so => /home/tobias/projects/stubs/so/libfoo.so (0x00007f49236c7000)
libbar.so => not found
This shows that the dynamic linker still finds libfoo.so but not libbar.so
Again update /etc/ld.so.cache and recheck.
sudo ldconfig /home/tobias/projects/stubs/so/
ldd libfoo.so
...
libbar.so => /home/tobias/projects/stubs/so/libbar.so (0x00007f935e0bd000)

ldd program
...
libfoo.so => /home/tobias/projects/stubs/so/libfoo.so (0x00007f2be4f11000)
libbar.so => /home/tobias/projects/stubs/so/libbar.so (0x00007f2be4d0e000)

Both libfoo.so and libbar.so are found.

Note this last step have no effect on the application program. If you are really strict running ldconfig is kind of relinking. Weird or not the linker need to know the dependencies of the libraries it links. There are a lot of other ways to implement this but this was chosen.

Zennie answered 5/11, 2010 at 17:40 Comment(0)
C
2

You did not give any system information, are you using glibc? If yes what is the output of this command:

LD_DEBUG=files program

Also check "How to write shared (ELF) libraries"(pdf) (whether you are using glibc or not)

Chemise answered 5/11, 2010 at 16:29 Comment(0)
C
1

Your program doesn't have to link with libbar.so.

I think that the problem is caused by failing to specify libbar.so as a dependency of libfoo.so when building the later. I am not sure what build system are you using but in CMake it can be done as follows:

add_library(bar SHARED bar.c)

add_library(foo SHARED foo.c)
target_link_libraries(foo bar)

add_executable(program program.c)
target_link_libraries(program foo)

As you can see program is linked only with foo (libfoo.so) and foo only with bar (libbar.so).

Or it may be that libbar.so can't be found. Try to specify the path to its directory in LD_LIBRARY_PATH environment variable.

Ceballos answered 5/11, 2010 at 16:34 Comment(5)
The question is what CMake does behind the scenes, it might be smart enough to build the right link command for programBentonbentonite
@Peer Stritzinger: Good question. I thought about this and did some experiments (building program while foo didn't have a link to bar, then building foo and bar, but not program) and it worked. So the magic from cmake side seems unlikely in this case, although it is smart indeed =).Ceballos
Does this really answer the question? Of course, linking libbar in with libfoo and then linking with program is going to work, independantly of the build system. However, if program is linked with libfoo and then libfoo is updated to link with libbar this might not be the case. If your CMake script, it is clear that program relinks with libfoo if libfoo changes to link with libbar without any magic.Digastric
Try the following test: write 2 CMake scripts, one that builds program by linking it with libfoo and one that builds libfoo. Proceed as OP asked: build libfoo, then build program and run the test. Then, change libfoo to link with libbar, re-build libfoo but don't rebuild program. Try to see if it still works.Digastric
@André Caron: Sorry if it was not clear from my answer, but I did almost exactly what you described in the last post.Ceballos
U
1

This should not be the case unless something about the bar_func symbol changed. Use the "nm" command to get a dump of the symbols in both your program, and the shared object - see if there is a mismatch and why.

Uniformed answered 5/11, 2010 at 17:42 Comment(1)
My read of the question is that the change in bar_func() is that previously it was not used, but now it is used.Viaticum

© 2022 - 2024 — McMap. All rights reserved.