【问题标题】:X86 Assembly Instruction Pointer AddressingX86 汇编指令指针寻址
【发布时间】:2011-06-27 03:05:38
【问题描述】:

我通常不会花太多时间阅读汇编,所以下面的编译器输出让我有点困惑。

假设我在运行 OSX 10.6 的 Intel Core 2 Duo 上编译这段 C 代码:

while (var != 69) // var is a global variable
{
    printf("Looping!\n");
}

“var != 69”比较的程序集如下所示:

cmpl    $69, _var(%rip)

我知道这实际上意味着将值“69”与全局变量“var”的内容进行比较,但我很难理解“_var(%rip)”部分。通常,我希望有一个偏移值,例如引用堆栈中的局部变量(例如:-4($ebp))。但是,我不太了解使用“_var”声明偏移指令指针会给我全局变量“var”的内容。

那条线到底是什么意思?

谢谢。

【问题讨论】:

    标签: c assembly x86


    【解决方案1】:

    这与使用offset(%ebp) 寻址堆栈中的局部变量几乎相同。在这种情况下,链接器会将该指令的偏移字段设置为var 的地址与该指令执行时%rip 的值之间的差异。 (如果我没记错的话,那个值是 next 指令的地址,因为%rip 总是指向当前正在执行的指令之后的指令。)因此加法给出了var的地址。

    为什么要这样?这是position-independent code 的标志。如果编译器生成了

    cmpl $69, _var
    

    并且链接器已经填写了var的绝对地址,那么当你运行程序时,可执行映像总是必须加载到内存中的一个特定地址,这样所有变量具有代码期望的绝对地址。通过这种方式,唯一需要修复的是代码和数据之间的距离;代码加上数据(即完整的可执行映像)可以在任何地址加载,它仍然可以工作。

    ... 何必呢?为什么必须在一个特定地址加载可执行文件是不好的?不一定。共享库必须与位置无关,否则您可能有两个库要在重叠地址加载,并且您不能在同一个程序中同时使用它们。 (一些系统通过保存所有库的全局注册表和它们所需的空间来解决这个问题,但显然这并不能扩展。)使可执行文件位置独立主要是作为一种安全措施:它是如果您不知道程序代码在内存中的位置(这称为address space layout randomization),那么利用缓冲区溢出会有些困难。

    【讨论】:

    • 优秀的答案和一些很好的链接!在 gdb 中反汇编也有助于说明这一点;那行代码变成了“cmpl $0x45, 0x1ed(%rip)”,它偏移了“var”变量。谢谢。
    • 需要注意的一点:var(%rip) 符号实际上是相当误导的;选择它是为了简洁而不是表现力。更一致的符号是[var-.](%rip),实际上这种东西用在 x86(32 位)PIC asm 中。
    • 另外需要注意的是,至少在 x86/x64 上,absolute 寻​​址(即cmpl $69, _var)要求操作数(_var)适合 32 位数量.这是由于架构中使用的指令编码。当 AMD 设计 64 位扩展时,他们保留了指令编码格式,但将所有 32 位“绝对”(实际上编码为“32 位相对无基址/索引寄存器”)转换为%rip-relative。这创造了一种将代码和数据都放入 64 位地址空间的简单方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-10
    • 2013-08-11
    • 2015-06-29
    • 1970-01-01
    • 2015-10-27
    • 2011-08-18
    相关资源
    最近更新 更多