|
| 1 | +# mainargs 传递机制 |
| 2 | + |
| 3 | +## 问题一:mainargs 是怎么生成并传递给 AM 的 main 函数的? |
| 4 | + |
| 5 | +这是一个**二进制补丁**技术,整个流程分为三步: |
| 6 | + |
| 7 | +### 1. 编译时:创建占位符 |
| 8 | + |
| 9 | +在 `scripts/platform/sim.mk` 中定义了: |
| 10 | + |
| 11 | +```makefile |
| 12 | +MAINARGS_MAX_LEN = 64 |
| 13 | +MAINARGS_PLACEHOLDER = the_insert-arg_rule_in_Makefile_will_insert_mainargs_here |
| 14 | +CFLAGS += -DMAINARGS_MAX_LEN=$(MAINARGS_MAX_LEN) -DMAINARGS_PLACEHOLDER=$(MAINARGS_PLACEHOLDER) |
| 15 | +``` |
| 16 | + |
| 17 | +然后在 `am/src/platform/sim/trm.c` 中: |
| 18 | + |
| 19 | +```c |
| 20 | +static const char mainargs[MAINARGS_MAX_LEN] = TOSTRING(MAINARGS_PLACEHOLDER); |
| 21 | +``` |
| 22 | + |
| 23 | +这里 `mainargs` 被初始化为一个 64 字节的字符数组,内容是那个很长的占位符字符串。 |
| 24 | + |
| 25 | +### 2. 链接后:二进制补丁替换 |
| 26 | + |
| 27 | +在生成 `.bin` 文件后,`insert-arg` 目标会执行: |
| 28 | + |
| 29 | +```makefile |
| 30 | +insert-arg: image |
| 31 | + @python3 $(AM_HOME)/tools/insert-arg.py $(IMAGE).bin $(MAINARGS_MAX_LEN) $(MAINARGS_PLACEHOLDER) "$(mainargs)" |
| 32 | +``` |
| 33 | + |
| 34 | +`tools/insert-arg.py` 脚本的核心逻辑: |
| 35 | + |
| 36 | +```python |
| 37 | +fp = open(bin, 'r+b') |
| 38 | +data = fp.read() |
| 39 | +idx = data.find(str.encode(placeholder)) # 在二进制中搜索占位符 |
| 40 | +fp.seek(idx) |
| 41 | +mainargs_pad = str.encode(mainargs) + ((max_len - len(mainargs)) * str.encode("\0")) # 补 \0 |
| 42 | +fp.write(mainargs_pad) # 原地替换 |
| 43 | +``` |
| 44 | +
|
| 45 | +它直接在二进制文件中**搜索占位符字符串**,然后用你传入的 `mainargs='xxx'` **原地替换**。 |
| 46 | +
|
| 47 | +### 3. 运行时:传递给 main |
| 48 | +
|
| 49 | +```c |
| 50 | +void _trm_init() { |
| 51 | + int ret = main(mainargs); |
| 52 | + halt(ret); |
| 53 | +} |
| 54 | +``` |
| 55 | + |
| 56 | +`_trm_init()` 是 AM 的入口函数,它调用 `main(mainargs)`,把这个字符串作为参数传给应用程序。 |
| 57 | + |
| 58 | +### 为什么这样设计? |
| 59 | + |
| 60 | +因为**裸机程序没有操作系统**,不存在 shell 来传递 `argc/argv`。这个设计通过: |
| 61 | +1. 编译时预留空间(占位符) |
| 62 | +2. 链接后直接修改二进制 |
| 63 | +3. 运行时读取被修改的数据 |
| 64 | + |
| 65 | +实现了一种"伪命令行参数"的效果。 |
| 66 | + |
| 67 | +--- |
| 68 | + |
| 69 | +## 问题二:为什么不会保留之前的 mainargs 状态? |
| 70 | + |
| 71 | +### 现象 |
| 72 | + |
| 73 | +```bash |
| 74 | +# 第一次运行 |
| 75 | +make run mainargs='riscv32-npc' |
| 76 | +# 输出:mainargs = 'riscv32-npc'. |
| 77 | + |
| 78 | +# 第二次运行(不指定 mainargs) |
| 79 | +make run |
| 80 | +# 输出:mainargs = ''. |
| 81 | +``` |
| 82 | + |
| 83 | +既然 `insert-arg.py` 已经把 `riscv32-npc` 写入了二进制文件,为什么第二次运行时 mainargs 变成了空? |
| 84 | + |
| 85 | +### 原因 |
| 86 | + |
| 87 | +关键在于 Makefile 的依赖链: |
| 88 | + |
| 89 | +```makefile |
| 90 | +# nemu.mk / npc.mk |
| 91 | +run: insert-arg |
| 92 | + $(MAKE) -C $(NEMU_HOME) ISA=$(ISA) run ... |
| 93 | + |
| 94 | +# sim.mk |
| 95 | +insert-arg: image |
| 96 | + @python3 $(AM_HOME)/tools/insert-arg.py $(IMAGE).bin ... "$(mainargs)" |
| 97 | + |
| 98 | +image: image-dep |
| 99 | + @$(OBJCOPY) -S --set-section-flags .bss=alloc,contents -O binary $(IMAGE).elf $(IMAGE).bin |
| 100 | +``` |
| 101 | + |
| 102 | +每次 `make run` 时的完整流程: |
| 103 | + |
| 104 | +1. **`run` 依赖 `insert-arg`** |
| 105 | +2. **`insert-arg` 依赖 `image`** |
| 106 | +3. **`image` 每次都从 `.elf` 重新生成 `.bin`** |
| 107 | + |
| 108 | +所以: |
| 109 | + |
| 110 | +| 运行 | OBJCOPY | insert-arg | |
| 111 | +|------|---------|------------| |
| 112 | +| `make run mainargs='riscv32-npc'` | 从 `.elf` 生成新 `.bin`(包含占位符) | 用 `riscv32-npc` 替换占位符 | |
| 113 | +| `make run` | 从 `.elf` **又重新生成** `.bin`(包含占位符) | 用**空字符串**替换占位符 | |
| 114 | + |
| 115 | +### 关键点 |
| 116 | + |
| 117 | +- `.bin` 文件是一个**临时产物**,每次都从 `.elf` 重新生成 |
| 118 | +- 之前写入的 `riscv32-npc` 已经被新的 `OBJCOPY` 覆盖掉了 |
| 119 | +- `.elf` 里面永远是那个占位符字符串 |
| 120 | +- 每次 `OBJCOPY` 后,`.bin` 又恢复成占位符状态,然后再用当前的 `$(mainargs)` 替换 |
0 commit comments