南京大学/NJU 人工智能/AI 计算机系统基础/ICS 编程作业/PA 记录

PA2.3

从这里开始记录。

已经能通过大部分测试用例——除了 hello-str 和 string。

image

观察这两个测试用例究竟是为什么挂了。首先定位到错误的位置,发现都是 putch 函数中发生了内存越界。

但是这两个代码本身并没有这个函数,因此先寻找代码与 putch 函数的关系。不断寻找,最终可以发现这个 putch 是某个 trm.c 中的函数。

根据经验,我们可以判断是 nemu/trm.c,毕竟这是加载在 nemu 上的程序。而想要验证也很简单,只需要在其 putch 函数前加入一条 printf("Hello") 即可,如果 nemu 的报错是识别到了没被植入的指令,就说明是这个 trm.c 了。

而使用 putch 的原因是进入了 panic,因此,只需要把对应的环境函数实现,即可避免进入 panic,从而避免错误。

字符串相关的函数都很好实现,难点在于 sprintf 等。

第一步,参数处理

va_list args;
va_start(args, fmt);
//TODO
va_end(args);

这样可以得到一个 args 的 va_list。

然后每次使用

va_arg(ap, int)
va_arg(ap, char*)

来获取参数列表中的对应类型。

第二部,实现逻辑

按照对应函数逻辑实现即可。

难点,printf

其实实现并不困难,只需要这样实现即可:

int printf(const char *fmt, ...) {
  // panic("Not Implemented");
  char out[4096];
  va_list args;
  va_start(args, fmt);
  int ret = vsnprintf(out, sizeof(out), fmt, args);
  va_end(args);
  putstr(out);
  return ret;
}

但是问题在于 putstr 这个函数并不能发挥作用!因为这个函数本身就是会访问一个非内存的地址!

实际上,原因在于需要在 make menuconfig 中打开 device 设置,打开之后,就会有一个 mmio(memory map input/output) 的代码被实现,也就是把映射到 nemu 中外设的地址,映射到 nemu 中外设的地址,再有 nemu 中模拟设备的代码,映射到真实的地址。

理论上,把这些东西实现之后,就可以通过了。

在中间的时候,有某个版本突然无法通过 cpu-test 了。本来想用 git bisect 查错,但是对于中间有 git merge 的提交历史,bisect 难以使用。最后没招,只好人眼识别两个版本哪里有差别,结果是因为 addi, xori, ori 的逻辑错了。

这三个指令,本来想着既然立即数只有低 12 位,那我就应该 &0xfff 来截断立即数,结果正确的逻辑就是需要符号扩展到 32 位。

posted @ 2025-11-04 21:09  哼唧昂叽  阅读(5)  评论(0)    收藏  举报