限制链接共享库时的符号可见性
当链接共享库时,限制符号的可见性的原因是导出的函数和全局变量的调用生成的代码比未导出的函数和全局变量的调用生成的代码效率低。这涉及到了一个额外的间接层。这适用于在编译时可能导出的任何函数。即使链接器脚本将函数取消导出,gcc仍会为其生成额外的间接层。因此,使用可见性属性将产生比链接器脚本更好的代码。
对于调用函数,你确定这是真的吗?根据我的经验,调用隐藏和导出的函数生成的代码是相同的。只是普通的带有R_X86_64_PLT32
重定位的调用。但是对于访问导出的变量,则存在差异,因为它首先必须在全局偏移表(GOT)中查找。
解决这个问题的方法是使用可见性属性来限制符号的可见性。通过在函数或变量声明前加上__attribute__((visibility("hidden")))
,可以将其限制为只能在本地可见。这样,在链接共享库时,编译器将生成更高效的代码。
在链接共享库时限制符号的可见性是一个常见的需求。当我们编译和链接共享库时,有时候我们希望只暴露一部分特定的符号给外部使用,而其他的符号则希望保持隐藏,不被外部访问到。这种需求可以通过在gcc选项中添加-fvisibility=hidden
来实现,然后在代码中使用__attribute__((visibility("default")))
将某些特定的符号设置为公共可见。具体的使用方法可以参考这里的文档here。
除了使用gcc选项外,还有一种可能的解决方法是使用ld链接器脚本,但是目前我对此了解有限,无法提供具体的操作方法。这种方法在Firefox中得到了应用。
需要注意的是,__attribute__((visibility("default")))
这个语法应该是有文档支持的,你可以参考文档进行修订。另外,提供的链接地址似乎有问题,需要修复。
限制在链接共享库时可见性的符号是一个常见的问题。为了解决这个问题,可以使用GNU ld在ELF平台上实现。下面是使用链接器版本脚本的方法:
/* foo.c */ int foo() { return 42; } int bar() { return foo() + 1; } int baz() { return bar() - 1; }
默认情况下,所有的符号都会被导出:
0000000000000718 T _fini 00000000000005b8 T _init 00000000000006b7 T bar 00000000000006c9 T baz 00000000000006ac T foo
假设你只想导出bar()
和baz()
,可以创建一个"version script" libfoo.version
:
FOO {
global: bar; baz; # 明确列出要导出的符号
local: *; # 隐藏其他所有符号
};
将其传递给链接器:
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
则可以实现这一点。