fallrain a révisé ce gist . Aller à la révision
1 file changed, 85 insertions
PA3.2.md(fichier créé)
| @@ -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及其组件。 | |
Plus récent
Plus ancien