我正在构建一个共享(.so)库,该库由几个.a文件和一个调用它们的API层组成。我只希望我的API和外部依赖是可见的,所以我使用GCC提供的 "隐藏 "可见性来构建我的代码(-fvisibility=hidden
).
然而,其中一个库是一个专有的第三方.a文件(我们已经付费使用),我只能访问它的二进制文件。当我把它静态地链接到我的.so文件中时,它的符号在我的.so的动态符号表中是可见的。我猜测这是因为该库在构建时没有使用隐藏的可见性选项。我宁愿将这些函数隐藏起来,因为它们管理着我们软件的敏感部分,我不希望第三方链接到这些符号。
有没有什么方法可以让我在事后将这些符号标记为 "隐藏",使它们不会出现在我的.so文件的符号列表中?我看过 objdump
和 objcopy
但我在术语方面有困难。
我试过的其他方法。
这里有一个解决你问题的工作实例。
这是你无法重新编译的专有静态库的源代码。
$ cat tpa.c
int tpa(void)
{
return 2;
}
$ cat tpb.c
int tpb(void)
{
return 3;
}
这个库 libtp.a
一定是这样建造的1:
$ gcc -fPIC -c -O1 tpa.c tpb.c
$ ar rcs libtp.a tpa.o tpb.o
符号表: tpa.o
和 tpb.o
是:-
$ 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
和 tpb
是 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
和 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
是 有据可查:
--exclud-libs lib,lib,...
指定一个不应自动导出符号的存档库列表,库名可以用逗号或冒号分隔。 库名可以用逗号或冒号分隔。指定--exclud-libs ALL可以将所有归档库中的符号排除在自动导出之外。 ... ... 对于 ELF 目标端口, 受此选项影响的符号将被视为隐藏。
为了保证 tp*
符号定义 有 已被链接,你仍然可以在共享库的完整符号表中看到它们。
$ 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
的方式来链接共享库,效果一样。
-fPIC
明确地生成与位置无关的对象代码,以便我可以在DSO中进行链接,但这是GCC自GCC 6以来的默认值。