Repository navigation
备忘:2026-09-04 生态批次之后仍未闭合的四条(armv7a 无 C 库/板级包、riscv32 无后端、ninja 空转未定因、armv7a 无 preemption) #558
Description
2026-09-04 那一轮生态推进(mcpp 2026.9.4.3、openarch 0.9.0、三个板级包、两个 C 库源码包)之后剩下的四条。都不是回归,都是没做完或没定因的;开一个汇总备忘,避免它们只活在设计文档里。
四条分属不同仓库,汇总在这里只是为了不丢;真正动手时应各自开 PR。
设计与实测记录:
.agents/docs/2026-09-04-four-gaps-after-the-ecosystem-batch.md(方案 + §7 实测回填).agents/docs/2026-09-04-named-runners-and-the-universal-command-surface.md§17
1. armv7a 没有 C 库路线,也没有板级包
归属:mcpplibs/picolibc + 一个新的板级包仓
已有的部分:
- mcpp 目标表有
armv7a-none-eabi/armv7a-none-eabihf两行,libdir是三元组 - openarch
0.9.0有 armv7a 后端(cpu / pte / context / trap 四组) - e2e
336_armv7a_builds_and_boots.sh与 openarch 的 armv7a job 每次 CI 都启动一个真实镜像并读退出码
缺的部分:
picolibc.picolibc的[target.'cfg(arch = ...)']块只有五个 M-profile 拼写(thumbv6m/thumbv7m/thumbv7em/thumbv8m.base/thumbv8m.main),没有armv7axim:picolibc-arm预编译载荷只有 7 个 thumb 目录,同样不覆盖- 没有 armv7a 的板级包(链接脚本、启动、控制台、runner)
⇒ 今天 armv7a 只能零 libc:能编、能启动、能退出,但 printf 用不了。
闭合它需要:picolibc 加一个 cfg(arch = "armv7a") 块(machine 目录与 thumb 共用 libc/machine/arm,主要是选文件与排除项),外加一个 armv7a-virt-rt 之类的板级包,目标机器是 qemu -M virt -cpu cortex-a15——那台机器 CI 已经在用了。
判据:一个 printf 程序在 armv7a 上打印并退 0,与 338_cortex_m_picolibc_sysroot.sh 同形。
2. riscv32 没有 openarch 后端;x86_64-none-elf 没有板级包
归属:mcpplibs/openarch;板级包另议
backend-auto 的 target 行覆盖 riscv64、aarch64、x86_64、五个 thumb 拼写、armv7a。riscv32 不在其中——riscv-virt-rt 自己是服务 rv32 的(CI 有 rv32 自测),但那上面用不了 openarch。
x86_64-none-elf 反过来:openarch 后端四组齐全,却没有板级包,所以没有「一行就能启动」的入口。
两条都不紧急,记下来是因为它们让「支持矩阵」有洞而矩阵本身看不出来。
3. ninja 空转,未定因
归属:mcpp(或上游 ninja)
签名已刻画且稳定:99.9% 占一个核、0 个子进程、系统时间为 0(纯用户态循环)、8 条边的图产物齐全无缺失输入。三次全新沙箱都复现,都发生在 mcpp build 之后那次 mcpp run。
不是图的问题:宿主上同一工程、同样两条 stage_file 边,2 秒完成;手工对同一目录跑同一个 ninja 二进制立刻退 0;事后在同一沙箱按同样顺序重跑两次都成功 ⇒ 非确定性。
取不到栈,三条路都堵死:
gdb -p被ptrace_scope=1拒(空转的 ninja 不是任何能 attach 的 shell 的后代)perf record -p被perf_event_paranoid=4拒- 把 gdb 变成 ninja 的父进程(yama level 1 下合法)之后,空转就不再发生——这一条最有信息量:触发条件与时序/环境有关,不是这份 manifest 的函数
嫌疑最窄的描述(推断,非测量):输出不变的 restat 边 → phony → 被当 order-only 依赖消费。这张图里唯一「不跑命令就完成」的边就是那条 phony,而 stage_file 是唯一一条命令经常被跳过却成功的规则。三段全是 mcpp 自己构造的,可改。
下次复现时的取证手段已经就位:MCPP_NINJA_DEBUG=explain 会给两处 ninja 启动追加 -d explain,空转时会反复打同一条边的判定理由。或者在开发机上放开 ptrace_scope=0,一次就能定。
已修的那一半(确定的):杀掉 mcpp 不再留下孤儿 ninja(进程组 / job object + SIGKILL,mcpp 2026.9.4.3),回归测试 340_no_orphan_survives_a_killed_mcpp.sh 对 2026.9.4.2 会红。
4. openarch:preemption 在 armv7a 上被扣住
归属:mcpplibs/openarch
arch_trap_switch 要求 trap 恢复到另一个上下文。riscv64 / aarch64 上恢复地址在寄存器里(mepc / ELR_EL1),dispatcher 可以在切换前后存取;Cortex-M 用 PendSV 专门做这件事。
ARMv7-A 上它由 srsdb 写在 SVC 栈上,所以 trap 中途换栈会改变 rfeia 弹出的帧——只有当被恢复的上下文也经同一路径挂起时才良定义。这是一个真实设计,但不是本后端测量过的设计,所以在 provides 里扣住该能力,让需要它的消费者在解析期按名字被拒,而不是链接到一个能编过却会错的实现。
这与 Cortex-M 扣住 openarch:address-space 是同一机制。
闭合它需要:把「进入 trap 的上下文必须经同一路径挂起」写成 backend 的契约,在两个上下文之间真的抢占一次并断言寄存器与返回地址,与 examples/preempt 同形。
不在此列(已闭合,写出来避免误读)
| 项 | 状态 |
|---|---|
mcpp run 折叠退出码 |
已修,三段契约写在 docs/11 §6(中英) |
| 孤儿 ninja | 已修,判据落在进程状态 |
| 板级包的模拟器分档 | 已修,并连同索引描述符的安装期边一起(只改包不改索引的话,分档什么都买不到) |
xim:ninja@1.12.1 无校验和 |
已补四平台 sha256 |
生态验证现状:全新沙箱 20/20 ECOSYSTEM OK,走已发布索引 + CN 镜像。
Activity