今天在写DragonOS的进程切换代码的时候,对于ra寄存器的设置与保存有点疑惑,于是写这篇文章来分析一下。
探索过程
我编写了这样的一个C程序:
#include <stdio.h>
void b() {
printf("b\n");
}
void a() {
printf("a\n");
b();
}
int main() {
a();
return 0;
}
接着使用以下命令编译:
riscv64-linux-musl-gcc -static -o a a.c
接着反汇编:
riscv64-linux-musl-objdump -D a > a.txt
于是得到了以下片段:
0000000000010216 <b>:
10216: 1141 addi sp,sp,-16
10218: e406 sd ra,8(sp)
1021a: e022 sd s0,0(sp)
1021c: 0800 addi s0,sp,16
1021e: 67c5 lui a5,0x11
10220: 23878513 addi a0,a5,568 # 11238 <__errno_location+0xa>
10224: 228000ef jal ra,1044c <puts>
10228: 0001 nop
1022a: 60a2 ld ra,8(sp)
1022c: 6402 ld s0,0(sp)
1022e: 0141 addi sp,sp,16
10230: 8082 ret
0000000000010232 <a>:
10232: 1141 addi sp,sp,-16
10234: e406 sd ra,8(sp)
10236: e022 sd s0,0(sp)
10238: 0800 addi s0,sp,16
1023a: 67c5 lui a5,0x11
1023c: 24078513 addi a0,a5,576 # 11240 <__errno_location+0x12>
10240: 20c000ef jal ra,1044c <puts>
10244: fd3ff0ef jal ra,10216 <b>
10248: 0001 nop
1024a: 60a2 ld ra,8(sp)
1024c: 6402 ld s0,0(sp)
1024e: 0141 addi sp,sp,16
10250: 8082 ret
0000000000010252 <main>:
10252: 1141 addi sp,sp,-16
10254: e406 sd ra,8(sp)
10256: e022 sd s0,0(sp)
10258: 0800 addi s0,sp,16
1025a: fd9ff0ef jal ra,10232 <a>
1025e: 4781 li a5,0
10260: 853e mv a0,a5
10262: 60a2 ld ra,8(sp)
10264: 6402 ld s0,0(sp)
10266: 0141 addi sp,sp,16
10268: 8082 ret
分析
可以看到:
- main函数使用jal指令,跳转到函数a,并且设置了ra寄存器(这是jal指令的操作)。
- 接着在函数a()内,首先把ra寄存器的值存储到栈上,然后开始执行其他操作。
- 在函数a()将要返回的时候,从栈上取出ra的值,并设置到ra寄存器内,于是ret指令就能返回到main函数了。
因此,riscv是“调用者设置ra,被调用者保存ra到栈上”。
转载请注明来源:https://longjin666.cn/?p=1852(在新窗口中打开)
欢迎关注我的公众号“灯珑”,让我们一起了解更多的事物~