# rv32emu trace code ###### tags: `Project` by [\<ypaskell\>](https://github.com/ypaskell) ## 目標 完整了解 rv32emu 的 code ## Setup environment ### 1. Add debug flag to Makefile Add `-g` in Makefile ```Makefile= include mk/common.mk include mk/toolchain.mk OUT ?= build BIN := $(OUT)/rv32emu CFLAGS = -std=gnu99 -O2 -Wall -Wextra CFLAGS += -Wno-unused-label -g #<--- Add CFLAGS -g here CFLAGS += -include src/common.h # Set the default stack pointer CFLAGS += -D DEFAULT_STACK_ADDR=0xFFFFF000 OBJS_EXT := # Control and Status Register (CSR) ENABLE_Zicsr ?= 1 $(call set-feature, Zicsr) ``` ### 2. Build debug build ```shell make all ``` ## Understanding elf files `hello.elf` ### Objdump `hello.elf` Run `riscv32-unknown-elf-objdump -dhs build/hello.elf | less` * `-d`: Display assembler contents of executable sections * `-h`: Display the contents of the section headers * `-s`: Display the full contents of all sections requested ```Text= $ riscv32-unknown-elf-objdump -dhs build/hello.elf build/hello.elf: file format elf32-littleriscv Sections: Idx Name Size VMA LMA File off Algn 0 .text 00000050 00000000 00000000 00001000 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .riscv.attributes 0000001a 00000000 00000000 00001050 2**0 CONTENTS, READONLY Contents of section .text: 0000 93020000 13035000 6f004000 13000000 ......P.o.@..... 0010 63826202 93080004 13051000 97050000 c.b............. 0020 93854502 1306d000 73000000 93821200 ..E.....s....... 0030 6ff01ffe 9308d005 13050000 73000000 o...........s... 0040 48656c6c 6f20576f 726c6421 0a000000 Hello World!.... Contents of section .riscv.attributes: 0000 41190000 00726973 63760001 0f000000 A....riscv...... 0010 05727633 32693270 3100 .rv32i2p1. Disassembly of section .text: 00000000 <.text>: 0: 00000293 li t0,0 4: 00500313 li t1,5 8: 0040006f j 0xc c: 00000013 nop 10: 02628263 beq t0,t1,0x34 14: 04000893 li a7,64 18: 00100513 li a0,1 1c: 00000597 auipc a1,0x0 20: 02458593 addi a1,a1,36 # 0x40 24: 00d00613 li a2,13 28: 00000073 ecall 2c: 00128293 addi t0,t0,1 30: fe1ff06f j 0x10 34: 05d00893 li a7,93 38: 00000513 li a0,0 3c: 00000073 ecall 40: 6548 flw fa0,12(a0) 42: 6c6c flw fa1,92(s0) 44: 6f57206f j 0x72f38 48: 6c72 flw fs8,28(sp) 4a: 2164 fld fs1,192(a0) 4c: 000a c.slli zero,0x2 ... ``` ### elf open Run `riscv32-unknown-elf-readelf -h build/hello.elf` ```text= $ riscv32-unknown-elf-readelf -h build/hello.elf ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: RISC-V Version: 0x1 Entry point address: 0x0 Start of program headers: 52 (bytes into file) Start of section headers: 4240 (bytes into file) Flags: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 2 Size of section headers: 40 (bytes) Number of section headers: 4 Section header string table index: 3 ``` 上面的 Elf Header 是要在 rv32emu `elf_*` 的 function 處理的。 ### Start using debugging tool (gdb or lldb) 我在 Apple Silicom M1 上跑,無法使用 gdb。但 lldb 和 gdb 大同小異,請自行轉換 Command Navigate to main function ```text (lldb) b main Breakpoint 1: 25 locations. (lldb) r * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 frame #0: 0x000000010000bca4 rv32emu`main at main.c:112:5 [opt] 109 static bool parse_args(int argc, char **args) 110 { 111 /* parse each argument in turn */ -> 112 for (int i = 1; i < argc; ++i) { 113 const char *arg = args[i]; 114 /* parse flags */ 115 if (arg[0] == '-') { (lldb) finish Process 28894 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = step out frame #0: 0x000000010000bdf4 rv32emu`main(argc=<unavailable>, args=0x000000016fdff368) at main.c:209:18 [opt] 206 } 207 208 /* open the ELF file from the file system */ -> 209 elf_t *elf = elf_new(); 210 if (!elf_open(elf, opt_prog_name)) { 211 fprintf(stderr, "Unable to open ELF file '%s'\n", opt_prog_name); 212 return 1; ``` Initialize `elf_t` for saving elf data ```text (lldb) s Process 28894 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = step in frame #0: 0x000000010000a81c rv32emu`elf_new at elf.c:137:16 [opt] 134 135 elf_t *elf_new() 136 { -> 137 elf_t *e = malloc(sizeof(elf_t)); 138 e->hdr = NULL; 139 e->raw_size = 0; 140 e->symbols = map_init(int, char *, map_cmp_uint); 141 e->raw_data = NULL; 142 return e; 143 } ``` `elf_t` declared in `elf.c` ```cpp= typedef struct elf_internal elf_t; // fron elf.h struct elf_internal { const struct Elf32_Ehdr *hdr; uint32_t raw_size; uint8_t *raw_data; /* symbol table map: uint32_t -> (const char *) */ map_t symbols; }; ``` ```cpp= bool elf_open(elf_t *e, const char *path) { bool elf_open(elf_t *e, const char *path) { /* free previous memory */ if (e->raw_data) release(e); #if defined(USE_MMAP) int fd = open(path, O_RDONLY); if (fd < 0) return false; /* get file size */ struct stat st; fstat(fd, &st); e->raw_size = st.st_size; /* map or unmap files or devices into memory. * The beginning of the file is ELF header. */ e->raw_data = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (e->raw_data == MAP_FAILED) { release(e); return false; } #else /* fallback to standard I/O text stream */ FILE *f = fopen(path, "rb"); if (!f) return false; /* get file size */ fseek(f, 0, SEEK_END); e->raw_size = ftell(f); fseek(f, 0, SEEK_SET); if (e->raw_size == 0) { fclose(f); return false; } /* allocate memory */ free(e->raw_data); e->raw_data = malloc(e->raw_size); /* read data into memory */ const size_t r = fread(e->raw_data, 1, e->raw_size, f); fclose(f); if (r != e->raw_size) { release(e); return false; } #endif /* USE_MMAP */ /* point to the header */ e->hdr = (const struct Elf32_Ehdr *) e->raw_data; /* check it is a valid ELF file */ if (!is_valid(e)) { release(e); return false; } return true; } ``` Q&A (2023/09/11) * Question: 為什麼原本使用 fopen 現在使用 `mmap`. [man mmap](https://man7.org/linux/man-pages/man2/mmap.2.html) ? * Jserv: `mmap` 在開啟大檔案時會有優勢。On-demand paging。反之,fopen 遇到大檔案時,開啟時間較慢。 * Question: man `mmap` 説,mmap 是利用 virtual memory。Virtual Memory 在 Disk 不是會比較慢嗎。 * Jserv: virtual memory 不見得跟 disk 有關,請「不要」讀恐龍書來。[這本書可自由下載,品質比恐龍書好多了](https://pages.cs.wisc.edu/~remzi/OSTEP/) [understanding mmap, the workhorse behind keeping memory access efficient in linux](https://www.youtube.com/watch?v=8hVLcyBkSXY) `struct stat` - [man](https://man7.org/linux/man-pages/man2/stat.2.html) ```cpp= struct stat { dev_t st_dev; /* ID of device containing file */ ino_t st_ino; /* inode number */ mode_t st_mode; /* protection */ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device ID (if special file) */ off_t st_size; /* total size, in bytes */ blksize_t st_blksize; /* blocksize for file system I/O */ blkcnt_t st_blocks; /* number of 512B blocks allocated */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last status change */ }; ```