Как создать общий объект, который статически связан с pthreads и libstdc ++ в Linux / gcc? - PullRequest
5 голосов
/ 14 февраля 2009

Как создать общий объект, который статически связан с pthreads и libstdc ++ в Linux / gcc?

Ответы [ 3 ]

8 голосов
/ 14 февраля 2009

Прежде чем я перейду к ответу на ваш вопрос, как он был описан, я отмечу, что не совсем ясно, чего вы пытаетесь достичь в конце, и, вероятно, есть лучшее решение вашей проблемы.

Тем не менее, есть две основные проблемы с попытками сделать то, что вы описали:

  1. Во-первых, вам нужно будет разложить libpthread и libstdc++ на объектные файлы, с которыми они сделаны. Это связано с тем, что бинарные файлы ELF (используемые в Linux) имеют два уровня загрузки библиотеки времени выполнения - даже когда исполняемый файл статически связан, загрузчик должен загружать статически связанные библиотеки в двоичном файле при выполнении, и сопоставьте правильные адреса памяти. Это делается до общего связывания библиотек, которые динамически загружаются (разделяемые объекты) и отображаются в разделяемую память. Таким образом, общий объект не может быть статически связан с такими библиотеками, поскольку на момент загрузки объекта все статические связанные библиотеки уже были загружены. Это одно из различий между связыванием со статической библиотекой и простым объектным файлом - статическая библиотека не просто вклеивается, как любой объектный файл, в исполняемый файл, но все же содержит отдельные таблицы, на которые ссылаются при загрузке. (Я полагаю, что это в отличие от гораздо более простых статических библиотек в MS-DOS и классических Windows, .LIB файлов, но может быть и больше, чем я помню) .

    Конечно, вам не нужно разлагать libpthread и libstdc++, вы можете просто использовать объектные файлы, сгенерированные при их создании. Однако собрать их может быть немного сложно (ищите объекты, на которые ссылается окончательное правило Makefile этих библиотек). И вам придется использовать ld напрямую, а не gcc / g++ для связи, чтобы избежать связи с динамическими версиями.

  2. Вторая проблема является косвенной. Если вы сделаете вышеописанное, у вас обязательно будет такой общий объект / динамическая библиотека, которую вы просили собрать. Однако это будет не очень полезно, так как после того, как вы попытаетесь связать обычный исполняемый файл, который использует эти libpthread / libstdc++ (последняя из которых является любой программой на C ++), с этим общим объектом, он потерпит неудачу с конфликтами символов - символами статические libpthread / libstdc++ объекты, с которыми вы связали свой общий объект, будут конфликтовать с символами из стандартного libpthread / libstdc++, используемого этим исполняемым файлом, независимо от того, динамически или статически он связан со стандартными библиотеками .

    Конечно, вы можете затем попытаться скрыть все символы в статических объектах из libstdc++ / libpthread, используемые вашей общей библиотекой, сделать их каким-то образом закрытыми или переименовать их автоматически при связывании, чтобы нет конфликта Однако, даже если вы заставите это работать, вы найдете некоторые нежелательные результаты во время выполнения, так как оба libstdc++ / libpthread сохраняют довольно много состояния в глобальных переменных и структурах, которые вы бы теперь дублировали и каждый не знал о другой. Это приведет к несоответствиям между этими глобальными данными и состоянием базовой операционной системы, таким как дескрипторы файлов и границы памяти (и, возможно, некоторые значения из стандартной библиотеки C, такие как errno для libstdc++, и обработчики сигналов и таймеры для libpthread.

Чтобы избежать слишком широкой интерпретации, я добавлю замечание: время от времени могут иметь веские основания для желания статически связываться даже с такими базовыми библиотеками, как libstdc++ и даже libc, и хотя с недавними системами и версиями этих библиотек это становится немного сложнее (из-за небольшой связи с используемым загрузчиком и специальными приемами компоновщика), определенно возможно - я сделал это несколько раз, и знать о других случаях, в которых это все еще делается. Однако , в этом случае вам нужно статически связать весь исполняемый файл . Статическая связь со стандартными библиотеками в сочетании с динамической связью с другими объектами обычно невозможна.


Редактировать : Одна проблема, о которой я забыл упомянуть, но которую важно учитывать, касается C ++. К сожалению, C ++ не был разработан, чтобы хорошо работать с классической моделью связывания и загрузки объектов (используется в Unix и других системах). Это делает разделяемые библиотеки в C ++ не очень переносимыми, как они должны быть, потому что многие вещи, такие как информация о типах и шаблоны, не разделены между объектами (часто принимаются вместе с большим количеством реального библиотечного кода во время компиляции из заголовков). ). libstdc++ по этой причине тесно связан с GCC, и код, скомпилированный с одной версией g++, будет работать только с libstdc++ с этой (или очень похожей) версией g++. Как вы наверняка заметите, если вы когда-нибудь попытаетесь собрать программу с GCC 4 с любой нетривиальной библиотекой в ​​вашей системе, которая была скомпилирована с GCC 3, это не просто libstdc++. Если ваша причина для этого заключается в том, чтобы убедиться, что ваш общий объект всегда связан с конкретными версиями libstdc++ и libpthread, против которых он был создан, это не поможет, потому что программа, использующая другой / несовместимый libstdc++ также будет создан с несовместимым компилятором C ++ или версией g++, и, таким образом, в любом случае не сможет связываться с вашим общим объектом, за исключением реальных libstdc++ конфликтов .

Если вы задаетесь вопросом «почему это не было сделано проще?», Стоит задуматься над общим рассуждением : чтобы C ++ прекрасно работал с динамическими / разделяемыми библиотеками (что означает совместимость между компиляторами и возможность замены динамическая библиотека с другой версией с совместимым интерфейсом без перестройки всего, что ее использует), требуется не только стандартизация компилятора, но и на уровне загрузчика операционной системы, структура и интерфейс файлов объектов и библиотек и работа компоновщика потребуется значительно расширить за пределы относительно простой классики Unix, используемой в распространенных операционных системах ( Microsoft Windows, системы на базе Mach и родственники NeXTStep, такие как Mac OS, родственники VMS и некоторые системы мэйнфреймов, также включены ) для встроенного кода сегодня. Компоновщик и динамический загрузчик должны знать о таких вещах, как шаблоны и типизацию, имея в некоторой степени функциональность небольшого компилятора , чтобы фактически адаптировать код библиотеки к заданному типу - и ( личное субъективное наблюдение здесь ) кажется, что промежуточный промежуточный код более высокого уровня (вместе с языками более высокого уровня и компиляцией точно в срок) завоевывает популярность быстрее и, вероятно, будет стандартизирован раньше, чем такие расширения нативного объекта форматы и компоновщики.

1 голос
/ 14 февраля 2009

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

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

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

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

    Если вы обнаружите, что разница в размерах значительна, но не хотите статически связывать все, вы пытаетесь уменьшить размер libstdc++, перестраивая его без некоторых частей, которые, как вы знаете, вам не нужны (есть configure- опций времени для некоторых его частей, и вы также можете попытаться удалить некоторые независимые объекты при окончательном создании libstdc++.so. Есть несколько инструментов для оптимизации размера библиотек - поиск в Интернете (я помню один из компании с именем MontaVista , но пока не вижу его на своем веб-сайте, есть и другие)

Помимо простого вышеизложенного, некоторые идеи и предложения для размышления:

Вы упомянули, что используете uClibc, который я никогда не возился с собой (мой опыт работы со встроенным программированием гораздо более примитивен, в основном он включает программирование на ассемблере для встроенного процессора и кросс-компиляцию с минимальными встроенными библиотеками). Я предполагаю, что вы это проверили, и я знаю, что uClibc предназначен для облегченной, но достаточно полной стандартной библиотеки C, но не забывайте, что код C ++ вряд ли зависит от библиотеки C, а g++ и libstdc++ зависят на некоторых деликатных вещах (я помню проблемы с libc на некоторых проприетарных версиях Unix), поэтому я не просто предположил бы, что g++ или GNU libstdc++ на самом деле работает с uClibc без попыток - я не помню видя это упомянутое на uClibc страницах.

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

И в этом случае также просто быстрая ссылка, на которую я наткнулся при поиске uClibc - если вы работаете во встроенной системе, используете uClibc и хотите использовать на ней код C ++ - посмотрите на <a href="http://cxx.uclibc.org/" rel="nofollow noreferrer">uClibc++</a>. Я не знаю, сколько стандартного C ++ вам нужно, и он уже поддерживается, и это, кажется, текущий проект, поэтому неясно, находится ли он в достаточно хорошем состоянии для вас, но при условии, что ваша работа также находится под Тем не менее, это может быть хорошей альтернативой libstdc++ GCC для вашей встроенной работы.

0 голосов
/ 14 февраля 2009

Я думаю этот парень довольно хорошо объясняет, почему это не имеет смысла. Код C ++, который использует ваш общий объект, но другой libstdc ++, будет связываться нормально, но не будет работать.

...