• Если при динамической загрузке разделяемой библиотеки (см. подраздел 42.1.1) указать в функции dlopen() флаг RTLD_GLOBAL, то символы, определенные в этой библиотеке, будут доступны для привязки только тем библиотекам, которые загружаются после нее. Параметр компоновщика — export — dynamic позволяет сделать глобальные символы главной программы доступными для динамически загружаемых библиотек.
Больше подробностей о видимости символов см. в [Drepper, 2004 (b)].
$ gcc — Wl, — version-script,myscriptfile.map…
Версионные сценарии обычно (но не всегда) имеют расширение. map. Некоторые из способов их применения описаны в следующих подразделах.
42.3.1. Управление видимостью символов с помощью версионных сценариев
Одно из применений версионных сценариев заключается в управлении видимостью символов, которые могут случайно стать глобальными (то есть доступными для приложения, скомпонованного с этой библиотекой). В качестве простого примера рассмотрим такую ситуацию: мы собираем разделяемую библиотеку из трех исходных файлов — vis_comm.c, vis_f1.c и vis_f2.c, в которых определены функции vis_comm(), vis_f1() и vis_f2() соответственно. Функция vis_comm() не рассчитана на непосредственный доступ из приложений, скомпонованных с библиотекой, и должна вызываться из vis_f1() и vis_f2(). Представьте, что мы собираем эту библиотеку обычным способом:
$ gcc — g — c — fPIC — Wall vis_comm.c vis_f1.c vis_f2.c
$ gcc — g — shared — o vis.so vis_comm.o vis_f1.o vis_f2.o
Если вывести с помощью следующей команды readelf динамические символы, экспортируемые библиотекой, то можно увидеть следующее:
$ readelf — syms — use-dynamic vis.so | grep vis_
30 12: 00000790 59 FUNC GLOBAL DEFAULT 10 vis_f1
25 13: 000007d0 73 FUNC GLOBAL DEFAULT 10 vis_f2
27 16: 00000770 20 FUNC GLOBAL DEFAULT 10 vis_comm
Эта разделяемая библиотека экспортирует три символа: vis_comm(), vis_f1() и vis_f2(). Однако нам нужно, чтобы экспортировались только символы vis_f1() и vis_f2(). Для этого можно воспользоваться следующим версионным сценарием:
$ cat vis.map
VER_1 {
global:
vis_f1;
vis_f2;
local:
*;
};
Идентификатор VER_1 является примером
Внутри раздела находится ключевое слово global, после которого через точку с запятой перечисляются символы, видимые за пределами библиотеки. Ключевое слово local описывает символы, не скрытые от внешнего мира. Для этого можно использовать шаблоны, такие как звездочка (*). Данные шаблоны аналогичны тем, что применяются при поиске по именам файлов — например, * и? (больше подробностей см. на странице glob(7) руководства). В нашем примере звездочка в списке local говорит о том, что все символы, не перечисленные в подразделе global, будут скрыты. Без этого функция vis_comm() осталась бы видимой, поскольку глобальные символы в языке C по умолчанию доступны внешним разделяемым библиотекам.
Теперь можно собрать разделяемую библиотеку, используя версионный сценарий:
$ gcc — g — c — fPIC — Wall vis_comm.c vis_f1.c vis_f2.c
$ gcc — g — shared — o vis.so vis_comm.o vis_f1.o vis_f2.o \
— Wl, — version-script,vis.map
При повторном вызове команды readelf можно увидеть, что функция vis_comm() больше не доступна извне:
$ readelf — syms — use-dynamic vis.so | grep vis_
25 0: 00000730 73 FUNC GLOBAL DEFAULT 11 vis_f2
29 16: 000006f0 59 FUNC GLOBAL DEFAULT 11 vis_f1
42.3.2. Версионирование символов