Skip to content

Commit 1f2d947

Browse files
committed
feat: 实现内核态ELF加载器支持
1 parent fb35055 commit 1f2d947

File tree

11 files changed

+1679
-49
lines changed

11 files changed

+1679
-49
lines changed

docs/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
- [进程调度](architecture/multitasking/scheduler.md)
2626
- [进程与线程模型](architecture/multitasking/process.md)
2727
- [系统调用接口设计](architecture/multitasking/syscalls.md)
28+
- [ELF 加载器](architecture/multitasking/elf-loader.md)
2829
- [安全](architecture/security.md)
2930
- [异常处理](architecture/panic.md)
3031

docs/architecture/memory.md

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
# 内存管理架构
2+
3+
Proka Kernel 采用分层内存管理架构,支持内核空间和用户空间的内存隔离与管理。
4+
5+
## 架构概览
6+
7+
```
8+
┌─────────────────────────────────────────────────────────────┐
9+
│ 用户空间 (User Space) │
10+
│ 0x0000_0000_0000 - 0x0000_7FFF_FFFF_FFFF (128TB) │
11+
│ ┌─────────────────────────────────────────────────────┐ │
12+
│ │ 栈 (Stack) - 向下增长 │ │
13+
│ │ mmap 区域 (Memory Mappings) │ │
14+
│ │ 堆 (Heap) - 向上增长 │ │
15+
│ │ 程序段 (Text/Data/BSS) │ │
16+
│ └─────────────────────────────────────────────────────┘ │
17+
├─────────────────────────────────────────────────────────────┤
18+
│ 内核空间 (Kernel Space) │
19+
│ 0xFFFF_8000_0000_0000 - 0xFFFF_FFFF_FFFF_FFFF (128TB) │
20+
│ ┌─────────────────────────────────────────────────────┐ │
21+
│ │ HHDM (Higher Half Direct Mapping) │ │
22+
│ │ 内核堆 (Kernel Heap) │ │
23+
│ │ 内核代码/数据 (Kernel Text/Data/BSS) │ │
24+
│ │ ELF 加载区域 (ELF Loading Region) │ │
25+
│ └─────────────────────────────────────────────────────┘ │
26+
└─────────────────────────────────────────────────────────────┘
27+
```
28+
29+
## 物理内存管理
30+
31+
### 帧分配器 (Frame Allocator)
32+
33+
使用 Buddy System 算法管理物理内存帧:
34+
35+
- **位置**: `kernel/src/memory/frame/buddy.rs`
36+
- **特点**: 支持连续物理内存分配,高效处理内存碎片
37+
- **接口**: `allocate_frame()`, `deallocate_frame()`
38+
39+
```rust
40+
// 获取全局帧分配器
41+
let mut frame_allocator = FRAME_ALLOCATOR;
42+
43+
// 分配单个帧
44+
let frame = frame_allocator.allocate_frame()?;
45+
46+
// 释放帧
47+
frame_allocator.deallocate_frame(frame);
48+
```
49+
50+
## 虚拟内存管理
51+
52+
### VmArea 抽象
53+
54+
VmArea (Virtual Memory Area) 表示一段连续的虚拟内存区域:
55+
56+
```rust
57+
pub struct VmArea {
58+
pub start: VirtAddr, // 起始地址
59+
pub end: VirtAddr, // 结束地址(不包含)
60+
pub flags: PageTableFlags, // 页表权限标志
61+
pub area_type: VmAreaType, // 区域类型
62+
}
63+
64+
pub enum VmAreaType {
65+
Text, // 代码段
66+
Rodata, // 只读数据
67+
Data, // 数据段
68+
Heap, //
69+
Stack, //
70+
Mmap, // 内存映射
71+
KernelText, KernelRodata, KernelData, KernelBss, KernelHeap,
72+
}
73+
```
74+
75+
### MemorySet
76+
77+
MemorySet 管理一个地址空间的所有 VMA 和页表:
78+
79+
```rust
80+
pub struct MemorySet {
81+
areas: Vec<VmArea>,
82+
page_table: OffsetPageTable<'static>,
83+
}
84+
```
85+
86+
## 用户空间内存管理
87+
88+
### 用户空间内存布局
89+
90+
```
91+
地址范围 用途
92+
────────────────────────────────────────────────
93+
0x0000_0000_0000 - 0x0000_000F_FFFF Null guard (1MB)
94+
0x0000_0010_0000 - ... 程序代码/数据
95+
0x0000_1000_0000 - 0x0000_7F9F_FFFF 用户堆 (向上增长)
96+
0x0000_7FA0_0000 - 0x0000_7FFF_FFFF mmap 区域 (256MB)
97+
0x0000_7FC0_0000 - 0x0000_8000_0000 用户栈 (向下增长, 4MB)
98+
```
99+
100+
### 创建用户地址空间
101+
102+
```rust
103+
// 创建新的用户进程地址空间
104+
let memory_set = MemorySet::new_user(&mut frame_allocator)?;
105+
```
106+
107+
### 内存映射 API
108+
109+
#### mmap_anon - 匿名内存映射
110+
111+
```rust
112+
/// 在指定地址映射匿名内存
113+
pub fn mmap_anon(
114+
&mut self,
115+
addr: Option<VirtAddr>, // 首选地址(None 表示自动选择)
116+
size: usize, // 映射大小
117+
flags: PageTableFlags, // 权限标志
118+
) -> Result<VirtAddr, MemoryError>
119+
```
120+
121+
#### munmap - 取消映射
122+
123+
```rust
124+
/// 取消内存映射并释放物理帧
125+
pub fn munmap(
126+
&mut self,
127+
addr: VirtAddr,
128+
size: usize,
129+
) -> Result<(), MemoryError>
130+
```
131+
132+
### 堆管理
133+
134+
```rust
135+
/// 扩展用户堆
136+
pub fn expand_user_heap(&mut self, new_end: VirtAddr) -> Result<(), MemoryError>
137+
138+
/// 收缩用户堆
139+
pub fn shrink_user_heap(&mut self, new_end: VirtAddr) -> Result<(), MemoryError>
140+
141+
/// 获取当前堆边界
142+
pub fn heap_break(&self) -> VirtAddr
143+
```
144+
145+
## 内核 ELF 加载区域
146+
147+
为支持内核态动态库加载,预留了专用的 ELF 加载区域:
148+
149+
```
150+
起始地址: 0xFFFF_FFFF_8100_0000
151+
大小: 1GB
152+
分配方式: Bump 分配器
153+
```
154+
155+
## 页表管理
156+
157+
### 页表标志
158+
159+
常用标志组合:
160+
161+
| 用途 | 标志 |
162+
|------|------|
163+
| 内核代码 | `PRESENT` |
164+
| 内核数据 | `PRESENT \| WRITABLE \| NO_EXECUTE` |
165+
| 用户代码 | `PRESENT \| USER_ACCESSIBLE` |
166+
| 用户数据 | `PRESENT \| USER_ACCESSIBLE \| WRITABLE \| NO_EXECUTE` |
167+
168+
### TLB 刷新
169+
170+
修改页表后需要刷新 TLB
171+
172+
```rust
173+
x86_64::instructions::tlb::flush_all();
174+
```
175+
176+
## 错误处理
177+
178+
```rust
179+
pub enum MemoryError {
180+
AreaOverlap, // 区域重叠
181+
AreaNotFound, // 区域未找到
182+
FrameAllocationFailed, // 帧分配失败
183+
MappingFailed, // 映射失败
184+
}
185+
```
186+
187+
## 相关文件
188+
189+
| 文件 | 功能 |
190+
|------|------|
191+
| `memory/frame/mod.rs` | 帧分配器接口 |
192+
| `memory/frame/buddy.rs` | Buddy System 实现 |
193+
| `memory/paging/mod.rs` | 分页支持 |
194+
| `memory/paging/vmm.rs` | 虚拟内存管理 |
195+
| `memory/heap.rs` | 内核堆管理 |
196+
| `memory/error.rs` | 内存错误类型 |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# EFI加载器
Lines changed: 173 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,181 @@
11
# ELF 加载器
22

3-
为了运行用户态程序,内核需要能够解析并加载 ELF64 格式的可执行文件。
3+
为了运行用户态程序和加载内核模块,内核需要能够解析并加载 ELF64 格式的可执行文件和共享库。
4+
5+
## 概述
6+
7+
Proka Kernel 使用 `elf_loader` crate 实现 ELF 加载功能,并通过自定义的 `KernelMmap` trait 实现内核态内存映射。
48

59
## 处理步骤
610

7-
1. **头部解析**:验证魔数 (0x7F 'E' 'L' 'F') 和架构信息。
8-
2. **段映射 (Segments)**:遍历程序头表 (Program Header Table),将 `PT_LOAD` 段映射到进程的虚拟地址空间。
9-
3. **栈分配**:为用户态初始化栈空间。
10-
4. **跳转入场**:设置 CPU 寄存器并跳转到 Entry Point。
11+
1. **头部解析**:验证魔数 (`0x7F 'E' 'L' 'F'`) 和架构信息。
12+
2. **段映射 (Segments)**:遍历程序头表 (Program Header Table),将 `PT_LOAD` 段映射到地址空间。
13+
3. **重定位**:处理重定位表,修正符号地址。
14+
4. **符号解析**:解析动态符号表,查找符号地址。
15+
16+
## KernelMmap 实现
17+
18+
`KernelMmap` 实现了 `elf_loader::os::Mmap` trait,提供内核态内存映射支持:
19+
20+
### 内存分配区域
21+
22+
ELF 加载使用内核空间专用区域:
23+
24+
```
25+
起始地址: 0xFFFF_FFFF_8100_0000
26+
大小: 1GB
27+
分配方式: Bump 分配器
28+
```
29+
30+
### 接口实现
31+
32+
```rust
33+
impl Mmap for KernelMmap {
34+
/// 通用内存映射
35+
unsafe fn mmap(
36+
addr: Option<usize>,
37+
len: usize,
38+
prot: ProtFlags,
39+
flags: MapFlags,
40+
offset: usize,
41+
fd: Option<isize>,
42+
need_copy: &mut bool,
43+
) -> Result<*mut c_void>;
44+
45+
/// 匿名内存映射
46+
unsafe fn mmap_anonymous(
47+
addr: usize,
48+
len: usize,
49+
prot: ProtFlags,
50+
flags: MapFlags,
51+
) -> Result<*mut c_void>;
52+
53+
/// 取消映射
54+
unsafe fn munmap(addr: *mut c_void, len: usize) -> Result<()>;
55+
56+
/// 修改内存保护属性
57+
unsafe fn mprotect(
58+
addr: *mut c_void,
59+
len: usize,
60+
prot: ProtFlags,
61+
) -> Result<()>;
62+
63+
/// 预留地址空间
64+
unsafe fn mmap_reserve(
65+
addr: Option<usize>,
66+
len: usize,
67+
use_file: bool,
68+
) -> Result<*mut c_void>;
69+
}
70+
```
71+
72+
### 权限标志转换
73+
74+
```rust
75+
fn prot_to_flags(prot: ProtFlags) -> PageTableFlags {
76+
let mut flags = PageTableFlags::PRESENT;
77+
78+
if prot.contains(ProtFlags::PROT_WRITE) {
79+
flags |= PageTableFlags::WRITABLE;
80+
}
81+
if !prot.contains(ProtFlags::PROT_EXEC) {
82+
flags |= PageTableFlags::NO_EXECUTE;
83+
}
84+
85+
flags
86+
}
87+
```
88+
89+
## 使用示例
90+
91+
### 加载共享库
92+
93+
```rust
94+
use crate::libs::elf::{load_elf, test_load_elf};
95+
96+
// 加载动态库
97+
let lib = load_elf("mylib.so")?;
98+
99+
// 获取符号
100+
let add_func = unsafe { lib.get::<fn(i32, i32) -> i32>("add")? };
101+
102+
// 调用函数
103+
let result = add_func(1, 2); // 返回 3
104+
```
105+
106+
### 编译共享库
107+
108+
使用 GCC 编译适用于内核加载的共享库:
109+
110+
```bash
111+
# 最小化编译(不链接 libc)
112+
gcc -shared -fPIC -nostdlib -o mylib.so mylib.c
113+
114+
# 示例源码
115+
cat > mylib.c << 'EOF'
116+
int add(int a, int b) {
117+
return a + b;
118+
}
119+
EOF
120+
```
121+
122+
## 加载流程详解
123+
124+
### 1. 文件读取
125+
126+
```rust
127+
let f = VFS.open(path)?;
128+
let file_data = f.read_all()?;
129+
let data = file_data.as_slice();
130+
```
131+
132+
### 2. 创建加载器
133+
134+
```rust
135+
let mut loader = Loader::new().with_mmap::<KernelMmap>();
136+
```
137+
138+
### 3. 加载和重定位
139+
140+
```rust
141+
let lib = loader
142+
.load_dylib(ElfBinary::new(path, data))?
143+
.relocator()
144+
.relocate()?;
145+
```
146+
147+
## 内核内存集访问
148+
149+
ELF 加载器需要访问内核页表进行映射。由于进程管理器初始化时会获取内核内存集,加载器通过以下方式访问:
150+
151+
```rust
152+
// 优先通过进程管理器获取内核进程的内存集
153+
if let Some(pcb) = process::process::lock().get_process(0) {
154+
let pcb_lock = pcb.lock();
155+
let mut ms = pcb_lock.memory_set.lock();
156+
// 使用页表进行映射...
157+
}
158+
```
159+
160+
## 注意事项
161+
162+
1. **地址空间**:确保使用内核空间的 canonical 地址(`0xFFFF_XXXX_XXXX_XXXX`
163+
2. **TLB 刷新**:映射完成后必须刷新 TLB
164+
3. **内存对齐**:所有映射地址必须页对齐(4KB)
165+
4. **错误处理**:正确处理内存分配失败的情况
166+
167+
## 相关文件
168+
169+
| 文件 | 功能 |
170+
|------|------|
171+
| `libs/elf.rs` | ELF 加载器和 KernelMmap 实现 |
172+
| `memory/paging/vmm.rs` | 虚拟内存管理 |
173+
| `fs/vfs/mod.rs` | 文件系统接口 |
11174

12175
## 待完善内容
13176

14-
- [ ] 动态链接器 (Interpreters) 的基础支持。
15-
- [ ] 辅助向量 (Auxiliary Vector) 的传递。
177+
- [ ] 文件映射支持(非匿名映射)
178+
- [ ] 动态链接器 (Interpreters) 支持
179+
- [ ] 辅助向量 (Auxiliary Vector) 传递
180+
- [ ] 符号版本控制
181+

0 commit comments

Comments
 (0)