【发布时间】:2021-05-11 21:14:26
【问题描述】:
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 main1 (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>)
添加:感谢 cmets。我想我忘了问我真正的问题。在 main 的开头,调用 main 的程序应该把它的返回地址(在 main 之后)放在 x30 中。由于 main 应该调用另一个函数本身,它应该修改 x30,所以它将 x30 保存在它的堆栈中。但是为什么它把它存储在 sp-#0x20 中呢?为什么变量 x,y,z 存储在 sp + #20, sp + #24, sp + #28 中?如果 main 函数调用 printf,我猜 sp 和 x29 会减少一些。这个数量是否取决于被调用函数(此处为 printf)使用多少堆栈区域?还是恒定的?以及 main 中的 x29、x30 存储位置是如何确定的?是否确定这两个值位于被调用函数(printf)的堆栈区域上方?抱歉问了太多问题。
【问题讨论】:
-
“为什么?似乎是多余的。它已经在 w0 中了” 您正在查看未优化的程序集。此类代码可能包含大量冗余指令。
-
是的。通常最好使用至少
-O1进行编译以避免奇怪的堆栈移动。 -
您的分析看起来大致正确。请注意,使用
z加载w1并不是多余的,因为这是传递给printf所需的位置。但是mov w1, w0会做得更简单;或者,如果代码经过优化,z可以首先在w1中计算。但是您有没有更具体的问题想问? -
不优化时,编译器生成的代码好像每个变量的“真正的家”都在内存中,因此对该变量的每个引用都应该相应地读取或写入内存。它没有注意到其他一些寄存器(甚至是 same 寄存器!)可能已经包含该值的副本。如您所见,结果往往效率低下且冗余,对于您应该如何编写自己的汇编代码来说,这不是一个好的模型。
-
x0不是w0的字符串地址,但是是的。 “填写”%d是printf所做的,它解析格式字符串,%d告诉它取w1中的数字,转换为十进制,然后输出。
标签: assembly callstack arm64 abi