Itanium C++ ABI中“GP /功能地址对”的含义是什么? GP代表什么?
答案 0 :(得分:4)
简短说明:对于所有符合Itanium ABI的函数,gp
是所有实用方法的隐藏参数。它是指向函数使用的全局变量的this
指针。据我所知,没有主流操作系统可以再做了。
GP代表“全局指针”。它是可执行文件静态分配的数据的基址,而Itanium体系结构只有一个寄存器。
例如,如果您的程序中有这些全局变量和此函数:
int foo;
int bar;
int baz;
int func()
{
foo++;
bar += foo;
baz *= bar / foo;
return foo + bar + baz;
}
gp / function对在概念上是&foo, &func
。为func
生成的代码将引用gp
来查找全局变量所在的位置。编译器知道可以在foo
找到gp
,bar
可以找到gp + 4
,baz
可以找到gp + 8
。
假设在外部库中定义了func
,如果从程序中调用它,编译器将使用如下所示的一系列指令:
func
对中的代码地址加载到某个寄存器中; 这使得可执行文件完全独立于位置,因为它们不会将绝对地址存储到数据符号,因此无论有多少进程使用它都可以在内存中只维护任何可执行文件的一个实例(你可以甚至在单个进程中多次加载相同的可执行文件,并且仍然只有一个系统范围内的可执行代码副本),代价是使函数指针有点奇怪。使用Itanium ABI,函数指针不是代码地址(就像它使用“常规”x86 ABI一样):它是gp值和代码地址的地址,因为如果它可以'代码地址可能不值得那么多' t访问它的全局变量,就像一个方法如果没有this
指针那么可能无法做很多事情。
我所知道的唯一使用此概念的其他ABI是Mac OS Classic PowerPC ABI。他们将这些对称为“过渡向量”。
由于x86_64支持RIP相对寻址(x86没有等效的EIP相对寻址),现在很容易创建与位置无关的代码,而无需使用额外的寄存器或必须使用“增强”函数指针。代码和数据只需保持不变的偏移量。因此,Itanium ABI的这一部分在英特尔平台上可能已经不错了。
来自Itanium Register Conventions:
8.2 gp注册
每个引用静态分配数据或调用另一个过程的过程都需要指向gp寄存器中其数据段的指针,以便它可以访问其静态数据及其链接表。每个加载模块都有自己的数据段,在调用该加载模块中的任何入口点之前,必须正确设置gp寄存器。
链接约定要求每个加载模块只定义一个gp值以引用其短数据段中的位置。预计将选择该位置以最大化用于寻址标量和链接表条目的短位移立即指令的有用性。在将数据段加载到内存后,DLL加载程序将确定每个加载模块的gp寄存器的绝对值。
对于加载模块内的调用,gp寄存器将保持不变,因此可以相应地优化已知为本地的调用。
对于加载模块之间的调用,必须使用新加载模块的正确gp值初始化gp寄存器,并且调用函数必须确保保存并恢复其自己的gp值。
答案 1 :(得分:0)
只是对其他答案的引用发表评论:
It is expected that this location will be chosen to maximize the usefulness of short-displacement immediate instructions for addressing scalars and linkage table entries.
这是在讨论:Itanium有三种不同的方法将值放入寄存器(其中'immediate'在这里意味着'偏离基数')。您可以从任何地方支持完整的64位偏移,但需要两条指令:
// r34 has base address
movl r33 = <my immediate>
;;
add r35 = r34, r35
;;
这不仅需要2个独立的时钟,而且需要2个捆绑的3个指令插槽来实现这一目标。
有两个较短的版本:add14
(也是adds
)和add22
(还有addl
)。差异在于每个人都可以处理的直接规模。每个都采用一个'A'槽iirc,并在一个时钟内完成。
add14
可以使用任何寄存器作为来源&amp;目标,但最多只能处理14位立即。
add22
可以使用任何寄存器作为目标,但对于源,只分配了两位。因此,您只能使用r0
,r1
,r2
,r3
作为来源注册。 r0
不是真正的寄存器 - 它被硬连线到0.但是使用其他3个中的一个作为本地堆栈寄存器,意味着与使用本地堆栈寄存器相比,使用简单的偏移可以对存储器进行256次寻址。因此,如果将全局基址设置为r1
(约定),则可以在为下一部分代码执行单独的movl和/或修改gp之前访问更多本地偏移量。