Skip to content

release: 0.4.12 — fix(elfpatch): self-patch opt-in + cross-platform polish#255

Merged
Sunrisepeak merged 1 commit intomainfrom
hotfix/0.4.12-elfpatch-self-patch
May 2, 2026
Merged

release: 0.4.12 — fix(elfpatch): self-patch opt-in + cross-platform polish#255
Sunrisepeak merged 1 commit intomainfrom
hotfix/0.4.12-elfpatch-self-patch

Conversation

@Sunrisepeak
Copy link
Copy Markdown
Member

Summary

Hotfix for 0.4.11's predicate-driven elfpatch regression. Bumps mcpplibs-xpkg 0.0.33 → 0.0.34, version 0.4.11 → 0.4.12.

  • Bug A (P0) — Rule 1 (self-patch) was unconditional. A loader-provider declaring `exports.runtime.loader` got auto-self-patched → rewrites `libc.so.6`'s `.interp` → falsifies offsets baked into glibc's static RO data → any consumer segfaults at execve+1 (SEGV_MAPERR @ 0x8). Now opt-in.
  • Bug B (P1) — `exec failed (code=1)` log spam on every shared lib (patchelf rejects --set-interpreter on .so without PT_INTERP). Now classifies via `_has_pt_interp()` so libs only get RPATH, no INTERP attempt.
  • Cross-platform polish — Windows early-bail in `M._apply`; macOS rpath-only auto-fire when deps declare `libdirs`; documented support matrix.

Repro on 0.4.11 (before fix)

```sh
$ xlings install openssl
$ openssl version
Segmentation fault (core dumped) # SEGV_MAPERR @ 0x8 right after execve
```

After fix (0.4.12)

```sh
$ xlings install openssl
$ openssl version
OpenSSL 3.1.5 30 Jan 2024 (Library: OpenSSL 3.1.5 30 Jan 2024)
```

Cross-platform behavior matrix

Platform Tool INTERP RPATH Predicate auto-fire
Linux patchelf ✅ `--set-interpreter` ✅ `--set-rpath` When ≥1 runtime dep declares `exports.runtime.loader`
macOS install_name_tool — (no Mach-O analog) ✅ `-add_rpath` When ≥1 runtime dep declares `exports.runtime.libdirs`
Windows Skipped at `M._apply` entry; PE has no analog

Migration

0.4.11 → 0.4.12 is binary-compatible. The in-place self-update path (xlings's own `xself install` / quick_install) handles the upgrade. Any payload installed under 0.4.11 that's known working stays as-is.

Test plan

  • libxpkg unit tests (`xpkg_executor_test`) 22/22 pass locally — covers Linux + macOS + Windows code paths
  • E2E in isolated `XLINGS_HOME` with 0.4.12 binary + 0.0.34 libxpkg:
    • `xlings install openssl` from scratch
    • glibc payload: untouched (libc.so.6 INTERP unchanged from tarball)
    • openssl bin: INTERP rewrites to xim-x-glibc loader; RUNPATH = correct closure
    • libssl.so.3: silent skip on `--set-interpreter`; RUNPATH set
    • `openssl version` → exits 0
  • CI (uses pinned `BOOTSTRAP_XLINGS_VERSION: v0.4.8` — pre-regression — so should pass cleanly)

Related

…olish

Bumps VERSION 0.4.11 → 0.4.12 + add_requires("mcpplibs-xpkg 0.0.34").

Hotfix for the predicate-driven elfpatch regression in 0.4.11. Three
issues that together made fresh installs of any glibc-dependent xpkg
unusable on Linux:

  * Bug A (P0): Rule 1 self-patch was unconditional. A loader-provider
    declaring `exports.runtime.loader` got auto-self-patched, which
    rewrites `libc.so.6`'s `.interp` segment and falsifies offsets baked
    into glibc's static RO data (link_map / _rtld_global). Any consumer
    linked against the patched glibc segfaulted at execve+1 with
    SEGV_MAPERR @ 0x8 — before any application code ran.

    Repro on 0.4.11 (isolated XLINGS_HOME):
      $ xlings install openssl
      $ openssl version
      Segmentation fault (core dumped)

    Fix: self-patch is now opt-in. Provider hooks that need it must call
    `elfpatch.set({ self_patch = true })` explicitly. The
    exports.runtime.loader field is now correctly treated as metadata
    *for consumers*, not a self-patch trigger. Most providers (glibc
    included) pre-relocate their own payload at install time; the
    install hook (e.g. glibc.lua's __relocate) is the right place for
    that.

  * Bug B (P1): patchelf --set-interpreter noise on shared libraries.
    Fallback scan + declarative bins paths attempted --set-interpreter
    on every ELF found, including .so files. patchelf exits 1 on shared
    libs (no PT_INTERP) → log spam `exec failed (code=1)` for each
    library. Now uses the existing _has_pt_interp() helper to skip
    --set-interpreter on files without an INTERP segment; RPATH still
    applied. Auto bin/lib classification works on PIE binaries with
    unusual names too — no `.so` filename heuristic.

  * Cross-platform polish:
      - Windows early-bail in M._apply: PE has no INTERP/RPATH analog
        (DLL search is governed by Windows loader: same dir → System32
        → PATH). Previously the predicate ran the full resolve + closure
        only to be no-op'd at dispatch.
      - macOS rpath-only auto-trigger when ≥1 dep declares libdirs
        (Mach-O has no INTERP, so deps shouldn't declare loader; old
        gate refused to fire without a loader → consumers on macOS got
        no auto-RPATH). Linux deliberately doesn't have this fallback —
        RPATH-only on Linux without managing INTERP would leave
        binaries pointing at build-host glibc.
      - Comprehensive cross-platform comments + support matrix doc in
        elfpatch.lua.

Migration: 0.4.11 → 0.4.12 is binary-compatible. The in-place self-update
path handles upgrade. Any payload installed under 0.4.11 that's known
working stays as-is (patched binaries are functional; only the
glibc-self-patch case was broken, and 0.4.11's segfault made those
installs fail to mark complete in many cases anyway).

Verified locally with isolated XLINGS_HOME:
  - libxpkg unit tests 22/22 pass (Linux/macOS/Windows cases)
  - fresh openssl install: glibc payload untouched, openssl bin INTERP
    points at xim-x-glibc loader, RUNPATH = correct closure,
    `openssl version` exits 0

libxpkg PR: openxlings/libxpkg#11 (merged, tagged v0.0.34)
@Sunrisepeak Sunrisepeak merged commit a41d5ea into main May 2, 2026
3 checks passed
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