Посилання на libstdc ++ статично: є якісь проблеми?


90

Мені потрібно розгорнути програму 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 року, і, можливо, багато чого змінилося з тих пір. Чи все ще актуальні його поради? Чи є якісь несподівані проблеми, про які я повинен знати?


Ні, статичне посилання на libstdc ++ цього не означає. Якби це означало, що тоді не було б сенсу до -static-libstdc++опції, ви б просто використали-static
Джонатан Уейклі

@JonathanWakely -static отримає kernel too oldпомилку в деякій системі ubuntu 1404. Glibc.so схожий kernel32.dllна вікно, він є частиною інтерфейсу операційної системи, і ми не повинні вставляти його в наш двійковий файл. Ви можете використовувати, objdump -T [binary path]щоб бачити його динамічно завантаженим libstdc++.soчи ні. Для програмиста golang ви можете додати #cgo linux LDFLAGS: -static-libstdc++ -static-libgccперед імпортом "C"
бронзова людина

@bronzeman, але ми говоримо про -static-libstdc++не -staticтак libc.so, не буде статично пов'язані.
Джонатан Уейклі

1
@NickHutchinson посилання на допис у блозі зникло. Це питання SO є популярним пошуковим запитом для відповідних термінів тут. Чи можете ви відтворити важливу інформацію з цього допису у своєму запитанні або запропонувати нове посилання, якщо знаєте, куди воно перенесено?
Брайан Кейн,

Відповіді:


134

Цей допис у блозі досить неточний.

Наскільки мені відомо, зміни в AB ++ на C ++ вносились з кожним основним випуском GCC (тобто з різними компонентами першої або другої версії номера).

Неправда. Єдині зміни CI ABI, введені після GCC 3.4, були зворотно сумісними, тобто C ++ ABI був стабільним майже дев'ять років.

Що ще гірше, більшість основних дистрибутивів 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)


8
Це пояснення, особливо щодо RPATH, є чудовим.
nilweed

3
Доставка libstdc ++ із вашим додатком на Linux - погана порада. Google для "steam libstdc ++", щоб побачити всю драму, що це приносить. Коротше кажучи, якщо ваш exe завантажує зовнішні бібліотеки (наприклад, opengl), які хочуть знову відкрити libstdc ++ (наприклад, драйвери Radeon), ці бібліотеки використовуватимуть ваш libstdc ++, оскільки він уже завантажений, а не власний, що саме їм потрібно і очікувати. Отже, ви повернулися до першого.

7
@cap, OP спеціально запитує про розгортання в дистрибутиві, де система libstdc ++ є старшою. Проблема Steam полягає в тому, що вони додали libstdc ++., Отже, він був старшим за системний (мабуть, він був новішим на той час, коли вони його об’єднували, але дистрибутиви перейшли на ще новіші). Це можна вирішити, якщо RPATH вказує на каталог, що містить libstdc++.so.6символічне посилання, яке встановлено під час встановлення, щоб вказувати на пакетну бібліотеку або на системну, якщо вона новіша. Існують і більш складні моделі змішаних зв’язків, якими користується Red Hat DTS, але їх важко зробити самостійно.
Джонатан Уейклі

5
привіт, вибачте, якщо я не хочу, щоб у мою модель для доставки зворотньо-сумісних двійкових файлів було включено "довіряти іншим людям зберігати libstdc ++ ABI compat" або "умовно пов'язувати libstdc ++ під час виконання" ... якщо це зіпсує перо тут і там, що я можу зробити, я маю на увазі відсутність неповаги. І якщо ви пам’ятаєте драму memcpy@GLIBC_2.14, ви не можете насправді звинуватити мене в тому, що з цим виникають проблеми довіри :)

6
Мені довелося використовувати '-Wl, -rpath, $ ORIGIN' (зверніть увагу на '-' перед rpath). Я не можу редагувати відповідь, оскільки редагування має містити принаймні 6 символів ....
user368507

11

Одне доповнення до чудової відповіді Джонатана Уейклі, чому dlopen () проблематично:

Завдяки новому пулу обробки винятків у GCC 5 (див. PR 64535 та PR 65434 ), якщо ви відкриєте та закриєте бібліотеку, статично пов'язану з libstdc ++, ви отримаєте витік пам'яті (об'єкта пулу) кожного разу. Отже, якщо є якийсь шанс, що ви коли-небудь скористаєтесь dlopen, здається дуже поганою ідеєю статично пов’язувати libstdc ++. Зверніть увагу, що це справжній витік, на відміну від доброякісного, згаданого у PR 65434 .


1
__gnu_cxx::__freeres()Здається, функція надає принаймні деяку допомогу з цією проблемою, оскільки вона звільняє внутрішній буфер об’єкта пулу. Але для мене досить незрозуміло, яке значення має виклик цієї функції щодо винятків, випадково викинутих згодом.
phlipsy

3

Доповнення до відповіді Джонатана Уейклі щодо RPATH:

RPATH працюватиме лише в тому випадку, якщо відповідним RPATH є RPATH запущеної програми . Якщо у вас є бібліотека, яка динамічно зв'язується з будь-якою бібліотекою через власний RPATH, RPATH бібліотеки буде замінений RPATH програми, яка її завантажує. Це проблема, коли ви не можете гарантувати, що RPATH програми такий же, як і ваш бібліотеки, наприклад, якщо ви очікуєте, що ваші залежності будуть у певному каталозі, але цей каталог не є частиною RPATH програми.

Наприклад, скажімо, у вас є програма App.exe, яка має динамічно пов’язану залежність від libstdc ++. So.x для GCC 4.9. App.exe вирішує цю залежність за допомогою RPATH, тобто

App.exe (RPATH=.:./gcc4_9/libstdc++.so.x)

Тепер, скажімо, існує інша бібліотека Dependency.so, яка має динамічно пов’язану залежність від libstdc ++. So.y для GCC 5.5. Залежність тут вирішується за допомогою RPATH бібліотеки, тобто

Dependency.so (RPATH=.:./gcc5_5/libstdc++.so.y)

Коли App.exe завантажує Dependency.so, він не додає та не додає RPATH бібліотеки . Він взагалі не консультується з цим. Єдиним RPATH, який буде розглянуто, буде виконуваний додаток або App.exe у цьому прикладі. Це означає, що якщо бібліотека покладається на символи, які містяться у gcc5_5 / libstdc ++. So.y, але не в gcc4_9 / libstdc ++. So.x, тоді бібліотека не зможе завантажитися.

Це лише попереджувальне слово, оскільки раніше я сам стикався з цими проблемами. RPATH - дуже корисний інструмент, але його реалізація все ще має деякі недоліки.


тому RPATH для спільних бібліотек начебто безглуздий! І я сподівався, що вони трохи вдосконалили Linux у цьому відношенні за останні 2 десятиліття ...
Френк Пак

2

Можливо, вам також доведеться переконатися, що ви не залежите від динамічного glibc. Запустіть lddотриманий виконуваний файл та зауважте будь-які динамічні залежності (libc / libm / libpthread - звичні підозри).

Додатковим вправою було б створення групи задіяних прикладів на C ++ 11 з використанням цієї методології та фактичне випробування отриманих двійкових файлів на реальній системі 10.04. У більшості випадків, якщо ви не зробите чогось дивного з динамічним завантаженням, ви одразу дізнаєтесь, працює програма чи вона виходить з ладу.


1
У чому проблема залежно від динамічного glibc?
Нік Хатчінсон,

Я вважаю, що принаймні деякий час тому libstdc ++ мав на увазі залежність від glibc. Не впевнений, де речі стоять сьогодні.
Олександр Л. Белікофф

9
libstdc ++ залежить від glibc (наприклад, реалізовані iostream printf), але доки glibc в Ubuntu 10.04 надає всі функції, необхідні для нового libstdc ++, немає ніяких проблем із залежністю від динамічного glibc, насправді настійно рекомендується ніколи не зв'язувати статично до glibc
Джонатан Уейклі

1

До відповіді Джонатана Уейклі я хотів би додати наступне.

Пограючись -static-libstdc++на Linux, я зіткнувся з проблемою dlclose(). Припустимо, у нас є додаток 'A', статично пов'язаний з ним, libstdc++і він завантажується динамічно пов'язаним з libstdc++плагіном 'P' під час виконання. Це чудово. Але коли 'A' вивантажує 'P', виникає помилка сегментації. Я припускаю, що після розвантаження libstdc++.so"A" більше не може використовувати символи, пов'язані з libstdc++. Зверніть увагу, що якщо обидва "A" і "P" статично пов'язані libstdc++, або якщо "A" пов'язано динамічно, а "P" статично, проблема не виникає.

Короткий зміст: якщо ваша програма завантажує / вивантажує плагіни, які можуть динамічно посилатись libstdc++, додаток також повинен бути динамічно пов’язаний з ним. Це лише моє спостереження, і я хотів би отримати ваші коментарі.


1
Можливо, це схоже на змішування реалізацій libc (скажімо, динамічне посилання на плагін, який, у свою чергу, динамічно пов'язує glibc, тоді як сам додаток статично пов'язаний з musl-libc). Річ Фелкер, автор musl-libc, стверджує, що проблема в такому сценарії полягає в тому, що управління пам’яттю glibc (за допомогою sbrk) робить певне припущення і майже сподівається бути самотнім в одному процесі ... не впевнений, що це обмежується конкретна версія glibc або будь-яка інша.
0xC0000022L

і люди все ще не бачать переваг інтерфейсу купи Windows, який здатний мати справу з декількома незалежними копіями libc ++ / libc всередині одного процесу. Такі люди не повинні розробляти програмне забезпечення.
Френк Пук

@FrankPuck, маючи пристойну кількість досвіду роботи як з Windows, так і з Linux, я можу сказати вам, що те, як це робить "Windows", не допоможе вам, коли MSVC є стороною, яка вирішує, який розподільник використовується і як. Основна перевага, яку я бачу в купі Windows, полягає в тому, що ви можете роздавати шматочки, а потім звільняти їх одним махом. Але з MSVC ви все одно зіткнетесь із проблемою, описаною вище, наприклад, коли проходите навколо покажчиків, виділених іншим середовищем виконання VC (випуск проти налагодження або статично проти динамічно пов'язаних). Отже, «Windows» не має імунітету. Слід бути обережними в обох системах.
0xC0000022L
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.