Ultima attività 1756095581

fallrain ha revisionato questo gist 1756095581. Vai alla revisione

1 file changed, 85 insertions

PA3.2.md(file creato)

@@ -0,0 +1,85 @@
1 + ### 必答题(需要在实验报告中回答) - hello程序是什么, 它从而何来, 要到哪里去
2 +
3 + 到此为止, PA中的所有组件已经全部亮相, 整个计算机系统也开始趋于完整. 你也已经在这个自己创造的计算机系统上跑起了hello这个第一个还说得过去的用户程序 (dummy是给大家热身用的, 不算), 好消息是, 我们已经距离运行仙剑奇侠传不远了(下一个阶段就是啦).
4 +
5 + 不过按照PA的传统, 光是跑起来还是不够的, 你还要明白它究竟怎么跑起来才行. 于是来回答这道必答题吧:
6 +
7 + > 我们知道`navy-apps/tests/hello/hello.c`只是一个C源文件, 它会被编译链接成一个ELF文件. 那么, hello程序一开始在哪里? 它是怎么出现内存中的? 为什么会出现在目前的内存位置? 它的第一条指令在哪里? 究竟是怎么执行到它的第一条指令的? hello程序在不断地打印字符串, 每一个字符又是经历了什么才会最终出现在终端上?
8 +
9 + 上面一口气问了很多问题, 我们想说的是, 这其中蕴含着非常多需要你理解的细节. 我们希望你能够认真整理其中涉及的每一行代码, 然后用自己的语言融会贯通地把这个过程的理解描述清楚, 而不是机械地分点回答这几个问题.
10 +
11 + 同样地, 上一阶段的必答题"理解穿越时空的旅程"也已经涵盖了一部分内容, 你可以把它的回答包含进来, 但需要描述清楚有差异的地方. 另外, C库中`printf()`到`write()`的过程比较繁琐, 而且也不属于PA的主线内容, 这一部分不必展开回答. 而且你也已经在PA2中实现了自己的`printf()`了, 相信你也不难理解字符串格式化的过程. 如果你对Newlib的实现感兴趣, 你也可以RTFSC.
12 +
13 + 总之, 扣除C库中`printf()`到`write()`转换的部分, 剩下的代码就是你应该理解透彻的了. 于是, 努力去理解每一行代码吧!
14 +
15 + ### 答:
16 +
17 + 要理解hello从何而来,自然是从它的makefile开始。hello的makefile include了navy-apps下的大makefile, 其中
18 +
19 + ```makefile
20 + ## 4. ISA-Specific Configurations
21 +
22 + ### Paste in ISA-specific configurations (e.g., from `scripts/x86.mk`)
23 + -include $(NAVY_HOME)/scripts/$(ISA).mk
24 + ```
25 +
26 + 又include了ISA相关的makefile, 继续找到`$(NAVY_HOME)/scripts/riscv32.mk`以及`$(NAVY_HOME)/scripts/riscv/common.mk`可以看到这几行
27 +
28 + ```makefile
29 + LNK_ADDR = $(if $(VME), 0x40000000, 0x83000000)
30 + CFLAGS += -fno-pic -march=rv64g -mcmodel=medany
31 + LDFLAGS += --no-relax -Ttext-segment $(LNK_ADDR)
32 + ```
33 +
34 + 目前还没做到VME, 所以LNK_ADDR为0x83000000,LDFLAGS是传递给链接器(ld)的参数,`-Ttext-segment`指定程序代码段(.text)运行时的地址。在编译出的elf文件中,可以看到相应的地址:
35 +
36 + ```
37 + $ riscv64-linux-gnu-readelf -a build/hello-riscv32
38 + ELF Header: (omitted)
39 +
40 + Section Headers:
41 + [Nr] Name Type Addr Off Size ES Flg Lk Inf Al
42 + [ 0] NULL 00000000 000000 000000 00 0 0 0
43 + [ 1] .text PROGBITS 830000b4 0000b4 005e00 00 AX 0 0 4
44 + [ 2] .rodata PROGBITS 83005eb4 005eb4 0003fd 00 A 0 0 4
45 + [ 3] .eh_frame PROGBITS 830062b4 0062b4 000fd0 00 A 0 0 4
46 + [ 4] .data PROGBITS 83008000 008000 000830 00 WA 0 0 8
47 + [ 5] .sdata PROGBITS 83008830 008830 000068 00 WA 0 0 4
48 + [ 6] .sbss NOBITS 83008898 008898 000018 00 WA 0 0 4
49 + [ 7] .bss NOBITS 830088b0 008898 000028 00 WA 0 0 4
50 + [ 8] .comment PROGBITS 00000000 008898 000012 01 MS 0 0 1
51 + [ 9] .riscv.attributes RISCV_ATTRIBUTE 00000000 0088aa 000061 00 0 0 1
52 + [10] .symtab SYMTAB 00000000 00890c 001010 10 11 126 4
53 + [11] .strtab STRTAB 00000000 00991c 0007ae 00 0 0 1
54 + [12] .shstrtab STRTAB 00000000 00a0ca 000066 00 0 0 1
55 + Key to Flags:
56 + W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
57 + L (link order), O (extra OS processing required), G (group), T (TLS),
58 + C (compressed), x (unknown), o (OS specific), E (exclude),
59 + D (mbind), p (processor specific)
60 +
61 + There are no section groups in this file.
62 +
63 + Program Headers:
64 + Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
65 + RISCV_ATTRIBUT 0x0088aa 0x00000000 0x00000000 0x00061 0x00000 R 0x1
66 + LOAD 0x000000 0x83000000 0x83000000 0x07284 0x07284 R E 0x1000
67 + LOAD 0x008000 0x83008000 0x83008000 0x00898 0x008d8 RW 0x1000
68 + GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10
69 + ```
70 +
71 + 编译出elf以后我把它复制到nanos下,命名为ramdisk.img
72 +
73 + ```
74 + cp $NAVY_HOME/tests/hello/build/hello-riscv32 ~/ics2024/nanos-lite/build/ramdisk.img
75 + ```
76 +
77 + nanos-like/src/resources.S中把这个ramdisk.img二进制包括进来,最后一起打包到编译出的nanos二进制文件中,并且定义了全局符号ramdisk_start, ramdisk_end用于记录img的起始和末尾地址。
78 +
79 + nanos启动时先调用`init_irq`然后经过一通调用最后在am的cte中执行csrw指令把cte的`__am_asm_trap`函数地址写入中断向量寄存器。CPU(nemu)执行到syscall指令时即跳转到这个函数,该函数保存上下文后最终会调用nanos注册的`do_event`函数。
80 +
81 + 启动过程继续调用`init_proc`再调用`naive_uload`, 解析ramdisk.img的elf头,按Program Headers把要执行的二进制复制到0x83000000开始的位置,然后跳转到entry开始执行hello。
82 +
83 + hello打印字符串时,调用libc(newlib)提供的printf函数,newlib内部最终会调用libos的syscall.c里面的`_write`函数,然后调用syscall也即ecall指令,并用寄存器传递参数。如上文所说最终跳转到`do_event`函数然后调用`do_syscall`函数,根据syscall number执行对应write操作,调用am提供的putch函数打印到终端。
84 +
85 + 由上文分析可见,navy-apps与nanos交互的interface是syscall, 相关支持库是newlib和libos. 而nanos和CPU(nemu)交互的interface是ISA, 相关支持库是abstract-machine及其组件。
Più nuovi Più vecchi