Skip to content

Commit fc15a96

Browse files
committed
fix-elf: also patchelf PT_INTERP to strip +brewing post-install
brewkit installs into ${prefix}+brewing/ then renames to ${prefix}/. The build's `ld --dynamic-linker=...+brewing/.../ld-linux*.so` flag bakes the +brewing path into PT_INTERP of every linked binary. fix-elf has historically only patched RPATH via patchelf, leaving PT_INTERP referencing a path that no longer exists post-rename. For typical pantry recipes this is harmless: their binaries' PT_INTERP points at the host runner's /lib(64)?/ld-linux-*.so, not back into their own bottle, so the bake doesn't matter. For low-level packages whose own bottle ships ld-linux* (glibc being the obvious example), PT_INTERP after install is broken — bin/ld.so, bin/getconf, etc. fail to exec because the kernel can't find the +brewing path. This blocked gnu.org/glibc (pkgxdev/pantry#5080 / pkgxdev/pantry#12968) from shipping its own bin/ tools as usable: the test-phase had to fall back on bash-builtin `test -f` rather than running e.g. `ld.so --version`. Add a no-op-by-default `fix_interpreter()` step alongside `set_rpaths()`: - read PT_INTERP with `patchelf --print-interpreter` - if it contains "+brewing", rewrite via `patchelf --set-interpreter` - skip if the rewritten target doesn't exist (guards against bad rewrites) - catch errors quietly when the file is statically linked / has no .interp section, consistent with the surrounding set_rpaths style. No behaviour change for any existing pantry bottle — they all consume the host runner's ld-linux at PT_INTERP, so the substring "+brewing" never matches. Refs: pkgxdev/pantry#12968 (gnu.org/glibc PR).
1 parent 2e18552 commit fc15a96

1 file changed

Lines changed: 40 additions & 0 deletions

File tree

lib/bin/fix-elf.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,49 @@ export default async function fix_rpaths(installation: Installation, pkgs: strin
2424
console.info("doing SLOW rpath fixes…")
2525
for await (const [exename] of exefiles(installation.path)) {
2626
await set_rpaths(exename, pkgs, installation)
27+
await fix_interpreter(exename)
2728
}
2829
}
2930

31+
/// Strip the `+brewing` build-prefix suffix from PT_INTERP if present.
32+
///
33+
/// brewkit installs into ${prefix}+brewing/ then renames to ${prefix}/.
34+
/// The build's `ld --dynamic-linker=...+brewing/.../ld-linux*.so` flag
35+
/// bakes the +brewing path into PT_INTERP of every linked binary.
36+
/// fix-elf has historically only patched RPATH; without also patching
37+
/// PT_INTERP, packages whose own bin/ tools have a PT_INTERP that points
38+
/// back into their own bottle (currently rare — glibc is the obvious
39+
/// example) become broken after the rename: the kernel tries to load
40+
/// the .../+brewing/.../ld-linux*.so path which no longer exists.
41+
///
42+
/// No-op on binaries whose PT_INTERP doesn't contain `+brewing`, which
43+
/// is the case for every existing pantry bottle (they consume the host
44+
/// runner's ld-linux at PT_INTERP=/lib(64)?/ld-linux-*.so).
45+
async function fix_interpreter(exename: Path) {
46+
let cur: string
47+
try {
48+
cur = (await backticks({
49+
cmd: ["patchelf", "--print-interpreter", exename],
50+
})).chuzzle() ?? ""
51+
} catch {
52+
return // not a dynamic ELF, or statically linked, or no PT_INTERP
53+
}
54+
if (!cur.includes("+brewing")) return
55+
56+
const fixed = cur.replace(/\+brewing/g, "")
57+
// Sanity check: only rewrite to a path that actually exists, otherwise
58+
// we'd silently produce a binary that can't be exec'd at all.
59+
if (!new Path(fixed).exists()) {
60+
console.warn(`fix-elf: PT_INTERP target ${fixed} doesn't exist; skipping rewrite for ${exename}`)
61+
return
62+
}
63+
const proc = new Deno.Command("patchelf", {
64+
args: ["--set-interpreter", fixed, exename.string]
65+
}).spawn()
66+
const { success } = await proc.status
67+
if (!success) console.warn(`fix-elf: patchelf --set-interpreter failed for ${exename}`)
68+
}
69+
3070

3171
//TODO it's an error if any binary has bad rpaths before bottling
3272
//NOTE we should have a `safety-inspector` step before bottling to check for this sort of thing

0 commit comments

Comments
 (0)