Skip to content

Commit 5354c73

Browse files
committed
fix(glibc)
1 parent 65f0adb commit 5354c73

1 file changed

Lines changed: 107 additions & 106 deletions

File tree

projects/gnu.org/glibc/package.yml

Lines changed: 107 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -38,31 +38,31 @@ build:
3838
# transitive-deps $ORIGIN RPATH chain onto every ELF in the
3939
# install, INCLUDING ld-linux-*.so.*. The loader parses its own
4040
# RPATH at startup and SIGSEGVs (see pkgxdev/brewkit#345 and the
41-
# diagnostic output in this PR's history). We do the patchelf
42-
# ourselves below for the ELFs that actually need RPATH (bin/*),
43-
# and leave ld.so alone.
41+
# diagnostic output in this PR's history). We handle relocatability
42+
# below: linker scripts use basenames, and glibc-owned ELF tools are
43+
# invoked through relative ld.so wrappers.
4444
skip:
4545
- fix-patchelf
4646

4747
dependencies:
48-
gnu.org/make: '>=4.0'
49-
gnu.org/gawk: '>=3'
50-
gnu.org/gcc: '*' # pkgx ships 10.5–16.1; verified with 16.1
51-
gnu.org/binutils: '>=2.40' # required by glibc 2.42+; older glibcs also build with 2.46
52-
gnu.org/gettext: '*'
53-
gnu.org/texinfo: '*' # required for `make install` (manual/libc.info)
54-
gnu.org/bison: '^3'
55-
gnu.org/sed: '*'
56-
gnu.org/coreutils: '*'
57-
gnu.org/findutils: '*'
58-
gnu.org/grep: '*'
59-
gnu.org/diffutils: '*'
60-
gnu.org/patch: '*'
61-
gnu.org/m4: '*'
62-
perl.org: '^5'
63-
python.org: '~3.11'
64-
kernel.org/linux-headers: '*' # the file that makes the build host-independent
65-
nixos.org/patchelf: '*' # we patchelf bin/* ourselves (see `skip: fix-patchelf` above)
48+
gnu.org/make: ">=4.0"
49+
gnu.org/gawk: ">=3"
50+
gnu.org/gcc: "*" # pkgx ships 10.5–16.1; verified with 16.1
51+
gnu.org/binutils: ">=2.40" # required by glibc 2.42+; older glibcs also build with 2.46
52+
gnu.org/gettext: "*"
53+
gnu.org/texinfo: "*" # required for `make install` (manual/libc.info)
54+
gnu.org/bison: "^3"
55+
gnu.org/sed: "*"
56+
gnu.org/coreutils: "*"
57+
gnu.org/findutils: "*"
58+
gnu.org/grep: "*"
59+
gnu.org/diffutils: "*"
60+
gnu.org/patch: "*"
61+
gnu.org/m4: "*"
62+
perl.org: "^5"
63+
python.org: "~3.11"
64+
kernel.org/linux-headers: "*" # the file that makes the build host-independent
65+
nixos.org/patchelf: "*" # used to detect ELF tools before wrapping them
6666

6767
working-directory: build
6868

@@ -77,8 +77,8 @@ build:
7777
# patch leaves `LD_SO_CACHE` referencing an undefined `PREFIX` macro.
7878
# We therefore gate the patches on the version.
7979
- run:
80-
- patch -p1 < props/dont-use-system-ld-so-cache.patch
81-
- patch -p1 --forward --no-backup-if-mismatch < props/dont-use-system-ld-so-preload.patch || true
80+
- patch -p1 < props/dont-use-system-ld-so-cache.patch
81+
- patch -p1 --forward --no-backup-if-mismatch < props/dont-use-system-ld-so-preload.patch || true
8282
if: ">=2.32"
8383
working-directory: ..
8484

@@ -94,7 +94,7 @@ build:
9494
- run:
9595
- EXTRA_CFLAGS=""
9696
- if [ {{version.major}}{{version.minor}} -lt 232 ]; then
97-
- EXTRA_CFLAGS="-fcommon -Wno-error"
97+
- EXTRA_CFLAGS="-fcommon -Wno-error"
9898
- fi
9999
# glibc decides its own pie/-shared per target — pkgx gcc 16
100100
# is built with --enable-default-pie, so its CC defaults add
@@ -131,63 +131,67 @@ build:
131131
132132
# Convenience: arch-agnostic ld.so symlink that points into the
133133
# sub-libdir (kept out of the top {{prefix}}/lib/).
134-
- run: |
135-
case "{{hw.arch}}" in
136-
x86-64) L=ld-linux-x86-64.so.2 ;;
137-
aarch64) L=ld-linux-aarch64.so.1 ;;
138-
esac
139-
ln -sf ../lib/glibc-{{version.marketing}}/$L ld.so
134+
- run: ln -sf ../lib/glibc-{{version.marketing}}/$LDSO ld.so
140135
working-directory: ${{prefix}}/bin
141136

142137
# libc.so / libm.so / … are TEXT linker scripts (GROUP (...)) with
143-
# +brewing baked into absolute paths. Strip +brewing so downstream
144-
# `-lc` doesn't hit "cannot find +brewing/lib/.../libc.so.6" after
145-
# brewkit's +brewing → final-prefix rename. (Detect via first-line
146-
# marker, not file(1) — not on the test-sandbox PATH.)
138+
# absolute build-prefix paths. Make the GROUP members basenames so
139+
# downstream `-L $LIBDIR -lc` works after the bottle relocates from
140+
# /opt to ~/.pkgx (or anywhere else). Detect via first-line marker,
141+
# not file(1) — not on the test-sandbox PATH.
147142
- run: |
143+
PREFIX_FINAL=$(echo "{{prefix}}" | sed 's/+brewing$//')
148144
for f in $(find . -maxdepth 2 -type f -name '*.so'); do
149145
if head -c 16 "$f" | grep -q "GNU ld script"; then
150-
sed -i 's|+brewing||g' "$f"
151-
echo "stripped +brewing from $f"
146+
sed -i \
147+
-e 's|{{prefix}}/lib/glibc-{{version.marketing}}/||g' \
148+
-e "s|$PREFIX_FINAL/lib/glibc-{{version.marketing}}/||g" \
149+
"$f"
150+
echo "relocated linker script $f"
152151
fi
153152
done
154153
working-directory: ${{prefix}}/lib
155154
156-
# With brewkit's fix-patchelf skipped (see `skip:` at top of build),
157-
# we patchelf the ELFs that need it ourselves:
155+
# PT_INTERP is absolute and cannot use $ORIGIN or fall back. Do not
156+
# bake {{prefix}} or its post-rename variant into installed binaries:
157+
# that breaks when the bottle relocates to ~/.pkgx. Instead, seal the
158+
# glibc-owned ELF tools into libexec and replace bin/* / sbin/* with
159+
# POSIX sh wrappers that invoke this bottle's ld.so by relative path.
158160
#
159-
# bin/*, sbin/* — fix +brewing PT_INTERP, add $ORIGIN-relative
160-
# RPATH so libc.so.6 resolves at runtime
161-
# lib/glibc-X.Y/ld-linux-* — leave alone (it's the loader;
162-
# bootstraps from nothing)
163-
# lib/glibc-X.Y/*.so.* — co-located with libc.so.6, no extra
164-
# RPATH needed (executable's RPATH propagates)
165-
#
166-
# patchelf --set-interpreter exits non-zero on non-ELF inputs;
167-
# we use that to filter shell scripts etc. without needing file(1).
168-
#
169-
# {{prefix}} at build time is the .../v2.43.0+brewing path; brewkit
170-
# renames it to .../v2.43.0 before the bottle ships. PT_INTERP needs
171-
# the *post-rename* path, otherwise the binary references a path
172-
# that no longer exists ("required file not found"). RPATH uses
173-
# $ORIGIN-relative so it survives the rename automatically.
161+
# Running an ELF through ld.so explicitly ignores the ELF's own
162+
# PT_INTERP, so the inner libexec binaries may keep whatever glibc's
163+
# build installed. The wrapper supplies --library-path explicitly.
174164
- run: |
175-
case "{{hw.arch}}" in
176-
x86-64) LDSO=ld-linux-x86-64.so.2 ;;
177-
aarch64) LDSO=ld-linux-aarch64.so.1 ;;
178-
esac
179-
PREFIX_FINAL=$(echo "{{prefix}}" | sed 's/+brewing$//')
180-
LIBDIR_INTERP="$PREFIX_FINAL/lib/glibc-{{version.marketing}}"
181-
RPATH='$ORIGIN/../lib/glibc-{{version.marketing}}'
182-
for f in {{prefix}}/bin/* {{prefix}}/sbin/*; do
183-
[ -f "$f" ] && [ ! -L "$f" ] || continue
184-
if patchelf --set-interpreter "$LIBDIR_INTERP/$LDSO" "$f" 2>/dev/null; then
185-
patchelf --force-rpath --set-rpath "$RPATH" "$f" 2>/dev/null
186-
echo "patchelfd $f"
187-
fi
165+
for dir in bin sbin; do
166+
mkdir -p "{{prefix}}/libexec/glibc-$dir"
167+
for f in "{{prefix}}/$dir"/*; do
168+
[ -f "$f" ] && [ ! -L "$f" ] || continue
169+
patchelf --print-interpreter "$f" >/dev/null 2>&1 || continue
170+
mv "$f" "{{prefix}}/libexec/glibc-$dir"
171+
sed -e "s/@LDSO@/$LDSO/g" -e "s/@DIR@/$dir/g" "$PROP" >$f
172+
chmod 775 $f
173+
echo "wrapped $f"
174+
done
188175
done
176+
prop: |
177+
#!/bin/sh
178+
179+
case "$0" in
180+
*/*) bindir=${0%/*} ;;
181+
*) bindir=$(command -v -- "$0"); bindir=${bindir%/*} ;;
182+
esac
183+
184+
prefix=$(CDPATH= cd -- "$bindir/.." && pwd)
185+
186+
libdir="$prefix/lib/glibc-{{version.marketing}}"
187+
188+
exec "$libdir/@LDSO@" --library-path "$libdir" "$prefix/libexec/glibc-@DIR@/$(basename "$0")" "$@"
189189
190190
env:
191+
x86-64:
192+
LDSO: ld-linux-x86-64.so.2
193+
aarch64:
194+
LDSO: ld-linux-aarch64.so.1
191195
SCRIPTS:
192196
- catchsegv
193197
- ldd
@@ -212,22 +216,23 @@ build:
212216
- --disable-werror
213217
- --without-gd
214218
- --without-selinux
215-
- --enable-kernel=3.10.0 # CentOS 7 / manylinux2014 baseline
219+
- --enable-kernel=3.10.0 # CentOS 7 / manylinux2014 baseline
216220
- --with-headers={{deps.kernel.org/linux-headers.prefix}}/include
217221
- --with-binutils={{deps.gnu.org/binutils.prefix}}/bin
218222
- --disable-multi-arch
219223

220224
# Compile + run test.c against the bottle's libc + ld.so end-to-end.
221225
# Exercises every piece of the bottle: headers, crt files, libc.so.6,
222-
# the loader, and `iconv --version` to confirm the patchelf'd bin/* work.
226+
# the loader, and `iconv --version` to confirm the wrapped bin/* work.
223227
#
224228
# Previously the loader's RPATH was poisoned by brewkit's fix-patchelf
225229
# (a 46-pkg $ORIGIN chain → SIGSEGV at startup; see pkgxdev/brewkit#345).
226-
# Now that we `skip: fix-patchelf` and patchelf bin/* ourselves with
227-
# clean RPATH, the loader bootstraps cleanly and the bin/* work.
230+
# Now that we `skip: fix-patchelf`, keep ld.so untouched, and wrap
231+
# bin/* through that loader, the loader bootstraps cleanly and the
232+
# bin/* work after relocation.
228233
test:
229234
dependencies:
230-
gnu.org/gcc: '*'
235+
gnu.org/gcc: "*"
231236
env:
232237
x86-64:
233238
LDSO: ld-linux-x86-64.so.2
@@ -242,51 +247,47 @@ test:
242247
- test -L "{{prefix}}/bin/ld.so" || { echo "missing bin/ld.so symlink"; exit 1; }
243248

244249
# 2) Loader self-test (was SIGSEGV before the fix-patchelf skip).
245-
- run: |
246-
echo "--- ld.so --version ---"
247-
"$LIBDIR/$LDSO" --version | head -2
250+
- echo "--- ld.so --version ---"
251+
- '"$LIBDIR/$LDSO" --version | head -2'
248252

249253
# 3) Compile test.c dynamically against the bottle's headers /
250254
# libs / loader. -B $LIBDIR picks our Scrt1.o / crti.o / crtn.o
251255
# instead of the host's pre-2.34 versions which reference
252256
# removed __libc_csu_init / __libc_csu_fini.
253-
- run: |
254-
gcc -o test-bottle test.c \
255-
-nostdinc \
256-
-isystem {{prefix}}/include \
257-
-isystem {{deps.gnu.org/gcc.prefix}}/lib/gcc/*-linux-gnu/{{deps.gnu.org/gcc.version}}/include \
258-
-B "$LIBDIR" \
259-
-L "$LIBDIR" \
260-
-Wl,--rpath="$LIBDIR" \
261-
-Wl,--dynamic-linker="$LIBDIR/$LDSO"
262-
readelf -d test-bottle | grep -E "NEEDED|RPATH" | head -5
257+
- gcc -o test-bottle test.c
258+
-nostdinc
259+
-isystem {{prefix}}/include
260+
-isystem {{deps.gnu.org/gcc.prefix}}/lib/gcc/*-linux-gnu/{{deps.gnu.org/gcc.version}}/include
261+
-B "$LIBDIR"
262+
-L "$LIBDIR"
263+
-Wl,--rpath="$LIBDIR"
264+
-Wl,--dynamic-linker="$LIBDIR/$LDSO"
265+
- readelf -d test-bottle | grep -E "NEEDED|RPATH|RUNPATH" | head -5
263266

264267
# 4) Run the dynamically-linked test bin — exercises PT_INTERP,
265268
# the loader, RPATH resolution, libc symbol lookup, printf, etc.
266-
- run: |
267-
set +e
268-
stdout=$(./test-bottle 2>/tmp/test.err)
269-
rc=$?
270-
stderr=$(cat /tmp/test.err)
271-
set -e
272-
echo "exit code: $rc"
273-
echo "stdout: $stdout"
274-
echo "stderr: $stderr"
275-
case "$stdout" in
276-
"gnu_get_libc_version() = {{version.marketing}}"*) echo PASS ;;
277-
*) echo "FAIL: expected gnu_get_libc_version() = {{version.marketing}}, got stdout=\"$stdout\" stderr=\"$stderr\" rc=$rc"; exit 1 ;;
278-
esac
269+
- set +e
270+
- stdout=$(./test-bottle 2>/tmp/test.err)
271+
- rc=$?
272+
- stderr=$(cat /tmp/test.err)
273+
- set -e
274+
- 'echo "exit code: $rc"'
275+
- 'echo "stdout: $stdout"'
276+
- 'echo "stderr: $stderr"'
277+
- case "$stdout" in
278+
- '"gnu_get_libc_version() = {{version.marketing}}"*) echo PASS ;;'
279+
- '*) echo "FAIL: expected gnu_get_libc_version() = {{version.marketing}}, got stdout=\"$stdout\" stderr=\"$stderr\" rc=$rc"; exit 1 ;;'
280+
- esac
279281

280282
# 5) Bonus: one of the bottle's own bin/* should now work end-to-end.
281-
# iconv --version prints "iconv (GNU libc) X.Y" — confirms our
282-
# manual patchelf step gave bin/iconv a usable PT_INTERP + RPATH.
283-
- run: |
284-
out=$("{{prefix}}/bin/iconv" --version 2>&1 | head -1)
285-
echo "bin/iconv --version: $out"
286-
case "$out" in
287-
*"(GNU libc) {{version.marketing}}"*) echo "bin/iconv PASS" ;;
288-
*) echo "FAIL: expected '(GNU libc) {{version.marketing}}' in iconv --version output, got: $out"; exit 1 ;;
289-
esac
283+
# iconv --version prints "iconv (GNU libc) X.Y" — confirms the
284+
# wrapper can find this bottle's relocated ld.so + libc.
285+
- out=$("{{prefix}}/bin/iconv" --version 2>&1 | head -1)
286+
- 'echo "bin/iconv --version: $out"'
287+
- case "$out" in
288+
- '*"(GNU libc) {{version.marketing}}"*) echo "bin/iconv PASS" ;;'
289+
- '*) echo "FAIL: expected (GNU libc) {{version.marketing}} in iconv --version output, got: $out"; exit 1 ;;'
290+
- esac
290291

291292
provides:
292293
# Binaries that are installed across all supported versions

0 commit comments

Comments
 (0)