c - 对链接到 .so 文件的第 3 方 .a 文件隐藏符号
问题描述
我正在构建一个共享 (.so) 库,该库由多个 .a 文件和调用它们的薄 API 层组成。我只希望我的 API 和外部依赖项可见,因此我使用 GCC ( ) 提供的“隐藏”可见性构建我的代码-fvisibility=hidden
。
但是,其中一个库是专有的第三方 .a 文件(我们已经付费使用),我只能访问它的二进制文件。当我将它静态链接到我的 .so 文件时,它的符号在我的 .so 的动态符号表中可见。我猜这是因为该库不是使用隐藏的可见性选项构建的。我宁愿隐藏这些功能作为管理我们软件的敏感部分,我不希望第三方链接到这些符号。
有什么方法可以在事后将这些符号标记为“隐藏”,以便它们不会出现在我的 .so 文件的符号列表中?我已经看过了objdump
,objcopy
但是我很难使用这些术语。
我尝试过的其他事情:
- 从 .a 中提取 .o 并尝试按照此处所述重新编译: how to add prebuilt object files to executable in cmake
解决方案
这是一个如何解决您的问题的工作示例。
这是您无法重新编译的专有静态库的源代码:
$ 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
链接器选项记录在案:
--exclude-libs 库,库,...
指定不应自动从中导出符号的存档库列表。库名称可以用逗号或冒号分隔。指定 --exclude-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
具有相同的效果。
[1] 我
-fPIC
明确指定以确保生成可以在 DSO 中链接的与位置无关的目标代码,但这是自 GCC 6 以来的 GCC 默认设置。
推荐阅读
- go - 为什么 path = os.Getenv("GOMOD"), path = "" 在项目中?
- apache-kafka - Flink 表接收器不适用于 debezium-avro-confluent 源
- windows - 使用 WM_MOUSEMOVE/WM_NCMOUSEMOVE 更新指定区域
- sql-server - Snowflake ODBC 驱动程序和 SQL Server Polybase
- javascript - 计算两个变量字符串之间的字数
- python - 当访问一个方法中更新的类变量时,它的更新值不会在scrapy spider的另一个方法中被拾取
- excel - 如何使用 VBA 将 Outlook 邮件中的“收件人:”字段与预定义的邮件 ID 进行比较?
- r - 如何根据主数据框中的选定特征生成动态汇总表
- swift - 使用 MVVM 架构快速更新数据
- visual-studio - 系统重新启动后,模拟器选项未在 VS 2019 中显示