2010-11-05 31 views
25

考虑,我们有以下的情况:了解如何动态链接适用于UNIX

  • 名为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本来会自动添加到program,的依赖列表中,而没有重新编译。但是,ldd program表明情况并非如此。

这似乎不可思议的是人们必须重新编译(重新链接)每个二进制依赖于某些库,每次该库的依赖关系发生变化。我有什么解决方案来防止这种情况发生?

+0

你能发布一个最小化的配置来重现问题吗? Vitaut在他的帖子后的评论似乎描述了同样的过程,并没有达到同样的问题。也许如果你采取简单的步骤,我们可以帮助回答这个问题? – 2010-11-05 20:29:35

回答

17

如果您还没有链接libfoo.solibbar,则会出现问题。在编译可执行文件时,默认情况下,链接程序不会让您留下未定义的引用。但是,当您编译共享库时,它将 - 它会期望它们在链接时满意。这样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的依赖。

+0

你说得对:我没有用'libbar.so'链接。现在就像一个魅力!非常感谢 ! – ereOn 2010-11-08 08:22:58

2

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

LD_DEBUG =文件程序

还要检查"How to write shared (ELF) libraries"(PDF)(无论您使用的glibc与否)

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.so)和foo只有barlibbar.so)链接。或者可能找不到libbar.so。尝试在LD_LIBRARY_PATH环境变量中指定其目录的路径。

+1

问题是CMake在幕后做了什么,它可能足够聪明,可以为程序 – 2010-11-05 16:39:02

+0

建立正确的链接命令@Peer Stritzinger:很好的问题。我想到了这一点,并做了一些实验(建立'program',而'foo'没有链接到'bar',然后建立'foo'和'bar',但不是'program')并且工作。所以在这种情况下,cmake方面的魔法似乎不太可能,尽管确实很聪明=)。 – vitaut 2010-11-05 16:52:12

+1

这是否真的回答了这个问题?当然,将'libbar'与'libfoo'连接起来,然后*与'program'连接就可以独立工作,而不依赖于构建系统。但是,如果'program'与'libfoo'链接,然后'libfoo'更新为'libbar'链接,则可能不是这种情况。如果你的CMake脚本,如果'libfoo'改变为与libbar链接没有任何魔力,'program'显然与'libfoo'重新链接。 – 2010-11-05 17:12:02

8

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作为共享库进行构建。
克++ -Wall -Wextra -fPIC -shared foo.cc -o libfoo.so
克++ -lfoo -L./ -Wall -Wextra program.cc foo.h中-o程序
LDD程序
...
libfoo.so =>未找到

更新/etc/ld.so.cache中
须藤LDCONFIG /家庭/托比亚斯/项目/株/所以/

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

添加调用libbar.so在libfoo.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
克++ -Wall -Wextra -fPIC -shared bar.cc -o libbar.so
克++ -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的。所以
再次更新/etc/ld.so.cache并重新检查。
须藤LDCONFIG /家庭/托比亚斯/项目/株/所以/
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 => /首页/托比亚斯/项目/存根/ so/libbar.so(0x00007f2be4d0e000)

B找到了libfoo.so和libbar.so。

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

+0

感谢您的信息:) – ereOn 2010-11-08 08:21:46

1

这不应该是这种情况,除非有关bar_func 符号的变化。使用“nm”命令获取程序和共享对象中的符号转储 - 查看是否存在不匹配以及原因。

+1

我读到的问题是,'bar_func()'的变化是以前它没有被使用,但现在它被使用。 – 2010-11-08 06:43:17