【问题标题】:i386 assembly question: why do I need to meddle with the stack pointer?i386 汇编问题:为什么我需要干预堆栈指针?
【发布时间】:2010-06-06 02:44:16
【问题描述】:

我认为在暑假期间学习 x86 汇编会有趣。所以我从一个非常简单的 hello world 程序开始,借用gcc -S 可以给我的免费示例。我最终得到了这个:

HELLO:
    .ascii "Hello, world!\12\0"
    .text

.globl _main
_main:
    pushl   %ebp        # 1. puts the base stack address on the stack
    movl    %esp, %ebp  # 2. puts the base stack address in the stack address register
    subl    $20, %esp   # 3. ???
    pushl   $HELLO      # 4. push HELLO's address on the stack
    call    _puts       # 5. call puts
    xorl    %eax, %eax  # 6. zero %eax, probably not necessary since we didn't do anything with it
    leave               # 7. clean up
    ret                 # 8. return
                        # PROFIT!

它编译甚至可以工作!我想我了解大部分

不过,魔法发生在第 3 步。如果我删除这一行,我的程序将在调用 puts 和调用 xor 之间因堆栈未对齐错误而死掉。我是否会将$20 更改为另一个值,它也会崩溃。所以我得出结论,这个值是very 重要的。

问题是,我不知道它的作用以及为什么需要它。

谁能解释一下? (我在 Mac OS 上,这有什么关系。)

【问题讨论】:

    标签: assembly x86 gnu-assembler


    【解决方案1】:

    在 x86 OSX 上,函数调用的堆栈需要 16 字节对齐,请参阅 ABI 文档here。所以,解释是

    推送堆栈指针 (#1) -4 奇怪的增量(#3)-20 推论(#4)-4 调用推送返回地址(#5)-4 总计 -32

    要检查,将第 3 行从 $20 更改为 $4,这也有效。

    此外,Ignacio Vazquez-Abrams 指出,#6 不是可选的。寄存器包含先前计算的残余,因此必须明确归零。

    我最近也学习了(仍在学习)组装。为避免您感到震惊,64 位调用约定有很大不同(在寄存器上传递的参数)。发现 this 对 64 位汇编很有帮助。

    【讨论】:

      【解决方案2】:

      注释的一般形式应该是“为局部变量分配空间”。为什么改变它会导致崩溃,我不确定。如果你减少它,我只能看到它崩溃。 6 的正确注释是“准备从此函数返回 0”。

      【讨论】:

      • 所以返回值传入%eax?我认为它总是会进入堆栈,因为它们可能大于 32 位。而且,如果我只使用其中的 4 个字节,为什么还要分配 24 个字节呢? (EDIT 它也适用于 4。所以我猜堆栈必须在某个边界上对齐。)
      • 听起来像是因为对齐问题而崩溃,而不是堆栈溢出。值在 edx:eax、eax 或其切片或 FPU 寄存器中返回。
      • 我很确定堆栈指针必须在 x86 上对齐 DWORD(4 字节)的倍数,因为它是 32 位的。
      • @mazin k.:正如 AcademicRobot 所提到的,它实际上必须以 16 字节的倍数对齐。
      • 它并没有因为堆栈溢出而崩溃(考虑到它是一个非混淆的 hello world 程序,这将是非常糟糕的)。我在问题中提到这是一个堆栈错位问题;汇编程序仍然充满了高级语言所没有的奥秘和问题,所以我一开始并没有完全抓住问题。
      【解决方案3】:

      请注意,如果您使用 -fomit-frame-pointer 进行编译,则某些 %ebp 指针样板将消失。基指针有助于调试,但在 x86 上实际上不是必需的。

      此外,我强烈建议使用 Intel 语法,所有 GCC/binutils 都支持这种语法。我曾经认为 AT&T 和 Intel 语法之间的区别只是品味问题,但有一天我遇到了this example,AT&T 的助记符与 Intel 的助记符完全不同。而且由于所有官方 x86 文档都使用 Intel 语法,这似乎是一种更好的方法。

      玩得开心!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-12-10
        • 1970-01-01
        • 2011-07-08
        • 2011-02-13
        • 2017-02-08
        • 2017-08-01
        • 1970-01-01
        相关资源
        最近更新 更多