Skip to content

Commit 0d50d96

Browse files
committed
复习C线
1 parent d283224 commit 0d50d96

File tree

7 files changed

+204
-5
lines changed

7 files changed

+204
-5
lines changed

abstract-machine/.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,6 @@ _*
1616
*~
1717
build/
1818
!.gitignore
19-
.vscode
19+
.vscode
20+
!elf-naming.md
21+
!insert-mainarg.md

abstract-machine/am/src/platform/sim/ioe/gpu.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ void __am_gpu_config(AM_GPU_CONFIG_T *cfg) {
2525
void __am_gpu_fbdraw(AM_GPU_FBDRAW_T *ctl) {
2626
int x = ctl->x, y = ctl->y; // 绘图的起点坐标 (x, y)
2727
int w = ctl->w, h = ctl->h; // 绘图的宽高 (w, h)
28-
if (!ctl->sync && (w == 0 || h == 0)) return; // 啥也没干, 直接返回
28+
if (!ctl->sync && (w == 0 || h == 0)) { return; } // 啥也没干, 直接返回
2929
uint32_t *pixels = ctl->pixels; // 绘图的数据
30-
if (pixels == NULL && !(w == 0 && h == 0)) return; // 非法数据, 让你绘制图像, 但是没告诉你绘制啥
30+
if (pixels == NULL && !(w == 0 && h == 0)) { return; } // 非法数据, 让你绘制图像, 但是没告诉你绘制啥
3131
uint32_t *fb = (uint32_t *)(uintptr_t)FB_ADDR; // 显存地址
3232
uint32_t screen_w = inl(VGACTL_ADDR) >> 16; // 屏幕的宽度
3333
volatile uint32_t *dst = fb + screen_w * y + x; // dst 指向显存

abstract-machine/elf-naming.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# ELF 命名规范
2+
3+
ELF (Executable and Linkable Format) 格式中的常量使用统一的命名前缀。
4+
5+
## 常用前缀
6+
7+
| 前缀 | 全称 | 含义 |
8+
|------|------|------|
9+
| `SHF_` | Section Header Flags | Section 的属性标志 |
10+
| `SHT_` | Section Header Type | Section 的类型 |
11+
| `PF_` | Program header Flags | Segment 的权限标志 |
12+
| `PT_` | Program header Type | Segment 的类型 |
13+
| `ET_` | ELF Type | ELF 文件类型 |
14+
| `EM_` | ELF Machine | 目标架构 |
15+
16+
## SHF_ (Section Header Flags)
17+
18+
| 标志 || 含义 |
19+
|------|---|------|
20+
| `SHF_WRITE` | 0x1 | 可写 |
21+
| `SHF_ALLOC` | 0x2 | 运行时需要分配内存 |
22+
| `SHF_EXECINSTR` | 0x4 | 包含可执行指令 |
23+
| `SHF_MERGE` | 0x10 | 可合并的数据 |
24+
| `SHF_STRINGS` | 0x20 | 包含字符串 |
25+
26+
## PF_ (Program Header Flags)
27+
28+
| 标志 || 含义 |
29+
|------|---|------|
30+
| `PF_X` | 0x1 | 可执行 |
31+
| `PF_W` | 0x2 | 可写 |
32+
| `PF_R` | 0x4 | 可读 |
33+
34+
## PT_ (Program Header Type)
35+
36+
| 类型 || 含义 |
37+
|------|---|------|
38+
| `PT_NULL` | 0 | 未使用 |
39+
| `PT_LOAD` | 1 | 可加载段 |
40+
| `PT_DYNAMIC` | 2 | 动态链接信息 |
41+
| `PT_INTERP` | 3 | 解释器路径 |
42+
| `PT_NOTE` | 4 | 辅助信息 |
43+
| `PT_PHDR` | 6 | Program Header 表自身 |
44+
45+
## ET_ (ELF Type)
46+
47+
| 类型 || 含义 |
48+
|------|---|------|
49+
| `ET_NONE` | 0 | 无类型 |
50+
| `ET_REL` | 1 | 可重定位文件 (.o) |
51+
| `ET_EXEC` | 2 | 可执行文件 |
52+
| `ET_DYN` | 3 | 共享目标文件 (.so) |
53+
| `ET_CORE` | 4 | Core 文件 |
54+
55+
## EM_ (ELF Machine)
56+
57+
| 类型 || 含义 |
58+
|------|---|------|
59+
| `EM_NONE` | 0 | 无机器类型 |
60+
| `EM_386` | 3 | Intel 80386 |
61+
| `EM_ARM` | 40 | ARM |
62+
| `EM_X86_64` | 62 | AMD x86-64 |
63+
| `EM_RISCV` | 243 | RISC-V |
64+
65+
## 参考
66+
67+
- 头文件: `/usr/include/elf.h`
68+
- ELF 规范: [System V ABI](https://refspecs.linuxfoundation.org/elf/elf.pdf)

abstract-machine/insert-mainarg.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
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)` 替换

abstract-machine/tools/insert-arg.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
from sys import argv
44

5+
#insert-arg: image
6+
# @python3 $(AM_HOME)/tools/insert-arg.py $(IMAGE).bin $(MAINARGS_MAX_LEN) $(MAINARGS_PLACEHOLDER) "$(mainargs)"
7+
# argv[1] = $(IMAGE).bin
8+
# argv[2] = $(MAINARGS_MAX_LEN)
9+
# argv[3] = $(MAINARGS_PLACEHOLDER)
10+
# argv[4] = "$(mainargs)"
11+
512
bin = argv[1]
613
max_len = int(argv[2])
714
placeholder = argv[3]
@@ -10,7 +17,7 @@
1017
if len(mainargs) >= max_len:
1118
print("Error: mainargs should not be longer than {0} bytes\n".format(max_len))
1219
exit(1)
13-
print("mainargs={0}".format(mainargs))
20+
print(f"mainargs={ format(mainargs) }")
1421

1522
fp = open(bin, 'r+b')
1623
data = fp.read()

am-kernels

nemu/src/device/vga.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ static uint32_t screen_size() {
3232
}
3333

3434
static void *vmem = NULL;
35+
// [0]: { screen_width, screen_height }
36+
// [1]: sync flag
3537
static uint32_t *vgactl_port_base = NULL;
3638

3739
#ifdef CONFIG_VGA_SHOW_SCREEN

0 commit comments

Comments
 (0)