关于c:MIT-6181068286S081-操作系统工程-Lab4-Trapsdoing

53次阅读

共计 2927 个字符,预计需要花费 8 分钟才能阅读完成。

RISC-V assembly (easy)

问题

  1. 哪些寄存器保留函数的参数?例如,在 main 函数对 printf 的调用中,哪个寄存器保留 13?
  2. main函数中哪里调用了汇编代码中的 f 函数和 g 函数?(编译器可能内联函数)
  3. printf位于哪个地址?
  4. 紧接着在 main 中跳转到 printf 之后,寄存器 ra 中保留的值是什么?
  5. 运行下列代码:

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

    输入什么?
    输入取决于 RISC- V 是小端序(little-endian)的事实。若 RISC- V 是大端序 (big-endian),则i 该当设置成什么能力给出雷同的输入?须要扭转 57616 吗?

  6. 下列代码中,y=之后将会输入什么?(非特定值)为何?

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

答复

  1. a0 a1 a2(main 函数只有这仨,实际上到 a7); a2 保留 13
  2. 在 0x26 处,看到间接将 12 给寄存器 a1,阐明 f 的调用被优化掉了
  3. 在 0x34 处,可知 jalr 跳转到 1554 + ra 地址处。而依据 0x30 处,寄存器 ra 保留了 0x30 的地址,因而 jalr 跳转到 1554(dec)+0x30(hex)=0x642(hex)的指标地址,因而 printf 的地址是 0x642,发现与正文雷同
  4. 0x38
  5. 输入:HE110 World

    • 首先 %x 输入 57616 的十六进制模式
    • 而后是值为 0x00646c72 的 i%s 实际上读取的是字符串,也就是一个又一个 char,所以i 这个 int 类型实际上能够看作由 4 个 char 类型组成,而后就是这 4 个 char 怎么排列的问题
    • 大端序即:0x12345678 -> 0x 12 34 56 78(从左到右地址序号增大)
    • 小端序即:0x12345678 -> 0x 78 56 34 12(从左到右地址序号增大)
    • 材料指出 RISC- V 采纳小端序,由此可知 i 在内存当中的贮存为:0x 72 6c 64 00
    • 因而会先读取值为 0x72char类型,在 ascii 中对应字母 r;而后是0x6c,对应字母l0x64 对应 d0x00 对应终止

    因而若是大端序,则 i 应设置为:i = 0x646c7200。而 57616 不须要变,因为没有产生扭转大小的类型转变。

  6. 实践上讲,因为传入两个参数,因而 a0 和 a1 寄存器都保留了确定的值,但在 printf 中因为应用了第三个参数,因而会应用 a2 的值作为 y= 后边的输入值;但 a2 寄存器中的值并不能确定,因而不确定输入后果。
    但实际上,我尝试屡次都是 1。可能是因为没有函数应用寄存器 a2,a2 就始终放弃值为 1 的状态吧

Backtrace (moderate)

要求

回溯对 debug 十分有用:在 error 产生时刻的堆栈上零碎调用的一个列表。为实现回溯,编译器生成机器码,在堆栈上保护以后调用链上每个函数对应的堆栈帧。每个堆栈帧由返回地址和一个指向调用者堆栈帧的帧指针组成。寄存器 s0 包含一个指向以后堆栈帧的指针(理论指向堆栈上保留的返回地址 +8). 你的 backtrace 该当应用帧指针遍历堆栈并打印堆栈帧上保留的返回地址。

kernel/printf.c 中实现 backtrace()。在sys_sleep 中调用此函数。运行命令bttest,会调用sys_sleep。输入格局该当如下所示:

backtrace:
0x0000000080002cda
0x0000000080002bb6
0x0000000080002898

在终端运行命令 addr2line -e kernel/kernel 并将 backtrace 输入的地址复制粘贴过去就能失去以下输入:

kernel/sysproc.c:74
kernel/syscall.c:224
kernel/trap.c:85

提醒

  • defs.h 中增加定义。
  • GCC 编译器将以后函数的帧指针保留在寄存器 s0 中。在 kernel/risv.h 中增加上面的函数:

    static inline uint64 r_fp() {
        uint64 x;
        asm volatile("mv %0, s0" : "=r" (x) );
        return x;
    }

    backtrace 中调用该函数可通过内联的机器码读取寄存器s0

  • 堆栈帧布局示意图(如下)。留神,返回地址位于堆栈帧帧指针固定偏移 - 8 的中央;以及保留的帧指针位于(以后)帧指针固定偏移 -16 的地位。

    Stack
                    .
                    .
        +->          .
        |   +-----------------+   |
        |   | 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 |
            +-----------------+
  • backtrace须要辨认最初的栈帧以进行递归。一个有用的事实是,为每个内核堆栈调配的内存由单个对齐内存页组成,因而给定堆栈上的所有堆栈帧都在同一内存页上。你能够应用 PGROUNDDOWN(fp) 辨认帧指针援用的内存页

如果 backtrace 运行失常,能够在 panic 中调用它。

实现

  1. defs.h 中增加定义(略)
  2. 将提醒给的 r_fp() 函数复制到risv.h(略)
  3. backtrace实现思路:

    • 首先还是能够确定,最简略的办法还是递归调用一个打印函数。
    • 递归就须要确定递归终止条件,这次提醒中也给出了终止条件——堆栈帧都保留在同一内存页上,即帧指针都在同一内存页。由此可得递归终止条件:若以后堆栈帧中保留的前一个帧指针与以后帧指针不在同一内存页,则阐明保留的并非帧指针,就能够终止递归了。
    • 而后就是返回地址获取办法:以后帧指针偏移 - 8 后解援用
    • 前一个帧指针获取办法:以后帧指针偏移 -16 后解援用
  4. printf.c 中实现backtrace

    void printFramePointer(uint64 fp) {uint64 pre_fp = *(uint64*)(fp - 16);
        printf("%p\n", *(uint64*)(fp - 8));
        if (PGROUNDDOWN(fp) == PGROUNDDOWN(pre_fp)) {printFramePointer(pre_fp);
        }
    }
    
    void backtrace(void) {printFramePointer(r_fp());
    }
  5. sys_sleep() 中增加backtrace()(略)

后果

运行后果

测试后果

正文完
 0