Summary
Propose a brewkit helper libexec/bkcvenv that lets a recipe produce a binary bottle whose runtime libc is fully hermetic — i.e. the binaries always use the bundled gnu.org/glibc@X.Y regardless of what the host has. Same shape as the existing libexec/bkpyvenv (two-phase stage / seal), and aligned with the pattern @codex described in #342:
Hermetic binary: use a wrapper:
exec "$prefix/lib/ld-linux-x86-64.so.2" \
--library-path "$prefix/lib:$other_libs" \
"$prefix/libexec/foo" "\$@"
The inner ELF can even have a dummy/system PT_INTERP; the wrapper explicitly invokes the loader.
Context
Triggered by:
Proposed API
Mirror bkpyvenv:
# In a recipe's build.script, after make install:
- bkcvenv stage {{prefix}} # sets CC/CXX/CPP/SYSROOT for hermetic build
- ../configure --prefix={{prefix}} # (already wired through CC etc.)
- make && make install
- bkcvenv seal {{prefix}} # bin/* → libexec/*; bin/* becomes shell wrappers
Phase 1: `bkcvenv stage `
- Resolve the glibc bottle prefix (from `build.dependencies.gnu.org/glibc` resolution, env-injected by brewkit)
- Determine arch's LDSO (`ld-linux-x86-64.so.2` / `ld-linux-aarch64.so.1`) and LIBDIR (`/lib/glibc-X.Y/`)
- Export:
- `SYSROOT=`
- `CC="$CC -nostdinc -isystem /include -L -Wl,--rpath= -Wl,--dynamic-linker=/"`
- `CXX="$CXX …"` similarly
- `CPP="$CPP -nostdinc -isystem …"`
Phase 2: `bkcvenv seal `
For every ELF executable in `$prefix/bin/`:
-
`mv $prefix/bin/foo → $prefix/libexec/foo`
-
Write `$prefix/bin/foo`:
#!/bin/sh
here=\"\$(cd \"\$(dirname \"\$0\")\" && pwd)\"
exec \"\$here/../lib/glibc-X.Y/<LDSO>\" \
--library-path \"\$here/../lib/glibc-X.Y:\$here/../lib\" \
\"\$here/../libexec/\$(basename \"\$0\")\" \"\$@\"
-
`chmod +x $prefix/bin/foo`
Skip symlinks, skip non-ELFs (shell scripts already handled by `fix-shebangs.ts`).
Relation to @jhheider's default-glibc plan
These compose orthogonally:
| Layer |
Mechanism |
Who needs it |
| Build defaults to glibc 2.17 (forward-compat baseline) |
brewkit `useShellEnv` injection (no recipe change) |
~95% of recipes |
| Override glibc version |
`build.dependencies.gnu.org/glibc: '>=2.34'` |
A few recipes needing newer symbols |
| Opt out entirely |
`build.skip: linux-glibc` (like `skip: fix-machos`) |
Rare — libc-less Go binaries, kernel modules |
| Hermetic binary (host glibc never touched at runtime) |
`bkcvenv` |
Numerical / scientific packages, packages that must run on Alpine + Debian + RHEL identically |
`bkcvenv` is purely opt-in — recipes that don't need runtime hermeticity simply don't call it.
Pilot recipes
Candidates that would benefit and could validate the helper:
- Something with C extensions and tight glibc-symbol coupling (numpy.org?)
- A scientific computing lib that has to work on HPC clusters (manylinux2014 era)
- llm.cpp or similar ML inference binary that ships a self-contained tarball today
Open questions
- Wrapper language: pure POSIX `sh` (least dependency, slowest startup) vs a tiny compiled wrapper (faster, but bootstrap problem). Lean toward `sh` like `fix-shebangs.ts` output, but worth discussing.
- `--library-path` content: only the bundled glibc's LIBDIR, or also `$prefix/lib` for the bottle's own non-libc shared libs? Probably both — but order matters (loader resolves left-to-right).
- Detecting "this prefix has bkcvenv-sealed wrappers": brewkit's auditor should know to follow `bin/foo` → `libexec/foo` and audit the inner ELF, not the wrapper.
- Interaction with PT_INTERP: the libexec inner ELF can keep whatever PT_INTERP it has (including `+brewing` — the wrapper ignores it). So this completely sidesteps the brewkit#342 problem.
Next steps
Happy to draft a PR for `libexec/bkcvenv` if there's appetite. Would also need:
- Pantry-side: at least one pilot recipe using `bkcvenv stage` / `seal`
- brewkit auditor: skip the wrapper's "shebang" check, follow into libexec for the real audit
- Docs in brewkit AGENTS.md ("hermetic glibc helper, mirrors bkpyvenv")
cc @jhheider
Summary
Propose a brewkit helper
libexec/bkcvenvthat lets a recipe produce a binary bottle whose runtime libc is fully hermetic — i.e. the binaries always use the bundledgnu.org/glibc@X.Yregardless of what the host has. Same shape as the existinglibexec/bkpyvenv(two-phasestage/seal), and aligned with the pattern @codex described in #342:Context
Triggered by:
gnu.org/glibc@2.17for Linux builds, override per-package viabuild.dependencies.+brewingleak viafix-elf. Wrong layer; @codex's review converged on the wrapper-script approach.\"\$LIBDIR/\$LDSO\" --library-path \"\$LIBDIR\" ./test-bottleproduces a verifiably-hermetic execution.Proposed API
Mirror
bkpyvenv:Phase 1: `bkcvenv stage `
Phase 2: `bkcvenv seal `
For every ELF executable in `$prefix/bin/`:
`mv $prefix/bin/foo → $prefix/libexec/foo`
Write `$prefix/bin/foo`:
`chmod +x $prefix/bin/foo`
Skip symlinks, skip non-ELFs (shell scripts already handled by `fix-shebangs.ts`).
Relation to @jhheider's default-glibc plan
These compose orthogonally:
`bkcvenv` is purely opt-in — recipes that don't need runtime hermeticity simply don't call it.
Pilot recipes
Candidates that would benefit and could validate the helper:
Open questions
Next steps
Happy to draft a PR for `libexec/bkcvenv` if there's appetite. Would also need:
cc @jhheider