9

ldd是检查给定可执行文件正在或将要使用的共享库的好方法。但是,它并不总是按预期工作。例如,请参阅以下 shell 片段,该片段演示了如何在 python 二进制文件中“失败”找到 libreadline“依赖项”

我尝试了许多其他发行版,但我是从 Tikanga 复制的

$ lsb_release -a
LSB Version:    :core-4.0-amd64:core-4.0-ia32:core-4.0-noarch:graphics-4.0-amd64:graphics-4.0-ia32:graphics-4.0-noarch:printing-4.0-amd64:printing-4.0-ia32:printing-4.0-noarch
Distributor ID: RedHatEnterpriseServer
Description:    Red Hat Enterprise Linux Server release 5.6 (Tikanga)
Release:        5.6
Codename:       Tikanga

查看ldd默认安装的内容python(来自官方存储库)。

$ which python
/usr/bin/python
$ ldd `which python`
    libpython2.4.so.1.0 => /usr/lib64/libpython2.4.so.1.0 (0x00000030e6200000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x00000030e0e00000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00000030e0a00000)
    libutil.so.1 => /lib64/libutil.so.1 (0x00000030ee800000)
    libm.so.6 => /lib64/libm.so.6 (0x00000030e0600000)
    libc.so.6 => /lib64/libc.so.6 (0x00000030e0200000)
    /lib64/ld-linux-x86-64.so.2 (0x00000030dfe00000)
$ ldd `which python` | grep readline
$

没有找到关于 readline 的信息。现在我从交互式使用中确实知道这个二进制文件确实具有实时功能,所以让我们不要尝试查看它的来源。

$ python &
[1] 21003
$ Python 2.4.3 (#1, Dec 10 2010, 17:24:35) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-50)] on linux2
Type "help", "copyright", "credits" or "license" for more information.

[1]+  Stopped                 python

在后台启动交互式 python 会话(pid 21003)

$ lsof -p 21003
COMMAND   PID    USER   FD   TYPE DEVICE     SIZE    NODE NAME
python  21003 ddvento  cwd    DIR   0,33    16384  164304 /glade/home/ddvento/loader-test
python  21003 ddvento  rtd    DIR    8,3     4096       2 /
python  21003 ddvento  txt    REG    8,3     8304 6813419 /usr/bin/python
python  21003 ddvento  mem    REG    8,3   143600 8699326 /lib64/ld-2.5.so
python  21003 ddvento  mem    REG    8,3  1722304 8699327 /lib64/libc-2.5.so
python  21003 ddvento  mem    REG    8,3   615136 8699490 /lib64/libm-2.5.so
python  21003 ddvento  mem    REG    8,3    23360 8699458 /lib64/libdl-2.5.so
python  21003 ddvento  mem    REG    8,3   145824 8699445 /lib64/libpthread-2.5.so
python  21003 ddvento  mem    REG    8,3   247544 6821551 /usr/lib64/libreadline.so.5.1
python  21003 ddvento  mem    REG    8,3    15840 8699446 /lib64/libtermcap.so.2.0.8
python  21003 ddvento  mem    REG    8,3  1244792 6833317 /usr/lib64/libpython2.4.so.1.0
python  21003 ddvento  mem    REG    8,3    18152 8699626 /lib64/libutil-2.5.so
python  21003 ddvento  mem    REG    8,3 56446448 6832889 /usr/lib/locale/locale-archive
python  21003 ddvento  mem    REG    8,3    21808 6965997 /usr/lib64/python2.4/lib-dynload/readline.so
python  21003 ddvento  mem    REG    8,3    25464 6901074 /usr/lib64/gconv/gconv-modules.cache
python  21003 ddvento    0u   CHR  136,1                3 /dev/pts/1
python  21003 ddvento    1u   CHR  136,1                3 /dev/pts/1
python  21003 ddvento    2u   CHR  136,1                3 /dev/pts/1
$ lsof -p 21003 | grep readline
python  21003 ddvento  mem    REG    8,3   247544 6821551 /usr/lib64/libreadline.so.5.1
python  21003 ddvento  mem    REG    8,3    21808 6965997 /usr/lib64/python2.4/lib-dynload/readline.so

答对了!这里是readline!

但是,此技术仅在库被有效加载时才有效,因此例如,/usr/lib64/libtcl8.4.so直到 python 进程不运行类似from Tkinter import *

所以我有两个问题:

  1. 我认为问题ldd在于它假设使用标准加载器,而 python 很可能正在使用它自己的特殊加载器(这样您就不必每次安装新的非纯 python 模块时重新链接可执行文件python 但有一些 c/c++/fortran 代码)。它是否正确?

  2. 显然,如果一个可执行文件使用它自己的加载器,那么“如何找到这个可执行文件可能加载的所有可能的库”这个问题没有明显的答案:这取决于加载器的作用。但是有没有办法找出 python 可以加载哪些库?

PS:与 1 相关。如果您遇到这个问题,您应该已经知道以下内容,但如果不知道,您应该:看看完全弄乱ldd输出是多么简单(仅部分弄乱它有点难):

$ cat hello.c 
#include <stdio.h>

int main() {
  printf("Hello world.\n");
  return 0;
}

$ gcc -static hello.c -o loader
$ gcc -Wl,--dynamic-linker,./loader hello.c -o hello
$ ./hello 
Hello world.
$ ldd ./hello
Hello world.
4

3 回答 3

8

Python、Perl 和其他解释语言确实使用dlopen(). (这与替换标准加载器不同;他们仍在使用它,实际上dlopen()是基于 ELF 系统上标准加载器的挂钩。)

可加载模块没有标准注册表。Python 使用自己的规则来确定可以从哪里加载扩展模块(查看sys.path),包括那些具有关联共享对象的模块。Perl 使用不同的规则。Apache 使用仍然不同的规则等。

因此,总结一下您的问题的答案:

  1. 不完全是

于 2012-04-20T20:41:40.233 回答
1

作为旁注,完成我在问题 2 中想要的一种可能的方法是:

  • 创建一个空的 chroot 环境

  • 在那里重新编译python,手动添加任何缺少的东西,一个接一个

根据您的目标,这可能是也可能不是一个好的解决方案(实际上对于我的目标来说并不算太糟糕 - 从问题中听起来可能很奇怪)

于 2012-04-23T16:06:11.667 回答
0

使用 ldd -v 也可以转储库依赖项。这是一个例子:

ave-ssh-test:tmp# ldd -v /sbin/coop.bin
        linux-vdso.so.1 =>  (0x00007fffd45f0000)
        libstdc++.so.6 => /lib64/libstdc++.so.6 (0x0000730cace67000)
.
.
.
Version information:

/lib64/libstdc++.so.6:
                ld-linux-x86-64.so.2 (GLIBC_2.3) => /lib64/ld-linux-x86-64.so.2
                libm.so.6 (GLIBC_2.2.5) => /lib64/libm.so.6
                libgcc_s.so.1 (GCC_4.2.0) => /lib64/libgcc_s.so.1
                libgcc_s.so.1 (GCC_3.3) => /lib64/libgcc_s.so.1
                libgcc_s.so.1 (GCC_3.0) => /lib64/libgcc_s.so.1
                libc.so.6 (GLIBC_2.14) => /lib64/libc.so.6
                libc.so.6 (GLIBC_2.4) => /lib64/libc.so.6
                libc.so.6 (GLIBC_2.3) => /lib64/libc.so.6
                libc.so.6 (GLIBC_2.3.2) => /lib64/libc.so.6
                libc.so.6 (GLIBC_2.2.5) => /lib64/libc.so.6
于 2019-12-17T23:57:53.910 回答