fix(build): preserve runtime symbol versions in the Linux renderer check #267

Open
monsterlag wants to merge 3 commits from monsterlag/Longshot:fix/elf-symbol-version-metadata into main
First-time contributor

Why

A valid Linux renderer can be rejected as having missing host functions when nm omits its runtime-symbol version annotations. The guard should distinguish those runtime imports from genuinely unresolved strong host symbols.

What changed

  • nx2/build.py, check_vk_symbols(): prefer GNU/LLVM readelf --dyn-syms --wide metadata, retaining versions and weak binding; preserve the existing optional nm fallback when metadata is unavailable or unrecognized.
  • tests/test_vk_symbols.py: 14 game-free cases cover both readers, versioned runtime imports, real missing strong imports, weak/defined symbols and tool failures.
  • No runtime-name allowlist, production library loading, new mandatory dependency or renderer change.

Flags and settings

None. Symbol-reader discovery is automatic; no player or build switch changes.

Measurements

main: 9c9d69ed09 (2026-11-14)
this PR: fix/elf-symbol-version-metadata @ fa257ba303
Hardware: Ryzen7 9800X3D, 96GiB RAM, Windows11 build26300 / native Python3.14.7; Ubuntu24.04 under WSL, Python3.12.3, GCC13.3, GNU binutils2.42. No GPU used by these checks.
Scene: authored symbol metadata and tiny ELF libraries; no game scene.
Method: focused warm-up, then two sequential interleaved main/PR runs of the same14 native regression methods. Separately, two interleaved main/PR pairs run the exact AST-extracted production guard against real authored ELF files with actual GNU nm --without-symbol-versions; a transparent PATH wrapper adds that flag, and recorded output matches direct GNU invocations. Report medians below; no speed improvement claimed.
How to reproduce: run python -I -B tests/test_vk_symbols.py; for main, use its production function with the same new test. Build an authored shared library referencing abort/access/acosf/calloc/free and an optional weak host symbol; a second library adds issue246_missing_host. Verify with RTLD_NOW, then run the guard with GNU version suppression. Main and this PR use the same files/tools.

Check main @ 9c9d69ed this PR @ fa257ba3
Native regression failures, run1 / run2 8 / 8 of14 0 / 0 of14
Median native regression failures 8 0
False unresolved runtime names in loadable ELF, run1 / run2 5 / 5 0 / 0
Median false runtime names 5 0
Missing-strong control diagnostic count, run1 / run2 6 / 6 1 / 1
Missing-strong control rejected Yes Yes; only issue246_missing_host
native main, each run: 14 methods, failures8, errors0, skips0
native this PR, each run: 14 methods, failures0, errors0, skips0
real ELF main, each pair: good rejected (5 names); bad rejected (6 names)
real ELF this PR, each pair: good accepted; bad rejected (only issue246_missing_host)
RTLD_NOW: good.so loads; bad.so fails for issue246_missing_host

Four self-contained files under docs/tested record the native regression runs. The measured source tree was0c4277d5; the final head adds only these records, with the guard and test unchanged. The reporter now identifies /usr/bin/nm, OpenSUSE GNU binutils2.47.0.20260921-3, and supplies versionless libm definitions. That supports the omission hypothesis, but is not the failing renderer's undefined-import output. This PR verifies an explicit version-suppression condition; it does not reproduce all288 names or claim issue246 resolved.

Testing

Tested: native14-case regression and two fresh interleaved comparisons; real GNU tool/ELF comparisons and independent loader agreement; weak-host preservation and deliberate strong-import rejection; GNU/LLVM metadata-layout and optional-fallback fixtures. Full native Windows python -I -B tests/run_all.py --jobs 8 on0c4277d5: 534 passed, 3 failed, 34 skipped in262s. The same three groups fail on untouched main9c9d69ed in fresh focused checks: test_oot_state_tap.py (Windows bridge connection race, issue481), test_tested_configurations.py (authored stream newline assumption, issue482), and test_walk_key.py (missing stand-in after the Skip-title merge, already claimed by mooki). No assertion was weakened and no skip was added.

The final head fa257ba3 adds only run-record data after that suite; repository-fragment validation passes, production/test files are unchanged, and git diff --check passes. Earlier GNU/LLVM-reader evidence remains historical, not an additional current-head run.

Not tested: exact reporter OpenSUSE/AppImage renderer or nm selection, a complete Linux renderer/AppImage build, or gameplay (the guard is build-time only). Actual GNU suppression was tested on Ubuntu/WSL, not the reporter's distro. Where readelf is absent/unusable, nm retains its existing versionless-output limitation.

Needs other testers: the OpenSUSE/AppImage environment from issue246: compare the actual linked renderer on main and this guard, verify the reported failure clears and genuinely unresolved strong imports remain rejected (T-ZPWZ; awaiting a maintainer testing-wanted label before others can claim it). This remains validation of the reported issue; the general guard hardening is covered by the authored regression and real ELF checks above.

Screenshots

Nothing on screen.

Risk and rollback

The risk is interpreting a symbol-reader output format incorrectly. Both GNU and LLVM layouts and failure fallbacks are tested; unrecognized output falls back to nm. Revert this commit to restore the previous reader selection. Runtime version exemptions are unchanged.

Reviewer notes

This hardens a reproduced checker precondition. It does not claim the specific OpenSUSE report fixed; diagnostics were requested on the issue. The earlier published measurement branch is retained; this branch was created from newer main without rewriting that history.

Issues

See #246

Checklist

For everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description).
  • Rebased on current main.
  • python -I -B tests/run_all.py run and passing (any failure that also fails on main is named above).
  • Tested in the game, or what wasn't is named under Testing.
  • Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests).
  • Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported.
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

If applicable

  • A file in docs/tested/ for each new measured run (format in docs/tested-configurations.md; no appended row or assembled view).
  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • A game-free test added for the fix, failing on main and passing here.
  • A check under tests/ that reads changed source updated in this PR, not weakened. The added symbol-reader regression reads the production function; existing source-reader checks also pass.
  • Docs updated.
## Why A valid Linux renderer can be rejected as having missing host functions when `nm` omits its runtime-symbol version annotations. The guard should distinguish those runtime imports from genuinely unresolved strong host symbols. ## What changed - `nx2/build.py`, `check_vk_symbols()`: prefer GNU/LLVM `readelf --dyn-syms --wide` metadata, retaining versions and weak binding; preserve the existing optional `nm` fallback when metadata is unavailable or unrecognized. - `tests/test_vk_symbols.py`: 14 game-free cases cover both readers, versioned runtime imports, real missing strong imports, weak/defined symbols and tool failures. - No runtime-name allowlist, production library loading, new mandatory dependency or renderer change. ## Flags and settings None. Symbol-reader discovery is automatic; no player or build switch changes. ## Measurements main: 9c9d69ed09e2c24965d35f1696aa5c8dd8fe6e40 (2026-11-14) this PR: fix/elf-symbol-version-metadata @ fa257ba3035c95f7f5f92d40b13cd0ab2495f488 Hardware: Ryzen7 9800X3D, 96GiB RAM, Windows11 build26300 / native Python3.14.7; Ubuntu24.04 under WSL, Python3.12.3, GCC13.3, GNU binutils2.42. No GPU used by these checks. Scene: authored symbol metadata and tiny ELF libraries; no game scene. Method: focused warm-up, then two sequential interleaved main/PR runs of the same14 native regression methods. Separately, two interleaved main/PR pairs run the exact AST-extracted production guard against real authored ELF files with actual GNU `nm --without-symbol-versions`; a transparent PATH wrapper adds that flag, and recorded output matches direct GNU invocations. Report medians below; no speed improvement claimed. How to reproduce: run `python -I -B tests/test_vk_symbols.py`; for main, use its production function with the same new test. Build an authored shared library referencing abort/access/acosf/calloc/free and an optional weak host symbol; a second library adds `issue246_missing_host`. Verify with `RTLD_NOW`, then run the guard with GNU version suppression. Main and this PR use the same files/tools. | Check | main @ 9c9d69ed | this PR @ fa257ba3 | |---|---|---| | Native regression failures, run1 / run2 | 8 / 8 of14 | 0 / 0 of14 | | Median native regression failures | 8 | 0 | | False unresolved runtime names in loadable ELF, run1 / run2 | 5 / 5 | 0 / 0 | | Median false runtime names | 5 | 0 | | Missing-strong control diagnostic count, run1 / run2 | 6 / 6 | 1 / 1 | | Missing-strong control rejected | Yes | Yes; only issue246_missing_host | ```text native main, each run: 14 methods, failures8, errors0, skips0 native this PR, each run: 14 methods, failures0, errors0, skips0 real ELF main, each pair: good rejected (5 names); bad rejected (6 names) real ELF this PR, each pair: good accepted; bad rejected (only issue246_missing_host) RTLD_NOW: good.so loads; bad.so fails for issue246_missing_host ``` Four self-contained files under docs/tested record the native regression runs. The measured source tree was0c4277d5; the final head adds only these records, with the guard and test unchanged. The reporter now identifies `/usr/bin/nm`, OpenSUSE GNU binutils2.47.0.20260921-3, and supplies versionless libm definitions. That supports the omission hypothesis, but is not the failing renderer's undefined-import output. This PR verifies an explicit version-suppression condition; it does not reproduce all288 names or claim issue246 resolved. ## Testing **Tested:** native14-case regression and two fresh interleaved comparisons; real GNU tool/ELF comparisons and independent loader agreement; weak-host preservation and deliberate strong-import rejection; GNU/LLVM metadata-layout and optional-fallback fixtures. Full native Windows `python -I -B tests/run_all.py --jobs 8` on0c4277d5: **534 passed, 3 failed, 34 skipped in262s**. The same three groups fail on untouched main9c9d69ed in fresh focused checks: `test_oot_state_tap.py` (Windows bridge connection race, issue481), `test_tested_configurations.py` (authored stream newline assumption, issue482), and `test_walk_key.py` (missing stand-in after the Skip-title merge, already claimed by mooki). No assertion was weakened and no skip was added. The final head fa257ba3 adds only run-record data after that suite; repository-fragment validation passes, production/test files are unchanged, and `git diff --check` passes. Earlier GNU/LLVM-reader evidence remains historical, not an additional current-head run. **Not tested:** exact reporter OpenSUSE/AppImage renderer or nm selection, a complete Linux renderer/AppImage build, or gameplay (the guard is build-time only). Actual GNU suppression was tested on Ubuntu/WSL, not the reporter's distro. Where readelf is absent/unusable, nm retains its existing versionless-output limitation. **Needs other testers:** the OpenSUSE/AppImage environment from issue246: compare the actual linked renderer on main and this guard, verify the reported failure clears and genuinely unresolved strong imports remain rejected (T-ZPWZ; awaiting a maintainer testing-wanted label before others can claim it). This remains validation of the reported issue; the general guard hardening is covered by the authored regression and real ELF checks above. ## Screenshots Nothing on screen. ## Risk and rollback The risk is interpreting a symbol-reader output format incorrectly. Both GNU and LLVM layouts and failure fallbacks are tested; unrecognized output falls back to nm. Revert this commit to restore the previous reader selection. Runtime version exemptions are unchanged. ## Reviewer notes This hardens a reproduced checker precondition. It does not claim the specific OpenSUSE report fixed; diagnostics were requested on the issue. The earlier published measurement branch is retained; this branch was created from newer main without rewriting that history. ## Issues See #246 ## Checklist ### For everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`). - [x] Rebased on current `main`. - [x] `python -I -B tests/run_all.py` run and passing (any failure that also fails on `main` is named above). - [x] Tested in the game, or what wasn't is named under Testing. - [x] Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests). - [x] Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported. - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### If applicable - [x] A file in `docs/tested/` for each new measured run (format in `docs/tested-configurations.md`; no appended row or assembled view). - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [x] A game-free test added for the fix, failing on `main` and passing here. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened. The added symbol-reader regression reads the production function; existing source-reader checks also pass. - [x] Docs updated.
fix(build): preserve runtime symbol versions in the Linux renderer check
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 3s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
c6de7d03c4
Prefer GNU or LLVM readelf dynamic-symbol metadata before the optional nm
fallback. This keeps versioned runtime imports distinct from genuinely missing
strong host imports when nm's output omits version suffixes.

Add game-free regression coverage for both readers, weak imports and fallback
failures. The OpenSUSE report's exact tool configuration is still unverified.

See #246

Assisted-by: OpenAI Codex
Co-Authored-By: OpenAI Codex <[email protected]>
Author
First-time contributor

Opened test request T-ZPWZ for the OpenSUSE/AppImage environment. It is waiting for a maintainer testing-wanted label before testers can claim it. The PR remains WIP: the reporter-specific cause is unconfirmed, and the full-suite run and later focused refresh are separately recorded rather than presented as an all-green current-head run.

Opened test request T-ZPWZ for the OpenSUSE/AppImage environment. It is waiting for a maintainer testing-wanted label before testers can claim it. The PR remains WIP: the reporter-specific cause is unconfirmed, and the full-suite run and later focused refresh are separately recorded rather than presented as an all-green current-head run.
Carry PR267 forward onto main 9c9d69ed without rewriting the published
symbol-guard commit. The merge is conflict-free and retains the focused
readelf change and its existing regression file.

The 14 native Windows symbol-guard regression cases pass under the
2 GiB disk guard. Exact OpenSUSE/AppImage behavior remains unverified.

Assisted-by: OpenAI Codex
Co-Authored-By: OpenAI Codex <[email protected]>
docs(tested): record current game-free regression comparisons
Some checks are pending
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 4s
fa257ba303
Keep each measured native regression run self-contained under docs/tested
using the current contribution format. These are CPU-only tool checks;
they make no gameplay, GPU compatibility or performance claim.

Assisted-by: OpenAI Codex
Co-Authored-By: OpenAI Codex <[email protected]>
Author
First-time contributor

Merged main9c9d69ed without rewriting history. Fresh native14-case pairs are8failures on main versus0 here; real GNU --without-symbol-versions pairs reject5 false runtime names on main versus0 here while the missing-strong control remains rejected. Current full suite534pass/3main-baseline-fail/34skip is recorded; final commit only adds validated run files. This completes the general guard-hardening evidence; exact OpenSUSE/AppImage issue246 validation remains explicitly unverified in T-ZPWZ, so this is See246, not a fixed-issue claim.

Merged main9c9d69ed without rewriting history. Fresh native14-case pairs are8failures on main versus0 here; real GNU --without-symbol-versions pairs reject5 false runtime names on main versus0 here while the missing-strong control remains rejected. Current full suite534pass/3main-baseline-fail/34skip is recorded; final commit only adds validated run files. This completes the general guard-hardening evidence; exact OpenSUSE/AppImage issue246 validation remains explicitly unverified in T-ZPWZ, so this is See246, not a fixed-issue claim.
monsterlag changed title from WIP: fix(build): preserve runtime symbol versions in the Linux renderer check to fix(build): preserve runtime symbol versions in the Linux renderer check 2026-11-14 16:31:14 +00:00
Some checks are pending
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 4s
This pull request doesn't have enough approvals yet. 0 of 1 approvals granted.
Some workflows are waiting to be reviewed.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'fix/elf-symbol-version-metadata:monsterlag-fix/elf-symbol-version-metadata'
git switch 'monsterlag-fix/elf-symbol-version-metadata'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot!267
No description provided.