Связывание libstdc ++ статически: есть ли ошибки?

Мне нужно развернуть приложение C ++, построенное на Ubuntu 12.10 с libstdc ++ GCC 4.7, на системах с Ubuntu 10.04, которая поставляется со значительно более старой версией libstdc ++.

В настоящее время я собираю с -static-libstdc++ -static-libgcc, как предлагается в этом блоге: Связывание libstdc ++ статически. Автор предостерегает от использования любого динамически загружаемого кода C ++ при статической компиляции libstdc ++, чего я еще не проверял. Тем не менее, кажется, что пока все идет гладко: я могу использовать возможности C ++ 11 в Ubuntu 10.04, чего я и добивался.

Я отмечаю, что эта статья написана в 2005 году, и, возможно, с тех пор многое изменилось. Его совет все еще актуален? Есть ли какие-то скрытые проблемы, о которых я должен знать?

72

Решение

Это сообщение в блоге довольно неточно.

Насколько я знаю, C ++ ABI-изменения вносились с каждым основным выпуском GCC (т. Е. С разными компонентами первого или второго номера версии).

Не правда. Единственные изменения C ++ ABI, введенные после GCC 3.4, были обратно совместимы, то есть ABI C ++ был стабилен в течение почти девяти лет.

Что еще хуже, в большинстве крупных дистрибутивов Linux используются снимки GCC и / или исправляются их версии GCC, что делает практически невозможным точное определение версий GCC, с которыми вы можете иметь дело при распространении двоичных файлов.

Различия между исправленными версиями GCC в дистрибутивах незначительны и не меняются в ABI, например Fedora 4.6.3 20120306 (Red Hat 4.6.3-2) совместима с ABI с вышедшими выпусками FSF 4.6.x и почти наверняка с любым 4.6.x из любого другого дистрибутива.

В GNU / Linux библиотеки времени исполнения GCC используют управление версиями символов ELF, поэтому легко проверить версии символов, необходимые для объектов и библиотек, и если у вас есть libstdc++.so который предоставляет те символы, которые он будет работать, не имеет значения, является ли это немного отличной исправленной версией от другой версии вашего дистрибутива.

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

Это тоже не правда.

Тем не менее, статически ссылка на libstdc++.a это один вариант для вас.

Причина может не работать, если вы динамически загружаете библиотеку (используя dlopen) заключается в том, что символы libstdc ++, от которых оно зависит, могут не понадобиться вашему приложению, когда вы (статически) связали его, поэтому эти символы не будут присутствовать в вашем исполняемом файле. Это можно решить путем динамического связывания общей библиотеки с libstdc++.so (что в любом случае правильно делать, если это зависит от него.) Вставка символов ELF означает, что символы, присутствующие в вашем исполняемом файле, будут использоваться общей библиотекой, а другие, которых нет в вашем исполняемом файле, будут найдены в зависимости от того, что libstdc++.so это ссылки на. Если ваше приложение не использует dlopen вам не нужно заботиться об этом.

Другой вариант (и тот, который я предпочитаю) заключается в развертывании более нового libstdc++.so вместе с вашим приложением и убедитесь, что оно найдено до системы по умолчанию libstdc++.so, что можно сделать, заставив динамический компоновщик искать в нужном месте, либо используя $LD_LIBRARY_PATH переменная среды во время выполнения или путем установки RPATH в исполняемом файле во время компоновки. Я предпочитаю использовать RPATH поскольку он не зависит от того, правильно ли настроена среда для работы приложения. Если вы свяжете свою заявку с '-Wl,-rpath,$ORIGIN' (обратите внимание на одинарные кавычки, чтобы предотвратить расширение оболочки $ORIGIN) тогда исполняемый файл будет иметь RPATH из $ORIGIN который говорит динамическому компоновщику искать общие библиотеки в том же каталоге, что и сам исполняемый файл. Если поставить новый libstdc++.so в том же каталоге, что и исполняемый файл, он будет найден во время выполнения, проблема решена. (Другой вариант — поместить исполняемый файл в /some/path/bin/ и более новая библиотека libstdc ++. /some/path/lib/ и связать с '-Wl,-rpath,$ORIGIN/../lib' или любое другое фиксированное расположение относительно исполняемого файла, и установите RPATH относительно $ORIGIN)

106

Другие решения

Еще одно дополнение к превосходному ответу Джонатана Уэйкли, почему dlopen () проблематичен:

Из-за нового пула обработки исключений в GCC 5 (см. PR 64535 а также PR 65434), если вы dlopen и dlclose библиотеки, которая статически связана с libstdc ++, вы будете каждый раз получать утечку памяти (объекта пула). Так что если есть шанс, что вы когда-нибудь будете использовать dlopen, кажется, что плохая идея статически связать libstdc ++. Обратите внимание, что это реальная утечка в отличие от доброкачественной, упомянутой в PR 65434.

8

Вам также может потребоваться убедиться, что вы не зависите от динамического glibc. Бежать ldd на вашем полученном исполняемом файле и обратите внимание на любые динамические зависимости (libc / libm / libpthread — подозрительные для использования).

Дополнительным упражнением было бы создание набора задействованных примеров на C ++ 11 с использованием этой методологии и попытка на самом деле получить получившиеся двоичные файлы в реальной системе 10.04. В большинстве случаев, если вы не делаете что-то странное с динамической загрузкой, вы сразу узнаете, работает ли программа или происходит сбой.

3
По вопросам рекламы [email protected]