有些平台要求你向链接器提供共享库的外部符号列表。但在大多数类Unix系统中,这并不是必需的:所有非静态符号都将默认可用。
我的理解是GNU工具链可以选择性地限制仅显式声明的符号的可见性。如何使用GNU ld实现这一点?
有些平台要求你向链接器提供共享库的外部符号列表。但在大多数类Unix系统中,这并不是必需的:所有非静态符号都将默认可用。
我的理解是GNU工具链可以选择性地限制仅显式声明的符号的可见性。如何使用GNU ld实现这一点?
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
-fvisibility=hidden
,然后在代码中将某些符号的可见性明确设置为公共的(通过__attribute__((visibility("default")))
)。请参阅此处的文档。可能也有一种方法可以通过ld链接脚本来完成,但我对此了解不多。调用任何已导出函数或使用已导出全局变量的代码效率都比那些未导出的要低。这涉及到额外的间接层。这适用于可能在编译时导出的任何函数。即使由链接器脚本取消导出的函数,gcc 仍将产生额外的间接引用。因此,使用可见性属性将比链接器脚本生成更好的代码。
R_X86_64_PLT32
重定位进行常规调用即可。但是,对于访问导出的变量,在查找之前必须先在GOT中查找它会有一些差别。 - knatten在GNU / Linux上管理导出符号似乎有几种方法。从我的阅读中,这些是三种方法:
-fvisibility = hidden
与__attribute __((visibility(“default”)))
#pragma GCC visibility
-Wl,--version-script =< version script file>
)我不会在这里列举例子,因为它们大多数已经被其他答案涵盖,但以下是我能想到的不同方法的一些注意事项、优缺点:
strip --strip-all --discard-all
。我相信还有其他方法。
以下是一些我发现有帮助的参考资料(附有示例):
cat export.sym
bar
baz
libtool -export-symbols export.sym ...
t
列出。 - PypeBros-fvisibility=hidden
那样让编译器优化代码。 - yugr