PIC寄存器(%ebx)的作用是什么?

11
我用C++编写了一个“危险”的程序,它在不同的堆栈帧之间跳来跳去。目标是从调用堆栈的最低级别跳到一个调用者,做一些事情,然后再次跳回,每次跳过中间所有的调用。
我通过手动更改堆栈基地址(设置%ebp)并跳转到标签地址来实现。这完全有效,gcc和icc都可以使用,而且没有任何堆栈损坏。那天这个程序能正常工作让我非常开心。
现在我正在将同样的程序用C语言重新编写,但它不能正常工作。特别是在使用gcc v4.0.1(Mac OS)时无法正常工作。一旦我跳到新的堆栈帧(正确设置堆栈基指针),以下指令会执行,在调用fprintf之前列出的最后一条指令崩溃,解除对NULL的引用:
lea    0x18b8(%ebx), %eax
mov    (%eax), %eax
mov    (%eax), %eax

我做了一些调试,发现通过手动设置%ebx寄存器,在切换堆栈帧时(使用我在第一个函数离开前观察到的值),可以解决错误。我读到这个寄存器在gcc中处理“位置无关代码”。

什么是位置无关代码?位置无关代码如何工作?此寄存器指向什么?


您可以考虑使用setjmp/longjmp来实现此功能,而无需直接操作%ebx。 - Max Lybbert
2
总的来说,是的,你是正确的。在这种情况下,我需要能够跳转到调用者,执行一些其他函数,然后再跳回到被调用者。使用setjmp/longjmp,被调用者的堆栈将被另一个函数覆盖。 - Andres Jaan Tack
2个回答

17

1
现代GCC并不总是使用EBX寄存器;至少在叶子函数中,它有时会使用EAX、ECX或EDX(临时寄存器)作为GOT指针。在这个编译器探索器输出的g++7.3 -O3 -m32 https://godbolt.org/g/UfPVRF中,请注意两个不同的函数使用的两个不同的thunk。 - Peter Cordes

7
PIC是一种动态重新定位加载的代码。非PIC的代码在链接时设置跳转和调用地址。PIC有一个表格引用所有这些值存在的位置,类似于.dll。
当图像被加载时,装载器将动态更新这些值。其他方案引用定义“基础”的数据值,目标地址通过对基础进行计算来确定。基础通常由装载器再次设置。
最后,其他方案使用各种跳板,调用已知相对偏移量。相对偏移量包含由装载器更新的代码和/或数据。
选择不同方案的原因有所不同。有些运行速度快,但加载速度较慢。有些加载速度快,但运行时性能较差。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接