RV64函数调用流程分析
- 1 RV64 函数调用实例
- 2 对应代码的分析
- 2.1 main函数及其对应的汇编程序
- 2.1.1 main的C代码实现
- 2.1.2 main函数对应汇编及其分析
- 2.1.3 执行完成之后栈的存放情况
- 2.2 test_fun_a函数及其对应的汇编程序
- 2.2.1 test_fun_a函数的C实现
- 2.2.2 test_fun_a函数对应汇编及其分析
- 2.2.3 执行完成之后栈帧的使用情况
- 2.3 test_fun_b函数及其对应的汇编程序
- 2.3.1 test_func_b函数的C实现
- 2.3.2 test_fun_b函数对应汇编及其分析
- 2.3.3 执行完成之后栈帧的使用情况
- 3 编译和反汇编的命令
- 3.1 编译的命令
- 3.2 反汇编的命令
[RV64 程序调用标准]
1 RV64 函数调用实例
下图是介绍一个简单函数调用的示例,在该示例中简单介绍了栈的使用。
2 对应代码的分析
2.1 main函数及其对应的汇编程序
2.1.1 main的C代码实现
int main(void)
{
long a = 1;
long b = 2;
printf("The current function is %s a:%ld b:%ld\r\n", __func__, a, b);
test_fun_a(a, b, 0, 1);
a = a + b;
b = a + b;
return 0;
}
2.1.2 main函数对应汇编及其分析
000000000000071e <main>:
71e: 1101 addi sp,sp,-32
为函数分配32字节的栈空间。720: ec06 sd ra,24(sp)
将寄存器 ra 的值存储到当前sp + 24的栈位置。722: e822 sd s0,16(sp)
将寄存器 s0(栈帧指针寄存器) 的值存储到sp+ 16的栈位置。724: 1000 addi s0,sp,32
将栈指针的值加上32,并将结果存储在寄存器 s0 中,即将s0指向当前函数的栈底。726: 4785 li a5,1
将局部变量a的值加载到a5寄存器中728: fef43023 sd a5,-32(s0)
将局部变量a的值保存到sp偏移0的位置,(fp - 32)的栈位置72c: 4789 li a5,2
将局部变量b的值加载到a5寄存器中72e: fef43423 sd a5,-24(s0)
将局部变量b的值保存到sp + 8,即(fp - 24)的栈位置处。732: fe843683 ld a3,-24(s0)
从栈中取出局部变量b的值,放到a3寄存器中736: fe043603 ld a2,-32(s0)
从栈中取出局部变量a的值,放到a2寄存器中73a: 00000597 auipc a1,0x0
73e: 15658593 addi a1,a1,342 # 890 <__func__.2104>
742: 00000517 auipc a0,0x0
746: 0fe50513 addi a0,a0,254 # 840 <__libc_csu_fini+0x62>
这些指令用于设置 a1 和 a0 寄存器的值。通过使用 auipc 指令和相对偏移量,将一个值加载到寄存器中。74a: e07ff0ef jal ra,550 <printf@plt>
跳转并链接到地址为550的函数(这里是 printf)。 jal 指令将返回地址(保存在 ra 寄存器中)。74e: 4685 li a3,1
将立即数1加载到a3寄存器中,作为调用test_fun_a函数的第4个参数750: 4601 li a2,0
将立即数0保存到a2寄存器中,作为调用test_fun_a函数的第3个参数752: fe843583 ld a1,-24(s0)
从栈中取出局部变量b的值放到a1寄存器中,作为调用test_fun_a函数的第2个参数756: fe043503 ld a0,-32(s0)
从栈中取出局部变量a的值存放到a0寄存器中,作为调用test_fun_a函数的第1个参数75a: f43ff0ef jal ra,69c <test_fun_a>
将返回值保存到ra寄存器中,调用test_fun_a函数,a0 ~ a4是调用test_fun_a函数的四个参数。75e: fe043703 ld a4,-32(s0)
从栈中加载局部变量b的值到a4寄存器中762: fe843783 ld a5,-24(s0)
从栈中加载局部变量a的值到a5寄存器中766: 97ba add a5,a5,a4
a = a + b,将计算的值保存到a5寄存器中768: fef43023 sd a5,-32(s0)
将a5寄存器的值保存到局部变量a的栈位置处,即更新局部变量a的值76c: fe843703 ld a4,-24(s0)
从栈中加载局部变量b的值到a4寄存器中770: fe043783 ld a5,-32(s0)
从栈中加载局部变量a的值到a5寄存器中774: 97ba add a5,a5,a4
将a + b的值放到a5寄存器中776: fef43423 sd a5,-24(s0)
将a + b的值存储在局部变量b的栈内存中,即更新局部变量a的值77a: 4781 li a5,0
加载立即数0到a5寄存器中77c: 853e mv a0,a5
将a5寄存器的值复制给a0寄存器,即给a0寄存器赋值为0,a0用来保存函数的返回值77e: 60e2 ld ra,24(sp)
恢复ra寄存器的值780: 6442 ld s0,16(sp)
恢复s0(FP)寄存器的值782: 6105 addi sp,sp,32
恢复栈784: 8082 ret
返回调用main的函数中
000000000000071e <main>:
71e: 1101 addi sp,sp,-32
720: ec06 sd ra,24(sp)
722: e822 sd s0,16(sp)
724: 1000 addi s0,sp,32
726: 4785 li a5,1
728: fef43023 sd a5,-32(s0)
72c: 4789 li a5,2
72e: fef43423 sd a5,-24(s0)
732: fe843683 ld a3,-24(s0)
736: fe043603 ld a2,-32(s0)
73a: 00000597 auipc a1,0x0
73e: 15658593 addi a1,a1,342 # 890 <__func__.2104>
742: 00000517 auipc a0,0x0
746: 0fe50513 addi a0,a0,254 # 840 <__libc_csu_fini+0x62>
74a: e07ff0ef jal ra,550 <printf@plt>
74e: 4685 li a3,1
750: 4601 li a2,0
752: fe843583 ld a1,-24(s0)
756: fe043503 ld a0,-32(s0)
75a: f43ff0ef jal ra,69c <test_fun_a>
75e: fe043703 ld a4,-32(s0)
762: fe843783 ld a5,-24(s0)
766: 97ba add a5,a5,a4
768: fef43023 sd a5,-32(s0)
76c: fe843703 ld a4,-24(s0)
770: fe043783 ld a5,-32(s0)
774: 97ba add a5,a5,a4
776: fef43423 sd a5,-24(s0)
77a: 4781 li a5,0
77c: 853e mv a0,a5
77e: 60e2 ld ra,24(sp)
780: 6442 ld s0,16(sp)
782: 6105 addi sp,sp,32
784: 8082 ret
2.1.3 执行完成之后栈的存放情况
2.2 test_fun_a函数及其对应的汇编程序
2.2.1 test_fun_a函数的C实现
void test_fun_a(long m, long n, long x, long y)
{
long b = 2;
long c = 3;
printf("The current function is %s b:%ld c:%ld\r\n", __func__, b, c);
test_fun_b(b, c, 0, 2);
b = b + c + m;
c = b + c + n;
}
2.2.2 test_fun_a函数对应汇编及其分析
000000000000069c <test_fun_a>:
69c: 7139 addi sp,sp,-64
为test_fun_a函数开辟一块64Byte的栈空间69e: fc06 sd ra,56(sp)
将ra加载到sp + 56的栈内存中6a0: f822 sd s0,48(sp)
将s0加载到sp + 48的栈内存中6a2: 0080 addi s0,sp,64
将栈底保存到s0(FP)寄存器中6a4: fca43c23 sd a0,-40(s0)
将第一个参数加载到fp - 40的栈内存中,即sp + 24的栈中,6a8: fcb43823 sd a1,-48(s0)
将第二个参数加载到fp - 48的栈内存中,即sp + 16的栈中,6ac: fcc43423 sd a2,-56(s0)
将第三个参数加载到fp - 56的栈内存中,即sp + 8的栈中,6b0: fcd43023 sd a3,-64(s0)
将第四个参数加载到fp - 64的栈内存中,即sp + 0的栈中,6b4: 4789 li a5,2
将局部变量b的值2加载到a5寄存器中6b6: fef43023 sd a5,-32(s0)
将局部变量b的值存储到fp - 32的栈内存中,即sp + 32的栈中6ba: 478d li a5,3
将局部变量c的值加载到a5寄存器中6bc: fef43423 sd a5,-24(s0)
将局部变量c的值存储到fp - 24的栈内存中,即sp + 40的栈中6c0: fe843683 ld a3,-24(s0)
从栈中取出局部变量c的值取出放到a3寄存器中6c4: fe043603 ld a2,-32(s0)
从栈中取出局部变量b的值取出放到局部变量a2中6c8: 00000597 auipc a1,0x0
6cc: 1b858593 addi a1,a1,440 # 880 <__func__.2098>
6d0: 00000517 auipc a0,0x0
6d4: 14050513 addi a0,a0,320 # 810 <__libc_csu_fini+0x32>
6d8: e79ff0ef jal ra,550 <printf@plt>
将返回值保存到ra寄存器中,调用printf6dc: 4689 li a3,2
加载立即数2到a3中6de: 4601 li a2,0
加载立即数0到a2中6e0: fe843583 ld a1,-24(s0)
从栈fp - 24的栈中取出局部变量c的值到a1寄存器中6e4: fe043503 ld a0,-32(s0)
从栈fp - 32的栈内存取出局部变量b的值到a0寄存器中6e8: f43ff0ef jal ra,62a <test_fun_b>
将返回值保存到ra寄存器中,调用test_fun_b函数6ec: fe043703 ld a4,-32(s0)
从fp - 32栈内存中取出局部变量b的值到a4寄存器中6f0: fe843783 ld a5,-24(s0)
从fp - 24栈内存中取出局部变量c的值到a5寄存器中6f4: 97ba add a5,a5,a4
b + c将计算的值更到到a5寄存器中6f6: fd843703 ld a4,-40(s0)
从fp - 40的栈中取出第一个参数m的值到a4寄存器中6fa: 97ba add a5,a5,a4
b + c + m的值保存到a5寄存器中6fc: fef43023 sd a5,-32(s0)
存储b + c + m的值到局部变量b的栈内存中700: fe043703 ld a4,-32(s0)
从栈中取出局部变量b的值到a4寄存器中704: fe843783 ld a5,-24(s0)
从栈中取出局部变量c的值到a5寄存器中708: 97ba add a5,a5,a4
b + c的值保存到a5寄存器中70a: fd043703 ld a4,-48(s0)
从栈中取出第二个参数n的值到a4寄存器中70e: 97ba add a5,a5,a4
b + c + n 的值保存到a5寄存器中710: fef43423 sd a5,-24(s0)
将b + c + n的值保存到局部变量c的栈内存中714: 0001 nop
716: 70e2 ld ra,56(sp)
从栈中恢复ra的值718: 7442 ld s0,48(sp)
从栈中恢复s0(FP)的值71a: 6121 addi sp,sp,64
恢复sp寄存器71c: 8082 ret
返回main函数调用test_fun_a的下一条指令处。
000000000000069c <test_fun_a>:
69c: 7139 addi sp,sp,-64
69e: fc06 sd ra,56(sp)
6a0: f822 sd s0,48(sp)
6a2: 0080 addi s0,sp,64
6a4: fca43c23 sd a0,-40(s0)
6a8: fcb43823 sd a1,-48(s0)
6ac: fcc43423 sd a2,-56(s0)
6b0: fcd43023 sd a3,-64(s0)
6b4: 4789 li a5,2
6b6: fef43023 sd a5,-32(s0)
6ba: 478d li a5,3
6bc: fef43423 sd a5,-24(s0)
6c0: fe843683 ld a3,-24(s0)
6c4: fe043603 ld a2,-32(s0)
6c8: 00000597 auipc a1,0x0
6cc: 1b858593 addi a1,a1,440 # 880 <__func__.2098>
6d0: 00000517 auipc a0,0x0
6d4: 14050513 addi a0,a0,320 # 810 <__libc_csu_fini+0x32>
6d8: e79ff0ef jal ra,550 <printf@plt>
6dc: 4689 li a3,2
6de: 4601 li a2,0
6e0: fe843583 ld a1,-24(s0)
6e4: fe043503 ld a0,-32(s0)
6e8: f43ff0ef jal ra,62a <test_fun_b>
6ec: fe043703 ld a4,-32(s0)
6f0: fe843783 ld a5,-24(s0)
6f4: 97ba add a5,a5,a4
6f6: fd843703 ld a4,-40(s0)
6fa: 97ba add a5,a5,a4
6fc: fef43023 sd a5,-32(s0)
700: fe043703 ld a4,-32(s0)
704: fe843783 ld a5,-24(s0)
708: 97ba add a5,a5,a4
70a: fd043703 ld a4,-48(s0)
70e: 97ba add a5,a5,a4
710: fef43423 sd a5,-24(s0)
714: 0001 nop
716: 70e2 ld ra,56(sp)
718: 7442 ld s0,48(sp)
71a: 6121 addi sp,sp,64
71c: 8082 ret
2.2.3 执行完成之后栈帧的使用情况
2.3 test_fun_b函数及其对应的汇编程序
2.3.1 test_func_b函数的C实现
void test_fun_b(long m, long n, long x, long y)
{
long c = 3;
long d = 4;
printf("The current function is %s c:%ld d:%ld\r\n", __func__, c, d);
c = c + d + m;
d = c + d + n;
}
2.3.2 test_fun_b函数对应汇编及其分析
000000000000062a <test_fun_b>:
62a: 7139 addi sp,sp,-64
为test_func_b开辟64Byte的栈空间62c: fc06 sd ra,56(sp)
将ra保存到sp + 56栈内存中62e: f822 sd s0,48(sp)
将s0(FP)保存到sp + 48栈内存中630: 0080 addi s0,sp,64
将栈底的值保存到s0(FP)寄存器中632: fca43c23 sd a0,-40(s0)
存储第一个参数到fp - 40栈内存中636: fcb43823 sd a1,-48(s0)
存储第二个参数到fp - 48栈内存中63a: fcc43423 sd a2,-56(s0)
存储第三个参数到fp - 56栈内存中63e: fcd43023 sd a3,-64(s0)
存储第四个参数到fp - 64栈内存中642: 478d li a5,3
加载局部变量c的值到a5寄存器中644: fef43023 sd a5,-32(s0)
将局部变量c的值存储到fp - 32栈内存中648: 4791 li a5,4
加载局部变量d的值到a5寄存器中64a: fef43423 sd a5,-24(s0)
存储局部变量d的值到fp - 24栈内存中64e: fe843683 ld a3,-24(s0)
从fp - 24的栈内存中取出局部变量d的值到a3寄存器中652: fe043603 ld a2,-32(s0)
从fp - 32的栈内存中取出局部变量c的值到a2寄存器中656: 00000597 auipc a1,0x0
65a: 21a58593 addi a1,a1,538 # 870 <__func__.2089>
65e: 00000517 auipc a0,0x0
662: 18250513 addi a0,a0,386 # 7e0 <__libc_csu_fini+0x2>
666: eebff0ef jal ra,550 <printf@plt>
将返回值保存到ra寄存中其,调用printf函数66a: fe043703 ld a4,-32(s0)
从栈fp - 32中取出局部变量c的值到a4寄存器中66e: fe843783 ld a5,-24(s0)
从栈fp - 24中取出局部变量d的值到a5寄存器中672: 97ba add a5,a5,a4
将b + c值保存到a5Jicunqi zhong674: fd843703 ld a4,-40(s0)
从栈中取出第一个参数m的值到a4寄存器中678: 97ba add a5,a5,a4
将b + c + m的值保存到a5寄存器中67a: fef43023 sd a5,-32(s0)
存储b + c + m 的值到栈fp - 32栈内存中,即局部变量c的栈内存位置处67e: fe043703 ld a4,-32(s0)
从栈中取出局部变量c的值到a4寄存器中682: fe843783 ld a5,-24(s0)
从栈中取出局部变量d的值到a5寄存器中686: 97ba add a5,a5,a4
688: fd043703 ld a4,-48(s0)
从栈中取出第二个参数n的值到a4寄存器中68c: 97ba add a5,a5,a4
将c + d + n的值到a5寄存器中68e: fef43423 sd a5,-24(s0)
将c + d + n的值存储到fp - 24的栈内存中692: 0001 nop
694: 70e2 ld ra,56(sp)
恢复ra寄存器的值696: 7442 ld s0,48(sp)
恢复s0(FP)寄存器的值698: 6121 addi sp,sp,64
恢复sp寄存器的值69a: 8082 ret
返回ra寄存器的地址
000000000000062a <test_fun_b>:
62a: 7139 addi sp,sp,-64
62c: fc06 sd ra,56(sp)
62e: f822 sd s0,48(sp)
630: 0080 addi s0,sp,64
632: fca43c23 sd a0,-40(s0)
636: fcb43823 sd a1,-48(s0)
63a: fcc43423 sd a2,-56(s0)
63e: fcd43023 sd a3,-64(s0)
642: 478d li a5,3
644: fef43023 sd a5,-32(s0)
648: 4791 li a5,4
64a: fef43423 sd a5,-24(s0)
64e: fe843683 ld a3,-24(s0)
652: fe043603 ld a2,-32(s0)
656: 00000597 auipc a1,0x0
65a: 21a58593 addi a1,a1,538 # 870 <__func__.2089>
65e: 00000517 auipc a0,0x0
662: 18250513 addi a0,a0,386 # 7e0 <__libc_csu_fini+0x2>
666: eebff0ef jal ra,550 <printf@plt>
66a: fe043703 ld a4,-32(s0)
66e: fe843783 ld a5,-24(s0)
672: 97ba add a5,a5,a4
674: fd843703 ld a4,-40(s0)
678: 97ba add a5,a5,a4
67a: fef43023 sd a5,-32(s0)
67e: fe043703 ld a4,-32(s0)
682: fe843783 ld a5,-24(s0)
686: 97ba add a5,a5,a4
688: fd043703 ld a4,-48(s0)
68c: 97ba add a5,a5,a4
68e: fef43423 sd a5,-24(s0)
692: 0001 nop
694: 70e2 ld ra,56(sp)
696: 7442 ld s0,48(sp)
698: 6121 addi sp,sp,64
69a: 8082 ret
2.3.3 执行完成之后栈帧的使用情况
3 编译和反汇编的命令
3.1 编译的命令
需要注意的是如果不加-Wl,--no-as-needed
编译选项,则在反汇编的时候无法查看到标准库的接口,不太容易理解对应的汇编程序。
riscv64-linux-gnu-gcc -Wl,--no-as-needed main.c -o rv_test
3.2 反汇编的命令
riscv64-linux-gnu-objdump -S -d rv_test