В Linux исполняемые файлы можно условно разделить на две группы – те, которые содержат в себе весь код, необходимые для работы, и те, которым необходимы разделяемые библиотеки. Первые называют статически собранными бинарными файлами, вторые называют динамически собранными исполняемыми файлами.
Статически собранные программы характеризуются тем, что могут корректно функционировать в любых условиях, и не зависят от наличия или отсутствия разделяемых библиотек, что может оказаться полезным в ситуациях, когда возникают конфликты версий разделяемых библиотек, или когда системные библиотеки повреждены или недоступны (например во время восстановления операционной системы после серьезного сбоя). К недостаткам таких исполняемых файлов следует отнести то, что они имеют значительный размер и для обновления программы необходимо полностью заменить ее исполняемый файл – например, если несколько статически собранных программ, которые работают с архивами ZIP, содержат ошибку, то для исправления ошибки необходимо заменить все эти программы, что может быть затруднено (например, будет трудно точно установить, какие именно программы содержат ошибочный код и нуждаются в обновлении). Кроме того, статически собранные программы не умеют совместно использовать совпадающие участки кода, что ведет к излишнему расходу системных ресурсов.
Динамически собранные исполняемые файлы для корректной работы требуют наличия файлов разделяемых библиотек, и соответственно при их отсутствии/повреждении не могут корректно функционировать, но зато для обновления программы и исправления ошибки часто оказывается достаточным просто заменить соответствующую разделяемую библиотеку, после чего ошибка исчезает во всех программах, которые эту библиотеку используют динамически. Динамически связанные программы также значительно меньше по объему, чем статически связанные, и код разделяемых библиотек может использоваться одновременно многими программами – что позволяет экономить системные ресурсы.
Подавляющее большинство программ в современных дистрибутивах Linux являются динамически собранными. Определить тип исполняемого файла (статический ли он либо с динамическим связыванием) можно, например, с помощью команды ldd:
# ldd /bin/su
linux-gate.so.1 => (0xb77d0000)
libpam.so.0 => /lib/libpam.so.0 (0xb77be000)
libpam_misc.so.0 => /lib/libpam_misc.so.0 (0xb77bb000)
libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb765f000)
libdl.so.2 => /lib/i686/cmov/libdl.so.2 (0xb765b000)
/lib/ld-linux.so.2 (0xb77d1000)
Но здесь нас подстерегает несколько НО:
1) Существует способ загрузки библиотек в обход ldd, функцией dlopen(). Такие библиотеки в выводе ldd не отображены.
2) Принцип работы ldd состоит в следующем: она устанавливает переменную окружения LD_TRACE_LOADED_OBJECTS, а затем запускает программу на исполнение. Входящий в libc загрузчик динамических библиотек (для GNU glibc это ld-linux.so) проверяет наличие этой переменной. Если она установлена, то вместо нормального запуска программы он выводит список используемых ею динамически подгружаемых библиотек и завершает работу.
То есть, мало того, что не все библиотеки (в идеале) мы сможем увидеть, так ещё и запускаем на исполнение файл! А если это руткит? Что же делать? Выход есть, правда не совсем удобный.
Выход первый (для пункта 2): использовать вместо ldd -> lddsafe (https://github.com/rg3/lddsafe) Скрипт использует objdump и безопасен, так как не запускает на исполнение проверяемую программу.
Выход второй (для пунктов 1 и 2): использовать команду strace. Правда вывод не такой удобный будет, как в случае с ldd.
Так же можно эту информацию получить и таким образом:
# readelf -d /sbin/fsck / grep 'NEEDED'
0x00000001 (NEEDED) Shared library: [libblkid.so.1]
0x00000001 (NEEDED) Shared library: [libc.so.6]
Для более детальной вычинки бинарника можно использовать утилиту objdump и strace.
Что же делать, если вы получаете сообщения такого плана:
error while loading shared libraries: xxxx.so.0
cannot open shared object file no such file or directory
Это значит, что нет подходящих библиотек. Но что делать, если они есть, а система их не находит? На помощь приходит утилита ldconfig, которая управляет динамическими библиотеками.
С помощью этой программы вы можете добавить свои библиотеки, просматривать кеш библиотек. Список подключаемых библиотек находится в файле /etc/ld.so.conf, а кеш (собственно откуда берут информацию /lib/ld-linux.so) – /etc/ld.so.cache.
– Создать кэш
#ldconfig
– Посмотреть список библиотек
# ldconfig -p | grep mysql
libmysqlclient_r.so.15 (libc6) => /usr/lib/libmysqlclient_r.so.15
libmysqlclient.so.15 (libc6) => /usr/lib/libmysqlclient.so.15
Подробнее можно узнать в справочном руководстве.
Так же можно использовать переменную окружения LD_LIBRARY_PATH.
Примечание.
В Solaris это можно сделать командой dtrace. Кстати, есть хорошая новость – её пытаются портировать на Linux.
Во FreeBSD используется команда truss