我试图了解 C 编译器在编译为程序集时会做什么。我编译成程序集的代码是这样的:
void main() {
int x = 10;
int y = 10;
int a = x + y;
}
生成以下程序集:
.Ltext0:
.globl main
main:
.LFB0:
0000 55 pushq %rbp
0001 4889E5 movq %rsp, %rbp
0004 C745F40A movl $10, -12(%rbp)
000b C745F80A movl $10, -8(%rbp)
0012 8B45F8 movl -8(%rbp), %eax
0015 8B55F4 movl -12(%rbp), %edx
0018 01D0 addl %edx, %eax
001a 8945FC movl %eax, -4(%rbp)
001d 5D popq %rbp
001e C3 ret
但是,我在理解这段代码中具体发生的事情时遇到了一些困难。我理解所有标签和一些组件。我认为它的作用如下:
- 推 rbp? - 这是用于堆栈框架还是其他东西?
- 将堆栈指针设置为基指针? (即清除堆栈)
- 将 10 移入堆栈?偏移-12?为什么是 12,为什么是负数?
- 将 10 移入堆栈,不过这次是 -8 而不是 -12(相差 4,可能是字节或其他什么?)
- 将 -8 处的值移至 eax
- 将 -12 处的值移至 edx
- 添加 eax 和 edc
- 将值从 eax 移入堆栈
- 流行 rbp?函数堆栈帧可能结束吗?
- 从函数返回?
任何人都可以澄清这个程序集的某些要点,也许是编译器选择 -8、-12 的原因,为什么它选择 eax 和 edc 而不是其他一些寄存器,为什么它推送和弹出 rbp 等?
最佳答案
push rbp? - is this for a stack frame or something?
是的。编译器为局部变量创建一个堆栈帧。 push %rbp
/movq %rsp, %rbp
是执行此操作的标准方法。它允许轻松访问局部变量。
moves 10 into stack? Offset by -12? Why 12, and why is it negative?
在本例中,编译器选择使用从 -12(%rbp)
到 - 的堆栈的 4 字节(
表示变量 int
大小)部分9(%rbp)x
。
创建堆栈帧后,您可以访问具有负偏移量的局部变量和具有正偏移量的函数参数:
------------------------------------------------------
| R |
New stack (locals) | B | Old stack (parameters)
| P |
------------------------------------------------------
^
RBP is updated to point here as well so you get negative offsets (to the left) for locals and positive offsets (to the right) for parameters.
请注意,由于存储的 RBP 以及函数的返回地址也占用空间,因此需要为任何参数偏移量添加 16 个字节。 (32位系统为8字节)
通常,您必须在使用局部变量进行任何操作之前更新 RSP
,如下所示:subq $12, %rsp
。离开该功能时,请使用 addq $12、%rsp
或 leave
。此示例更新堆栈指针以显示我们正在堆栈上使用 12 个字节。当您完成它们后,您只需恢复堆栈指针即可。但在您的示例中,这些都不需要,因为除了局部变量之外,该函数对堆栈没有其他用途。
moves 10 into stack, though this time at -8 instead of -12
再次引用局部变量,只不过这次编译器选择了从 -8(%rbp)
到 -5(%rbp)
的 4 字节部分> 对于变量 y
。
在这种情况下,pop %rbp
将函数末尾的堆栈恢复到进入之前的状态:
------------------------------------------------------
| R |
New stack (locals) | B | Old stack (parameters)
| P |
------------------------------------------------------
^
RSP points here, so a `pop %rbp` will restore both RSP and RBP
编译器可能首先尝试使用 EAX
和 EDX
,因为 EAX
是为数学运算而设计的,而 EDX
专为通用数据操作而设计。您经常会发现它们在操作中配对。
关于assembly - 编译器在这段汇编代码中做了什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33302295/