i386汇编问题:为什么我需要插入堆栈指针?

zne*_*eak 6 x86 assembly gnu-assembler

我决定在暑假期间学习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!
Run Code Online (Sandbox Code Playgroud)

它编译甚至工作!而且我认为我理解其中的大部分内容.

虽然,魔法发生在第3步.我是否会删除此行,我的程序将在调用putsxor来自未对齐的堆栈错误之间死亡.我会改变$20另一个值,它也会崩溃.所以我得出结论,这个价值很very重要.

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

有人能解释一下吗?(我在Mac OS上,它会不会重要.)

aca*_*bot 3

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

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

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

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

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