Этот ответ объясняет, что происходит во время связывания, почему работает решение с -Wl,--no-as-needed
и что нужно сделать вместо этого, чтобы иметь более надежный подход.
В двух словах: -lHSrts-ghcXXX.so
зависит от libHSbaseXXX.so
, libHSinteger-gmpXXX.so
и libHSghc-primXXX.so
, которые должны быть предоставлены компоновщику во время связывания.
Предлагаемое здесь решение зависит от большого количества ручной работы и не очень масштабируемо.Однако я не знаю достаточно о cabal
, чтобы рассказать вам, как это автоматизировать, но я надеюсь, что вы сможете сделать последний шаг.
Или, возможно, вам будет хорошо с использованием -Wl,--no-as-needed
-разрешения, потому что вы знаете, что происходит за кулисами.
Давайте начнем с пошагового процесса связывания дляверсия без вызова foo
, несколько упрощенно ( здесь - отличная статья от Эли Бендерского, даже если речь идет о статической связи):
компоновщик поддерживает таблицу символов и должен найти определения / машинный код для всех них.Упростим и предположим, что в начале у него есть только символ main
в таблице, и определение этого символа неизвестно.
Определение символа main
найдено егообъект-файл test.o
.Однако функция main
использует функции hs_init
и hs_exit
.Таким образом, мы нашли определение main
, но оно не будет работать, если мы не знаем определения hs_init
и hs_exit
.Так что теперь мы должны искать их определения.
На следующем шаге компоновщик смотрит на foo.so
, но foo.so
не определяет интересующий нас символ (foo
не используется!) И компоновщик просто пропускает foo.so
и никогда не оглядывается назад.
Компоновщик просматривает -lHSrts-ghcXXX.so
.Там он находит определения hs_init
и hs_exit
.Таким образом, используется весь контент разделяемой библиотеки, но для него нужны определения таких символов, как, например, base_GHCziTopHandler_flushStdHandles_closure
.Это означает, что компоновщик начинает искать определения этих символов.
Однако в командной строке больше нет библиотек, поэтому компоновщику не на что смотреть, и связывание не выполняется.не удачно, потому что определения некоторых символов отсутствуют.
Что отличается для случая, когда используется foo
?После шага 2. требуются не только hs_init
и hs_exit
, но также foo
, который находится в foo.so
.Поэтому необходимо включить foo.so
.
В связи с тем, как была построена библиотека foo.so
, содержится следующая информация:
>>> readelf -d dist/build/foo.so/foo.so | grep NEEDED
0x0000000000000001 (NEEDED) Shared library: [libHSrts-ghc7.10.3.so]
0x0000000000000001 (NEEDED) Shared library: [libHSbase-4.8.2.0-HQfYBxpPvuw8OunzQu6JGM-ghc7.10.3.so]
0x0000000000000001 (NEEDED) Shared library: [libHSinteger-gmp-1.0.0.0-2aU3IZNMF9a7mQ0OzsZ0dS-ghc7.10.3.so]
0x0000000000000001 (NEEDED) Shared library: [libHSghc-prim-0.4.0.0-8TmvWUcS1U1IKHT0levwg3-ghc7.10.3.so]
0x0000000000000001 (NEEDED) Shared library: [libgmp.so.10]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
>>> readelf -d dist/build/foo.so/foo.so | grep RPATH
0x000000000000000f (RPATH) Library rpath: [
/usr/lib/ghc/base_HQfYBxpPvuw8OunzQu6JGM:
/usr/lib/ghc/rts:
/usr/lib/ghc/ghcpr_8TmvWUcS1U1IKHT0levwg3:
/usr/lib/ghc/integ_2aU3IZNMF9a7mQ0OzsZ0dS]
Из этой информации компоновщик знает, какойнеобходимы общие библиотеки (NEEDED
-flag) и где их можно найти в вашей системе (RPATH
).Эти библиотеки найдены / открыты / обработаны (т. Е. Помечены как необходимые) и, таким образом, присутствуют все необходимые определения.
Вы можете следить за всем процессом, добавив
g++ ...
-Wl,--trace-symbol=base_GHCziTopHandler_flushStdHandles_closure \
-Wl,--verbose \
-o test
к шагу связывания.
То же самое происходит, если мы обеспечим, чтобы foo.so
был включен в получившийся исполняемый файл через -Wl,--no-as-needed
, как предложено @ Yuras.
Каковы последствия этого анализа?
Мы должны предоставить необходимые библиотеки в командной строке (после -lHSrts-ghcXXX.so
) и не зависеть от того, добавляются ли они случайно через другие разделяемые библиотеки.Очевидно, что некоторые загадочные имена действительны только для моей установки:
g++ ...
-L/usr/lib/ghc/base_HQfYBxpPvuw8OunzQu6JGM -lHSbase-4.8.2.0-HQfYBxpPvuw8OunzQu6JGM-ghc7.10.3 \
-L/usr/lib/ghc/integ_2aU3IZNMF9a7mQ0OzsZ0dS -lHSinteger-gmp-1.0.0.0-2aU3IZNMF9a7mQ0OzsZ0dS-ghc7.10.3 \
-L/usr/lib/ghc/ghcpr_8TmvWUcS1U1IKHT0levwg3 -lHSghc-prim-0.4.0.0-8TmvWUcS1U1IKHT0levwg3-ghc7.10.3 \
...
-o test
Теперь он собирается, но не загружается во время выполнения (в конце концов, правильные rpath
установлены только в foo.so
но foo.so
не используется).Чтобы исправить это, мы могли бы либо расширить LD_LIBRARY_PATH
или добавить -rpath
командную строку ссылки:
g++ ...
-L... -lHSbase-... -Wl,-rpath,/usr/lib/ghc/base_HQfYBxpPvuw8OunzQu6JGM \
-L... -lHSinteger-gmp-... -Wl,-rpath,/usr/lib/ghc/integ_2aU3IZNMF9a7mQ0OzsZ0dS \
-L... -lHSghc-prim-... -Wl,-rpath,/usr/lib/ghc/ghcpr_8TmvWUcS1U1IKHT0levwg3 \
...
-o test
Должна быть утилита для автоматического получения путей и имен библиотек (кажется, что cabalсделать это при сборке foo.so
), но я не знаю, как это сделать, потому что у меня нет опыта работы с haskell / cabal.