Skip to content

Commit b4fab13

Browse files
committed
feat(libexec): bklibcvenv — extract glibc wrapper-script pattern
Extracts the recipe-side hermetic libc-bundling pattern @jhheider just validated inline in pkgxdev/pantry@5354c73f. Recipes that ship their own libc (glibc today; musl, custom libcs tomorrow) get a ~one-line call: - bklibcvenv seal {{prefix}} glibc-{{version.marketing}} instead of the ~20-line inline wrapper-installer that's currently duplicated in the glibc recipe. ## Shape Mirror of libexec/bkpyvenv (Python venv → relocatable stubs): libexec/bklibcvenv — the helper script share/brewkit/libcvenv-wrapper.sh — the POSIX sh wrapper template The helper: 1. takes `<prefix> <libdir-name>` as args 2. auto-detects LDSO from `uname -m` (overridable via LDSO env) 3. for each ELF in <prefix>/{bin,sbin}/* with a PT_INTERP: - moves it to <prefix>/libexec/<libc-name>-<dir>/ - replaces it with a sed-templated sh wrapper 4. wrapper does `cd $(dirname $0)/..` to compute prefix at runtime, then `exec $libdir/$ldso --library-path $libdir $libexec_path "$@"` `<libc-name>` (libdir-name up to first '-') prefixes the libexec subdir so multiple libc bottles can coexist (glibc + musl): - libexec/glibc-bin/ - libexec/glibc-sbin/ ## Why this is useful - **Fully relocatable** — bottle extracted at `/opt`, `~/.pkgx`, or any other path works the same; no absolute paths baked in. - **Sidesteps PT_INTERP** — invoking ld.so explicitly via wrapper ignores the ELF's PT_INTERP. brewkit's fix-elf currently can't rewrite PT_INTERP anyway (#345) and the bottle's own ld.so suffers from fix-elf's RPATH pollution when not `build.skip: fix-patchelf`'d. - **Reusable** — same shape works for musl, dietlibc, or any alternate libc anyone packages. ## Connection to bkwinvenv (#347) The Windows analog (`bkwinvenv`, sketched at #347) uses the same seal-into-libexec + emit-wrapper-script structure, just with `.cmd` wrappers + Windows DLL search-order semantics (no explicit loader invocation; co-location with the inner .exe is enough). Validating this Linux pattern empirically (which @jhheider's commit does) validates the Windows design too. ## Migration Once this lands, pkgxdev/pantry's glibc recipe can shrink its inline wrapper-installer block (currently ~20 lines + a prop: template) to one line. Same for any future libc recipe. Refs: #344 (this RFC), #347 (bkwinvenv sibling), pkgxdev/pantry@5354c73f (origin of pattern).
1 parent 2e18552 commit b4fab13

2 files changed

Lines changed: 124 additions & 0 deletions

File tree

libexec/bklibcvenv

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#!/usr/bin/env -S pkgx +nixos.org/patchelf bash
2+
# bklibcvenv — hermetic libc bundler for relocatable libc bottles.
3+
#
4+
# Mirror of libexec/bkpyvenv. Recipe-side helper that makes a libc
5+
# bottle (glibc, eventually musl etc.) ship its bin/* + sbin/* as
6+
# POSIX-sh wrappers that route through the bundled ld.so by relative
7+
# path, so the bottle is fully relocatable.
8+
#
9+
# Usage:
10+
# bklibcvenv seal <prefix> <libdir-name>
11+
#
12+
# Where:
13+
# <prefix> install root (e.g. {{prefix}})
14+
# <libdir-name> subdir of <prefix>/lib/ containing libc.so.6 + ld.so
15+
# (e.g. "glibc-2.43"). The script derives:
16+
# LIBC_NAME = libdir-name up to the first '-'
17+
# (so "glibc-2.43" → "glibc")
18+
# LIBC_NAME is used as the libexec subdir prefix
19+
# (libexec/<LIBC_NAME>-bin/, libexec/<LIBC_NAME>-sbin/)
20+
# so multiple libc bottles can coexist without colliding.
21+
#
22+
# Effect: for each ELF in <prefix>/{bin,sbin}/* that has a PT_INTERP,
23+
# - move it to <prefix>/libexec/<LIBC_NAME>-<dir>/
24+
# - replace <prefix>/<dir>/<name> with a POSIX sh wrapper that:
25+
# * resolves its own bindir (handles invocation by path or PATH)
26+
# * climbs to prefix
27+
# * invokes <prefix>/lib/<libdir-name>/<LDSO> --library-path …
28+
# <prefix>/libexec/<LIBC_NAME>-<dir>/<name> "$@"
29+
#
30+
# LDSO is auto-detected from `uname -m`. Override via LDSO env var
31+
# if needed (cross-arch host, exotic loader name, etc.).
32+
#
33+
# Refs: pkgxdev/brewkit#344 (RFC), pkgxdev/pantry@5354c73f (the
34+
# inline-in-glibc-recipe origin of this pattern by @jhheider).
35+
36+
set -eo pipefail
37+
38+
CMD=$1; shift
39+
40+
case "$CMD" in
41+
seal)
42+
if [ $# -lt 2 ]; then
43+
echo "bklibcvenv seal: missing args" >&2
44+
echo "usage: $0 seal <prefix> <libdir-name>" >&2
45+
exit 64
46+
fi
47+
48+
PREFIX=$1
49+
LIBDIR_NAME=$2
50+
51+
# Auto-detect ld.so name from arch. Override-able via LDSO env.
52+
if [ -z "${LDSO:-}" ]; then
53+
case "$(uname -m)" in
54+
x86_64) LDSO=ld-linux-x86-64.so.2 ;;
55+
aarch64|arm64) LDSO=ld-linux-aarch64.so.1 ;;
56+
armv7*|armhf) LDSO=ld-linux-armhf.so.3 ;;
57+
i686|i386) LDSO=ld-linux.so.2 ;;
58+
*) echo "bklibcvenv: unsupported arch $(uname -m); set LDSO env" >&2; exit 1 ;;
59+
esac
60+
fi
61+
62+
# libc name from libdir's first dash-separated component.
63+
LIBC_NAME=${LIBDIR_NAME%%-*}
64+
65+
# Locate the wrapper template (share/brewkit/libcvenv-wrapper.sh).
66+
SELF_DIR=$(CDPATH= cd -- "$(dirname "$0")" && pwd)
67+
TEMPLATE="$SELF_DIR/../share/brewkit/libcvenv-wrapper.sh"
68+
if [ ! -f "$TEMPLATE" ]; then
69+
echo "bklibcvenv: wrapper template not found at $TEMPLATE" >&2
70+
exit 2
71+
fi
72+
73+
set -x
74+
75+
for dir in bin sbin; do
76+
[ -d "$PREFIX/$dir" ] || continue
77+
mkdir -p "$PREFIX/libexec/$LIBC_NAME-$dir"
78+
79+
for f in "$PREFIX/$dir"/*; do
80+
[ -f "$f" ] && [ ! -L "$f" ] || continue
81+
# Skip non-ELF (shell scripts, etc.) — patchelf exits non-zero
82+
# when the file isn't an ELF with a PT_INTERP.
83+
patchelf --print-interpreter "$f" >/dev/null 2>&1 || continue
84+
85+
name=$(basename "$f")
86+
mv "$f" "$PREFIX/libexec/$LIBC_NAME-$dir/"
87+
88+
sed \
89+
-e "s|@LDSO@|$LDSO|g" \
90+
-e "s|@LIBDIR@|$LIBDIR_NAME|g" \
91+
-e "s|@LIBC_NAME@|$LIBC_NAME|g" \
92+
-e "s|@DIR@|$dir|g" \
93+
"$TEMPLATE" > "$f"
94+
chmod 775 "$f"
95+
96+
echo "wrapped $f"
97+
done
98+
done
99+
;;
100+
101+
*)
102+
echo "bklibcvenv: unknown subcommand '$CMD'" >&2
103+
echo "usage: $0 seal <prefix> <libdir-name>" >&2
104+
exit 64
105+
;;
106+
esac

share/brewkit/libcvenv-wrapper.sh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/bin/sh
2+
# Template for bklibcvenv-generated wrappers.
3+
#
4+
# Replaced by `bklibcvenv seal`:
5+
# @LDSO@ e.g. ld-linux-x86-64.so.2
6+
# @LIBDIR@ e.g. glibc-2.43 (subdir of $prefix/lib/)
7+
# @LIBC_NAME@ e.g. glibc (prefix for libexec/<libc>-<dir>/)
8+
# @DIR@ e.g. bin (or sbin)
9+
10+
case "$0" in
11+
*/*) bindir=${0%/*} ;;
12+
*) bindir=$(command -v -- "$0"); bindir=${bindir%/*} ;;
13+
esac
14+
15+
prefix=$(CDPATH= cd -- "$bindir/.." && pwd)
16+
libdir="$prefix/lib/@LIBDIR@"
17+
18+
exec "$libdir/@LDSO@" --library-path "$libdir" "$prefix/libexec/@LIBC_NAME@-@DIR@/$(basename "$0")" "$@"

0 commit comments

Comments
 (0)