[MIT 6.1810]Lab4 traps

1 RISC-V assembly (easy)

Which registers contain arguments to functions? For example, which register holds 13 in main’s call to printf?

在RISC-V中,函数的参数存放在寄存器 a0-a7 中,如果超过8个参数,可以用栈来传递。a0-a1 寄存器还用来传递函数的返回值。

1
24:	4635                	li	a2,13

从上述代码中可以看出,printf 的参数13放在寄存器 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.)

fg 都被inlined到 main 中了,编译器预计算出了 f(8) + 1 的值,直接将 12 加载到寄存器 a1 中,而没有直接调用函数。

At what address is the function printf located?

1
30:	68c000ef          	jal	6bc <printf>

从上述代码中可知,printf 在0x6bc处

What value is in the register ra just after the jalr to printf in main?

1
2
30:	68c000ef          	jal	6bc <printf>
34: 4501 li a0,0

jal 指令执行完毕后,会将下一条指令的地址放在 ra 中,因此 ra 中的值是0x34

Run the following code.

1
2
unsigned int i = 0x00646c72;
printf("H%x Wo%s", 57616, (char *) &i);

What is the output? Here’s an ASCII table that maps bytes to characters.

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 change 57616 to a different value?

在小端(little-endian)系统中,最低有效字节存储在最低地址。

i = 0x00646c72,在小端系统中,它会按字节顺序存储为 72 6c 64 00

  • 0x72 对应字符 'r'
  • 0x6c 对应字符 'l'
  • 0x64 对应字符 'd'
  • 0x00 对应空字符(字符串结尾)

printf("H%x Wo%s", 57616, (char *) &i); 中,%x 会将 57616 以十六进制格式输出,而 %s 会将 i 的值按字符字符串输出。

因此,输出是:

  • H%x 会输出 H 加上 57616 的十六进制形式,即 e110
  • Wo%s 会输出 Wo 加上 i 的内容,即 rld

所以在小端系统上,这段代码的输出为:

1
He110 World

在大端(big-endian)系统上,0x00646c72 将按字节顺序存储为 00 64 6c 72,这会将 i 解释为不同的字符序列,因此我们需要重新设置 i 以确保输出相同。

为了在大端系统上得到 rld 的字符顺序,我们需要将 i 设为 0x726c6400,这样在大端系统中存储的字节序将是 72 6c 64 00,即 rld

57616 的值没有依赖字节顺序,它只是一个整数,所以无需更改。

In the following code, what is going to be printed after 'y='? (note: the answer is not a specific value.) Why does this happen?

1
printf("x=%d y=%d", 3);

这段代码使用 printf 函数来输出两个占位符 %d,分别用于打印变量 xy 的值。然而,只提供了一个参数 3,没有给出第二个参数。

由于缺少第二个参数,printf 会在内存中继续读取下一个位置的值来填充 y 的占位符。这种行为是未定义行为(undefined behavior),可能导致以下情况之一:

  1. 随机值printf 会从栈或寄存器中读取一些未初始化的值,因此 y 可能会显示一个随机值。
  2. 程序崩溃:在某些系统上,未提供的参数会导致程序崩溃或打印出无效数据。

2 Backtrace (moderate)

本题要求我们实现 backtrace() 函数用来打印函数调用栈,函数调用栈的结构如下所示。可以看到,ra 寄存器的地址存放在 fp - 8 的位置上。在xv6中,内核栈的大小是一页,因此所有的栈帧都在同一页中,我们可以据此来停止 backtrace

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
                 .
.
+-> .
| +-----------------+ |
| | return address | |
| | previous fp ------+
| | saved registers |
| | local variables |
| | ... | <-+
| +-----------------+ |
| | return address | |
+------ previous fp | |
| saved registers | |
| local variables | |
+-> | ... | |
| +-----------------+ |
| | return address | |
| | previous fp ------+
| | saved registers |
| | local variables |
| | ... | <-+
| +-----------------+ |
| | return address | |
+------ previous fp | |
| saved registers | |
| local variables | |
$fp --> | ... | |
+-----------------+ |
| return address | |
| previous fp ------+
| saved registers |
$sp --> | local variables |
+-----------------+
1
2
3
4
5
6
7
8
9
10
11
12
13
void
backtrace(void)
{
uint64 ra, cur_fp, prev_fp, page;
cur_fp = r_fp();
page = PGROUNDDOWN(cur_fp);
while (cur_fp >= page && cur_fp < page + PGSIZE) {
ra = *((uint64*)(cur_fp - 8));
prev_fp = *((uint64*)(cur_fp - 16));
printf("%p\n", (void*)ra);
cur_fp = prev_fp;
}
}

[MIT 6.1810]Lab4 traps
https://erlsrnby04.github.io/2024/11/12/MIT-6-1810-Lab4-traps/
作者
ErlsrnBy04
发布于
2024年11月12日
许可协议