简短说明:gp
对于所有实际方法来说,它是所有符合 Itanium ABI 的函数的隐藏参数。这是一种this
指向函数使用的全局变量的指针。据我所知,目前还没有主流操作系统做到这一点。
GP 代表“全局指针”。它是由可执行文件静态分配的数据的基地址,Itanium 架构有一个专门用于它的寄存器。
例如,如果您的程序中有这些全局变量和此函数:
int foo;
int bar;
int baz;
int func()
{
foo++;
bar += foo;
baz *= bar / foo;
return foo + bar + baz;
}
gp/函数对在概念上是&foo, &func
。生成的代码为func
会指gp
找到全局变量所在的位置。编译器知道foo
可以在以下位置找到gp
, bar
可以在以下位置找到gp + 4
and baz
可以在以下位置找到gp + 8
.
假设func
是在外部库中定义的,如果您从程序中调用它,编译器将使用如下指令序列:
- 将当前gp值保存到堆栈中;
- 从该对加载代码地址
func
进入某个寄存器;
- 将同一对的 gp 值加载到 GP 中;
- 对我们存储代码地址的寄存器进行间接调用;
- 恢复我们之前保存在堆栈中的旧 gp 值,恢复调用函数。
这使得可执行文件完全位置无关,因为它们从不存储数据符号的绝对地址,因此可以在内存中仅维护任何可执行文件的一个实例,无论有多少进程使用它(您甚至可以加载在一个进程中多次执行相同的可执行文件,并且系统范围内仍然只有一份可执行代码的副本),代价是使函数指针有点奇怪。对于 Itanium ABI,函数指针不是代码地址(就像“常规”x86 ABI 一样):它是 gp 值和代码地址的地址,因为如果可以的话,该代码地址可能没有多大价值。无法访问其全局变量,就像一个方法如果没有一个方法可能无法做很多事情一样this
指针。
我知道的唯一使用此概念的其他 ABI 是 Mac OS Classic PowerPC ABI。他们将这些对称为“转移向量”。
由于 x86_64 支持 RIP 相对寻址(x86 没有等效的 EIP 相对寻址),因此现在可以非常轻松地创建与位置无关的代码,而无需使用额外的寄存器或使用“增强型”函数指针。代码和数据只需保持恒定的偏移量即可。因此,安腾 ABI 的这一部分可能在英特尔平台上永远消失了。
来自安腾寄存器约定 http://people.cs.pitt.edu/%7Emock/cs2210/handouts/itanium-register-convention.pdf:
8.2 gp寄存器
每个引用静态分配数据或调用另一个过程的过程都需要一个指向 gp 寄存器中其数据段的指针,以便它可以访问其静态数据及其链接表。每个加载模块都有自己的数据段,并且在调用该加载模块内的任何入口点之前必须正确设置 gp 寄存器。
链接约定要求每个加载模块精确定义一个 gp 值来引用其短数据段内的位置。预计该位置将被选择为最大化用于寻址标量和链接表条目的短位移立即指令的有用性。 DLL加载器将其数据段加载到内存后,将确定每个加载模块的gp寄存器的绝对值。
对于加载模块内的调用,gp 寄存器将保持不变,因此可以相应地优化已知的本地调用。
对于加载模块之间的调用,必须使用新加载模块的正确 gp 值初始化 gp 寄存器,并且调用函数必须确保保存和恢复其自己的 gp 值。