我在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中的“位置无关代码”。
什么是与位置无关的代码?位置无关代码如何工作?该寄存器指向什么?
答案 0 :(得分:15)
EBX
指向全球抵消表。见this reference about PIC on i386。该链接解释了PIC的用法EBX
。
答案 1 :(得分:7)
PIC是在加载时动态重定位的代码。非PIC的代码具有在链接时设置的跳转和调用地址。 PIC有一个表引用存在这些值的所有位置,就像.dll。
加载图像后,加载程序将动态更新这些值。其他方案引用定义“基础”的数据值,并且通过在基础上执行计算来确定目标地址。基数通常由装载机再次设置。
最后,其他方案使用调用已知相对偏移的各种蹦床。相对偏移包含由加载程序更新的代码和/或数据。
为什么选择不同的方案有不同的原因。有些在运行时很快,但加载速度较慢。有些加载速度很快,但运行时性能较差。