我决定在暑假期间学习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上,它会不会重要。)
答案 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语法,因此它似乎是一种更好的方法。
玩得开心!