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

时间:2010-06-06 02:44:17

标签: assembly x86 gas

我决定在暑假期间学习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步。我是否会删除此行,我的程序将在调用putsxor之间因未对齐的堆栈错误而死亡。我会将$20改为另一个值,它也会崩溃。所以我得出结论,这个值very很重要。

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

任何人都可以解释一下吗? (我在Mac OS上,它会不会重要。)

3 个答案:

答案 0 :(得分:3)

评论的一般形式应该是“为局部变量分配空间”。为什么任意改变它会崩溃它我不确定。如果你减少它,我只会看到它崩溃。对6的正确评论是“准备从这个函数返回0”。

答案 1 :(得分:3)

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

push stack pointer (#1)         -4
strange increment (#3)         -20
push argument (#4)              -4
call pushes return address (#5) -4
total                          -32

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

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

我最近也学习了(还在学习)集会。为了节省电击,64位调用约定有很多不同(寄存器上传递的参数)。发现this对64位汇编非常有帮助。

答案 2 :(得分:1)

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

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

玩得开心!