链接共享库时限制符号的可见性

时间:2009-01-12 13:05:08

标签: linker shared-libraries gnu linker-scripts

某些平台要求您向链接器提供共享库的外部符号列表。但是,在大多数不需要的unixish系统上:默认情况下,所有非静态符号都可用。

我的理解是GNU工具链可以选择性地将可见性限制为显式声明的符号。如何使用GNU ld实现这一目标?

5 个答案:

答案 0 :(得分:67)

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

答案 1 :(得分:38)

我认为最简单的方法是将-fvisibility=hidden添加到gcc选项,并明确地在代码中公开一些符号(__attribute__((visibility("default"))))。请参阅文档here

可能有一种方法可以通过ld链接器脚本实现这一点,但我对此并不了解。

答案 2 :(得分:6)

为调用任何导出函数或使用任何导出的全局变量而生成的代码效率低于未导出的函数。涉及额外的间接水平。这适用于在编译时间可能导出的任何函数。 gcc仍然会为稍后由链接描述文件导出的函数产生额外的间接。因此,使用visibility属性将产生比链接器脚本更好的代码。

答案 3 :(得分:1)

如果您使用libtool,还有另一种选择,就像雇用俄罗斯人的答案一样。

使用他的例子,它将是:

cat export.sym
bar
baz

然后使用以下选项运行libtool:

libtool -export-symbols export.sym ...

请注意,使用-export-symbols时,默认情况下不导出所有符号,只导出export.sym中的符号(因此libfoo.version中的" local:*"行实际上是隐式的在这种方法中。)

答案 4 :(得分:0)

似乎有几种方法可以在GNU / Linux上管理导出的符号。根据我的阅读,这是3种方法:

  • 源代码注释/修饰:
    • 方法1:-fvisibility=hidden__attribute__((visibility("default")))
    • 方法2(自GCC 4起):#pragma GCC visibility
  • 方法3:传递给链接器(例如-Wl,--version-script=<version script file>)的版本脚本(又称“符号映射”)

我不会在这里讨论示例,因为这些示例大部分都包含在其他答案中,但是以下是一些提示,利弊,这些提示使我无所适从:

  • 使用带注释的方法可使编译器稍微优化代码(减少一个间接调用)。
  • 如果使用带注释的方法,请考虑同时使用strip --strip-all --discard-all
  • 带注释的方法可以为内部功能级别的单元测试增加更多的工作,因为单元测试可能无法访问符号。这可能需要构建单独的文件:一个用于内部开发和测试,另一个用于生产。 (从单元测试纯粹主义者的角度来看,这种方法通常不是最佳方法。)
  • 使用版本脚本会失去优化功能,但会允许符号版本控制,这似乎在带注释的方法中不可用。
  • 使用版本脚本可以进行单元测试,前提是先将代码构建到存档(.a)文件中,然后将其链接到DSO(.so)中。单元测试将与.a链接。

我确定还有其他人。

这里有一些参考资料(包括示例)对我有帮助: