feat(elfpatch): the stable farm goes first, and the vendor's closure is checked (0.0.54) - #38
Merged
Merged
Conversation
…is checked (0.0.54) Two changes to `host_link_interposer`, both from measuring what the RPATH it writes actually resolves to. **The subos lib directory is now first in `libdirs`.** `closure_lib_paths` returns the resolver's payload directories -- each an exact version directory, `.../libX11/1.8.10/lib` -- with the subos directory last. Written into an ELF that ordering is a snapshot: upgrade libX11 and every entry above still names 1.8.10, and once that payload is collected the entry is a dead directory the loader walks past. The subos lib directory is the one name that does not move; it is a symlink farm the packages declare into, so it follows `xlings use` and re-points on upgrade. Every ecosystem that hands a foreign driver a search path converged on exactly this -- /run/opengl-driver on NixOS, /overrides in pressure-vessel, $SNAP/gpu-2404 in a snap -- and we already had one, at the wrong end of the list. The payload directories stay as the fallback, because the recipe's concern is real: installed into a subos short of libX11, the farm would be quietly missing it. **And the assertion the other three were missing.** soname / needed / rpath all check the shape of the object we made, and all three can hold while the thing it exists for does not work: an RPATH naming directories that do not contain the vendor's own DT_NEEDED resolves them from the host, which still renders -- on llvmpipe -- and prints nothing. That is this repository's recurring failure shape, sitting inside the function written to prevent it. So the vendor's DT_NEEDED are now resolved the way the loader would, against the final RPATH, and the log reports the FRACTION rather than a bare "interposer X -> Y" that was true in every case including total failure. A warning, not an error, and the line is deliberate: the object IS correctly built, and on a normal host the loader finds these through its own ld.so.cache. Refusing would break a working install. What must not happen is that it is invisible -- in a sandbox or an empty-host container there is no cache, and the failure appears as `no device` from a GL call three layers away. Returns `unresolved` so a caller can act on it. Design: openxlings/xlings .agents/docs/2026-08-07-graphics-experience-industry-survey-and-plan.md §B1 Tested: 55 executor tests pass, including a new one whose fake patchelf answers --print-needed differently for the vendor than for the object under construction -- which the previous fake could not do, and is why this check could not have been tested with it.
Sunrisepeak
added a commit
to mcpplibs/mcpp-index
that referenced
this pull request
Aug 6, 2026
…ssertion (#178) openxlings/libxpkg#38. Two changes to host_link_interposer: * The subos lib directory -- the one name that does not move, a symlink farm the packages declare into -- goes FIRST in the RPATH, with the resolver's version-pinned payload directories after it as the fallback. Written into an ELF, the old order was a snapshot: upgrade libX11 and every entry still named 1.8.10. Every ecosystem that hands a foreign driver a search path converged on a stable indirection directory (/run/opengl-driver, /overrides, $SNAP/gpu-2404); we had one at the wrong end of the list. * The vendor's own DT_NEEDED closure is resolved against the final RPATH and the fraction reported. The three existing assertions check the shape of the object we made, and all three can hold while an RPATH naming the wrong directories sends the vendor's dependencies to the host -- which still renders, on llvmpipe, and prints nothing. Entry added to all three platform blocks. A bump landing in only one xpm.<platform> reads as "not found" on the others, which both index repos now enforce in CI. CN asset published with gtc and verified by GET (HEAD answers 401): byte-identical to the GitHub tag tarball, sha256 5fec15183be54a0a6d937da2289340da21fb42a023e68a975dfc43eb523391ef.
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.
Two changes to
host_link_interposer, both from measuring what the RPATH itwrites actually resolves to.
The subos lib directory is now first in
libdirs.closure_lib_pathsreturns the resolver's payload directories -- each an exact version directory,
.../libX11/1.8.10/lib-- with the subos directory last. Written into an ELFthat ordering is a snapshot: upgrade libX11 and every entry above still names
1.8.10, and once that payload is collected the entry is a dead directory the
loader walks past. The subos lib directory is the one name that does not move;
it is a symlink farm the packages declare into, so it follows
xlings useandre-points on upgrade.
Every ecosystem that hands a foreign driver a search path converged on exactly
this -- /run/opengl-driver on NixOS, /overrides in pressure-vessel,
$SNAP/gpu-2404 in a snap -- and we already had one, at the wrong end of the
list. The payload directories stay as the fallback, because the recipe's
concern is real: installed into a subos short of libX11, the farm would be
quietly missing it.
And the assertion the other three were missing. soname / needed / rpath
all check the shape of the object we made, and all three can hold while the
thing it exists for does not work: an RPATH naming directories that do not
contain the vendor's own DT_NEEDED resolves them from the host, which still
renders -- on llvmpipe -- and prints nothing. That is this repository's
recurring failure shape, sitting inside the function written to prevent it.
So the vendor's DT_NEEDED are now resolved the way the loader would, against
the final RPATH, and the log reports the FRACTION rather than a bare
"interposer X -> Y" that was true in every case including total failure.
A warning, not an error, and the line is deliberate: the object IS correctly
built, and on a normal host the loader finds these through its own
ld.so.cache. Refusing would break a working install. What must not happen is
that it is invisible -- in a sandbox or an empty-host container there is no
cache, and the failure appears as
no devicefrom a GL call three layers away.Returns
unresolvedso a caller can act on it.Design: openxlings/xlings .agents/docs/2026-08-07-graphics-experience-industry-survey-and-plan.md §B1
Tested: 55 executor tests pass, including a new one whose fake patchelf answers
--print-needed differently for the vendor than for the object under
construction -- which the previous fake could not do, and is why this check
could not have been tested with it.