【发布时间】:2021-04-07 23:36:18
【问题描述】:
考虑以下简单的共享库源代码:
library.cpp:
static int global = 10;
int foo()
{
return global;
}
在 clang 中使用 -fPIC 选项编译,它会生成这个对象程序集 (x86-64):
foo(): # @foo()
push rbp
mov rbp, rsp
mov eax, dword ptr [rip + global]
pop rbp
ret
global:
.long 10 # 0xa
由于符号是在库中定义的,因此编译器按预期使用 PC 相对寻址:mov eax, dword ptr [rip + global]
但是,如果我们将 static int global = 10; 更改为 int global = 10; 使其成为具有外部链接的符号,则生成的程序集是:
foo(): # @foo()
push rbp
mov rbp, rsp
mov rax, qword ptr [rip + global@GOTPCREL]
mov eax, dword ptr [rax]
pop rbp
ret
global:
.long 10 # 0xa
如您所见,编译器使用全局偏移表添加了一个间接层,在这种情况下这似乎完全没有必要,因为符号仍然在同一个库(和源文件)中定义。
如果符号是在另一个共享库中定义的,则 GOT 将是必要的,但在这种情况下感觉是多余的。为什么编译器仍在将这个符号添加到 GOT 中?
注意:我相信this question 与此类似,但是可能由于缺乏细节,答案并不相关。
【问题讨论】:
-
事实上,共享库符号可以被其他库重新定义。因此代码可以使用另一个库中的新符号结束。将其设置为外部(即公开)您允许重新定义它。我不记得这个功能的确切名称了。
-
这不是违反 ODR 规则吗?
-
我不记得确切的细节,但 ODR 是 C++ 的东西,而这是一个加载器机制。每个共享库只有一个符号定义。实际上,“重新定义”不是正确的术语,但我不记得技术术语了。
-
好的,找到了。符号可以是interposed。
-
@MargaretBloom:是的,这是我要链接的博客文章,以了解有关 Linux/Unix 动态链接的更多信息。如果您不需要/不希望符号参与符号插入,那么您希望将 ELF 可见性设置为
hidden的原因是对您自己的全局变量和函数的低效访问定义/使用相同名称的库有自己的符号定义私有副本。
标签: c++ assembly symbols dynamic-linking got