fix(compat.glx-runtime): ln -sf was last-wins, and nothing checked the ABI (mcpp#352) - #177
Merged
Merged
Conversation
…the ABI (mcpp#352)
On Fedora 44 this package produced
libGLX.so.0 -> /usr/lib/libGLX.so.0 # 32-bit
libOpenGL.so.0 -> /usr/lib64/libOpenGL.so.0
and the application died with `libGLX.so.0: wrong ELF class: ELFCLASS32`,
surfacing as a silent exit code 255 with no output at all.
THE OBVIOUS DIAGNOSIS IS NOT THE BUG. The issue reads this as "generated
assuming the Debian layout", but `/usr/lib64` is already ahead of `/usr/lib` in
`candidate_dirs()`. Two other things were wrong:
1. **`ln -sf` overwrites.** The loop reached /usr/lib64 first and linked the
correct file, then reached /usr/lib and replaced it. Last-wins, not
first-wins. `libOpenGL.so.0` survived as 64-bit only because that host's
32-bit glvnd does not ship it -- which is why exactly one link in the bug
report was right, and that is evidence rather than coincidence.
2. **No ABI check anywhere**, including in `required`, which asserted that
libGLX.so.0 and libGL.so.1 EXIST. Both existed. Both were 32-bit. So the
package reported success.
There is no layout to assume, which is why a better ordering is not the fix: the
FHS biarch clause makes /usr/lib 32-bit (Fedora/RHEL/SUSE), Debian explicitly
declined that clause and uses /usr/lib/<triplet> so its /usr/lib is 64-bit, and
Arch is a third answer again. An ABI check makes the order stop mattering.
So: enumerate and decide per file (first-wins, keyed by soname), read
e_ident[EI_CLASS] out of the file itself -- five bytes, no external tool, because
`file`/`readelf` may not be installed when a hook runs -- and make `required`
assert 64-bit rather than merely present. Both failure paths now name the cause
and what to install.
Verified against a forged biarch host (32-bit libGLX.so.0 in /usr/lib, the real
one in /usr/lib64): both required links resolve to /usr/lib64 and both are
ELF64. The same fixture under the old loop lands on /usr/lib.
The same three rules -- ask the loader, check the ABI, first hit wins -- are now
one shared module in the xlings index (libs/hostlib.lua), where four call sites
had four answers and three were wrong.
Refs: mcpp-community/mcpp#352
Design: openxlings/xlings .agents/docs/2026-08-07-graphics-experience-industry-survey-and-plan.md §8.1, §9.2
Sunrisepeak
force-pushed
the
fix/glx-runtime-elf-class
branch
from
August 6, 2026 21:10
80f5474 to
38ce338
Compare
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.
deploy-site and refresh-site-cache both checked out at the default depth of 1.
xpkgindex detects a shallow clone and skips the growth curve, the history line
and the contributor list rather than replaying a truncated log — so the
deployed site has quietly been missing all three. fetch-depth: 0 restores them.
Both now also pass GITHUB_TOKEN, which raises the API rate limit and enables
the author -> login mapping that merges one person's several git identities.
deploy-site's path filter gained .xpkgindex/** and docs/**: the plugin decides
how every package reads, the docs are rendered as site pages, and the cache is
what an offline build renders from. A change to any of them changes the site as
much as a descriptor does, and none of them triggered a deploy.
site-check is new. validate.yml proves the packages compile; this proves they
still render — offline, so a pull request spends no rate limit and the result
depends only on what is in the repository, and --strict, so a replayed history
that disagrees with the tree fails rather than warns. It also fails on any
warning at all, because a warning nobody reads becomes permanent, and asserts
that the pages a reader actually lands on exist in all three locales.