Ограничение символов в статической библиотеке Linux - PullRequest
22 голосов
/ 26 декабря 2008

Я ищу способы ограничить количество символов C, экспортируемых в статическую библиотеку Linux (архив). Я хотел бы ограничить их только теми символами, которые являются частью официального API для библиотеки. Я уже использую «static», чтобы объявить большинство функций как статические, но это ограничивает их область видимости файла. Я ищу способ ограничения области видимости для библиотеки.

Я могу сделать это для разделяемых библиотек, используя приемы из Ульриха Дреппера, * Как писать общие библиотеки , но я не могу применить эти приемы к статическим архивам. В своей более ранней статье Good Practices in Library Design он пишет:

Единственная возможность - объединить все объектные файлы, которые нужны определенные внутренние ресурсы в один, используя 'ld -r', а затем ограничить символы которые экспортируются этим комбинированным объектным файлом. Линкер GNU имеет опции для сделай только это.

Может ли кто-нибудь помочь мне выяснить, какими могут быть эти варианты? У меня был некоторый успех с 'strip -w -K prefix_ *', но это кажется грубым. В идеале мне бы хотелось, чтобы решение работало с GCC 3 и 4.

Спасибо!

Ответы [ 5 ]

10 голосов
/ 26 декабря 2008

Статические библиотеки не могут делать то, что вы хотите для кода, скомпилированного с помощью GCC 3.x или 4.x.

Если вы можете использовать общие объекты (библиотеки), компоновщик GNU делает то, что вам нужно, с помощью функции, называемой сценарием версии. Это обычно используется для предоставления точек входа для конкретной версии, но вырожденный регистр просто различает публичные и приватные символы без какой-либо версии. Сценарий версии указывается с параметром командной строки --version-script = для ld.

Содержимое скрипта версии, который делает точки входа foo и bar открытыми и скрывает все остальные интерфейсы:

{ global: foo; bar; local: *; };

См. Документ лд по адресу: http://sourceware.org/binutils/docs/ld/VERSION.html#VERSION

Я большой сторонник общих библиотек, и эта способность ограничивать видимость глобалов является одним из их больших достоинств.

Документ, который предоставляет больше преимуществ общих объектов, но написанный для Solaris (Грег Нахимовский из счастливой памяти), находится в http://developers.sun.com/solaris/articles/linker_mapfiles.html

Надеюсь, это поможет.

9 голосов
/ 06 января 2009

Я не верю, что в GNU ld есть такие опции; Ульрих, должно быть, имел в виду objcopy, который имеет много таких вариантов: --localize-hidden, --localize-symbol=symbolname, --localize-symbols=filename.

В частности, --localize-hidden позволяет очень точно контролировать, какие символы отображаются. Рассмотрим:

int foo() { return 42; }
int __attribute__((visibility("hidden"))) bar() { return 24; }

gcc -c foo.c
nm foo.o
000000000000000b T bar
0000000000000000 T foo

objcopy --localize-hidden foo.o bar.o
nm bar.o
000000000000000b t bar
0000000000000000 T foo

Таким образом, bar() больше не экспортируется из объекта (даже если он все еще присутствует и может использоваться для отладки). Вы также можете удалить bar() все вместе с objcopy --strip-unneeded.

8 голосов
/ 12 мая 2015

Достоинства этого ответа будут зависеть от того, почему вы используете статические библиотеки. Если это позволит компоновщику отбросить неиспользуемые объекты позже, мне нечего добавить. Если это делается с целью организации - минимизации количества объектов, которые необходимо передать для связи приложений, - это расширение ответа «Занятый русский» может оказаться полезным.

Во время компиляции видимость всех символов в единице компиляции может быть установлена ​​с помощью:

-fvisibility=hidden
-fvisibility=default

Это означает, что можно скомпилировать один файл "interface.c" с видимостью по умолчанию и большее количество файлов реализации со скрытой видимостью, без указания источника. Перемещаемая ссылка затем создаст один объектный файл, в котором не-API-функции «скрыты»:

ld -r interface.o implementation0.o implementation1.o -o relocatable.o

Объединенный объектный файл теперь может быть подвергнут объекту:

objcopy --localize-hidden relocatable.o mylibrary.o

Таким образом, у нас есть один объектный файл "библиотека" или "модуль", который предоставляет только предполагаемый API.


Вышеприведенная стратегия умеренно хорошо взаимодействует с оптимизацией времени соединения. Скомпилируйте с -flto и выполните перемещаемую ссылку, передав компилятору -r через компилятор:

gcc -fuse-linker-plugin -flto -nostdlib -Wl,-r {objects} -o relocatable.o

Используйте objcopy для локализации скрытых символов, как и раньше, затем вызовите компоновщик в последний раз, чтобы убрать локальные символы и любой другой мертвый код, который он может найти в объекте post-lto. К сожалению, relocatable.o вряд ли сохранил какую-либо информацию, связанную с этим:

gcc -nostdlib -Wl,-r,--discard-all relocatable.o mylibrary.o

Текущие реализации lto, по-видимому, активны на этапе перемещения. При включенном значении скрытые => локальные символы были удалены последней перемещаемой ссылкой. Без lto скрытые => локальные символы сохранились до последней перемещаемой ссылки.

Будущие реализации lto, вероятно, сохранят требуемые метаданные на этапе перемещения ссылки, но в настоящее время результатом перемещения ссылки является обычный старый объектный файл.

1 голос
/ 14 июля 2018

Это уточнение ответов от EmployedRussian и JonChesterfield, которые могут быть полезны, если вы генерируете как динамические, так и статические библиотеки.

Начните со стандартного механизма скрытия символов в DSO (динамическая версия вашей библиотеки). Скомпилируйте все файлы с -fvisibility=hidden. В заголовочном файле, который определяет ваш API, измените объявления классов и функций, которые вы хотите сделать общедоступными:

   #define DLL_PUBLIC __attribute__ ((visibility ("default")))
   extern DLL_PUBLIC int my_api_func(int);

Подробнее см. здесь . Это работает как для C, так и для C ++. Этого достаточно для DSO, но вам нужно добавить следующие шаги сборки для статических библиотек:

ld -r obj1.o obj2.o ... objn.o -o static1.o
objcopy --localize-hidden static1.o static2.o
ar -rcs mylib.a static2.o

Шаг ar является необязательным - вы можете просто связать его с static2.o.

0 голосов
/ 26 декабря 2008

Мой способ сделать это - пометить все, что не нужно экспортировать, с помощью Internal, включить защиту всех .h файлов, скомпилировать сборки dev с помощью -DINTERNAL = и скомпилировать сборки выпуска с помощью одного файла .c, который включает все остальные файлы библиотеки .c с -DINTERNAL = static.

...