test. c(裸金属)
#include <stdio.h>
int add1(int a, int b)
{
int c;
c = a + b;
return c;
}
int main()
{
int x, y, z;
x = 3;
y = 4;
z = add1(x,y);
printf("z = %d\n", z);
}
我做了aarch64-none-elf-gcc test.c -specs=rdimon.specs
得到了a. out,我做了aarch64-none-elf-objdump -d a.out
得到了汇编代码,这是主函数。
00000000004002e0 <add1>:
4002e0: d10083ff sub sp, sp, #0x20 <-- reduce sp by 0x20 (just above it are saved fp and lr of main)
4002e4: b9000fe0 str w0, [sp, #12] <-- save first param x at sp + 12
4002e8: b9000be1 str w1, [sp, #8] <-- save second param y at sp + 8
4002ec: b9400fe1 ldr w1, [sp, #12] <-- load w1 with x
4002f0: b9400be0 ldr w0, [sp, #8] <-- load w0 with y
4002f4: 0b000020 add w0, w1, w0 <-- w0 = w1 + w0
4002f8: b9001fe0 str w0, [sp, #28] <-- store x0 to sp+28
4002fc: b9401fe0 ldr w0, [sp, #28] <-- load w0 with the result (seems redundant)
400300: 910083ff add sp, sp, #0x20 <-- increment sp by 0x20
400304: d65f03c0 ret
0000000000400308 <main>:
400308: a9be7bfd stp x29, x30, [sp, #-32]! <-- save x29(fp) and x30(lr) at sp - 0x20
40030c: 910003fd mov x29, sp <-- set fp to new sp, the base of stack growth(down)
400310: 52800060 mov w0, #0x3 // #3
400314: b9001fe0 str w0, [sp, #28] <-- x is assigned in sp + #28
400318: 52800080 mov w0, #0x4 // #4
40031c: b9001be0 str w0, [sp, #24] <-- y is assiged in sp + #24
400320: b9401be1 ldr w1, [sp, #24] <-- load func param for y
400324: b9401fe0 ldr w0, [sp, #28] <-- load func param for x
400328: 97ffffee bl 4002e0 <add1> <-- call add1 (args are in w0, w1)
40032c: b90017e0 str w0, [sp, #20] <-- store x0(result z) to sp+20
400330: b94017e1 ldr w1, [sp, #20] <-- load w1 with the result (why? seems redundant. it's already in w0)
400334: d0000060 adrp x0, 40e000 <__sfp_handle_exceptions+0x28>
400338: 91028000 add x0, x0, #0xa0 <-- looks like loading param x0 for printf
40033c: 940000e7 bl 4006d8 <printf>
400340: 52800000 mov w0, #0x0 // #0 <-- for main's return value..
400344: a8c27bfd ldp x29, x30, [sp], #32 <-- recover x29 and x30 (look's like values in x29, x30 was used in the fuction who called main)
400348: d65f03c0 ret
40034c: d503201f nop
我添加了我的理解与<--
标记。有人可以看到代码,并给予我一些更正吗?任何小的评论将不胜感激。(请从<main>
看到)
附加:谢谢你的评论。我想我忘了问我真实的的问题了。在main的开头,调用main的程序应该把它的返回地址(在main之后)。既然main应该自己调用另一个函数,它应该修改x30,所以它把x30保存在它的堆栈中。但是为什么它把它保存在sp - #0x20中呢?为什么变量x,y,如果主函数调用printf,我猜sp和x29会减少一些。这个数量取决于被调用函数的堆栈区域大小吗(此处为printf)使用?还是常量?以及x29 x30在main中的存储位置是否已确定?是否已确定这两个值正好位于被调用函数(printf)的堆栈区域上方?抱歉,问题太多。
1条答案
按热度按时间zhte4eai1#
在为
main
布局堆栈时,编译器必须满足以下约束:x29
和x30
需要保存在堆栈上,它们各占用8个字节。x,y,z
需要堆栈空间,每个4字节。(如果进行优化,您会看到它们被保存在寄存器中,或者被优化后完全不存在。)这样,我们总共需要8+8+4+4+4=28
字节。sp
必须始终保持与16字节对齐;这是一个架构和ABI约束(操作系统可以选择放宽此要求,但通常不会)。我们必须四舍五入到16的下一个倍数,即32。这就是你提到的32或
0x20
的来源。注意,它完全是main
本身使用的堆栈内存。它不是一个通用常量;如果在main
中添加或删除了足够多的局部变量,就会发现它发生了变化。它与
printf
的需求无关。如果printf
需要为它自己的局部变量提供堆栈空间,那么printf
中的代码就必须相应地调整堆栈指针。编译器在编译main
时不知道需要多少空间,也不关心。现在,编译器需要将这五个对象
x29, x30, x, y, z
组织在它为自己创建的32字节堆栈空间中。除了以下几点之外,选择将什么放在哪里几乎是完全任意的。该函数的序言需要从堆栈指针中减去32,并将寄存器
x29, x30
存储在所分配的空间内的某个位置。这都可以通过预先变址的存储对指令stp x29, x30, [sp, #-32]!
在单个指令中完成。它从sp
中减去32
。然后将x29
和x30
存储在从sp
现在指向的地址开始的16个字节中。因此,为了使用此指令,我们必须接受将x29
和x30
放置在分配空间的底部,在相对于sp
的 new 值的偏移量[sp+0]
和[sp+8]
处。将它们放在其他位置将需要额外的指令,并且效率较低。(实际上,因为这是最方便的方式,所以ABI实际上要求堆栈帧以这种方式设置,当使用它们时,
x29, x30
在堆栈上以这种顺序连续(5.2.3)。)从
[sp+16]
开始,我们还有16个字节要处理,其中必须放置x,y,z
。编译器选择将它们分别放在[sp+28], [sp+24], [sp+20]
地址。[sp+16]
处的4个字节未使用,但请记住,为了实现正确的堆栈对齐,我们不得不在某处浪费4个字节。排列这些对象的选择,以及哪些时隙不使用完全是任意的,任何其他的安排也会同样有效。