Вы не можете достичь того, чего хотите, создав статическую библиотеку libww.a
.Если вы прочитаете статические библиотеки , вы поймете, почему.Статическая библиотека может быть использована для предложения связке N объектных файлов для компоновщика, из которого он будет извлекать k (возможно = 0), в котором он нуждается, и связывать их .Таким образом, вы ничего не можете достичь, связавшись со статической библиотекой, чего вы не можете достичь, связав эти объектные файлы k напрямую.Для целей связывания статические библиотеки на самом деле не существуют.
Но общие библиотеки действительно существуют для целей связывания, и глобальные символы, предоставляемые совместно используемой библиотекой, приобретают дополнительное свойство, dynamicвидимость , которая существует именно для вашей цели.Динамически видимые символы являются подмножеством глобальных символов: они являются глобальными символами, которые видимы для динамического связывания, то есть для связывания совместно используемой библиотеки с чем-то еще (программой или другой совместно используемой библиотекой).
Динамическая видимость не является атрибутом, о котором стандарты исходного языка ничего не говорят, потому что они ничего не говорят о динамическом связывании.Таким образом, управление динамической видимостью символов должно выполняться индивидуально с помощью цепочки инструментов, которая поддерживает динамическое связывание.GCC делает это с помощью спецификатора объявления компилятора 1 :
__attribute__((visibility("default|hidden|protected|internal")
и / или переключателя компилятора 2 :
-fvisibility=default|hidden|protected|internal
Вот демонстрация того, как сборка libww.so
, так что utilityTwiddle
скрыта от клиентов библиотеки, в то время как wombatTwiddle
и widgetTwiddle
видны.
Ваш исходный код должен быть немного уточнен одним илидругой для компиляции.Вот первое сокращение:
ww.h (1)
#ifndef WW_H
#define WW_H
struct widget {
int bits;
};
struct wombat {
int bits;
};
extern void widgetTwiddle ( struct widget * w );
extern void wombatTwiddle ( struct wombat * w );
#endif
utility.h (1)
#ifndef UTILITY_H
#define UTILITY_H
extern void utilityTwiddle ( int * bitsPtr, int bits );
#endif
utility.c
#include "utility.h"
void utilityTwiddle ( int * bitsPtr, int bits ) {
*bitsPtr ^= bits;
}
wombats.c
#include "utility.h"
#include "ww.h"
void wombatTwiddle ( struct wombat * w ) {
utilityTwiddle(&w->bits, 1);
}
widgets.c
#include "utility.h"
#include "ww.h"
void widgetTwiddle ( struct widget * w ) {
utilityTwiddle(&w->bits, 1);
}
Скомпилируйте все файлы *.c
в файлы *.o
по умолчанию:
$ gcc -Wall -Wextra -c widgets.c wombats.c utility.c
и свяжите их с libww.so
по умолчанию:
$ gcc -shared -o libww.so widgets.o wombats.o utility.o
Вот *Twiddle
символов в глобальной таблице символов libww.so
$ nm libww.so | egrep '*Twiddle'
000000000000063a T utilityTwiddle
00000000000005fa T widgetTwiddle
000000000000061a T wombatTwiddle
Это всего лишь сумма глобальных (extern
) *Twiddle
символов, которые вошли в связьlibww.so
из объектных файлов.Все они определены (T
), как и должно было бы быть, если бы сама библиотека была связана без внешних *Twiddle
зависимостей.
Любой файл ELF (объектфайл, разделяемая библиотека, программа) имеет глобальную таблицу символов, но совместно используемая библиотека также имеет таблицу символов dynamic .Вот символы *Twiddle
в таблице динамических символов libww.so
:
$ nm -D libww.so | egrep '*Twiddle'
000000000000063a T utilityTwiddle
00000000000005fa T widgetTwiddle
000000000000061a T wombatTwiddle
Они точно такие же.Это то, что мы хотим изменить, чтобы исчезло utilityTwiddle
.
Вот второй разрез.Мы должны немного изменить исходный код.
utility.h (2)
#ifndef UTILITY_H
#define UTILITY_H
extern void utilityTwiddle ( int * bitsPtr, int bits ) __attribute__((visibility("hidden")));
#endif
Затем перекомпилировать и перекомпоновать, как раньше:
$ gcc -Wall -Wextra -c widgets.c wombats.c utility.c
$ gcc -shared -o libww.so widgets.o wombats.o utility.o
Вот символы *Twiddle
, которые теперь находятся в глобальной таблице символов:
$ nm libww.so | egrep '*Twiddle'
000000000000063a T utilityTwiddle
00000000000005fa T widgetTwiddle
000000000000061a T wombatTwiddle
Никаких изменений там нет.И вот теперь символы *Twiddle
в таблице символов dynamic :
$ nm -D libww.so | egrep '*Twiddle'
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle
utilityTwiddle
пропали.
Вот третий разрез, который достигает того жерезультат по-другому.Это более многословно, но иллюстрирует, как играет опция компилятора -fvisibility
.На этот раз utility.h
снова соответствует (1) , но ww.h
равно:
ww.h (2)
#ifndef WW_H
#define WW_H
struct widget {
int bits;
};
struct wombat {
int bits;
};
extern void widgetTwiddle ( struct widget * w ) __attribute__((visibility("default")));
extern void wombatTwiddle ( struct wombat * w ) __attribute__((visibility("default")));
#endif
Теперь мы перекомпилируем так:
$ gcc -Wall -Wextra -fvisibility=hidden -c widgets.c wombats.c utility.c
Мы говорим компилятору аннотировать каждый глобальный символ, который он генерирует с __attribute__((visibility("hidden")))
, если нет явной компенсации __attribute__((visibility("...")))
в исходном коде.
Затем повторно подключите общую библиотеку, как и ранее.Снова мы видим в глобальной таблице символов:
$ nm libww.so | egrep '*Twiddle'
00000000000005ea t utilityTwiddle
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle
и в таблице динамических символов:
$ nm -D libww.so | egrep '*Twiddle'
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle
Наконец, чтобы показать, что удаление utilityTwiddle
из таблицы динамических символов libww.so
одним из этих способов действительно скрывает это от клиентов, связывающихся сlibww.so
.Вот программа, которая хочет вызвать все *Twiddle
s:
prog.c
#include <ww.h>
extern void utilityTwiddle ( int * bitsPtr, int bits );
int main()
{
struct widget wi = {1};
struct wombat wo = {2};
widgetTwiddle(&wi);
wombatTwiddle(&wo);
utilityTwiddle(&wi.bits,wi.bits);
return 0;
}
У нас нет проблем при ее создании, например:
$ gcc -Wall -Wextra -I. -c prog.c
$ gcc -o prog prog.o utility.o widgets.o wombats.o
Но никто не может построить его так:
$ gcc -Wall -Wextra -I. -c prog.c
$ gcc -o prog prog.o -L. -lww
prog.o: In function `main':
prog.c:(.text+0x4a): undefined reference to `utilityTwiddle'
collect2: error: ld returned 1 exit status
Понятно, что -fvisibility
это опция компиляции , а не опция связывания.Вы передаете его своим командам компиляции, а не командам связывания, потому что это действует так же, как разбрасывание квалификаторов __attribute__((visibility("...")))
по объявлениям в вашем исходном коде, что компилятор должен выполнять, вводя информацию о связяхв объектные файлы, которые он генерирует.Если вы хотите увидеть доказательства этого, вы можете просто повторить последнюю компиляцию и запросить сохранение файлов сборки:
$ gcc -Wall -Wextra -fvisibility=hidden -c widgets.c wombats.c utility.c -save-temps
Затем сравните, скажем:
widgets.s
.file "widgets.c"
.text
.globl widgetTwiddle
.type widgetTwiddle, @function
widgetTwiddle:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movl $1, %esi
movq %rax, %rdi
call utilityTwiddle@PLT
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size widgetTwiddle, .-widgetTwiddle
.ident "GCC: (Ubuntu 7.3.0-16ubuntu3) 7.3.0"
.section .note.GNU-stack,"",@progbits
с:
утилита.s
.file "utility.c"
.text
.globl utilityTwiddle
.hidden utilityTwiddle
^^^^^^^^^^^^^^^^^^^^^^
.type utilityTwiddle, @function
utilityTwiddle:
...
...
[1] См.
GCCmanual :
[2] См. Руководство GCC, 3.16 Варианты для соглашений по генерации кода .