Выборочное статическое связывание библиотечных функций в общей библиотеке - PullRequest
7 голосов
/ 04 декабря 2009

Я хочу создать общую библиотеку, которая использует функции из сторонней статической библиотеки. Например, foo и bar из libfoobar.a. Я знаю, что мое основное приложение также использует foo и будет экспортировать этот символ. Поэтому я просто хочу добавить ссылку в bar, чтобы сохранить размер кода и оставить «foo» неразрешенным (как это будет предоставлено основным приложением). Если я включу libfoobar.a, компоновщик ld будет включать обе функции в моей общей библиотеке. Если я не включу libfoobar.a, моя библиотека не будет иметь доступа к функции bar, поскольку само приложение не связывается с bar. Вопросы:

  • Есть ли способ указать ld разрешать только определенные символы при построении общей библиотеки?
  • Превратить libfoobar.a в общую библиотеку?
  • Извлечь файл, содержащий функцию bar из libfoobar.a и указать это в строке компоновщика?
  • Не беспокойтесь об этом, загрузчик во время выполнения будет использовать bar из вашего приложения, поэтому копия bar в общей библиотеке не будет загружена?

Ответы [ 3 ]

4 голосов
/ 16 декабря 2009

Следующие пункты пытаются ответить на поставленные мной вопросы:

  • ld , по-видимому, не позволяет вам исключать ссылки в определенных символах из статической библиотеки. Использование --just-symbols или --undefined (или команды сценария компоновщика EXTERN) не помешает ld связать символы.
  • Чтобы преобразовать статическую библиотеку, libfoobar.a , в общую, libfoobar.so.1.0 и экспортировать все видимые символы. Вы также можете использовать --version-script и другие методы для экспорта только подмножества символов.

    ld -shared -soname libfoobar.so.1 -o libfoobar.so.1.0 --whole-archive libfoobar.a --no-whole-archive

  • Лучше удалить членов архива из копии вашей статической библиотеки, чем извлекать их, поскольку могут существовать внутренние зависимости, которыми вы должны управлять. Например, если вы экспортируете все символы, вы можете сгенерировать файл карты из вашего основного исполняемого файла. Затем вы можете выполнить поиск для всех членов архива, которые исполняемый файл извлек из копии статической библиотеки, и удалить их из копии. Поэтому, когда ваш DSO связывается со статической библиотекой, он оставляет те же символы неразрешенными.

  • Можно указать ваш основной исполняемый файл как общую библиотеку для вашего DSO, если вы скомпилируете исполняемый файл с параметром --pie. Ваш DSO сначала свяжется с вашим исполняемым файлом, если он предшествовал статической библиотеке в команде link. Предупреждение: основной исполняемый файл должен быть доступен через LD_LIBRARY_PATH или -rpath. Кроме того, использование strace показывает, что, поскольку исполняемый файл является зависимостью вашей библиотеки, он загружается снова при загрузке DSO.

    ld -shared -rpath '$ORIGIN' -L. -lc -ldl -o DSO.so DSO.o app libfoobar.a

  • Динамический компоновщик будет сначала использовать версию исполняемого файла foo , если только вы не вызовете dlopen () с флагом RTLD_DEEPBIND. Использование strace показывает, что весь DSO отображается в файле mmap2 () в памяти. Тем не менее, Википедия утверждает, что для mmap «фактические чтения с диска выполняются« ленивым »образом после получения доступа к определенному местоположению». Если это так, то дубликат foo не будет загружен. Обратите внимание, что переопределение происходит только в том случае, если ваш DSO экспортировал функцию foo . В противном случае функция foo , которая была статически связана с вашим DSO, будет использоваться всякий раз, когда ваш DSO вызывает foo .

В заключение, если mmap () использует ленивое чтение, тогда лучшее решение - это связать DSO обычным образом и позволить динамическому компоновщику и linux позаботиться обо всем остальном.

1 голос
/ 08 декабря 2009

Отвечая на ваш уточненный более ясный вопрос.

Имейте в виду, что обычно общая библиотека заключается в том, что несколько программ могут ссылаться на нее. Таким образом, ваша оптимизация использования символа основной программы для нужной вам функции будет работать только в том случае, если основная программа всегда предоставляет этот символ (через статическую библиотеку или иным образом). Обычно это не то, что люди хотят делать.

Если это всего лишь пара небольших функций, возможно, вам следует это сделать. Вы, вероятно, в конечном итоге получите две копии кода для функций: одну в вашем shlib и одну в основной программе. Если они маленькие (или, по крайней мере, не очень большие), или их не часто называют и они не критичны по производительности, то попадание в размер кода / размер I-кэша из-за наличия двух копий не является проблемой (перевод: я не знаю, как избежать этого из головы, поэтому я мог бы не потратить время на его поиск и создание более сложного Makefile, чтобы избежать этого.)

Смотрите мой другой ответ для некоторых комментариев о том, как возиться с ar для извлечения материала из статической библиотеки. резюме: возможно нетривиально, так как вы не знаете зависимости между различными файлами .o в .a.

Может быть возможно сделать то, на что вы надеетесь, если ваша общая библиотека экспортирует символы, которые она извлекает из статической библиотеки. Затем, когда вы связываете основное приложение, поместите вашу общую библиотеку перед статической библиотекой в ​​командной строке компоновщика. ld найдет «foo» в вашем shlib и будет использовать эту копию (если этот трюк реэкспорта возможен), но для «bar» он должен будет включать копию из статической библиотеки.

ld --export-dynamic может быть тем, что вам нужно для экспорта всех символов в таблицу динамических символов. Попробуй это. И найдите «экспорт» на странице документации / руководства. «экспорт» - это жаргон для обозначения символа в библиотеке. --export-all-symbols находится в разделе i386 PE (Windows DLL), в противном случае это, вероятно, поможет

1 голос
/ 05 декабря 2009

Я не самый большой эксперт по разделяемым библиотекам, поэтому я могу ошибаться здесь!

Если я правильно догадываюсь о том, что вы пытаетесь сделать, просто свяжите вашу общую библиотеку с libc.so. Вам не нужна дополнительная копия sscanf, встроенная в вашу библиотеку.

Я ответил на ваши вопросы, прежде чем понял, к чему вы клоните, на случай, если вы заинтересованы в ответах.

Есть ли способ указать ld разрешать только определенные символы при сборке общей библиотеки?

только внешние, не статические, функции и переменные попадают в таблицу символов общей библиотеки.

При создании общей библиотеки все символы, не найденные в объектах в командной строке компоновщика, останутся неразрешенными. Если компоновщик жалуется на это, вам, вероятно, нужно связать вашу общую библиотеку с shared libc. У вас могут быть общие библиотеки, которые зависят от других общих библиотек, и ld.so может работать с цепочками зависимостей.

Если бы у меня было больше представителей, я бы спросил это как комментарий: У вас есть настроенная версия sprintf / sscanf, или ваша общая библиотека может использовать реализацию в -lc? Если с -lc все в порядке, мой ответ, вероятно, решит вашу проблему. Если нет, то вам нужно создать свою общую библиотеку из объектов, которые имеют только те функции, которые вам нужны. т.е. не связывать его с /usr/lib/libc.a.

Может быть, я запутался в твоих

libc.a (на самом деле не "настоящий" libc) линия. /usr/lib/libc.a действительно glibc (в Linux). Это статически связанная копия того же кода в libc.so. Если вы не говорите о своем собственном libc.a (о чем я думал вначале) ...

Превратить libc.a в общую библиотеку? Вы, вероятно, можете, но не можете, потому что он, вероятно, не скомпилирован как позиционно-независимый код, поэтому он потребует большого количества перемещений ld.so во время выполнения.

Извлеките sscanf из libc.a и укажите это в строке компоновщика?

Может быть возможно. ar t /usr/lib/libc.a для просмотра содержимого. (Аргументы ar аналогичны tar. tar был ar для лент .... Здесь используется старая школа Unix.) Вероятно, это не так просто, потому что sscanf, вероятно, зависит от символов в других файлах .o в .a.

...