Ограничение видимости символов при связывании разделяемых библиотек

На некоторых платформах вы указываете, что вы предоставляете список общих символов для общей библиотеки для компоновщика. Однако в большинстве систем unixish это не обязательно: все нестатические символы будут доступны по умолчанию.

Я понимаю, что инструментальная цепочка GNU может опционально ограничивать видимость только явно объявленными символами. Как это можно достичь с помощью GNU ld?

Ответ 1

GNU ld может делать это на платформах ELF.

Вот как это сделать с версией компоновщика script:

/* 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(). Создайте "версию script" 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

Ответ 2

Я думаю, что самый простой способ сделать это - добавить опции -fvisibility=hidden в gcc и явно сделать видимость некоторых символов общедоступными в коде (через __attribute__((visibility("default")))). См. Документацию здесь.

Возможно, есть способ сделать это с помощью ld-скриптов-компоновщиков, но я мало что знаю об этом.

Ответ 3

Код, сгенерированный для вызова любых экспортированных функций или использования любых экспортированных глобалов, менее эффективен, чем те, которые не экспортируются. Существует дополнительный уровень косвенности. Это относится к любой функции, которая может быть экспортирована во время компиляции. gcc все равно будет выдавать дополнительную косвенность для функции, которая позже не экспортируется компоновщиком script. Таким образом, использование атрибута видимости даст лучший код, чем компоновщик script.

Ответ 4

Если вы используете libtool, есть еще один вариант, подобный ответу на русский язык.

Используя его пример, это будет примерно так:

cat export.sym
bar
baz

Затем запустите libtool со следующей опцией:

libtool -export-symbols export.sym ...

Обратите внимание, что при использовании символов -export все символы НЕ экспортируются по умолчанию, и экспортируются только те, которые экспортируются в export.sym(поэтому в этом подходе подразумевается "локальная: *" строка в libfoo.version).