Мне нужно развернуть приложение 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 году, и, возможно, с тех пор многое изменилось. Его совет все еще актуален? Есть ли какие-то скрытые проблемы, о которых я должен знать?
Это сообщение в блоге довольно неточно.
Насколько я знаю, 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
)
Еще одно дополнение к превосходному ответу Джонатана Уэйкли, почему dlopen () проблематичен:
Из-за нового пула обработки исключений в GCC 5 (см. PR 64535 а также PR 65434), если вы dlopen и dlclose библиотеки, которая статически связана с libstdc ++, вы будете каждый раз получать утечку памяти (объекта пула). Так что если есть шанс, что вы когда-нибудь будете использовать dlopen, кажется, что плохая идея статически связать libstdc ++. Обратите внимание, что это реальная утечка в отличие от доброкачественной, упомянутой в PR 65434.
Вам также может потребоваться убедиться, что вы не зависите от динамического glibc. Бежать ldd
на вашем полученном исполняемом файле и обратите внимание на любые динамические зависимости (libc / libm / libpthread — подозрительные для использования).
Дополнительным упражнением было бы создание набора задействованных примеров на C ++ 11 с использованием этой методологии и попытка на самом деле получить получившиеся двоичные файлы в реальной системе 10.04. В большинстве случаев, если вы не делаете что-то странное с динамической загрузкой, вы сразу узнаете, работает ли программа или происходит сбой.