ld.so(8) динамический компоновщик/загрузчик

Other Alias

ld-linux.so*

ОБЗОР

Динамический компоновщик может запускаться неявно из динамически скомпонованной программы или общего объекта (в этом случае динамическому компоновщику невозможно передать параметры командной строки, как и в случае с ELF, когда динамический компоновщик хранится в разделе .interp исполняемой программы) или явно с помощью вызова:

/lib/ld-linux.so.* [ПАРАМЕТРЫ] [ПРОГРАММА [АРГУМЕНТЫ]]

ОПИСАНИЕ

Программы ld.so и ld-linux.so* ищут и загружают общие объекты (общие библиотеки), необходимые программе, а также подготавливают программу к запуску и запускают её.

Для двоичных файлов Linux требуется динамическая компоновка (компоновка во время выполнения), если при сборке программе ld(1) не был передан параметр -static.

Программа ld.so предназначена для обработки двоичных файлов в формате a.out (старый формат); ld-linux.so* предназначена для обработки файлов в формате ELF (/lib/ld-linux.so.1, если используется libc5 и /lib/ld-linux.so.2, если glibc2), который используется последние несколько лет. Обе программы ведут себя одинаково и используют те же самые файлы поддержки и программы ldd(1), ldconfig(8) и /etc/ld.so.conf.

При определении зависимостей общего объекта, динамический компоновщик сначала просматривает каждую строку зависимости в поисках символа косой черты (такое случается, если путь общего объекта с косой чертой был задан во время компоновки). Если косая черта найдена, то строка зависимости воспринимается как путь (относительный или абсолютный), и общий объект загружается исходя из этого пути.

Если в зависимости общего объекта отсутствует косая черта, то поиск выполняется в следующем порядке:

  • (только для ELF) В каталогах, указанных в атрибуте DT_RPATH динамического раздела двоичного файла, если он есть и если атрибут DT_RUNPATH не существует. Использование DT_RPATH не рекомендуется.
  • В переменной окружения LD_LIBRARY_PATH (если исполняемый файл не выполняется в режиме безопасного выполнения; смотрите далее, в этом случае она игнорируется).
  • (только для ELF) В каталогах, указанных в атрибуте DT_RUNPATH динамического раздела двоичного файла, если он есть.
  • В кэш-файле /etc/ld.so.cache, содержащем скомпилированный список кандидатов общих объектов, которые ранее были найдены по указанным путям расположения библиотек. Однако, если при сборке двоичного файла компоновщику был указан параметр -z nodeflib, то общие объекты в путях по умолчанию будут пропущены. Общие объекты, установленные в каталоги для аппаратных возможностей (см. далее) имеют больший приоритет, чем остальные общие объекты.
  • В каталоге по умолчанию /lib и затем в /usr/lib (на некоторых 64-битных архитектурах путь по умолчанию для 64-битных объектов — /lib64 и затем /usr/lib64). Если при сборке двоичного файла компоновщику был указан параметр -z nodeflib, то этот шаг пропускается.

Раскрытие токена rpath

ld.so распознаёт определённые строки согласно спецификации rpath (DT_RPATH или DT_RUNPATH); эти строки заменяются на

$ORIGIN (или ${ORIGIN})
Она раскрывается в каталог, содержащий программу или общий объект. Таким образом, приложение, расположенное в somedir/app может компилироваться с


    gcc -Wl,-rpath,'$ORIGIN/../lib'

для того, чтобы оно могло найти связанный общий объект в somedir/lib и не важно где в иерархии каталогов будет находиться somedir. Это облегчает создание приложений «под ключ», которые вместо установки в специальные каталоги, можно просто распаковать в любой каталог, и они всё равно найдут свои общие объекты.

$LIB (или ${LIB})
Она раскрывается в lib или lib64, в зависимости от архитектуры (например, на x86-64 она заменяется на lib64, а на x86-32 она заменяется на lib).
$PLATFORM (или ${PLATFORM})
Преобразуется в строку, соответствующую типу процессора узла (например, «x86_64»). На некоторых архитектурах ядро Linux не предоставляет строку платформы динамическому компоновщику. Значение этой строки берётся из значения AT_PLATFORM вспомогательного вектора (смотрите getauxval(3)).

ПАРАМЕТРЫ

--list
Выдать список всех зависимостей и как они удовлетворяются.
--verify
Проверить, что программа является динамически скомпонованной и что компоновщик понимает её формат.
--inhibit-cache
Не использовать /etc/ld.so.cache.
--library-path путь
Использовать путь вместо значения переменной окружения LD_LIBRARY_PATH (смотрите далее).
--inhibit-rpath список
Игнорировать информацию в RPATH и RUNPATH об именах объектов из списка. Этот параметр игнорируется в режиме безопасного выполнения (смотрите далее).
--audit список
Использовать объекты из списка в качестве аудиторов.

ОКРУЖЕНИЕ

На поведение динамического компоновщика влияют различные переменные окружения.

Безопасный режим выполнения

По соображением безопасности, влияние некоторых переменных окружения отменяется или изменяется, если динамический компоновщик определяет, что двоичный файл должен выполняться в режиме безопасного выполнения. Такое решение исполняется после проверки, что запись AT_SECURE во вспомогательном векторе (смотрите getauxval(3)) не равна нулю. Данная запись содержит ненулевое значение по различным причинам, включая:
*
У процесса различаются реальный и эффективный идентификатор пользователя/группы. Обычно, это происходит в результате выполнения программ с установленным битом set-user-ID или set-group-ID.
*
Процесс с пользовательским ID не равным root, выполняет файл, которому присвоены разрешающие или эффективные мандаты.
*
Ненулевое значение может установить Linux Security Module.

Переменные окружения

Среди наиболее важных переменных окружения следует выделить следующие:
LD_ASSUME_KERNEL (в glibc с версии 2.2.3)
Каждый общий объект может сообщать динамическому компоновщику о требуемой минимальной версии ядерного ABI (это требование кодируется в разделе ELF note, который можно просмотреть с помощью readelf -n, под меткой NT_GNU_ABI_TAG). Во время выполнения динамический компоновщик определяет версию ABI запущенного ядра и не будет загружать общие объекты, у которых минимальная версия ABI превышает версию ядра.

LD_ASSUME_KERNEL можно использовать, чтобы заставить динамический компоновщик предполагать, что он работает в системе с другой версией ядра ABI. Например, следующая команда заставляет динамический компоновщик при загрузке общих объектов, требуемых myprog, предполагать, что он запущен на Linux с версией 2.2.5:

$ LD_ASSUME_KERNEL=2.2.5 ./myprog

В системах, предоставляющих несколько версий общего объекта (в различных каталогах пути поиска) с разными требованиями к минимальной версии ядра ABI, LD_ASSUME_KERNEL может использоваться для выбора версии объекта, которую нужно задействовать (в зависимости от порядка поиска в каталогах). Исторически, свойство LD_ASSUME_KERNEL наиболее часто использовалось при ручном выборе старых реализаций нитей POSIX LinuxThreads в системах, которые предоставляли и LinuxThreads, и NPTL (в последствии стала базовой в таких системах); смотрите pthreads(7).

LD_BIND_NOW
(libc5; в glibc начиная с версии 2.1.1) Если переменная содержит непустую строку, то динамический компоновщик будет искать все символы при запуске программы вместо того, чтобы отложить поиск вызовов функций до момента, когда они встретятся в первый раз. Это полезно при отладке.
LD_LIBRARY_PATH
Список каталогов, в которых ищутся библиотеки ELF при выполнении. Элементы списка разделяются двоеточиями или точками с запятой. Похожа на переменную окружения PATH. Данная переменная игнорируется в режиме безопасного выполнения.
LD_PRELOAD
Список дополнительных, указанных пользователем, общих объектов ELF для загрузки раньше чем все остальные. Элементы списка разделяются пробелами или двоеточиями. Может использоваться для выборочной замены функций из других общих объектов. Объекты ищутся согласно правилам, указанным в ОПИСАНИИ. В режиме безопасного выполнения предварительная загрузка файлов с символами косой черты не выполняется, а общие объекты из стандартных каталогов поиска загружаются только, если на файле общего объекта включён бит режима set-user-ID.
LD_TRACE_LOADED_OBJECTS
(только для ELF) Если установлена, то вместо нормального запуска программы будут выданы её динамические зависимости, как если бы она была запущена ldd(1).

Также существует большое количество более или менее полезных переменных, многие из которых устарели или предназначены только для внутреннего использования.

LD_AOUT_LIBRARY_PATH
(libc5) Тоже что и LD_LIBRARY_PATH, но только для двоичных файлов в формате a.out. Старые версии ld-linux.so.1 также поддерживают LD_ELF_LIBRARY_PATH.
LD_AOUT_PRELOAD
(libc5) Тоже что и LD_PRELOAD, но только для двоичных файлов в формате a.out. Старые версии ld-linux.so.1 также поддерживают LD_ELF_PRELOAD.
LD_AUDIT
(в glibc начиная с 2.4) Определяемый пользователем список общих объектов ELF разделяемых двоеточиями, которые будут загружены раньше всех остальных в отдельном пространстве имён компоновщика (т. е., они не внедряются вместо обычных привязываемых символов, которые могли бы быть в этом процессе). Эти объекты можно использовать для контрольной проверки операций динамического компоновщика. LD_AUDIT игнорируется в режиме безопасного выполнения.

Динамический компоновщик будет уведомлять общие объекты контроля в так называемых точках контроля — например, при загрузке нового общего объекта, поиске символа или при вызове символа из другого динамического объекта — вызывая соответствующую функцию из общего объекта контроля. Подробности смотрите в rtld-audit(7). Интерфейс контроля в значительной степени совместим с представленным в Solaris, описан в его Руководстве по компоновщику и библиотекам (Linker and Libraries Guide) в главе Интерфейс контроля компоновщика во время выполнения (Runtime Linker Auditing Interface).

LD_BIND_NOT
(в glibc начиная с 2.1.95) Если эта переменная окружения не равна пустой строке, то не обновлять GOT (global offset table — таблицу глобальных перемещений) и PLT (procedure linkage table — таблицу компоновки процедур) после нахождения символа.
LD_DEBUG
(в glibc начиная с 2.1) Выводить подробную отладочную информацию о динамическом компоновщике. Если значение равно all, то выдаётся вся имеющаяся отладочная информация, если значение равно help, то выдаётся справка о некоторых категориях, которые могут быть указаны в этой переменной окружения. Начиная с glibc 2.3.4, LD_DEBUG игнорируется в режиме безопасного выполнения, если не существует файл /etc/suid-debug (содержимое может быть любым).
LD_DEBUG_OUTPUT
(в glibc начиная с 2.1) Файл, куда будет записываться вывод LD_DEBUG — по умолчанию это стандартный поток ошибок. LD_DEBUG_OUTPUT игнорируется в режиме безопасного выполнения.
LD_DYNAMIC_WEAK
(в glibc начиная с 2.1.91) Если эта переменная окружения определена (с любым значением), то разрешено перезаписывать слабые символы (возвращение к старому поведению glibc). Начиная с glibc 2.3.4, LD_DYNAMIC_WEAK игнорируется в режиме безопасном выполнения.
LD_HWCAP_MASK
(в glibc начиная с 2.1) Маска для совместимости с аппаратными возможностями.
LD_KEEPDIR
(только для a.out)(libc5) Не игнорировать каталог для загрузки в именах a.out библиотек. Настоятельно не рекомендуется использовать этот параметр.
LD_NOWARN
(только a.out)(libc5) Подавлять предупреждения о библиотеках a.out с несовместимыми младшими номерами версий.
LD_ORIGIN_PATH
(в glibc начиная с 2.1) Путь, где находится двоичный файл (для не set-user-ID программ). Начиная с glibc 2.4, LD_ORIGIN_PATH игнорируется в режиме безопасного выполнения.
LD_POINTER_GUARD
(в glibc с 2.4 по 2.22) Значение 0 отключает защиту указателя. Любое другое значение включает защиту указателя, что является действием по умолчанию. Защита указателя — это механизм безопасности, в результате которого некоторые указатели на код, хранящийся в перезаписываемой памяти программы (адреса возврата, сохраняемые setjmp(3) или указатели на функцию, используемые различными внутренними функциями glibc), искажаются полупроизвольным образом, что затрудняет атакующему подбор указателей для проведения атак переполнения буфера или срыва стека. Начиная с glibc 2.23, LD_POINTER_GUARD можно больше не использовать для отключения защиты указателя, так как теперь она всегда включена.
LD_PROFILE
(в glibc начиная с 2.1) В переменной задаётся имя динамического объекта (одного) для профилирования, в виде пути или имени so. Результат профилирования записывается в файл с именем: «$LD_PROFILE_OUTPUT/$LD_PROFILE».
LD_PROFILE_OUTPUT
(в glibc начиная с 2.1) Каталог, куда будет сохраняться результат работы с LD_PROFILE. Если эта переменная не определена или её значение равно пустой строке, то по умолчанию результат будет сохранён в каталог /var/tmp. Переменная LD_PROFILE_OUTPUT игнорируется в режиме безопасного выполнения.
LD_SHOW_AUXV
(в glibc начиная с 2.1) Если эта переменная окружения определена (с любым значением), то разрешено передавать вспомогательный вектор из ядра (смотрите также getauxval(3)). Начиная с glibc 2.3.5, LD_SHOW_AUXV игнорируется в режиме безопасного выполнения.
LD_TRACE_PRELINKING
(в glibc начиная с 2.4) Если эта переменная окружения определена (с любым значением), то выполнять трассировку объекта предварительной компоновки, чьё имя указано в этой переменной окружения (для получения списка трассируемых объектов используйте ldd(1)). Если имя объекта не распознано, то трассируется все действия предварительной компоновки (prelinking activity).
LD_USE_LOAD_BIAS
По умолчанию (т. е., если переменная не определена) исполняемые и предварительно скомпонованные объекты учитывают базовые адреса общих объектов, от которых они зависят, а (предварительно не скомпонованные) перемещаемые исполняемые (PIE) и другие общие объекты не учитывают их. Если переменной LD_USE_LOAD_BIAS присвоено значение 1, то и исполняемые файлы, и PIE учитывают базовые адреса. Если значение переменной LD_USE_LOAD_BIAS равно 0, то ни исполняемые файлы ни PIE не учитывают базовые адреса. Эта переменная игнорируется в режиме безопасного выполнения.
LD_VERBOSE
(в glibc начиная с версии 2.1) Если значение равно непустой строке, то выводится информация о символах программы (если также установлена переменная окружения LD_TRACE_LOADED_OBJECTS).
LD_WARN
(только для ELF)(в glibc начиная с 2.1.3) Если значение равно непустой строке, то выдаются предупреждения о ненайденных символах.
LD_PREFER_MAP_32BIT_EXEC
(только в x86-64, в glibc начиная с 2.23) Согласно с руководством по оптимизации ПО Intel Silvermont для 64-битных приложений, выполнение предсказания ветвления может отразится негативным образом, если цель ветви находится дальше чем 4ГБ от ветви. Если эта переменная окружения установлена (любое значение), то ld.so сначала попытается отобразить исполняемые страницы с помощью mmap(2) с флагом MAP_32BIT и, если это не удастся, то выполнит отображение без флага. Примечание: с флагом MAP_32BIT отображение выполняется в нижние 2ГБ (не 4ГБ) адресного пространства. Так как при MAP_32BIT сокращается адресный диапазон, доступный для случайного выравнивания адресного пространства (ASLR), то LD_PREFER_MAP_32BIT_EXEC всегда отключается в режиме безопасного выполнения.
LDD_ARGV0
(libc5) Значение argv[0] будет использоваться ldd(1), если другого нет.

ФАЙЛЫ

/lib/ld.so
динамический компоновщик/загрузчик a.out
/lib/ld-linux.so.{1,2}
динамический компоновщик/загрузчик ELF
/etc/ld.so.cache
Файл с скомпилированным списком каталогов, в которых производится поиск общих объектов и сортированный список общих объектов-кандидатов.
/etc/ld.so.preload
Файл со списком общих объектов ELF (через пробел), которые будут загружены перед программой.
lib*.so*
общие объекты

ЗАМЕЧАНИЯ

Функциональность ld.so доступна для исполняемых программ, скомпилированных с использованием libc версии 4.4.3 или выше. Функциональность ELF доступна начиная с Linux 1.1.52 и libc5.

Аппаратные возможности

Некоторые общие объекты скомпилированы с использованием специальных аппаратных инструкций, которые существуют не в каждом ЦП. Такие объекты должны быть установлены в каталоги, чью имена (например, /usr/lib/sse2/) определяют требования к аппаратным возможностям. Динамический компоновщик проверяет эти каталоги учитывая аппаратуру машины и выбирает наиболее подходящую версию требуемого общего объекта. Каталоги аппаратных возможностей могут каскадироваться для объединения свойств ЦП. Список имён поддерживаемых аппаратных возможностей зависит от ЦП. В настоящее время распознаются следующие имена:
Alpha
ev4, ev5, ev56, ev6, ev67
MIPS
loongson2e, loongson2f, octeon, octeon2
PowerPC
4xxmac, altivec, arch_2_05, arch_2_06, booke, cellbe, dfp, efpdouble, efpsingle, fpu, ic_snoop, mmu, notb, pa6t, power4, power5, power5+, power6x, ppc32, ppc601, ppc64, smt, spe, ucache, vsx
SPARC
flush, muldiv, stbar, swap, ultra3, v9, v9v, v9v2
s390
dfp, eimm, esan3, etf3enh, g5, highgprs, hpage, ldisp, msa, stfle, z900, z990, z9-109, z10, zarch
x86 (только 32-битные)
acpi, apic, clflush, cmov, cx8, dts, fxsr, ht, i386, i486, i586, i686, mca, mmx, mtrr, pat, pbe, pge, pn, pse36, sep, ss, sse, sse2, tm