Windows DLL и порядок динамической инициализации

У меня есть вопрос относительно динамической инициализации (т.е. конструкторов перед основным) и порядка ссылок DLL - как для Windows, так и для POSIX.

Чтобы было легче говорить, я дам определение паре терминов:

Библиотеки времени загрузки: библиотеки, которые были "связаны" во время компиляции таким образом, что когда система загружает мое приложение, они загружаются автоматически. (т. е. помещенные в команду target_link_libraries CMake).

Библиотеки времени выполнения: библиотеки, которые я загружаю вручную с помощью dlopen или эквивалентов. Для целей этого обсуждения я скажу, что я когда-либо вручную загружал библиотеки, используя dlopen в основном, так что это должно упростить ситуацию.

Динамическая инициализация. Если вы не знакомы с определением этого в спецификации C++, не пытайтесь ответить на этот вопрос.

Итак, допустим, у меня есть приложение (MyAwesomeApp), и оно ссылается на динамическую библиотеку (MyLib1), которая, в свою очередь, ссылается на другую библиотеку (MyLib2). Итак, дерево зависимостей:

MyAwesomeApp -> MyLib1 -> MyLib2

Для этого примера предположим, что MyLib1 и MyLib2 являются библиотеками времени загрузки.

Каков порядок инициализации вышеизложенного? Очевидно, что вся статическая инициализация, включая связывание экспортируемых/импортируемых функций (только для Windows) будет происходить первой... Но что происходит с динамической инициализацией? Я ожидаю общий порядок:

ВСЕ связывание символов импорта/экспорта
ВСЕ статические инициализации
ВСЕ динамические инициализации MyLib2
ВСЕ динамические инициализации MyLib1
ВСЕ динамические инициализации MyAwesomeApp
Функция main() MyAwesomeApp

Но я не могу найти ничего в спецификациях, которые требуют этого. Я ДЕЙСТВИТЕЛЬНО видел что-то с эльфом, намекающее на это, но мне нужно найти гарантии в спецификациях, чтобы сделать то, что я пытаюсь сделать.

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

Чтобы привести более сложный пример, чтобы убедиться, что нет другого определения моего первого примера, которое дает другой ответ:

MyAwesomeApp зависит от MyLib1 и MyLib2 MyLib1 зависит от MyLib2

Я ожидаю следующую инициализацию:

ВСЕ связывание символов импорта/экспорта
ВСЕ статические инициализации
ВСЕ динамические инициализации MyLib2
ВСЕ динамические инициализации MyLib1
ВСЕ динамические инициализации MyAwesomeApp
Функция main() MyAwesomeApp

Я был бы рад любой помощи, указывающей на спецификации, которые говорят, что это так. Или, если это неправильно, любая спецификация говорит о том, что НА САМОМ ДЕЛЕ происходит!

Заранее спасибо!

-Кристофер


person iAdjunct    schedule 19.07.2014    source источник
comment
Соответствующее предложение из стандарта выглядит следующим образом: В противном случае инициализация переменной неопределенно упорядочена по отношению к инициализации переменной, определенной в другой единице перевода. Конечно, я не понимаю всей остальной части абзаца, но я полагаю, что это означает, что нет никакой гарантии, произойдет ли динамическая инициализация из MyLib1 до или после инициализации из MyLib2.   -  person Harry Johnston    schedule 19.07.2014
comment
Во-первых, это в первую очередь относится к библиотеке, а не к границам DLL. Во-вторых, в то время как стандарт C++ не может гарантировать это для каждой возможной цели C++, ELF и двоичные стандарты Microsoft могут, и это то, что мне нужно.   -  person iAdjunct    schedule 19.07.2014
comment
ХОРОШО. В Windows любая такая гарантия (если возможно) должна быть предоставлена ​​компилятором, потому что Windows не требует, чтобы компиляторы выполняли динамическую инициализацию каким-либо особым образом. Похоже, что точки входа DLL вызываются в предложенном вами порядке, см. Загрузчик NT DLL: основная операция, хотя мне не ясно, является ли это договорным. (Тем не менее, маловероятно, что он изменится.) Таким образом, можно ожидать, что компилятор, выполняющий динамическую инициализацию наиболее очевидным способом, будет вести себя так, как вы предлагаете.   -  person Harry Johnston    schedule 20.07.2014
comment
Я добавил вознаграждение, так как считаю, что вопрос интересен и ему не уделялось особого внимания. Тем не менее, мне показалось бы странным, что компилятор предоставляет такую ​​гарантию в отношении динамических библиотек во время загрузки, учитывая, что для статически связанных модулей такой гарантии не существует, поэтому я предполагаю, что в большинстве случаев, по крайней мере, поведение будет недокументировано и может быть изменено. Надеюсь, эксперты по компиляторам смогут помочь. :-)   -  person Harry Johnston    schedule 23.07.2014


Ответы (1)


Ничто в стандарте C++ не определяет, как работает динамическая компоновка.

Сказав это, Visual Studio поставляется с исходным кодом C Runtime (он же CRT), и вы можете увидеть, где статические инициализаторы запускаются в dllcrt0.c.

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

  1. Для разрешения импорта/экспорта нужны только .dll.
  2. Для статической инициализации нужны только .dll.
  3. Динамическая инициализация требует разрешения всех операций импорта для .dll.

Шаг 1 и 2 не зависят друг от друга, поэтому они могут выполняться независимо друг от друга. Шаг 3 требует 1 и 2 для каждой .dll, поэтому это должно произойти после 1 и 2.

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

Другими словами, если вам нужно позаботиться о конкретном порядке определенных шагов, вы, вероятно, делаете что-то опасное, основанное на конкретных деталях реализации, которые не будут сохранены в основных или второстепенных версиях ОС. Например, способ работы блокировки загрузчика для .dll значительно изменился в различных выпусках Windows.

person MSN    schedule 28.07.2014
comment
Nothing in the C++ standard mandates how dynamic linking works. На самом деле стандарт ничего не знает о динамической компоновке. Кроме того, довольно хороший и точный ответ. - person Max Truxa; 29.07.2014