ldd报告的库是否可以解析输入库的所有未定义引用?

时间:2019-01-07 09:45:05

标签: linux ld ldd

我想知道ldd到底是做什么的。是否仅打印DT_NEEDED节的.dynamic结构中的库?据我所知,这不是解析ldd输入库的所有未定义符号所需的库的完整列表。在这种情况下,ldd的用途是什么?

还是真的列出了ldd输入库实际依赖的所有库?

这不是关于ldd是否显示依赖关系的问题-这是关于ldd报告的库是否可以解析ldd输入库的所有未定义符号的问题。

3 个答案:

答案 0 :(得分:3)

  

ldd仅打印DT_NEEDED节的.dynamic结构中的库吗?

不,这就是readelf --dynamic的作用。

  

ldd的全部用途是什么?

ldd显示运行时链接程序ld.so在启动可执行文件或加载共享库时加载的库。这是一个递归过程,例如可执行文件需要共享库(DT_NEEDED),以便加载库。然后继续加载已加载库(DT_NEEDED)的依存关系,依此类推。

您不一定需要ldd,只需设置LD_DEBUG=all环境变量即可使ld.so打印该信息以及更多信息。有关更多信息,请参见man ld.so

每个已加载的可执行文件或共享库将其定义的导出动态符号作为查找范围(哈希表)公开。 查找范围形成一个列表。解析未定义的符号ld.so时,将遍历查找范围并找到定义该符号并解析符号引用的第一个符号。如果ld.so到达查找范围的末尾,它将报告该符号为未解析。

未解析的符号名称与其应来自的可执行文件/共享库之间没有对应关系。 ld.so递归加载DT_NEEDED节中的所有共享库,构建查找范围列表,然后在其中查找未解析的符号。

How To Write Shared Libraries by U. Drepper对此进行了详细说明。

答案 1 :(得分:3)

无法从共享库本身派生解析共享库中未定义符号所需的库列表。这样的列表可能存在或可能不存在。创建带有未定义符号的库很容易,该符号无法被世界上任何现有的库解析。

# cat test.c 
extern void foo99988776543quzzu();
void test() {
    foo99988776543quzzu();
}
# gcc -fPIC -shared -o libtest.so test.c

在这里,我们创建了一个带有未定义符号的库,这是世界上其他任何库都无法满足的,直到我们建立一个库为止。

# cat foo.c 
void foo99988776543quzzu() {}
# gcc -fPIC -shared -o libfoo.so foo.c

世界上没有魔术可以帮助ldd libtest.so找到libfoo.so。但是从libtest.so和libfoo.so构建可加载,可运行的程序很容易。

# cat main.c
extern void test();
int main() { test(); }
# gcc main.c -lfoo -ltest -L. -Wl,-rpath=.
# ./a.out

ldd不会尝试生成不可能的librarir列表,以解决未定义的符号。它的功能与锡罐上所说的完全相同:

  

ldd打印命令行上指定的每个程序或共享库所需的共享库(共享库)。

此处的“必需”一词并不表示“需要解析未定义的符号”。如上所述,无法生成解析未定义符号所需的对象列表。相反,“必需”是指一组动态依赖项,也就是“递归DT_NEEDED所需的共享对象”,如ldd(1)和ld.so(8)中所述。

  

ldd的用途是什么?

DT_NEEDED部分包含 sonames 。 ldd使用DT_RUNPATH,DT_RPATH,LD_LIBRARY_PATH,/ etc / ld.so.conf中的信息以及我们在本周搜索的所有位置,以递归方式收集这些名称,并将它们映射到文件路径。因此,ldd的输出包含一个共享对象的文件路径列表,该文件路径将在ldd命令行上的共享库被加载时被加载。这是一个示例:

#ldd ./test
    linux-vdso.so.1 (0x00007fff0593f000)
    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fe7e6776000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe7e6385000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fe7e5fe7000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fe7e6d01000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fe7e5dcf000)

即使对于大多数偶然的观察者来说,这也是一堆有用的信息。我们看到test似乎是x86 Linux的64位C ++程序,例如,它是用gcc的较新版本或兼容的编译器构建的。我们还看到它没有第三方依赖性。

另一方面,

# ldd  /usr/bin/kdiff3
    linux-vdso.so.1 (0x00007ffeeed79000)
    libkparts.so.4 => /usr/lib/libkparts.so.4 (0x00007f801a14d000)
    libkio.so.5 => /usr/lib/libkio.so.5 (0x00007f8019c9b000)
    libkdeui.so.5 => /usr/lib/libkdeui.so.5 (0x00007f8019637000)
    libkdecore.so.5 => /usr/lib/libkdecore.so.5 (0x00007f801916b000)
    libQtCore.so.4 => /usr/lib/x86_64-linux-gnu/libQtCore.so.4 (0x00007f8018c79000)
    libQtGui.so.4 => /usr/lib/x86_64-linux-gnu/libQtGui.so.4 (0x00007f8017f84000)
    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f8017bfb000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f801785d000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f801746c000)
    libQtXml.so.4 => /usr/lib/x86_64-linux-gnu/libQtXml.so.4 (0x00007f8017226000)
    libQtNetwork.so.4 => /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4 (0x00007f8016ed1000)
    libQtSvg.so.4 => /usr/lib/x86_64-linux-gnu/libQtSvg.so.4 (0x00007f8016c78000)
    libX11.so.6 => /usr/lib/x86_64-linux-gnu/libX11.so.6 (0x00007f8016940000)
    ... many more lines ...

列出了很多依赖项。如果加载失败,我们应该可以使用列表找出原因。例如,任何显示=> not found的行都会很有帮助。

答案 2 :(得分:2)

  

ldd报告的库是否可以解析输入库的所有未定义引用?

不。可以链接包含未定义引用的共享库 (这很平常)。因此它可能被链接包含未定义的引用, 不会通过其任何(递归)DSO依赖关系或存在的任何DSO或目标文件来解决。

foo.c

#include <stdio.h>

extern void bar(void);

void foo(void)
{
    puts(__func__);
    bar();
}

制作共享库:

$ gcc -shared -o libfoo.so foo.c

ldd libfoo.so递归列出libfoo.so的DSO依赖项:

$ ldd libfoo.so
    linux-vdso.so.1 (0x00007ffc30bf5000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd19209b000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fd19268e000)

它们都不解决对bar的未定义引用。