i386 汇编问题:为什么我需要干预堆栈指针?
我认为在暑假期间学习 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
更改为另一个值,它也会崩溃。所以我得出的结论是这个值非常
重要。
问题是,我不知道它的作用以及为什么需要它。
谁能给我解释一下吗? (我使用的是 Mac 操作系统,这有什么关系吗?)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
在 x86 OSX 上,堆栈需要为函数调用进行 16 字节对齐,请参阅 ABI 文档 此处。因此,解释是
要进行检查,请将第 3 行从 $20 更改为 $4,这也有效。
此外,Ignacio Vazquez-Abrams 指出,#6 不是可选的。寄存器包含先前计算的剩余部分,因此必须明确将其清零。
我最近也学习了(仍在学习)汇编。为了避免您感到震惊,64 位调用约定有很大不同(参数在寄存器上传递)。已找到 这对于 64 位非常有帮助集会。
On x86 OSX, the stack needs to be 16 byte aligned for function calls, see ABI doc here. So, the explanation is
To check, change line #3 from $20 to $4, which also works.
Also, Ignacio Vazquez-Abrams points out, #6 is not optional. Registers contain remnants of previous calculations so it has to explicitly be zeroed.
I recently learned (still learning) assembly, too. To save you the shock, 64bit calling conventions are MUCH different (parameters passed on the register). Found this very helpful for 64bit assembly.
注释的一般形式应该是“为局部变量分配空间”。我不确定为什么任意更改它会使其崩溃。如果你减少它,我只能看到它崩溃。 6 的正确注释是“准备从此函数返回 0”。
The general form of the comment should be "Allocates space for local variables". Why changing it arbitrarily would crash it I'm not sure. I can only see it crashing if you reduce it. And the proper comment for 6 is "Prepare to return a 0 from this function".
请注意,如果使用 -fomit-frame-pointer 进行编译,则部分
%ebp
指针样板文件将会消失。基指针对于调试很有帮助,但在 x86 上实际上并不是必需的。另外,我强烈建议使用 Intel 语法,所有 GCC/binutils 都支持该语法。我曾经认为 AT&T 和 Intel 语法之间的差异只是品味问题,但有一天我遇到了 这个示例,其中 AT&T 助记符与 Intel 助记符完全不同。由于所有官方 x86 文档都使用 Intel 语法,因此这似乎是更好的方法。
玩得开心!
Note that if you compile with -fomit-frame-pointer some of that
%ebp
pointer boilerplate will disappear. The base pointer is helpful for debugging but isn't actually necessary on x86.Also I highly recommend using Intel syntax, which is supported by all the GCC/binutils stuff. I used to think that the difference between AT&T and Intel syntax was just a matter of taste, but then one day I came across this example where the AT&T mnemonic is just totally different from the Intel one. And since all the official x86 documentation uses Intel syntax, it seems like a better way to go.
Have fun!