Skip to content

fix(gnu.org/gcc): add libc-wrapper for end-user pkgx gcc (#8423)#13094

Open
tannevaled wants to merge 5 commits into
pkgxdev:mainfrom
tannevaled:fix/gcc-libc-wrapper
Open

fix(gnu.org/gcc): add libc-wrapper for end-user pkgx gcc (#8423)#13094
tannevaled wants to merge 5 commits into
pkgxdev:mainfrom
tannevaled:fix/gcc-libc-wrapper

Conversation

@tannevaled
Copy link
Copy Markdown
Contributor

Summary

Adds a thin POSIX-sh wrapper at {{prefix}}/bin/<tool> (gcc, g++, cpp, c++, gfortran) that injects -isystem $glibc/include -L$glibc/lib for end-user invocations, without poisoning gcc's own bootstrap build. The real binaries are moved to {{prefix}}/libexec/gcc-wrap/. Adds gnu.org/glibc: '<2.38' + kernel.org/linux-headers: '*' as Linux deps so pkgx provisions a sibling glibc bottle the wrapper can find.

Why this approach

Previous attempts at #8423:

This take avoids both pitfalls because the wrapper:

  • is only added to the FINAL installed gcc (no build-time poisoning)
  • NO-OPs when CPATH is already set (brewkit build context — guards future bootstrap loops)
  • resolves the sibling glibc bottle path lazily via $0-relative lookup, not via baked-in configure flags
  • pins glibc <2.38 to avoid C23 symbol redirects (__isoc23_*) absent on older runners

Pattern is modeled on the bklibcvenv shim (brewkit#348).

Pairs with

Test plan

  • CI builds gcc on linux/x86-64 + linux/aarch64 (cross + native)
  • Verify pkgx gcc -E -x c /dev/null succeeds without distro glibc-devel
  • On a stock Fedora rootfs without glibc-devel, pkgx gcc test.c -o test && ./test succeeds
  • pkgx +gnu.org/gcc cc -v 2>&1 | grep gnu.org/glibc confirms the wrapper found the bottle
  • Bootstrap of gcc from a wrapped gcc (next gcc version build) doesn't double-inject
  • Darwin build unchanged (wrapper guarded by if: linux)

🤖 Generated with Claude Code

tannevaled and others added 5 commits May 29, 2026 20:03
End-user `pkgx gcc test.c` on a host without distro glibc-devel /
libc6-dev couldn't find stdlib.h + crt*.o. Earlier attempts via
runtime.env (pkgxdev#13084) and --with-sysroot (pkgxdev#13092) failed because both
poisoned gcc's own bootstrap build.

This take installs a thin POSIX-sh wrapper at {{prefix}}/bin/<tool>
(modeled on the bklibcvenv pattern, brewkit#348) that:

  1. resolves the sibling gnu.org/glibc bottle relative to its own
     install path
  2. exec's the real binary (moved to libexec/gcc-wrap/) with
     -isystem $glibc/include + -L$glibc/lib appended
  3. NO-OPs when CPATH is already set (brewkit build-context — the
     bootstrap gcc loop uses brewkit-composed CPATH, so injecting
     pkgx-glibc paths twice is unnecessary and could trigger
     C23 symbol mismatches like __isoc23_strtoul)

Pinning gnu.org/glibc <2.38 avoids C23 symbol redirects absent on
older CI/host runners; the wrapper's runtime CPATH check guards
against build-time poisoning regardless.

Wrappers cover gcc, g++, cpp, c++, gfortran. The existing
cc -> gcc and gc++ -> c++ symlinks still resolve (now via the
wrapper). The wrapper dispatches by $0 basename.

Pairs with pkgxdev#13083 (multi-arch triplet symlinks). Together they
should fully close pkgxdev#8423.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The runtime dep on gnu.org/glibc poisoned gcc's own bootstrap build:
brewkit exposes runtime deps via CPATH/LIBRARY_PATH during build too,
so configure-time test programs end up compiling against pkgx libc
2.34 but running on the host's ld-linux — and ld-linux finds host
libc 2.35 in the default path even with LD_LIBRARY_PATH set,
producing `cannot run C++ compiled programs`.

The wrapper itself doesn't need the dep declared — it does a runtime
sibling lookup (`bindir/../../../glibc/v*`) that no-ops if no pkgx
glibc is present. Users who want the bottled libc explicitly add it:

    pkgx +gnu.org/gcc +gnu.org/glibc gcc test.c

This trades automatic resolution for a clean bootstrap. Closing
this trade-off the "right" way needs a pkgx feature to separate
runtime-only deps from build-time-injected deps.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When the wrapper detects a sibling pkgx glibc bottle, it now also
passes:
  -Wl,--dynamic-linker=$glibc/lib/ld-linux-*.so.*
  -Wl,-rpath,$glibc/lib

so the produced binary's ELF interpreter (PT_INTERP) points at pkgx
ld-linux. End-to-end consistency: the same libc is used at compile,
link, and exec. Without this, the binary's PT_INTERP would be the
host's /lib64/ld-linux-x86-64.so.2 and the kernel would load host
ld-linux at exec time — even with -L pointing at pkgx libc and
rpath set — mixing libc internal layouts across host/pkgx.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When a pkgx glibc sibling is found, wrapper now also passes -nostdinc
(and -nostdinc++ for g++/c++) to drop /usr/include from gcc's search
path, then re-adds:

  - gcc's own builtin headers (lib/gcc/<triplet>/<ver>/include[+-fixed])
  - libstdc++ headers for C++ tools (include/c++/<ver>[/<triplet>])
  - pkgx glibc headers via -isystem

Without -nostdinc, gcc still found /usr/include/stdlib.h first because
-isystem only appends to the search chain — so pkgx-glibc headers were
shadowed by whatever the host had. Modeled on Nix's cc-wrapper.sh
(pkgs/build-support/cc-wrapper/). See pkgxdev#8423.

The wrapper stays opportunistic (CPATH unset + sibling glibc), so the
gcc bootstrap loop is unaffected.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The previous shape moved the real gcc binary to libexec/gcc-wrap/,
which broke gcc's relative lookup for cc1 / cc1plus / collect2 (gcc
expects them at lib/gcc/<triplet>/<version>/ relative to its OWN
location). Result: `cannot execute 'cc1': No such file or directory`
during fixincludes at make-install time.

Keep the real binary at bin/.${tool}-real (hidden by leading dot
to avoid cluttering bin/) and have bin/<tool> point at the wrapper.
cc1 lookup then resolves the same way it always did.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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