feat(graphics): one owner for the loader tag, and two things that now say so (2026.8.10.4) - #536
Merged
Merged
Conversation
… say so (2026.8.10.4)
Implements E1b/E3a/E3b of the graphics stack design, on top of libxpkg 0.0.57
which makes elfpatch stamp DT_RPATH on executables (E1a).
RULE E — the tag invariant, evaluated where the payload lands
DT_RUNPATH is consulted only for the object carrying it; DT_RPATH is consulted
for every dlopen anywhere in the process. A GL program reaches its driver
through three levels of dlopen, so the TAG — not the path — decides whether it
renders on the GPU. Measured on a real home: 1 of 73 installed executables
carried DT_RPATH, 68 carried DT_RUNPATH, and 55 of those 68 already carried
the correct path.
closure_check is the right place and it was built for exactly this move. Its
own header says a predicate that is right but "runs in exactly one place — a
repository workflow the user's machine never sees" belongs where the payload
actually lands. The tag was in a worse place than that: it lived in each
recipe author's head, and the one package whose author found the problem fixed
it locally with nothing to carry the finding to the other 68.
The tag is parsed natively rather than shelled out, because there is nothing
to shell out to: `patchelf --print-rpath` prints the value of whichever tag
exists and does not say which, and readelf is not something a payload is
guaranteed to have. Reading the whole dynamic section rather than the first
hit matters — with both tags present the loader IGNORES DT_RPATH, and
DT_RPATH-first is the common layout, so a first-hit reading would report the
opposite and look right in casual testing.
Warn-only, matching rule A and D. Libraries are deliberately out of scope:
forcing the transitive tag on a LIBRARY is measured harmful
(xim-pkgindex#593) because transitivity runs downward too.
HOST DRIVER DRIFT — `subos info` now says it
The NVIDIA userspace driver is the one part of this stack we do not own; it is
in lockstep with the host kernel module, so a distribution update moves it
under us and the recorded wiring describes a driver that is gone. The
detector already existed and worked (`xlings-gl-doctor`) — what it lacked was
a way to reach the user without being remembered. Two plain file reads, no
subprocess, same instant-query contract as the wiring record. Reported ABOVE
the per-vendor rows, because when it fires every row under it is about the old
driver. An unknown on either side is not drift: a machine whose module is not
loaded has not changed driver.
SANDBOX WITHOUT --gpu — say it once, only where it can be acted on
bwrap's fresh /dev excludes the GPU nodes, so `--sandbox` without `--gpu` is a
software-rendering environment by construction. That default is correct and
`--gpu` genuinely restores it; the gap is silence — the user gets "runs, draws,
exits 0", indistinguishable from the GPU case except in frame rate.
Three conditions, and the narrowness is the point: `warn_storage_dormant_on_
shell_` a few lines away is a hint that fired on every entry of its kind,
became noise, and is commented out today. This one requires that the subos has
a GL dispatch, that the host actually has GPU device nodes, and that --gpu was
not passed. The GPU predicate is derived from `passthrough_args` rather than a
second list of device paths, so the two cannot drift.
Verified on real hardware, each in both directions:
sandbox hint fires without --gpu; silent with --gpu; silent on a home
with no graphics stack
drift row absent when versions agree, present when they disagree,
absent again after restoring
tag parser falsified — reading the first hit instead of the whole
dynamic section makes the both-tags test fail
37/37 test binaries pass. Design:
.agents/docs/2026-08-10-graphics-stack-design.md §E1b, §E3a, §E3b.
…lready exists The gap I flagged in this PR's own description: rule E's parser was falsified against synthetic ELFs, but nothing had watched it fire during an actual install — and that case cannot be produced by installing anything today, because since libxpkg 0.0.57 the writer is consistent and every package with a `bin/` also has a `lib/`, so elfpatch always stamps DT_RPATH. closure_guard_differential already builds exactly the right rig for it: a real gcc-compiled executable, given PT_INTERP into a payload glibc so it is form X, carried into a payload by a fixture recipe and installed for real. One `patchelf --set-rpath` WITHOUT `--force-rpath` — which is precisely what elfpatch wrote before 0.0.57 — turns it into a payload arriving from an older client, which is the case rule E exists to catch. Two assertions, because the second is the one that decides whether the message is useful. The path in a DT_RUNPATH executable is usually correct, so a warning that only named the tag would read as pedantry rather than as the reason a GL program renders in software; 4b requires the message to say what it costs. The rig asserts its own shape before relying on it (`readelf` must show RUNPATH), so a future patchelf that writes something else fails loudly instead of making 4b vacuous. Falsified: forcing the rig to DT_RPATH makes 4b fail, and restoring it passes.
Sunrisepeak
added a commit
to openxlings/xim-pkgindex
that referenced
this pull request
Aug 10, 2026
The loader tag now has one owner. elfpatch (libxpkg 0.0.57) stamps DT_RPATH on executables instead of DT_RUNPATH, and closure_check gains rule E to say so at install time on the user's machine when a payload arrives with the old tag. DT_RUNPATH is consulted only for the object carrying it; DT_RPATH is consulted for every dlopen anywhere in the process. A GL program reaches its driver through three levels of dlopen, so the tag — not the path — decided whether it rendered on the GPU. Of 73 installed executables on a real home, 1 carried DT_RPATH and 55 of the other 68 already carried the correct path. Also: `subos info` reports host driver drift (the detector existed as `xlings-gl-doctor` and lacked a channel), and `subos use --sandbox` without `--gpu` says the sandbox exposes no GPU — under three conditions, so it can only fire where acting on it changes the outcome. All THREE platform sections, verified by PARSING rather than reading the diff: linux latest=2026.8.10.4 arch=[aarch64,x86_64] macosx latest=2026.8.10.4 arch=[aarch64] windows latest=2026.8.10.4 arch=[x86_64] Hashes taken from the assets in xlings-res — what `XLINGS_RES` actually resolves to — not from the upstream release they were mirrored from. CN mirror uploaded from a local shell and verified by SIZE on all four platforms; before the upload all four returned 127 bytes, so the status code would have said 200 for assets that were not there. Upstream: openxlings/xlings#536, openxlings/libxpkg#41, mcpplibs/mcpp-index#202 Co-authored-by: sunrisepeak <x.d2learn.org@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
落地图形栈设计的 E1b / E3a / E3b,建立在 libxpkg 0.0.57(E1a,让 elfpatch 给可执行文件打 DT_RPATH)之上。
rule E —— 把标签不变式放到载荷真正落地的地方
DT_RUNPATH 只对携带它的对象生效;DT_RPATH 对进程内任意深度的 dlopen 都生效。GL 程序要穿三层 dlopen 才够到驱动,所以决定它在不在 GPU 上渲染的是标签,不是路径。
真机实测:73 个已安装可执行文件里 1 个 DT_RPATH、68 个 DT_RUNPATH,而那 68 个里有 55 个路径本来就是对的。
closure_check就是为这个动作建的模块 —— 它自己的注释写着:一条正确但"只活在仓库工作流里、用户机器永远看不到"的谓词,应该搬到载荷真正落地的地方。标签此前的处境比那还差:它活在每个 recipe 作者的脑子里;唯一做对的那个包,是它的作者独立发现了问题并在自己包里修好,而没有任何机制把这个发现带给另外 68 个。标签是原生解析的,不是外部命令 —— 因为没有命令可用:
patchelf --print-rpath打印的是"两个标签里存在的那个"的值,不说是哪个;readelf不保证载荷里有。读整个 dynamic section 而不是第一个命中,这点是有分量的:两个标签同时存在时加载器忽略 DT_RPATH,而 DT_RPATH 在前是常见布局 —— 先到先得的读法会在一半样本上给出相反答案,而且在随手测试时看起来是对的。
warn-only,与 rule A/D 一致。库不在范围内:在库上强制传递标签是实测有害的(xim-pkgindex#593),传递性向下也成立。
宿主驱动漂移 ——
subos info现在会说NVIDIA 用户态是这套栈里唯一不属于我们的部分,它与宿主内核模块锁步,发行版一升级它就在我们脚下换掉,而记录下来的接线描述的是一个已经不在的驱动。
检测器早就存在并且有效(
xlings-gl-doctor)—— 它缺的是一条不依赖"用户想起来"的通道。两次普通文件读、无子进程,与接线记录同一个即时查询契约。报在逐 vendor 行之上,因为它一旦触发,下面每一行说的都是旧驱动。任一侧未知都不算漂移:模块没加载的机器不是"换了驱动",是"没有驱动在跑"。
沙箱缺
--gpu—— 只在能被采取行动时才说bwrap 重建的 /dev 不含 GPU 节点,所以不带
--gpu的沙箱按构造就是软件渲染环境。这个缺省是对的,--gpu也确实能恢复;缺的是沉默 —— 用户拿到"能跑、有画面、exit 0",与 GPU 可用时观感完全相同,只差帧率。三个条件,窄是要点。 几行之外的
warn_storage_dormant_on_shell_就是一条"每次都响"的提示,变成噪音后被注释掉了。这条要求:subos 有 GL dispatch、宿主确实有 GPU 设备节点、且没传--gpu。GPU 判据由passthrough_args派生而不是另立一份设备清单,两者无法漂移。真机验证(每项双向)
37/37 测试二进制全绿。
设计:
.agents/docs/2026-08-10-graphics-stack-design.md§E1b / §E3a / §E3b上游:openxlings/libxpkg#41,mcpplibs/mcpp-index#202
关联:#532 #533 #534
追加:E1a 的端到端验证(此前只验了命令构造)
用本 PR 的二进制(内含 libxpkg 0.0.57)真实安装,删掉载荷目录强制重装(仅
remove会留下载荷,重装是空操作 —— 已知陷阱):新装的 wayland 也是
(RPATH)。打包侧的写入者现在是一致的。诚实标注一处未观察到的行为
rule E 的解析器已双向证伪(合成 ELF:改成读第一个命中,both-tags 用例变红),
但"它在真实安装中发声"这一幕没有观察到 —— 因为 0.0.57 之后正常安装不再产出
DT_RUNPATH 的可执行文件,这个场景无法按需构造(仓库里每个有
bin/的包都有lib/,所以 elfpatch 总会打标签)。
有一条反向的真实文件交叉验证成立:新装的 wayland / libxkbcommon 可执行文件经
readelf是 RPATH,而 rule E 用我写的解析器扫同一批文件时保持沉默 —— 解析器若把RPATH 误读为 RUNPATH,那次安装就会报警。所以真实文件上的一致性在否定方向已验证;
肯定方向缺一个真实样本。
rule E 的实际作用面因此是两类:被更旧的 elfpatch 打过标签的载荷(用户升级后重装老包时),
以及自己覆盖标签的 recipe。两者都不是现在能按需制造的。