Проверка разделяемых библиотек для нестандартных загрузчиков

ldd - это хороший простой способ проверить наличие общих библиотек, которые использует или будет использовать данный исполняемый файл. Однако это не всегда работает так, как ожидалось. Например, см. Следующий фрагмент кода, который демонстрирует, как он "не удается" найти зависимость "libreadline" в двоичном файле python

Я пробовал много других дистрибутивов, но я копирую из 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 *

У меня есть два вопроса:

  • Я считаю, что проблема с ldd заключается в том, что он предполагает использование стандартного загрузчика, тогда как очень вероятно, что python использует свой собственный специальный загрузчик (так что вам не нужно повторно запускать исполняемый файл каждый раз, когда вы установите новый модуль python, который не является чистым python, но имеет некоторый код c/С++/fortran). Правильно ли это?

  • Очевидно, что если исполняемый файл использует свой собственный загрузчик, нет очевидного ответа на вопрос "как найти все возможные библиотеки, которые может загружать этот исполняемый файл": это зависит от того, что делает загрузчик. Но есть ли способ узнать, какие библиотеки могут быть загружены с помощью 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.

Ответ 1

Python, Perl и другие интерпретируемые языки динамически загружают вещи, используя dlopen(). (Это не то же самое, что замена стандартного загрузчика, они все еще используют это, и фактически dlopen() является привязкой к стандартным загрузчикам на основе ELF-систем.)

Нет стандартного реестра для загружаемых модулей. Python использует свои собственные правила, чтобы определить, где можно загрузить модули расширения (посмотрите sys.path), включая те, которые имеют связанные общие объекты. Perl использует разные правила. Apache использует еще разные правила и т.д.

Итак, суммируем ответы на ваши вопросы:

  • не точно

    нет
  • нет

Ответ 2

В качестве побочного примечания, возможный способ выполнить то, что я хотел в вопросе 2, было бы:

  • создать пустую chrooted среду

  • перекомпилируйте python там, вручную добавив все, что отсутствует, по одному

В зависимости от ваших целей это может быть или не быть хорошим решением (и на самом деле оказывается не так уж плохо для моих целей - странно, как может показаться из вопроса)