【发布时间】:2022-01-22 15:42:09
【问题描述】:
某些平台要求您向链接器提供共享库的外部符号列表。但是,在大多数 unixish 系统上,这不是必需的:默认情况下,所有非静态符号都可用。
我的理解是 GNU 工具链可以选择性地将可见性限制为显式声明的符号。使用 GNU ld 如何实现?
【问题讨论】:
标签: linker shared-libraries gnu linker-scripts
某些平台要求您向链接器提供共享库的外部符号列表。但是,在大多数 unixish 系统上,这不是必需的:默认情况下,所有非静态符号都可用。
我的理解是 GNU 工具链可以选择性地将可见性限制为显式声明的符号。使用 GNU ld 如何实现?
【问题讨论】:
标签: linker shared-libraries gnu linker-scripts
GNU ld 可以在 ELF 平台上做到这一点。
以下是如何使用链接器版本脚本:
/* foo.c */
int foo() { return 42; }
int bar() { return foo() + 1; }
int baz() { return bar() - 1; }
gcc -fPIC -shared -o libfoo.so foo.c && nm -D libfoo.so | grep ' T '
默认情况下,所有符号都被导出:
0000000000000718 T _fini
00000000000005b8 T _init
00000000000006b7 T bar
00000000000006c9 T baz
00000000000006ac T foo
假设您只想导出bar() 和baz()。创建“版本脚本”libfoo.version:
FOO {
global: bar; baz; # explicitly list symbols to be exported
local: *; # hide everything else
};
将其传递给链接器:
gcc -fPIC -shared -o libfoo.so foo.c -Wl,--version-script=libfoo.version
观察导出的符号:
nm -D libfoo.so | grep ' T '
00000000000005f7 T bar
0000000000000609 T baz
【讨论】:
t 列出。
-fvisibility=hidden一样优化代码。
我认为最简单的方法是将-fvisibility=hidden 添加到 gcc 选项中,并在代码中明确公开某些符号的可见性(__attribute__((visibility("default"))))。请参阅文档here。
可能有一种方法可以通过 ld 链接器脚本来实现,但我对此了解不多。
【讨论】:
为调用任何导出的函数或使用任何导出的全局变量而生成的代码效率低于未导出的代码。涉及到一个额外的间接级别。这适用于可能在编译时导出的任何函数。 gcc 仍会为稍后由链接描述文件导出的函数产生额外的间接。因此使用可见性属性将产生比链接描述文件更好的代码。
【讨论】:
似乎有几种方法可以在 GNU/Linux 上管理导出的符号。根据我的阅读,这些是 3 种方法:
-fvisibility=hidden 和 __attribute__((visibility("default")))
#pragma GCC visibility
-Wl,--version-script=<version script file>)我不会在这里讨论示例,因为它们大部分都被其他答案所涵盖,但这里有一些关于不同方法的注意事项、优缺点:
strip --strip-all --discard-all。我确定还有其他人。
以下是一些我认为有帮助的参考资料(带有示例):
【讨论】:
如果您使用的是 libtool,还有另一个选项,类似于 Employed Russian 的回答。
使用他的例子,它会是这样的:
cat export.sym
bar
baz
然后使用以下选项运行 libtool:
libtool -export-symbols export.sym ...
请注意,当使用 -export-symbols 时,默认情况下不会导出所有符号,并且仅导出 export.sym 中的符号(因此 libfoo.version 中的“local: *”行实际上隐含在此方法中)。
【讨论】:
-fvisibility=hidden 相比,这会生成次优代码。