限制 Linux 静态库中的符号
- 2024-11-14 08:30:00
- admin 原创
- 24
问题描述:
我正在寻找方法来限制导出到 Linux 静态库(存档)的 C 符号数量。我想将这些符号限制为仅属于库的官方 API 的那些符号。我已经使用“static”将大多数函数声明为静态,但这将它们限制在文件范围内。我正在寻找一种方法来将范围限制在库内。
我可以使用 Ulrich Drepper 的《如何编写共享库》中的技术对共享库执行此操作,但我无法将这些技术应用于静态档案。在他早期的《库设计中的良好实践》论文中,他写道:
唯一的办法是使用“ld -r”将所有需要特定内部资源的目标文件合并为一个,然后限制此合并的目标文件导出的符号。GNU 链接器有选项可以执行此操作。
有人能帮我找出这些选项吗?我使用“strip -w -K prefix_*”取得了一些成功,但这感觉很粗暴。理想情况下,我想要一个可以同时适用于 GCC 3 和 4 的解决方案。
谢谢!
解决方案 1:
我不相信 GNU ld 有任何这样的选项;Ulrich 肯定是指objcopy
,它有很多这样的选项:--localize-hidden
,--localize-symbol=symbolname
,--localize-symbols=filename
。
特别--localize-hidden
是,它允许人们非常精细地控制哪些符号被暴露。考虑一下:
int foo() { return 42; }
int __attribute__((visibility("hidden"))) bar() { return 24; }
gcc -c foo.c
nm foo.o
000000000000000b T bar
0000000000000000 T foo
objcopy --localize-hidden foo.o bar.o
nm bar.o
000000000000000b t bar
0000000000000000 T foo
因此bar()
不再从对象中导出(即使它仍然存在并且可用于调试)。您也可以将bar()
和 一起删除objcopy --strip-unneeded
。
解决方案 2:
对于使用 GCC 3.x 或 4.x 编译的代码,静态库无法完成您想要的操作。
如果您可以使用共享对象(库),GNU 链接器会使用称为版本脚本的功能满足您的需求。这通常用于提供特定于版本的入口点,但退化情况仅区分公共和私有符号,而不进行任何版本控制。版本脚本是使用 ld 的 --version-script= 命令行选项指定的。
版本脚本的内容使入口点 foo 和 bar 公开并隐藏所有其他接口:
{ global: foo; bar; local: *; };
请参阅 ld 文档:http: //sourceware.org/binutils/docs/ld/VERSION.html#VERSION
我是共享库的忠实拥护者,限制全局变量可见性的能力是其一大优点。
有一份文档详细介绍了共享对象的优点,但该文档是为 Solaris 编写的(由 Greg Nakhimovsky 撰写),网址为http://developers.sun.com/solaris/articles/linker_mapfiles.html
我希望这会有所帮助。
解决方案 3:
这个答案的优点取决于你使用静态库的原因。如果是为了让链接器稍后删除未使用的对象,那么我没什么可补充的。如果是为了组织的目的——尽量减少必须传递以链接应用程序的对象数量——那么 Employed Russian 答案的这个扩展可能会有用。
在编译时,可以使用以下命令设置编译单元内所有符号的可见性:
-fvisibility=hidden
-fvisibility=default
这意味着可以编译具有默认可见性的单个文件“interface.c”和具有隐藏可见性的大量实现文件,而无需注释源。可重定位链接将生成非 api 函数被“隐藏”的单个目标文件:
ld -r interface.o implementation0.o implementation1.o -o relocatable.o
组合后的目标文件现在可以进行 objcopy 了:
objcopy --localize-hidden relocatable.o mylibrary.o
因此,我们有一个仅公开预期 API 的单一目标文件“库”或“模块”。
上述策略与链接时间优化的相互作用相当好。使用 -flto 进行编译,并通过编译器将 -r 传递给链接器来执行可重定位链接:
gcc -fuse-linker-plugin -flto -nostdlib -Wl,-r {objects} -o relocatable.o
像之前一样使用 objcopy 来本地化隐藏符号,然后最后一次调用链接器来剥离本地符号以及它在后 lto 对象中找到的任何其他死代码。遗憾的是,relocatable.o 不太可能保留任何与 lto 相关的信息:
gcc -nostdlib -Wl,-r,--discard-all relocatable.o mylibrary.o
lto 的当前实现似乎在可重定位链接阶段处于活动状态。启用 lto 后,隐藏=>本地符号会被最终的可重定位链接剥离。未启用 lto 时,隐藏=>本地符号会在最终的可重定位链接中保留下来。
lto 的未来实现似乎可能会通过可重定位链接阶段保留所需的元数据,但目前可重定位链接的结果似乎是一个普通的旧目标文件。
解决方案 4:
这是对 EmployedRussian 和 JonChesterfield 的答案的改进,如果您同时生成动态库和静态库,这可能会有所帮助。
从隐藏 DSO(库的动态版本)中的符号的标准机制开始。使用 编译所有文件-fvisibility=hidden
。在定义 API 的头文件中,更改要公开的类和函数的声明:
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
extern DLL_PUBLIC int my_api_func(int);
详情请参阅此处。这适用于 C 和 C++。这对于 DSO 来说已经足够了,但您需要为静态库添加以下构建步骤:
ld -r obj1.o obj2.o ... objn.o -o static1.o
objcopy --localize-hidden static1.o static2.o
ar -rcs mylib.a static2.o
该ar
步骤是可选的 - 您可以直接链接到static2.o
。
解决方案 5:
我的做法是将所有不导出的内容标记为 INTERNAL,包括所有 .h 文件,使用 -DINTERNAL= 编译开发版本,并使用包含所有其他库 .c 文件的单个 .c 文件编译发布版本,并使用 -DINTERNAL=static。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件