Skip to content

Build-time physics check for form-X binaries (rules A/B): fail at link time instead of crashing at run time #396

Description

@Sunrisepeak

Context

xlings' closure design (openxlings/xlings .agents/docs/2026-08-09-ecosystem-closure-design.md) defines three enforcement points for the runtime-closure contract. Points 1 (index CI) and 2 (xlings client, install-time, warn-only) landed on 2026-08-09. Point 3 is mcpp's, because mcpp's default toolchain config is what puts user binaries into form X:

llvm@22.x clang.cfg:
  -Wl,--dynamic-linker=<xpkgs>/xim-x-glibc/<ver>/lib64/ld-linux-x86-64.so.2
  -Wl,-rpath,<xpkgs>/xim-x-glibc/<ver>/lib64

A form-X binary (our loader + our libc) has no host fallback: our ld.so's compiled-in cache path exists on no machine, so any host library entering the process resolves only by RPATH — or fails at run time with an error that names neither the package nor the cause. #392 is this exact shape: host libtinfo.so.6 entered via the GL/GLX chain, our glibc 2.39 could not serve GLIBC_2.42, and the user saw a symbol-version error pointing at a library they never chose.

What point 3 should check (physics only, NO policy)

At link time, for the binary just produced:

  • Rule B (same-source): the PT_INTERP the driver injected and the libc.so.6 that RPATH will resolve must come from one glibc payload. Mixing (host interp + our libc, or two payload versions) is a pre-main crash with a GLIBC_PRIVATE / vDSO message.
  • Rule A (version floor): if the link pulled in ANY host library (-l resolved outside the xlings store), then our_glibc >= host_glibc must hold — glibc is backward-compatible only.

Deliberately not checked: whether the user links host libraries at all. That is the user's choice (xlings rule D applies to index packages only). Point 3 can therefore fail hard from day one — everything it rejects is a binary that cannot run.

Suggested failure messages (from the design doc, C2.4)

rule B: PT_INTERP comes from glibc@2.39 but RPATH resolves libc from glibc@2.44 —
        one process cannot have two libcs.
rule A: this binary is anchored to glibc 2.39 while the host is 2.43 and the link
        pulled in host libtinfo.so.6 — symbols up to GLIBC_2.43 may be required.
        Fix: use a runtime >= the host (xlings subos new <name> --runtime glibc@2.44).

Mitigations already landed on the xlings side (2026.8.9.1)

These remove the common triggers, but they do not remove the class: any host library newer than the chosen runtime reproduces it, and only the build tool sees the link set early enough to say so.

Activity

  1. Sunrisepeak commented on Aug 9, 2026

    @Sunrisepeak
    MemberAuthor

    Draft implementation checkpoint: PR #400 adds exact glibc payload lookup plus Rule A/Rule B validation, sticky post-link verdicts, and mcpp doctor reuse of the stored result. Focused runtime-physics tests and E2E #206 pass locally. The issue remains open until review, normal merge, release, and fresh released-artifact verification complete.

  2. speak-agent commented on Aug 9, 2026

    @speak-agent
    Member

    已在 2026.8.10.1 发布(PR #400,合入 commit 0006ce6)。

    链接后校验按 Rule A / Rule B 执行,用的是内置 ELF64-LE 读取器(解析 PT_INTERP、
    DT_RPATH/RUNPATH、DT_NEEDED、GNU verneed/verdef),不 shell out 探测:

    • Rule A —— 宿主 DSO 的 GLIBC_* 需求不得高于选中 libc 的 export floor。
    • Rule B —— 最终解释器、直接及传递 libc 必须与同一个 RuntimeBinding 一致;
      拒绝 host/private 混用,也拒绝两个 private libc 混用。

    verdict 与 artifact stat + RuntimeBinding contract hash 一起缓存。缓存的失败仍然是失败:
    validated_artifact_snapshot 只在每一条已存 verdict 都是 PASS 时才允许工程级快速路径接管,
    所以「什么都没改,再跑一次就绿了」这条路是堵死的。mcpp self doctor 读的是构建时存下的
    verdict,不会拿一个可能已经变了的当前宿主重新猜。

    回归锁:tests/unit/test_elf_runtime.cpp(397 行,非 Linux 平台按 typed skip)、
    tests/e2e/206_runtime_binding_physics.sh,以及本轮新增的
    tests/e2e/209_runtime_verdict_never_reruns_green.sh —— 它污染一条已存 verdict、
    不动任何源文件,然后要求下一次 mcpp build 仍然失败并打印存档诊断,
    要求 mcpp self doctor 从存档解释,最后要求重建后的产物被重新求值
    (verdict 是缓存,不是判决,否则第一次真失败就会把项目卡死到只能删 target/)。

    发布后在隔离 fresh-home 复核:真实项目上 mcpp why runtime 报
    validation: pass (source post_link),覆盖可执行文件与 12 个随产物部署的 X 库。

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions