Skip to content

feat(elfpatch): the stable farm goes first, and the vendor's closure is checked (0.0.54) - #38

Merged
Sunrisepeak merged 2 commits into
mainfrom
feat/interposer-farm-first
Aug 6, 2026
Merged

feat(elfpatch): the stable farm goes first, and the vendor's closure is checked (0.0.54)#38
Sunrisepeak merged 2 commits into
mainfrom
feat/interposer-farm-first

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

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.

…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 Sunrisepeak closed this Aug 6, 2026
@Sunrisepeak Sunrisepeak reopened this Aug 6, 2026
@Sunrisepeak Sunrisepeak closed this Aug 6, 2026
@Sunrisepeak Sunrisepeak reopened this Aug 6, 2026
@Sunrisepeak
Sunrisepeak merged commit 5f4a704 into main Aug 6, 2026
1 check passed
@Sunrisepeak
Sunrisepeak deleted the feat/interposer-farm-first branch August 6, 2026 23:34
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.
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.

1 participant