6.S081-Lab4

本文详细解析RISC-V汇编语言,重点探讨在Lab4中如何处理陷阱,包括backtrace、alarm功能的实现,以及trapframe的使用。文章介绍了寄存器在函数调用中的作用,如参数传递、堆栈帧结构,并讲解了如何在xv6中添加sigalarm系统调用,以实现周期性报警功能。此外,还讨论了控制寄存器在陷阱处理中的角色,如stvec、sepc等。
摘要由CSDN通过智能技术生成

Lab: traps

课程地址

看make后的user/call.asm.

main:

void main(void) {
   
  printf("%d %d\n", f(8)+1, 13);
  exit(0);
}

Question:

addi sp,sp,-16: 将栈指针 sp 减去 16 字节,为将来在栈上分配空间做准备。

sd ra,8(sp): 将寄存器 ra 中的值存储在距离 sp 偏移 8 字节的位置,以便在返回时恢复 ra 的值。

sd s0,0(sp): 将寄存器 s0 中的值存储在距离 sp 偏移 0 字节的位置,以便在返回时恢复 s0 的值。

addi s0,sp,16: 将 s0 的值设置为栈指针 sp 加上 16,这是一个将来在栈上分配空间时要使用的偏移量。

auipc ra,0x0: 将全局地址 ra 设置为当前 PC 寄存器值的高位。

jalr -46(ra) # 1c : 跳转到 main 函数的地址,即将 PC 设置为 ra-46。跳转指令的返回地址将存储在 ra 中。

li a0,0: 将寄存器 a0 设置为 0,这是将要传递给 exit 函数的参数。

auipc ra,0x0: 将全局地址 ra 设置为当前 PC 寄存器值的高位。

jalr 628(ra) # 2c8 : 跳转到 exit 函数的地址,即将 PC 设置为 ra+628。这将退出程序并返回 0。

哪些寄存器包含函数参数?例如,哪个寄存器在main函数调用printf时保存了数字13?

在RISC-V中,前六个整数参数通过a0-a5传递,其他参数通过栈传递。因此,在main函数调用printf时,数字12和13分别存储在a1和a2寄存器中。
在main函数的汇编代码中,函数f和g的调用分别在哪里?(提示:编译器可能会内联函数。)

函数f的调用位于printf的第一个参数计算之前,即在地址0x24处。由于函数g是f的子例程,编译器可能会内联调用g函数,因此在main函数的汇编代码中可能没有对函数g的显式调用。
函数printf的地址在哪里?

函数printf的地址是0x630。
在main函数中jalr到printf后寄存器ra中的值是什么?

在jalr指令执行后,寄存器ra中存储着返回地址,即下一条指令的地址。因此,在jalr到printf的调用之后,ra寄存器中包含指向exit调用的下一条指令地址的值。
函数g的汇编代码在哪里?

0000000000000000 <g>:
   0:	1141                	addi	sp,sp,-16
   2:	e422                	sd	s0,8(sp)
   4:	0800                	addi	s0,sp,16
  return x+3;
   6:	250d                	addiw	a0,a0,3
   8:	6422                	ld	s0,8(sp)
   a:	0141                	addi	sp,sp,16
   c:	8082                	ret

函数g的汇编代码在地址0x0。

Backtrace

对于调试,回溯通常很有用:在错误发生点之上的堆栈上的函数调用列表。 为了帮助回溯,编译器生成机器代码,在堆栈上维护一个堆栈帧,对应于当前调用链中的每个函数。 每个堆栈帧由返回地址和指向调用者堆栈帧的“帧指针”组成。 寄存器 s0 包含指向当前堆栈帧的指针(它实际上指向堆栈上保存的返回地址的地址加上 8)。 您的回溯应使用帧指针在堆栈中向上移动并在每个堆栈帧中打印保存的返回地址。

fp 指向当前栈帧的开始地址,sp 指向当前栈帧的结束地址。 (栈从高地址往低地址生长,所以 fp 虽然是帧开始地址,但是地址比 sp 高)
栈帧中从高到低第一个 8 字节 fp-8 是 return address,也就是当前调用层应该返回到的地址。
栈帧中从高到低第二个 8 字节 fp-16 是 previous address,指向上一层栈帧的 fp 开始地址。
剩下的为保存的寄存器、局部变量等。一个栈帧的大小不固定,但是至少 16 字节。

在 xv6 中,使用一个页来存储栈,如果 fp 已经到达栈页的上界,则说明已经到达栈底。
int g(int x) {
0: 1141 addi sp,sp,-16 // 扩张调用栈,得到一个 16 字节的栈帧
2: e422 sd s0,8(sp) // 将返回地址存到栈帧的第一个 8 字节中
4: 0800 addi s0,sp,16
return x+3;
}
6: 250d addiw a0,a0,3
8: 6422 ld s0,8(sp) // 从栈帧读出返回地址
a: 0141 addi sp,sp,16 // 回收栈帧
c: 8082 ret // 返回
注意栈的生长方向是从高地址到低地址,所以扩张是 -16,而回收是 +16。

PGROUNDDOWN(fp); 返回页面低地址
PGROUNDUP(fp); 返回页面高地址
用up-down 是一个页的大小(4096)

代码:
defs.h

/
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值