Skip to content

fix(kernel): fix riscv64 static-pie segfault in ELF loader#1033

Merged
ZR233 merged 7 commits into
rcore-os:devfrom
SongShiQ:fix/riscv64-static-pie-segfault
Jun 1, 2026
Merged

fix(kernel): fix riscv64 static-pie segfault in ELF loader#1033
ZR233 merged 7 commits into
rcore-os:devfrom
SongShiQ:fix/riscv64-static-pie-segfault

Conversation

@SongShiQ

@SongShiQ SongShiQ commented May 29, 2026

Copy link
Copy Markdown
Contributor

概述

修复 riscv64 static-pie 二进制文件在 StarryOS 上启动时的 segfault 问题。

根本原因是 ELF loader 中的 apply_relocations() 函数使用 checked_sub(base) 计算文件偏移,这对于 PIE 二进制中虚拟地址从 0 开始的情况是错误的。同时,relocation 写入前未调用 populate_area() 导致页面未映射。

修复内容

1. 添加 vaddr_to_file_offset() 函数

  • 正确地将虚拟地址转换为文件偏移:搜索 PT_LOAD 段,计算 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()
  • 确保 relocation 写入时页面已映射,避免 page fault

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/:完整的测试应用配置
  • 使用 musl 工具链编译真正的 ET_DYN static-pie 二进制(-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 |

测试二进制验证

  • ELF Type: DYN (Position-Independent Executable file)
  • No INTERP segment(不依赖动态链接器)
  • No NEEDED entries(不依赖外部共享库)
  • 23 个 R_RISCV relocation entries

技术细节

问题根因

PIE 二进制的程序头中 virtual_addr 从 0 开始:


PT_LOAD: offset=0x0, vaddr=0x0, filesz=1608

PT_LOAD: offset=0xE30, vaddr=0x1E30, filesz=584

原始代码使用 (rela_addr as usize).checked_sub(base) 计算文件偏移,但 base 是加载地址(如 USER_SPACE_BASE),不是虚拟地址的起始位置,导致计算错误。

修复方案

vaddr_to_file_offset() 函数正确处理这个情况:

fn vaddr_to_file_offset(vaddr: u64, ph: &[ProgramHeader64]) -> Option<usize> {

    for seg in ph {

        if seg.get_type() != Ok(Type::Load) { continue; }

        let seg_vaddr = seg.virtual_addr as usize;

        let seg_filesz = seg.file_size as usize;

        if vaddr >= seg_vaddr && vaddr < seg_vaddr + seg_filesz {

            return Some(seg.offset as usize + (vaddr - seg_vaddr));

        }

    }

    None

}

已知问题

  • 本次修复仅针对 riscv64 架构(#[cfg(target_arch = "riscv64")]
  • 其他架构(x86_64、aarch64、loongarch64)的 static-pie 支持未涉及

修改文件

  • os/StarryOS/kernel/src/mm/loader.rs - 核心修复
  • apps/starry/static-pie-test/ - 回归测试应用(新增)

root added 4 commits May 29, 2026 18:30
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
@SongShiQ
SongShiQ force-pushed the fix/riscv64-static-pie-segfault branch from 7ef6d9c to 2a3a389 Compare May 29, 2026 11:04

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review 中文总结

PR 变更内容

本 PR 修复 riscv64 static-pie 二进制在 StarryOS 上的 segfault 问题。核心修改:

  1. 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() 确保页面映射后再写入重定位
  2. apps/starry/static-pie-test/(新增测试应用):

    • prebuild.sh 用 musl 工具链编译 C 程序
    • static-pie-test.sh 运行编译后的二进制并检查输出
    • qemu-riscv64.toml 配置 QEMU 运行参数和 success/fail 正则
  3. 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 paths check:状态 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 硬失败。

建议修复方向

  1. R_RISCV_64:let value = (base as i64 + st_value as i64 + addend) as u64;
  2. Clippy:移除 #[cfg(not(target_arch = "riscv64\))] 存根,或将 apply_relocations 调用从 cfg 块中移出
  3. prebuild.sh:将 else 分支的 warning 改为 exit 1 硬失败

Powered by mimo-v2.5-pro

Comment thread os/StarryOS/kernel/src/mm/loader.rs Outdated
Comment thread os/StarryOS/kernel/src/mm/loader.rs
Comment thread apps/starry/static-pie-test/prebuild.sh Outdated
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.

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review 总结

PR 变更内容

修复 riscv64 static-pie 二进制在 StarryOS ELF loader 中的 segfault 问题。核心修改:

  1. 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 警告
  2. 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 #1
  • populate_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

Comment thread os/StarryOS/kernel/src/mm/loader.rs Outdated
Comment thread apps/starry/static-pie-test/prebuild.sh Outdated
…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.

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review 总结(第三轮)

PR 变更内容

修复 riscv64 static-pie 二进制在 StarryOS ELF loader 中的 segfault 问题:

  1. 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 警告
  2. apps/starry/static-pie-test/(新增测试应用):

    • prebuild.sh 用 musl 工具链编译 C 程序,工具链缺失时 exit 1 硬失败
    • static-pie-test.sh 运行二进制并检查输出
    • qemu-riscv64.toml 配置 QEMU 运行参数和 success/fail 正则
  3. 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 为独立的新功能修复

已知限制(非阻塞)

  1. 测试覆盖范围static-pie-test 测试二进制仅含 R_RISCV_RELATIVE 重定位(23 个),未覆盖 R_RISCV_64 和 R_RISCV_JUMP_SLOT 代码路径。这些路径通过代码审查验证正确性,建议后续补充包含 __cxa_finalize 等符号的测试二进制以实现端到端覆盖。
  2. 工具链路径prebuild.sh 中工具链路径硬编码为 /root/project/toolchains/...,在 CI 环境中需确保路径可用或改用环境变量。

以上限制不影响核心修复的正确性,可在后续 PR 中改进。

Powered by mimo-v2.5-pro

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review 总结(第四轮)

PR 变更内容

修复 riscv64 static-pie 二进制在 StarryOS ELF loader 中的 segfault 问题。核心修改:

  1. 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 警告
  2. apps/starry/static-pie-test/(新增测试应用):

    • prebuild.sh 用 musl 工具链编译 C 程序,工具链缺失时 exit 1 硬失败
    • static-pie-test.sh 运行二进制并检查输出
    • qemu-riscv64.toml 配置 QEMU 运行参数和 success/fail 正则
  3. 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 为独立的新功能修复

已知限制(非阻塞)

  1. 测试覆盖范围static-pie-test 测试二进制仅含 R_RISCV_RELATIVE 重定位(23 个),未覆盖 R_RISCV_64 和 R_RISCV_JUMP_SLOT 代码路径。这些路径通过代码审查验证正确性,建议后续补充包含 __cxa_finalize 等符号的测试二进制以实现端到端覆盖。
  2. 工具链路径prebuild.sh 中工具链路径硬编码为 /root/project/toolchains/...,在不同 CI 环境中需确保路径可用或改用环境变量。

以上限制不影响核心修复的正确性,可在后续 PR 中改进。

Powered by mimo-v2.5-pro

@ZR233
ZR233 merged commit 78cf4cc into rcore-os:dev Jun 1, 2026
51 checks passed
@AsakuraMizu

Copy link
Copy Markdown
Collaborator

@SongShiQ You need to config your local git username and email. Currently it's root <root@DESKTOP-GK445T5>, which is not associated with your GitHub account. Please refer to https://docs.github.com/en/get-started/git-basics/setting-your-username-in-git for help.

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants