Ограничение видимости символов при связывании общих библиотек
-
10-07-2019 - |
Вопрос
Некоторые платформы требуют, чтобы вы предоставили компоновщику список внешних символов совместно используемой библиотеки. Однако в большинстве unixish-систем это необязательно: все нестатические символы будут доступны по умолчанию.
Насколько я понимаю, набор инструментов GNU может по желанию ограничивать видимость только символами, явно объявленными. Как этого добиться, используя GNU ld?
Решение
GNU ld
может делать это на платформах ELF.
Вот как это сделать с помощью скрипта версии компоновщика:
/* foo.c */
int foo() { return 42; }
int bar() { return foo() + 1; }
int baz() { return bar() - 1; }
gcc -fPIC -shared -o libfoo.so foo.c && nm -D libfoo.so | grep ' T '
По умолчанию все символы экспортируются:
0000000000000718 T _fini
00000000000005b8 T _init
00000000000006b7 T bar
00000000000006c9 T baz
00000000000006ac T foo
Допустим, вы хотите экспортировать только bar ()
и baz ()
. Создайте «скрипт версии» <Код> libfoo.version код>:
FOO {
global: bar; baz; # explicitly list symbols to be exported
local: *; # hide everything else
};
Передайте его компоновщику:
gcc -fPIC -shared -o libfoo.so foo.c -Wl,--version-script=libfoo.version
Соблюдайте экспортированные символы:
nm -D libfoo.so | grep ' T '
00000000000005f7 T bar
0000000000000609 T baz
Другие советы
Я думаю, что самый простой способ сделать это - добавить -fvisibility = hidden
в параметры gcc и явно сделать видимость некоторых символов общедоступной в коде (с помощью __ attribute __ ((visibility (") ; по умолчанию & Quot;))) код>). См. Документацию здесь .
Может быть, это можно сделать с помощью сценариев ld linker, но я об этом немного знаю.
Код, сгенерированный для вызова любых экспортируемых функций или использования любых экспортированных глобальных переменных, менее эффективен, чем те, которые не экспортируются. Существует дополнительный уровень косвенности. Это относится к любой функции, которую можно экспортировать во время компиляции . gcc будет по-прежнему генерировать дополнительное косвенное обращение для функции, которая впоследствии не экспортируется сценарием компоновщика. Поэтому использование атрибута видимости даст лучший код, чем скрипт компоновщика.
Если вы используете libtool, есть и другой вариант, очень похожий на ответ Employed Russian.
Используя его пример, это будет что-то вроде:
cat export.sym
bar
baz
Затем запустите libtool со следующей опцией:
libtool -export-symbols export.sym ...
Обратите внимание, что при использовании -export-символов все символы НЕ экспортируются по умолчанию, а экспортируются только символы в export.sym (поэтому строка " local: * " в libfoo.version фактически подразумевается в этом подходе) . р>