Взаимозависимость глобальных конструкторов разделяемой библиотеки Linux - PullRequest
3 голосов
/ 11 июля 2011

Операционная система Centos 5.6 i686 2.6.18-53.1.4.el5vm.
gcc версия 4.1.2 20080704 (Red Hat 4.1.2-48)
ld версия 2.17.50.0.6-6.el520061020

Я компилирую следующим образом:
gcc -c -fnon-call-исключения -fexceptions -Wall -DUNICODE -D_UNICODE -D_REENTRANT -I.
и скомпоновать следующим образом:
gcc -lstdc ++ -pthread -ldl -lrt --no-relocate -Wl, -rpath, $ SO_DIR -L $ SO_DIR $ LIBRARIES

У меня есть 3 библиотеки иисполняемый файл: A.so, B.so, C.so, ElfExec
B.so зависит от A.so.C.so зависит от B.so.
В коде A.so есть заголовок, через который он предоставляет функциональность Ah, B.so в коде имеет заголовок Bh, который включает в себя функциональные возможности Ah и B.Код C.so включает в себя Bh
Ah, определив статическую переменную K типа, которая может использоваться тогда и только тогда, когда инициализирован статический менеджер памяти из A.so.Переменная K прямо определена в Ah, в заголовке, поэтому ее инициализация распространяется в глобальных конструкторах всех объектов, составляющих B.so и C.so.

Я связываю все так:
gcc "ALL B MODULES" -lstdc ++ -pthread -ldl -lrt --no-relocate -Wl, -rpath, $ SO_DIR -L $ SO_DIR A.so
gcc "ALL C MODULES" -lstdc ++ -pthread -ldl -lrt --no-relocate -Wl, -rpath, $ SO_DIR -L $ SO_DIR B.so
gcc "ALL ElfExec MODULES" -lstdc ++ -pthread -ldl -lrt --no-relocate -Wl, -rpath, $SO_DIR -L $ SO_DIR C.so
Я также пытался:
gcc "ВСЕ МОДУЛИ ElfExec" -lstdc ++ -pthread -ldl -lrt --no-relocate -Wl, -rpath, $ SO_DIR -L $ SO_DIR A.so B.so C.so

При запуске ElfExec получает SIGSEGV, потому что он пытается инициализировать переменную K до инициализации статического менеджера памяти из A.so.
Это происходит потому, чтоглобальные конструкторы из C.so вызываются раньше, чем из A.so.
Если я создаю приложение ElfExec2, для которого требуется только B.so
gcc "ВСЕ ElfExec1 МОДУЛИ" -lstdc ++ -pthread -ldl -lrt --no-relocate -Wl, -rpath, $ SO_DIR -L $ SO_DIR B.so
это работает правильно.

В случае ElfExec1 компоновщик видит, что глобальные конструкторы из A.so должны вызываться в первую очередь перед вызовами из B.so.
Это нев случае ElfExec.

Мое решение состоит в том, чтобы связать C. так вот так: gcc "ALL C MODULES" -lstdc ++ -pthread -ldl -lrt --no-relocate -Wl, -rpath, $SO_DIR -L $ SO_DIR A.so B.so
Это устанавливает прямую зависимость в C.so от A.so.

Есть ли другой способ сообщить компоновщику порядок вызова глобальных конструкторов?

1 Ответ

4 голосов
/ 19 июля 2011

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

Это то, что вы бы сделали, если бы это не были библиотеки, верно?Конструкторы / функции инициализации, которые вызывали друг друга в правильном порядке?

Мой первый выбор - создать библиотеку так, чтобы не иметь или использовать глобальные переменные.

Если вы не можете этого сделать, мой второй выбор - для каждой библиотеки, которая должна инициализировать глобальные переменные, чтобы иметьметод init.Потребители библиотеки должны вызвать этот метод init, прежде чем они смогут что-либо сделать, и библиотека должна попытаться предотвратить использование / конструирование, пока init не будет правильно выполнен.Возможно, создание статического метода для метода init и установка для них глобальных указателей (K * k) может помочь в этой реализации.Этого должно быть достаточно, чтобы объединить цепочку инициализации в правильном порядке.

Наконец, если есть препятствие для пользователя какой-либо библиотеки (имеется в виду B для A или C для B, приложение для C)вызовите метод init, вы можете использовать такие языковые расширения для gcc:

extern "C" __attribute__ ((constructor)) void A_lib_ctor()
{
    // ....
}
extern "C" __attribute__ ((destructor)) void A_lib_dtor()
{
    // ....
}

, чтобы автоматически делать то, что вам нужно, при загрузке библиотеки.Это жертвует некоторой переносимостью.Возможно, пожертвовав еще, более новые версии gcc поддерживают синтаксис конструктора (приоритет).

Мой последний выбор - сложные шаги по ручной загрузке библиотек с помощью dlopen.

Дизайн лучше с лучшим выбором, который подходит вам, и хуже с последующими.

...