TL; DR: вы не должны связывать библиотеку один раз как статическую зависимость, а один раз как динамическую зависимость.
Как деструкторы статических переменных выполняются в Itanium ABI (используется clang, gcc, icc ...)?
Стандартная библиотека C ++ предлагает стандартное средство для планирования выполнения функции во время завершения работы программы ( после завершения main) в формате atexit
.
Поведение относительно простое, atexit
в основном создает стек обратных вызовов и, таким образом, выполняет их в обратном порядке их планирования.
Всякий раз, когда создается статическая переменная, сразу после завершения ее создания в стеке atexit
регистрируется обратный вызов для его уничтожения во время завершения работы.
Что происходит, когда существует статическая переменная и в статически связанной библиотеке и в динамически связанной библиотеке?
Он пытается существовать дважды.
Каждая библиотека будет иметь:
- область памяти, зарезервированная для переменной, на которую указывает соответствующий символ (искаженное имя для переменной),
- запись в разделе загрузки для создания переменной и планирования ее уничтожения.
Удивление связано с тем, как в загрузчике работает разрешение символов. По сути, загрузчик создает отображение между символом и местоположением (указателем) в порядке очереди.
Однако секции загрузки / выгрузки являются безымянными, поэтому каждая из них выполняется полностью.
Таким образом:
- статическая переменная создается впервые,
- статическая переменная строится во второй раз по сравнению с первой (которая просочилась),
- статическая переменная уничтожается впервые,
- статическая переменная уничтожается второй раз; как правило, там, где проблема обнаружена.
И что?
Решение простое: НЕ связывать как со статической библиотекой A (напрямую), так и с динамической библиотекой B, также связывающейся с A (динамически или статически).
В зависимости от варианта использования вы можете:
- ссылка статически против B,
- динамически связывается с A и B.
Так как в Windows все работает нормально, решение должно быть похоже на изменение какой-либо опции связывания или чего-то подобного, но не на изменение структуры проекта или на использование статических переменных.
В маловероятном случае, когда вам действительно нужны два независимых экземпляра статической переменной, кроме рефакторинга вашего кода, можно вместо этого скрыть символы в вашей динамической библиотеке.
Это поведение по умолчанию для Windows, поэтому там требуется атрибут DLLExport
и почему, поскольку он был забыт для CTest::test
, поведение в Windows отличается.
Однако обратите внимание, что любой будущий сопровождающий этого проекта будет громко ругать вас, если вы выберете такое поведение. Никто не ожидает, что статическая переменная будет иметь несколько экземпляров.