From 22b101dc47b903699e0ecaaad811626f77db59f7 Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Wed, 19 Aug 2026 08:30:49 -0400 Subject: [PATCH] [FIX] gcc: two defects only a chroot could find MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our gcc package was built a dozen times and never once used to compile anything -- stage 1 compiles with Ubuntu's gcc. The first program it was ever asked to link was in the stage-2 chroot, and it failed twice. collect2: fatal error: cannot find 'ld' /usr/bin/ld was present, executable, on PATH, and ran as the same user. -B, COMPILER_PATH and three symlinks changed nothing. strace settled it in one line: it was looking for s390x-linux-gnu-ld, the DEBIAN triplet, and printing the generic name. gcc's configure had recorded Ubuntu's copy, a file no Arch system will ever have. --with-ld and --with-as now pin absolute paths, correct on the host, in the chroot and on the target alike. /usr/bin/ld: cannot find -lgcc_s package_gcc() moves libgcc_s{,_asneeded}.so three directories deeper. The asneeded one is a linker script and survives; libgcc_s.so is a symlink naming its target relative to /usr/lib, so after the move it points at nothing. The only libgcc_s.so on the system was dangling, so -lgcc_s could not resolve and nothing linked at all -- not even int main(void){return 0;}. Verified: the chroot now compiles and runs a program. --- FR --- Notre paquet gcc a été bâti une douzaine de fois sans jamais servir à compiler : l'étage 1 compile avec le gcc d'Ubuntu. Le premier programme qu'on lui a demandé de lier l'a été dans le chroot d'étage 2, et il a échoué deux fois. collect2: fatal error: cannot find 'ld' /usr/bin/ld était présent, exécutable, dans le PATH, et répondait pour le même utilisateur. -B, COMPILER_PATH et trois liens symboliques n'ont rien changé. strace a tranché en une ligne : il cherchait s390x-linux-gnu-ld, le triplet DEBIAN, en affichant le nom générique. La configuration de gcc avait enregistré la copie d'Ubuntu, un fichier qu'aucun système Arch n'aura jamais. --with-ld et --with-as épinglent désormais des chemins absolus, corrects sur l'hôte, dans le chroot et sur la cible. /usr/bin/ld: cannot find -lgcc_s package_gcc() déplace libgcc_s{,_asneeded}.so trois répertoires plus bas. Le second est un script ld et survit ; libgcc_s.so est un symlink dont la cible est relative à /usr/lib, si bien qu'après le déplacement il ne pointe plus sur rien. Le seul libgcc_s.so du système pendait dans le vide : -lgcc_s ne pouvait se résoudre et plus rien ne se liait — pas même int main(void){return 0;}. Vérifié : le chroot compile et exécute désormais un programme. Assisted-by: Claude Opus 5 --- patches/pkgbuild/gcc.sh | 90 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/patches/pkgbuild/gcc.sh b/patches/pkgbuild/gcc.sh index f2b77f6..f2db2d5 100755 --- a/patches/pkgbuild/gcc.sh +++ b/patches/pkgbuild/gcc.sh @@ -311,3 +311,93 @@ for m in re.finditer(r"depends=\(([^)]*)\)", s): assert not left, "gcc: dropped names still depended on: %s" % sorted(set(left)) print("gcc: depends pruned of %d dropped components" % len(drop)) PY + +# --with-ld / --with-as: our gcc looked for a Debian-named linker. +# +# Found only by building a chroot from stage 1 and trying to compile in it: +# +# collect2: fatal error: cannot find 'ld' +# +# THE MESSAGE NAMES THE WRONG FILE. /usr/bin/ld was present and executable, +# ld --version ran as the same user, and PATH contained /usr/bin. strace +# settled it in one line: +# +# newfstatat("/usr/bin/s390x-linux-gnu-ld") = ENOENT +# +# collect2 was not looking for `ld`. It was looking for +# `s390x-linux-gnu-ld` -- the DEBIAN triplet -- and printing the generic name +# in its error. gcc -dumpmachine says s390x-ibm-linux-gnu, so the prefix is +# not the target either; it is what gcc's configure recorded after finding +# Ubuntu's /usr/bin/s390x-linux-gnu-ld, which that host ships and no Arch +# system ever will. +# +# THE REASON STAGE 1 NEVER NOTICED is that every stage-1 build ran on the +# host, where that file exists. The defect was in the shipped compiler all +# along and only a chroot could see it -- the same lesson as the missing +# dynamic linker, one layer up: sixty-eight successful builds proved nothing +# about whether the compiler works anywhere else. +# +# Absolute paths rather than the canonical triplet, because they are correct +# in BOTH places: /usr/bin/ld is Ubuntu's during stage 1 and ours in the +# chroot and on the target. A triplet-prefixed symlink would have papered +# over it and shipped a gcc that still needs a Debian-named tool. +python3 - <<'PY' +import io +s = io.open("PKGBUILD", encoding="utf-8").read() +assert "--with-ld=" not in s, "gcc: linker already pinned, hook ran twice" +# _confflags is an ARRAY, shared by both configure passes -- the compiler and +# libgccjit -- so one insertion covers both, and no line-continuation +# backslash belongs on the new entries. +anchor = " --with-linker-hash-style=gnu\n" +n = s.count(anchor) +assert n == 1, "gcc: expected one --with-linker-hash-style in _confflags, found %d" % n +s = s.replace(anchor, anchor + " --with-ld=/usr/bin/ld\n --with-as=/usr/bin/as\n", 1) +io.open("PKGBUILD", "w", encoding="utf-8").write(s) +print("gcc: ld and as pinned to absolute paths") +PY +grep -q -- '--with-ld=/usr/bin/ld' PKGBUILD || { + echo "gcc: --with-ld not inserted" >&2; exit 1; } + +# libgcc_s.so: the PKGBUILD moves a RELATIVE symlink into a deeper directory. +# +# /usr/bin/ld: cannot find -lgcc_s: No such file or directory +# +# package_gcc() does, at the end: +# +# mv -v usr/lib/libgcc_s{,_asneeded}.so $_libdir/ +# +# where $_libdir is /usr/lib/gcc//16. The two files are not alike. +# libgcc_s_asneeded.so is a linker SCRIPT -- `INPUT ( AS_NEEDED ( -lgcc_s ) )` +# -- so it survives being moved anywhere. libgcc_s.so is a SYMLINK whose +# target is the bare name `libgcc_s.so.1`, i.e. the same directory. That was +# true in /usr/lib, where libgcc_s.so.1 lives; three directories deeper it +# points at nothing. +# +# So -lgcc_s cannot resolve: the only libgcc_s.so on the system is a dangling +# link, and the asneeded script that ld reads instead asks for -lgcc_s again. +# Nothing links, including the trivial `int main(void){return 0;}`. +# +# WHY NOTHING CAUGHT THIS EARLIER, and it is the same reason as the linker +# lookup above: stage 1 compiles with UBUNTU's gcc. Our gcc package was built +# a dozen times and never once used to compile anything. The first program it +# was ever asked to link was in the stage-2 chroot. +# +# The link is repointed rather than the mv removed, because $_libdir is where +# gcc's own search path expects it -- that part of the PKGBUILD is right. +# Three levels up from /usr/lib/gcc//16 is /usr/lib. +python3 - <<'PY' +import io +s = io.open("PKGBUILD", encoding="utf-8").read() +old = ' mv -v usr/lib/libgcc_s{,_asneeded}.so $_libdir/\n' +assert s.count(old) == 1, "gcc: libgcc_s move not in the expected form" +fix = old + ( + ' # The moved symlink named its target relative to /usr/lib; from\n' + ' # $_libdir that resolves to nothing. Repoint it, or -lgcc_s cannot\n' + ' # be satisfied and the compiler links nothing at all.\n' + ' ln -sfv ../../../libgcc_s.so.1 $_libdir/libgcc_s.so\n') +s = s.replace(old, fix, 1) +io.open("PKGBUILD", "w", encoding="utf-8").write(s) +PY +grep -qF 'ln -sfv ../../../libgcc_s.so.1' PKGBUILD || { + echo "gcc: libgcc_s.so link not repointed" >&2; exit 1; } +echo "gcc: libgcc_s.so repointed to ../../../libgcc_s.so.1"