了解动态链接在 UNIX 上的工作原理

发布于 2024-10-01 07:58:20 字数 972 浏览 1 评论 0原文

考虑我们有以下情况:

  • 一个名为 program 的程序动态依赖于 libfoo.so
  • libfoo.so 不依赖于任何东西(嗯,它取决于 libstdc++ 和其他东西,但我想我们可以忽略它)

program 运行完美。

突然,libfoo 代码发生了变化,某些函数现在在内部使用 func_bar() 另一个库 libbar.so 提供的函数。

libfoo.so 已重新编译,现在依赖于 libbar.soprogram 保持不变,它仍然仅依赖于 libfoo.so

现在,当我执行program时,它抱怨找不到func_bar()

这是我的问题:

  • libfoo.so 接口没有改变,只有它的实现。为什么program必须显式libbar.so链接?
  • 依赖树不是递归的吗?我本以为,由于 libfoo.so 依赖于 libbar.so,因此 libbar.so 会自动添加到 libbar.so 的依赖列表中代码>程序,无需重新编译。然而,ldd 程序表明情况并非如此。

每当库的依赖项发生变化时,就必须重新编译(重新链接)每个依赖于某个库的二进制文件,这似乎很奇怪。我有什么解决方案可以防止这种情况发生?

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 ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

从来不烧饼 2024-10-08 07:58:20

当您没有将 libfoo.so 链接到 libbar 时,就会出现问题。当您编译可执行文件时,默认情况下链接器不会让您留下未定义的引用。但是,当您编译共享库时,它 - 并且它会期望它们在链接时得到满足。这样 libfoo 就可以使用 program 本身导出的函数 - 当您尝试运行它时,动态链接器期望 func_bar()程序提供。问题如下所示:(

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

此时,./p 正确运行,正如您所期望的那样。然后,我们创建 libbar.so 并修改 foo.c 以使用它:

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

此时,./p 给出您所描述的错误。如果我们检查ldd libfoo.so,我们会注意到它依赖于libbar.so - 这就是错误。要纠正错误,我们必须正确链接 libfoo.so

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

此时,./p 再次正确运行,并且 ldd libfoo.so显示对 libbar.so 的依赖。

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.

在 Fedora 上,动态链接由 ld-linux.so.2 执行。
动态链接器使用/etc/ld.so.cache和/etc/ld.so.preload来查找库文件。

运行 ldconfig 告诉系统 libfoo 应在哪里查找 libbar。

ldconfig 查找 /lib、/usr/lib 以及 /etc/ld.so.conf 中列出的任何目录。
您可以使用 ldd 检查程序使用哪些库。

每个命令的手册页上提供了更多详细信息。

这是使用共享库的应用程序的示例。
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__;
}

使用 libfoo.so 作为共享库进行构建。
g++ -Wall -Wextra -fPIC -shared foo.cc -o libfoo.so
g++ -lfoo -L./ -Wall -Wextra program.cc foo.h -o 程序
ldd程序
...
libfoo.so =>;未找到

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

ldd 显示动态链接器找到 libfoo.so
ldd程序
...
libfoo.so =>; /home/tobias/projects/stubs/so/libfoo.so (0x00007f0bb9f15000)

在 libfoo.so 中添加对 libbar.so 的调用
新建 foo.cc

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

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

构建 libbar.so 并重建 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 =>;未找到

ldd 程序
...
libfoo.so =>; /home/tobias/projects/stubs/so/libfoo.so (0x00007f49236c7000)
libbar.so =>;未找到
这表明动态链接器仍然找到 libfoo.so 但找不到 libbar.so
再次更新 /etc/ld.so.cache 并重新检查。
sudo ldconfig /home/tobias/projects/stubs/so/
ldd libfoo.so
...
libbar.so =>; /home/tobias/projects/stubs/so/libbar.so (0x00007f935e0bd000)

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

libfoo.so 和 libbar.so 均已找到。

注意最后一步对应用程序没有影响。
如果你真的很严格,运行 ldconfig 就是一种重新链接。
不管奇怪与否,链接器需要知道它链接的库的依赖关系。
有很多其他方法可以实现这一点,但我们选择了这个。

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.

苍景流年 2024-10-08 07:58:20

你没有提供任何系统信息,你使用的是glibc吗?如果是,此命令的输出是什么:

LD_DEBUG=files program

另请检查 “如何编写共享(ELF) ) 库"(pdf)(无论您是否使用 glibc)

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)

垂暮老矣 2024-10-08 07:58:20

您的程序不必与 libbar.so 链接。

我认为该问题是由于在构建后者时未能将 libbar.so 指定为 libfoo.so 的依赖项引起的。
我不确定您使用的是什么构建系统,但在 CMake 中可以按如下方式完成:

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)

如您所见 program 仅与 foo (libfoo. so) 和 foo 仅与 bar (libbar.so) 一起使用。

或者可能是找不到libbar.so。尝试在 LD_LIBRARY_PATH 环境变量中指定其目录的路径。

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.

So要识趣 2024-10-08 07:58:20

除非有关 bar_func 符号 的某些内容发生更改,否则情况不应如此。使用“nm”命令获取程序和共享对象中的符号转储 - 查看是否存在不匹配以及原因。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文