我正在阅读这篇关于汇编的教程:http://orangejuiceliberationfront.com/intel-assembler-on-mac-os-x/,我遇到了这个基本的汇编代码:
.text
.globl _main
_main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl $0, %eax
leave
ret
我对此大部分了解,但我不知道为什么subl $8, %esp
被召唤。我知道它从esp中减去了8个字节,但我不知道为什么这是必要的或者为什么要这样做。该教程说它将堆栈平衡到一个16字节的边界,但我不知道"平衡"堆栈意味着为什么使用数字8形成一个16字节的边界。
本教程后面将展示如何定义一个函数,并将其调用如下:
.text
.globl _doSomething
_doSomething:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
nop
leave
ret
.globl _main
_main:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl $3, (%esp)
call _doSomething
movl $0, %eax
leave
ret
教程中有" 8对齐,16为我们的4字节参数和填充"在这一行:subl $24, %esp
但如果有一个4字节的参数和填充,为什么我们使用数字16?还有什么参数?
我使用的是Intel Core mac,运行OS X 10.9.3,使用gcc -S -m32进行编译。
我对装配很新,所以请尽可能简单地回答你的问题。谢谢!
答案 0 :(得分:2)
让我们看一系列指令:
1. nop #call-stack is aligned to 16 bytes (sp is multiple of 16) to start.
2. call function #pushes return address (4 bytes) onto stack.
---(called function)
3. push %ebp #pushes base-pointer (4 bytes) onto stack, which is now 8-byte aligned
---cannot call function that expects to find 16-byte aligned stack---
4. sub $8, %esp #aligns stack to 16 bytes
5. call other_function
答案 1 :(得分:1)
如何在机器语言级别调用函数是由称为调用约定的东西强制执行的,该调用约定通常是体系结构和特定于操作系统的。它的设计使您可以以可互操作的方式调用其他功能。在这种情况下,调用约定指定被调用函数可以预期,当调用函数时,堆栈指针在16字节边界上对齐。
这意味着堆栈指针(esp
)是16个字节的倍数。
这样做是因为某些指令仅适用于存储在16的倍数的内存位置的数据,主要是出于性能原因。其他指令甚至可以在未对齐的数据上工作,但效率却低得多。
答案 2 :(得分:1)
如果你想问“为什么程序看起来不像这样?”:
_main:
mov $0, %eax ; sub %eax, %eax would be more efficient
ret
你是对的。以下四条说明:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
leave
在示例中完全没用。
如果您手动编写此汇编程序函数(或对C编译器输出进行后优化),则不会编写(或删除)这四条指令。
然而,大多数C编译器都针对“正常情况”进行了优化:除了返回常量值之外什么都不做的C函数(这种情况就是这种情况)并不经常出现。
在大多数C函数(包含变量和实际代码)中,上面显示的四条指令都有意义!这就是为什么大多数C编译器总是插入这四个指令,即使在特殊情况下不需要它们。
我想汇编教程只是插入四条指令,因为在下一步中会有更多指令添加到函数中......