掉入陷阱
上回书说到,内存里有个trapframe很是碍眼。这个实验就来揭开他的神秘面纱,你会发现traps是实现系统调用的根本机制。
Lab4 Traps
简答题:RISC-V assembly
这里展示了样例程序的汇编call.asm
,其内容为一些简单的函数调用,要求我们回答一些问题增进对RISC-V汇编的理解。内容不难,多看RISC-V手册(riscv-spec.pdf (mit.edu)),多问gpt。
-
Which registers contain arguments to functions? For example, which register holds 13 in main’s call to printf?
a0-a7. a2.
-
Where is the call to function f in the assembly code for main? Where is the call to
g
? (Hint: the compiler may inline functions.)0x26: compiler computed return value of f . 0x14: compiler inlined g to f.
-
At what address is the function printf located?
0x64a.
-
What value is in the register ra just after the jalr to printf in main?
0x38.
|
|
-
Run the following code. What is the output? The output depends on that fact that the RISC-V is little-endian. If the RISC-V were instead big-endian what would you set
i
to in order to yield the same output? Would you need to change57616
to a different value?HE110 World. 0x726c6400. No, c code doesn’t care little-endian or not, compiler will handle it.
-
In the following code,
printf("x=%d y=%d", 3);
what is going to be printed after'y='
? (note: the answer is not a specific value.) Why does this happen?a2. 典中典格式化字符串漏洞。
Lab4-1: Backtrace
实现backtrace()
,打印当前函数堆栈。要求插桩到sys_sleep()
系统调用处执行。我们知道,所谓函数栈就是通过帧指针fp链起来的一个栈,每个函数调用实例都按固定格式维护一堆局部变量,称为栈桢(stack frame),其内容包括:返回地址、fp、寄存器值、局部变量。布局如图:
|
|
fp指向上一个栈帧的起始地址,而sp指向当前栈顶。因此,我们可以根据fp递归遍历,根据fp和ret的固定偏移获取ret值,即所执行代码的地址。具体到实现,还需要解决以下问题:
- 从sp如何获取fp?gcc将fp存储到了s0寄存器中。实验手册提供了读取fp的内联汇编代码。
- 如何判断递归结束?按理说到main就应该结束,但并没有判断函数名的方法。好在实验指导说堆栈固定在一个内存页中,只需要用
PGROUNDDOWN(fp)
和PGROUNDUP(fp)
判断page边界即可。
代码量很少:
|
|
实验指导说用addr2line检验以下:
最后0x12找不到对应的代码一开始猜测应该是用户态文件所以-e找不到,但后来发现是没检测page的上界导致的。
Lab4-2: Alarm
实现sigalarm(interval, handler)
系统调用。在调用的 n 个时间单位后,调用handler函数。
还是先从测试代码user/alarmtest.c
入手,发现分为4关