fix(kernel): fix riscv64 static-pie segfault in ELF loader#1033
Conversation
Add apply_relocations() to the ELF loader to process .rela.dyn and .rela.plt sections for static-pie (ET_DYN) binaries on riscv64. Supported relocation types: - R_RISCV_RELATIVE (type=3): base + addend - R_RISCV_JUMP_SLOT (type=5): symbol lookup from .dynsym This fixes the segfault when running riscv64 static-pie binaries (e.g., llama-cli compiled with -static-pie) where unresolved PLT entries caused a jump to 0x3b0 (PLT stub) instead of the actual function. Other architectures (x86_64, aarch64) provide a no-op stub.
Add handling for R_RISCV_64 (type=2) relocations in .rela.dyn section. This type appears in musl static-pie binaries for __cxa_finalize, _init, and _fini symbol references. R_RISCV_64 formula: S + A (symbol value + addend)
- Add vaddr_to_file_offset() to correctly convert virtual addresses to file offsets using PT_LOAD segments, replacing incorrect checked_sub(base) which failed for non-zero vaddr ranges - Add populate_area() call before apply_relocations() to ensure PT_LOAD segments are page-faulted in before relocation writes - Skip R_RISCV_64 and R_RISCV_JUMP_SLOT relocations when st_value==0 (undefined symbols should not overwrite GOT entries) - Handle R_RISCV_COPY relocations gracefully (count and skip) - Add static-pie-test app for regression testing Verified: - cargo xtask starry build --arch riscv64: PASS - cargo xtask starry app run -t static-pie-test --arch riscv64: PASS (STATIC_PIE_TEST_PASSED, RC=0, ET_DYN, no INTERP, 23 relocations) - cargo xtask starry test qemu --arch riscv64 -c busybox: PASS - cargo fmt --check: PASS - cargo clippy --package starry-kernel: PASS
7ef6d9c to
2a3a389
Compare
There was a problem hiding this comment.
Review 中文总结
PR 变更内容
本 PR 修复 riscv64 static-pie 二进制在 StarryOS 上的 segfault 问题。核心修改:
-
os/StarryOS/kernel/src/mm/loader.rs(+271 行):- 新增
vaddr_to_file_offset()函数,正确将虚拟地址转为文件偏移(搜索 PT_LOAD 段) - 新增
apply_relocations()函数,处理.rela.dyn和.rela.plt中的 R_RISCV_RELATIVE / R_RISCV_JUMP_SLOT / R_RISCV_64 / R_RISCV_COPY 重定位 - 在
map_elf()中对 PIE 二进制调用populate_area()确保页面映射后再写入重定位
- 新增
-
apps/starry/static-pie-test/(新增测试应用):prebuild.sh用 musl 工具链编译 C 程序static-pie-test.sh运行编译后的二进制并检查输出qemu-riscv64.toml配置 QEMU 运行参数和 success/fail 正则
-
apps/starry/llama-cpp/riscv64-static-pie.md:记录 llama.cpp 在 riscv64 上 segfault 的根因分析
实现逻辑分析
重定位处理的核心逻辑(ELF spec 各 relocation type 的语义):
R_RISCV_RELATIVE:*(B + offset) = B + addend— ✅ 代码正确R_RISCV_JUMP_SLOT:*(B + offset) = B + st_value— ✅ 代码正确R_RISCV_64:*(B + offset) = S + addend = (B + st_value) + addend— ❌ 代码缺少base,见下方 blocking issue #1
vaddr_to_file_offset() 通过遍历 PT_LOAD 段进行地址转换,逻辑正确。populate_area() 在写入重定位前强制分配物理页,方法合理。
验证结果
| 验证项 | 结果 |
|---|---|
cargo fmt --check |
✅ PASS |
cargo clippy --manifest-path os/StarryOS/kernel/Cargo.toml --all-features -- -D warnings |
❌ FAIL(dead_code 错误) |
cargo xtask starry app run -t static-pie-test --arch riscv64(QEMU 实际运行) |
❌ FAIL(/usr/bin/static-pie-test: not found,RC=127) |
CI 状态
Detect changed pathscheck:状态 queued,未完成。无其他 required checks。
重复/重叠分析
origin/dev分支上 loader.rs 无任何 relocation 处理或 static-pie 相关代码- 无其他 open PR 修改 loader.rs 或涉及 static-pie
- 本 PR 为独立的新功能修复,不与其他 PR 冲突
Blocking Issues
1. R_RISCV_64 重定位计算缺少 base(正确性 bug)
ELF spec 规定 R_RISCV_64 计算公式为 S + A,其中 S = 运行时符号地址 = base + st_value。代码只计算了 st_value + addend,遗漏了加载基地址 base。对比同文件 R_RISCV_JUMP_SLOT 的处理(base as u64 + st_value)可以验证 base 是必需的。
2. Clippy 错误:非 riscv64 存根函数死代码
调用 apply_relocations() 的代码在 #[cfg(target_arch = "riscv64")] 块内,导致 #[cfg(not(target_arch = "riscv64"))] 的存根函数永远不被调用,触发 -D dead-code 错误。修复方案:将调用移到 cfg 块外,或删除存根函数并仅保留 riscv64 实现。
3. QEMU 测试失败:测试二进制缺失(RC=127)
prebuild.sh 在工具链不存在时仅输出 warning 并跳过编译,但测试脚本仍尝试执行该二进制。实际 QEMU 运行结果:/usr/bin/static-pie-test: not found,RC=127。这违反了「测试不应在意外失败时静默跳过」的规则。prebuild.sh 应在工具链缺失时 exit 1 硬失败。
建议修复方向
- R_RISCV_64:
let value = (base as i64 + st_value as i64 + addend) as u64; - Clippy:移除
#[cfg(not(target_arch = "riscv64\))]存根,或将apply_relocations调用从 cfg 块中移出 - prebuild.sh:将
else分支的 warning 改为exit 1硬失败
Powered by mimo-v2.5-pro
Move #[cfg(target_arch = "riscv64")] from call site to populate_area block only, so apply_relocations() is called on all architectures. The non-riscv64 stub returns Ok(()) immediately. Fixes clippy -D warnings failure in CI.
There was a problem hiding this comment.
Review 总结
PR 变更内容
修复 riscv64 static-pie 二进制在 StarryOS ELF loader 中的 segfault 问题。核心修改:
-
os/StarryOS/kernel/src/mm/loader.rs(+272 行):- 新增
vaddr_to_file_offset()通过遍历 PT_LOAD 段正确将虚拟地址转为文件偏移 - 新增
apply_relocations()处理.rela.dyn(R_RISCV_RELATIVE)和.rela.plt(R_RISCV_JUMP_SLOT)重定位 - 在
map_elf()中对 PIE 二进制调用populate_area()确保页面映射后再写入重定位 - 最新提交已修复非 riscv64 架构的 dead_code 警告
- 新增
-
apps/starry/static-pie-test/(新增测试应用):prebuild.sh用 musl 工具链编译 C 程序static-pie-test.sh运行编译后的二进制并检查输出qemu-riscv64.toml配置 QEMU 运行参数
实现逻辑分析
vaddr_to_file_offset():遍历 PT_LOAD 段进行地址转换,逻辑正确。R_RISCV_RELATIVE:*(B + offset) = B + addend— ✅ 正确R_RISCV_JUMP_SLOT:*(B + offset) = B + st_value— ✅ 正确R_RISCV_64:*(B + offset) = S + A = (B + st_value) + addend— ❌ 缺少base,见 blocking issue #1populate_area()在写入重定位前强制分配物理页,方法合理。
验证结果
| 验证项 | 结果 |
|---|---|
cargo fmt --check |
✅ PASS |
cargo clippy --manifest-path os/StarryOS/kernel/Cargo.toml --all-features -- -D warnings |
✅ PASS(dead_code 已修复) |
QEMU 实际运行 cargo xtask starry app run -t static-pie-test --arch riscv64 |
✅ PASS(需创建工具链 symlink 后)输出 STATIC_PIE_TEST_PASSED,RC=0 |
| 测试二进制 relocations 分析 | 仅含 23 个 R_RISCV_RELATIVE,无 R_RISCV_64 或 R_RISCV_JUMP_SLOT |
QEMU 验证说明:测试二进制仅含 R_RISCV_RELATIVE 重定位,不覆盖 R_RISCV_64 路径。prebuild.sh 中工具链路径硬编码为 /root/project/toolchains/...,与实际环境不匹配(/opt/riscv64-linux-musl-cross/),需创建 symlink 后才能编译成功。
CI 状态
所有 CI checks 状态为 skipped,未实际运行。mergeable_state=blocked。
重复/重叠分析
origin/dev上 loader.rs 无任何 relocation 处理代码- 无其他 open PR 修改 loader.rs 或涉及 static-pie
- PR #1034(ArceOS epoll)不相关
- 本 PR 为独立的新功能修复
上一轮 Review 状态
- ✅ 已修复:Clippy dead_code 警告 —
apply_relocations()调用已移出#[cfg(target_arch = "riscv64")]块 - ❌ 未修复:R_RISCV_64 计算缺少
base(blocking #1) - ❌ 未修复:
prebuild.sh工具链缺失时静默跳过(blocking #2)
Blocking Issues
1. R_RISCV_64 重定位计算缺少 base(正确性 bug)
ELF spec 规定 R_RISCV_64 计算公式为 S + A,其中 S = 运行时符号地址 = base + st_value。代码只计算了 st_value + addend,遗漏了加载基地址 base。对比同文件 R_RISCV_JUMP_SLOT 的处理(base as u64 + st_value)可验证 base 是必需的。
2. prebuild.sh 工具链缺失时静默跳过导致测试失败
工具链不存在时仅输出 warning 并跳过编译,但测试脚本仍尝试执行二进制,导致 not found(RC=127)。应将 else 分支改为 exit 1 硬失败。此外工具链路径硬编码在 CI 环境中不可用,建议使用环境变量或更通用的路径查找方式。
Powered by mimo-v2.5-pro
…ror handling - R_RISCV_64: add base to symbol value (S + A where S = base + st_value) - prebuild.sh: exit 1 when toolchain is missing instead of silently skipping Addresses reviewer blocking issues.
There was a problem hiding this comment.
Review 总结(第三轮)
PR 变更内容
修复 riscv64 static-pie 二进制在 StarryOS ELF loader 中的 segfault 问题:
-
os/StarryOS/kernel/src/mm/loader.rs(+272 行):vaddr_to_file_offset()通过遍历 PT_LOAD 段正确将虚拟地址转为文件偏移apply_relocations()处理.rela.dyn(R_RISCV_RELATIVE、R_RISCV_64)和.rela.plt(R_RISCV_JUMP_SLOT)重定位map_elf()中对 PIE 二进制调用populate_area()确保页面映射- 非 riscv64 架构使用 no-op 存根,无 dead_code 警告
-
apps/starry/static-pie-test/(新增测试应用):prebuild.sh用 musl 工具链编译 C 程序,工具链缺失时exit 1硬失败static-pie-test.sh运行二进制并检查输出qemu-riscv64.toml配置 QEMU 运行参数和 success/fail 正则
-
apps/starry/llama-cpp/riscv64-static-pie.md:根因分析文档
实现逻辑分析
重定位处理逻辑正确,符合 ELF spec:
R_RISCV_RELATIVE:*(B + offset) = B + addend✅R_RISCV_64:*(B + offset) = B + st_value + addend✅(已修复,包含base)R_RISCV_JUMP_SLOT:*(B + offset) = B + st_value✅R_RISCV_COPY:计数并跳过 ✅
vaddr_to_file_offset() 逻辑正确,通过遍历 PT_LOAD 段查找包含目标地址的段并计算文件偏移。
上一轮 Blocking Issues 状态
| 问题 | 状态 | 说明 |
|---|---|---|
R_RISCV_64 缺少 base |
✅ 已修复 | let value = (base as i64 + st_value as i64 + addend) as u64 |
| Clippy dead_code 警告 | ✅ 已修复 | apply_relocations() 调用移出 #[cfg] 块 |
| prebuild.sh 静默跳过 | ✅ 已修复 | else 分支改为 exit 1 |
验证结果
| 验证项 | 结果 |
|---|---|
cargo fmt --check |
✅ PASS |
cargo clippy --manifest-path os/StarryOS/kernel/Cargo.toml --all-features -- -D warnings |
✅ PASS |
QEMU 运行 cargo xtask starry app run -t static-pie-test --arch riscv64 |
✅ PASS(STATIC_PIE_TEST_PASSED,RC=0) |
CI 状态
CI checks 状态为 skipped(未实际运行),mergeable_state=blocked。基于本地验证结果。
重复/重叠分析
origin/dev上 loader.rs 无任何 relocation 处理代码- 无其他 open PR 修改 loader.rs 或涉及 static-pie
- PR #1034(ArceOS epoll)完全不相关
- 本 PR 为独立的新功能修复
已知限制(非阻塞)
- 测试覆盖范围:
static-pie-test测试二进制仅含 R_RISCV_RELATIVE 重定位(23 个),未覆盖 R_RISCV_64 和 R_RISCV_JUMP_SLOT 代码路径。这些路径通过代码审查验证正确性,建议后续补充包含__cxa_finalize等符号的测试二进制以实现端到端覆盖。 - 工具链路径:
prebuild.sh中工具链路径硬编码为/root/project/toolchains/...,在 CI 环境中需确保路径可用或改用环境变量。
以上限制不影响核心修复的正确性,可在后续 PR 中改进。
Powered by mimo-v2.5-pro
There was a problem hiding this comment.
Review 总结(第四轮)
PR 变更内容
修复 riscv64 static-pie 二进制在 StarryOS ELF loader 中的 segfault 问题。核心修改:
-
os/StarryOS/kernel/src/mm/loader.rs(+272 行):vaddr_to_file_offset()通过遍历 PT_LOAD 段正确将虚拟地址转为文件偏移apply_relocations()处理.rela.dyn(R_RISCV_RELATIVE、R_RISCV_64)和.rela.plt(R_RISCV_JUMP_SLOT)重定位map_elf()中对 PIE 二进制调用populate_area()确保页面映射- 非 riscv64 架构使用 no-op 存根,无 dead_code 警告
-
apps/starry/static-pie-test/(新增测试应用):prebuild.sh用 musl 工具链编译 C 程序,工具链缺失时exit 1硬失败static-pie-test.sh运行二进制并检查输出qemu-riscv64.toml配置 QEMU 运行参数和 success/fail 正则
-
apps/starry/llama-cpp/riscv64-static-pie.md:根因分析文档
实现逻辑分析
重定位处理逻辑正确,符合 ELF spec:
R_RISCV_RELATIVE:*(B + offset) = B + addend✅R_RISCV_64:*(B + offset) = B + st_value + addend✅(已修复,包含base)R_RISCV_JUMP_SLOT:*(B + offset) = B + st_value✅R_RISCV_COPY:计数并跳过 ✅
vaddr_to_file_offset() 逻辑正确,通过遍历 PT_LOAD 段查找包含目标地址的段并计算文件偏移。populate_area() 在写入重定位前强制分配物理页,方法合理。
前几轮 Blocking Issues 状态
| 问题 | 状态 | 说明 |
|---|---|---|
R_RISCV_64 缺少 base |
✅ 已修复 | let value = (base as i64 + st_value as i64 + addend) as u64 |
| Clippy dead_code 警告 | ✅ 已修复 | apply_relocations() 调用移出 #[cfg] 块 |
| prebuild.sh 静默跳过 | ✅ 已修复 | else 分支改为 exit 1 |
验证结果
| 验证项 | 结果 |
|---|---|
cargo fmt --check |
✅ PASS |
cargo clippy --manifest-path os/StarryOS/kernel/Cargo.toml --all-features -- -D warnings |
✅ PASS |
QEMU 运行 cargo xtask starry app run -t static-pie-test --arch riscv64 |
✅ PASS(STATIC_PIE_TEST_PASSED,RC=0) |
CI 状态
CI checks 状态全部为 skipped(未实际运行),mergeable_state=clean。基于本地验证结果,CI 跳过不影响本 PR 的代码正确性评估。
重复/重叠分析
origin/dev上 loader.rs 无任何 relocation 处理代码- 无其他 open PR 修改 loader.rs 或涉及 static-pie
- 本 PR 为独立的新功能修复
已知限制(非阻塞)
- 测试覆盖范围:
static-pie-test测试二进制仅含 R_RISCV_RELATIVE 重定位(23 个),未覆盖 R_RISCV_64 和 R_RISCV_JUMP_SLOT 代码路径。这些路径通过代码审查验证正确性,建议后续补充包含__cxa_finalize等符号的测试二进制以实现端到端覆盖。 - 工具链路径:
prebuild.sh中工具链路径硬编码为/root/project/toolchains/...,在不同 CI 环境中需确保路径可用或改用环境变量。
以上限制不影响核心修复的正确性,可在后续 PR 中改进。
Powered by mimo-v2.5-pro
|
@SongShiQ You need to config your local git username and email. Currently it's @ZR233 Perhaps you could add this check to your bot's prompts. It's important to check not only the correctness and quality of the code, but also the Git collaboration guidelines. Students new to community projects might be unfamiliar with these, and we need to guide them. |
概述
修复 riscv64 static-pie 二进制文件在 StarryOS 上启动时的 segfault 问题。
根本原因是 ELF loader 中的
apply_relocations()函数使用checked_sub(base)计算文件偏移,这对于 PIE 二进制中虚拟地址从 0 开始的情况是错误的。同时,relocation 写入前未调用populate_area()导致页面未映射。修复内容
1. 添加
vaddr_to_file_offset()函数segment_offset + (vaddr - segment_vaddr)(addr as usize).checked_sub(base)逻辑.rela.dyn、.rela.plt和.dynsym的偏移计算2. 添加
populate_area()调用apply_relocations()之前,对所有 PT_LOAD 段调用populate_area()3. 改进 relocation 处理
R_RISCV_64/R_RISCV_JUMP_SLOT:跳过st_value==0的符号(未定义符号不应覆写 GOT)R_RISCV_COPY:计数并跳过(不需要内核处理)in_load_mem()函数4. 添加 static-pie-test 回归测试
apps/starry/static-pie-test/:完整的测试应用配置-static选项)STATIC_PIE_TEST_PASSED,RC=0,无 segfault/panic/EFAULT验证结果
| 测试 | 结果 |
|------|------|
|
cargo xtask starry build --arch riscv64| ✅ PASS ||
cargo xtask starry app run -t static-pie-test --arch riscv64| ✅ PASS (STATIC_PIE_TEST_PASSED, RC=0) ||
cargo xtask starry test qemu --arch riscv64 -c busybox| ✅ PASS ||
cargo fmt --check| ✅ PASS ||
cargo clippy --package starry-kernel| ✅ PASS |测试二进制验证:
DYN (Position-Independent Executable file)技术细节
问题根因
PIE 二进制的程序头中
virtual_addr从 0 开始:原始代码使用
(rela_addr as usize).checked_sub(base)计算文件偏移,但base是加载地址(如USER_SPACE_BASE),不是虚拟地址的起始位置,导致计算错误。修复方案
vaddr_to_file_offset()函数正确处理这个情况:已知问题
#[cfg(target_arch = "riscv64")])修改文件
os/StarryOS/kernel/src/mm/loader.rs- 核心修复apps/starry/static-pie-test/- 回归测试应用(新增)