В каком порядке инициализируются и завершаются разделяемые библиотеки?

Динамические объекты в процессе поступают из нескольких источников:

  1. Сам исполняемый файл
  2. Любые необходимые библиотеки (DT_NEEDED для ELF)
  3. Библиотеки загружены явно (dlopen или аналогичные)
  4. Любые библиотеки, необходимые для таких явных загрузок

Они могут быть выгружены явно (dlclose) или неявно, когда процесс exits выполняет их финализацию (функции atexit, деструкторы статической длительности в C++ и функции __attribute__((destructor))) в любом случае.

Что определяет порядок, в котором динамические объекты инициализируются и завершаются в этих различных случаях? Очевидно, последний dlclose для библиотеки выгружает ее немедленно, но как насчет его дерева зависимостей (некоторые из которых также могут быть зависимостями других загруженных библиотек)? Что, если библиотека записана на dlopen, но затем выгружена с помощью exit?

Я склонен ожидать обычного порядка инициализации задний ход, но, возможно, есть разница между DT_NEEDED и dlopen, поскольку «плагины» загружаются последним, и можно ожидать, что они будут зависеть от данных исполняемого файла, а не наоборот.

5
0
297
1

Ответы 1

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

Деструкторы библиотеки регистрируются в одном из ее конструкторов (через вызов __cxa_atexit). Регистрация осуществляется добавлением кумулятивной библиотечной функции dtor к специальному списку внутри Glibc. На exit список перемещается в прямом направлении, поэтому зависимые деструкторы будут вызываться перед их зависимостями.

Топологический порядок не ограничивает ничего, упомянутого только dlopen. Этот подотряд не указан?

Davis Herring 26.10.2018 16:11

@DavisHerring Нет, если между двумя библиотеками нет зависимости, они могут быть инициализированы в произвольном относительном порядке. Как ни странно, однажды я экспериментировал с фаззингом этого порядка и нашел некоторые ошибки в существующих программах.

yugr 26.10.2018 17:08

Я считаю, что фактическая картина заказа немного сложнее, например, DF_1_INITFIRST и, возможно, другие флаги.

Employed Russian 28.10.2018 18:47

@EmployedRussian Да, но загрузчик GNU поддерживает только одну библиотеку DF_1_INITFIRST, а в libpthread она уже есть, поэтому на практике этот флаг нельзя использовать в обычных библиотеках.

yugr 28.10.2018 18:52

Другие вопросы по теме