这是如何解决您的问题的一个有效示例。
这是您无法重新编译的专有静态库的源代码:
$ cat tpa.c
int tpa(void)
{
return 2;
}
$ cat tpb.c
int tpb(void)
{
return 3;
}
The library, libtp.a
, must have been built essentially like this1:
$ gcc -fPIC -c -O1 tpa.c tpb.c
$ ar rcs libtp.a tpa.o tpb.o
的符号表tpa.o
and tpb.o
are:-
$ readelf -s libtp.a
File: libtp.a(tpa.o)
Symbol table '.symtab' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS tpa.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 2
4: 0000000000000000 0 SECTION LOCAL DEFAULT 3
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 6
7: 0000000000000000 0 SECTION LOCAL DEFAULT 7
8: 0000000000000000 0 SECTION LOCAL DEFAULT 4
9: 0000000000000000 10 FUNC GLOBAL DEFAULT 1 tpa
File: libtp.a(tpb.o)
Symbol table '.symtab' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS tpb.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 2
4: 0000000000000000 0 SECTION LOCAL DEFAULT 3
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 6
7: 0000000000000000 0 SECTION LOCAL DEFAULT 7
8: 0000000000000000 0 SECTION LOCAL DEFAULT 4
9: 0000000000000000 10 FUNC GLOBAL DEFAULT 1 tpb
您可以看到两个功能符号tpa
and tpb
are GLOBAL
( = 可用于链接) 并且有DEFAULT
动态可见性,不HIDDEN
.
现在这是您自己的静态库的源代码,libus.a
$ cat usa.c
int usa(void)
{
return 5;
}
$ cat usb.c
int usb(void)
{
return 7;
}
你像这样构建:
$ gcc -fPIC -c -O1 -fvisibility=hidden usa.c usb.c
$ ar rcs libus.a usa.o usb.o
中的函数符号libus.a
也都是GLOBAL
但他们的动态
能见度是HIDDEN
:-
$ readelf -s libus.a
File: libus.a(usa.o)
Symbol table '.symtab' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS usa.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 2
4: 0000000000000000 0 SECTION LOCAL DEFAULT 3
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 6
7: 0000000000000000 0 SECTION LOCAL DEFAULT 7
8: 0000000000000000 0 SECTION LOCAL DEFAULT 4
9: 0000000000000000 10 FUNC GLOBAL HIDDEN 1 usa
File: libus.a(usb.o)
Symbol table '.symtab' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS usb.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 2
4: 0000000000000000 0 SECTION LOCAL DEFAULT 3
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 6
7: 0000000000000000 0 SECTION LOCAL DEFAULT 7
8: 0000000000000000 0 SECTION LOCAL DEFAULT 4
9: 0000000000000000 10 FUNC GLOBAL HIDDEN 1 usb
这是共享库的源代码:
$ cat usc.c
extern int tpa(void);
extern int tpb(void);
extern int usa(void);
extern int usb(void);
int usc(void)
{
return tpa() * tpb() * usa() * usb();
}
你编译的:-
$ gcc -fPIC -c -O1 usc.c
现在你想要链接usc.o
, libtp.a
and libus.a
在您的共享库中libsus.so
。如果你
用普通方法做:
$ gcc -shared -o libsus.so usc.o -L. -ltp -lus
然后你会发现:
$ readelf --dyn-syms libsus.so
Symbol table '.dynsym' contains 8 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __cxa_finalize
2: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
3: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
4: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
5: 0000000000001139 38 FUNC GLOBAL DEFAULT 12 usc
6: 000000000000115f 10 FUNC GLOBAL DEFAULT 12 tpa
7: 0000000000001169 10 FUNC GLOBAL DEFAULT 12 tpb
认为HIDDEN
可见性符号来自libus.a
动态中不存在
符号表,但是DEFAULT
可见性符号来自libtp.a
被包含在内,
你不想要的。
要排除后者,请按如下方式链接您的共享库:
$ gcc -shared -o libsus.so usc.o -L. -ltp -lus -Wl,--exclude-libs=libtp.a
那么动态符号表就变成:
$ readelf --dyn-syms libsus.so
Symbol table '.dynsym' contains 6 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __cxa_finalize
2: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
3: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
4: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
5: 00000000000010f9 38 FUNC GLOBAL DEFAULT 10 usc
如你所愿。
链接器选项--exclude-libs
is 有记录的 https://sourceware.org/binutils/docs/ld/Options.html#Options:
--排除库 lib,lib,...
指定不应自动导出符号的存档库列表。
库名称可以用逗号或冒号分隔。指定 --exclude-libs ALL 会从自动导出中排除所有存档库中的符号。
...
对于 ELF 目标端口,受此选项影响的符号将被视为隐藏。
为了确保tp*
符号定义have已链接,你
仍然可以在共享库的完整符号表中看到它们:
$ readelf -s libsus.so | egrep 'FUNC.*(us|tp)(a|b|c)'
5: 00000000000010f9 38 FUNC GLOBAL DEFAULT 10 usc
41: 0000000000001133 10 FUNC LOCAL DEFAULT 10 usa
44: 000000000000111f 10 FUNC LOCAL DEFAULT 10 tpa
46: 000000000000113d 10 FUNC LOCAL DEFAULT 10 usb
48: 0000000000001129 10 FUNC LOCAL DEFAULT 10 tpb
50: 00000000000010f9 38 FUNC GLOBAL DEFAULT 10 usc
就像显式隐藏一样us*
符号,它们成为LOCAL
,不可用于进一步链接。 (你看usc
两次在grep
因为它被列为全局符号和动态符号)。
正如你可以从中推断出的那样,我们不需要费力去编译我们的
自己的us*
代码与-fvisibility=hidden
,只要我们要存档
它在libus.a
以便进一步联动。我们可以像这样链接共享库:
$ gcc -shared -o libsus.so usc.o -L. -ltp -lus -Wl,--exclude-libs=libtp.a,libus.a
具有相同的效果。
[1] I specify
-fPIC
explicitly to be sure of generating position-independent
object code that I can link in a DSO, but this has been the GCC default since
GCC 6.