x86_64汇编语言中,%rsp与%esp有何不同?

4
我最近在研究汇编语言,不过我的程序遇到了一个奇怪的 bug。我发现如果使用 64 位的数学方式修改 %rsp,那么一切都没问题;但是,如果使用相同的 32 位数学方式修改 %esp,就会出现分段错误。我尝试打印出 %esp%rsp 的值,每次运行结果都相同。
问题:为什么当整个寄存器只使用了 32 位时,我使用 64 位数学或 32 位数学方式修改有所区别?
.cstring
    _format: .asciz "%d\n"

.text
.globl _main
_main:
    # program setup
    pushq   %rbp
    movq    %rsp, %rbp

    # program - 16 byte aligned at this point
    # print stack pointer memory
    movq    %rsp, %rax
    call    bob            # prints the same value as the next call to bob
    xorq    %rax, %rax
    movl    %esp, %eax
    call    bob            # prints the same value as previous call to bob

    # this code breaks
    subl    $16, %esp      # bug here if I use this (32 bit math)
    subq    $16, %rsp      # works fine if I use this (64 bit math)
    call    bob
    addq    $16, %rsp

    # program cleanup
    movq    %rbp, %rsp
    popq    %rbp
    ret

# assumes 16 byte aligned when called. Prints %rax
 bob:
    subq    $8, %rsp
    movq    %rax, %rsi
    lea     _format(%rip), %rdi
    call    _printf
    addq    $8, %rsp
    ret

4
subl $16, %esp指令会将RSP寄存器的高32位清零。Kaboom。这里解释了一下 - Hans Passant
相关链接:http://stackoverflow.com/questions/15656887/a-modification-to-esp-cause-sigsegv - nrz
2个回答

3

在x86_64中,地址占用64位,那么你如何期望对其进行32位运算并仍然能正常工作呢?此外,大多数x86_64的32位操作会将前32位清零,导致地址无效。


2
不仅是大多数指令,每一条使用32位的%esomething寄存器作为输出的指令都会将高32位清零。因此,它们不依赖于寄存器先前的值,适用于乱序执行。 - Peter Cordes

1

在与KerrekSB的交谈中,差异的原因是打印错误。当我说我打印了RSP和ESP时,我是在_printf调用中使用了%d,它打印了4个字节的值。当应用于RSP时,它只打印了低4个字节(相当于ESP),导致我认为RSP只有低4个字节中有有意义的数据。当我将其更改为打印%lu时,我发现RSP和ESP确实不相等,并且RSP使用了上4个字节。这就是为什么32位数学会导致分段错误的原因。


2
请查看http://stackoverflow.com/tags/x86/info以获取指南链接和有关在汇编代码上使用gdb的信息。这将会捕捉到您的错误。 - Peter Cordes

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