26

考虑我们有以下情况:

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

program完美运行。

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

libfoo.so已重新编译,现在取决于libbar.so. program保持不变,它仍然只取决于libfoo.so.

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

以下是我的问题:

  • libfoo.so接口没有改变,只有它的实现。为什么program必须明确链接到libbar.so
  • 依赖树不是递归的吗?我会认为,由于libfoo.so依赖libbar.so,libbar.so会自动添加到 的依赖列表中program而无需重新编译。然而,ldd program事实证明并非如此。

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

4

5 回答 5

18

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

foo.c是独立的)

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.

于 2010-11-08T06:16:49.183 回答
9

在 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 检查程序使用哪些库。

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

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

酒吧.h

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

酒吧.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 program
ldd program
...
libfoo.so => not found

更新 /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 program
...
libfoo.so => /home/tobias/projects/stubs/so/libfoo.so (0x00007f49236c7000)
libbar.so => not found
这表明动态链接器仍然找到 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 是一种重新链接。链接器是否奇怪需要知道它链接的库的依赖关系。有很多其他方法可以实现这一点,但选择了这个。

于 2010-11-05T17:40:34.703 回答
2

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

LD_DEBUG=文件程序

还要检查“如何编写共享(ELF)库”(pdf)(无论您是否使用 glibc)

于 2010-11-05T16:29:14.463 回答
1

您的程序不必与 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链接,并且仅与( ) 链接。foolibfoo.sofoobarlibbar.so

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

于 2010-11-05T16:34:51.703 回答
1

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

于 2010-11-05T17:42:43.317 回答