swm_version: 1 base: distro_version: dev pins: {} mutations: - type: source_patch tarball: https://ftp.gnu.org/gnu/gcc/gcc-15.2.0/gcc-15.2.0.tar.xz sha256: 438fd996826b0c82485a29da03a72d71d6e3541a83ec702df4271f6fe025d24e build: compiler: gcc target: x86_64-linux-musl link: dynamic flags: [] phases: configure: | set -e # GCC EXIGE construir fuera del árbol de fuentes; si no, el configure aborta. mkdir -p build && cd build # Los flags de ABI son los de Alpine (`main/gcc`, verificado el 2026-09-06). No son preferencias: # esta librería tiene que ser intercambiable con la que el lab le dio a firefox al enlazar. ../configure \ --prefix=/usr \ --build=x86_64-alpine-linux-musl \ --host=x86_64-alpine-linux-musl \ --target=x86_64-alpine-linux-musl \ --enable-languages=c,c++ \ --disable-bootstrap \ --disable-symvers \ --disable-libstdcxx-pch \ --disable-multilib \ --disable-nls \ --disable-werror \ --disable-cet \ --disable-fixed-point \ --disable-libssp \ --disable-libsanitizer \ --disable-libgomp \ --disable-libitm \ --disable-libquadmath \ --enable-__cxa_atexit \ --enable-default-pie \ --enable-default-ssp \ --enable-shared \ --enable-threads \ --enable-tls \ --enable-checking=release \ --with-linker-hash-style=gnu compile: | set -e cd build # SÓLO los target libs. `all` construiría el compilador entero, que no queremos y que multiplica el # tiempo. Make resuelve solo que para llegar acá hace falta un xgcc de etapa 1. make -j"$(nproc)" all-target-libgcc all-target-libstdc++-v3 install: | set -e cd build make install-target-libgcc install-target-libstdc++-v3 DESTDIR=/out # ⚠ GCC INSTALA EN `lib64`, Y `--disable-multilib` NO LO APAGA. En x86_64 el `t-linux64` de GCC fija # `MULTILIB_OSDIRNAMES = ../lib64`, así que el install aterriza en `/usr/lib/../lib64/` — o sea # `/usr/lib64/`— por más que se haya desactivado el multilib. Esta distro es `lib`-only, como el # Alpine del que sale el lab, así que se normaliza acá. # # No es cosmético: el enlazador dinámico busca en `/usr/lib`, y un `.so` perfecto en `/usr/lib64` # es exactamente el fallo que esta receta viene a arreglar, sólo que un directorio más allá. Lo # cazó el guardián 1 en la primera corrida (`!! falta libstdc++.so.6`), que es para lo que está. if [ -d /out/usr/lib64 ]; then mkdir -p /out/usr/lib cp -a /out/usr/lib64/. /out/usr/lib/ # `-a` preserva los symlinks .so.6 -> .so.6.0.34 rm -rf /out/usr/lib64 fi # ── libgcc.a SE SALVA DE LA PODA, Y ES UNA CORRECCIÓN (2026-09-17) ─────────────────────────── # `/usr/lib/libgcc_s.so` que esta receta instala **no es una librería: es un ld script** # # GROUP ( libgcc_s.so.1 -lgcc ) # # y ese `-lgcc` es `libgcc.a`, que NINGUNA receta del corpus proveía —ésta la borraba con el # `find -name '*.a' -delete` de acá abajo, y de paso `rm -rf /usr/lib/gcc` se llevaba el original—. # Consecuencia medida en la caja de producción con el `rust` recién sellado: los binarios ESTÁTICOS # compilan y corren, pero **cualquier dylib —y eso incluye TODAS las proc-macros— muere**: # # ld.lld: error: unable to find library -lgcc # # O sea `cargo build` con `derive` (serde, clap…) imposible sobre takana, con el compilador perfecto. # Es la misma familia que el hueco que originó esta receta: todo presente, todo reproducible, y el # camino que hace falta no existe. Y no alcanza con dar `-L /usr/lib`: con eso se resuelven `-lc` y # `-lgcc_s`, y entonces aparece justo este `-lgcc`. # # Va a `/usr/lib` —no al directorio privado de gcc— porque es donde el `-L` del enlazador ya mira, y # porque el gcc completo no está en esta distro: acá la librería tiene que valerse sola. A=$(find /out -name libgcc.a | head -1) test -n "$A" || { echo "!! el install no dejó ninguna libgcc.a — sin ella el ld script de libgcc_s miente" >&2; exit 1; } cp "$A" /out/usr/lib/libgcc.a # Se PODA todo lo que no sea runtime: headers, el resto de los `.a`, `.la`, libsupc++, los `.py` del # pretty-printer. Esta receta existe para que un binario ARRANQUE —y ahora también para que el # `libgcc_s.so` que envía sea ENLAZABLE—, no para compilar C++ contra ella; dejar los headers # invitaría a que otra receta se enlace contra esto en vez de contra el lab, que es una decisión # distinta y no la que se está tomando acá. find /out -name '*.la' -delete rm -rf /out/usr/include /out/usr/share /out/usr/lib/gcc find /out -name '*.a' ! -name 'libgcc.a' -delete find /out -name '*.py' -delete L=/out/usr/lib test -d "$L" || { echo "!! no se instaló nada en $L" >&2; find /out -maxdepth 3 >&2; exit 1; } # ── GUARDIÁN 1: los dos sonames, y que sean los que el loader busca ────────────────────────── # No alcanza con que exista un fichero llamado así: lo que el enlazador dinámico usa es el SONAME # de dentro. Un `.so` bien nombrado con otro soname no lo encuentra nadie. for so in libstdc++.so.6 libgcc_s.so.1; do f=$(find "$L" -name "$so*" -o -name "$so" | head -1) test -n "$f" || { echo "!! falta $so" >&2; ls -la "$L" >&2; exit 1; } real=$(readelf -d "$f" 2>/dev/null | sed -n 's/.*SONAME.*\[\(.*\)\].*/\1/p') test "$real" = "$so" || { echo "!! $f tiene SONAME '$real' y esperaba '$so'" >&2; exit 1; } echo "guardián: $so ok (SONAME correcto, $(stat -Lc%s "$f") bytes)" done # ── GUARDIÁN 2: LA PRUEBA DEL CONSUMIDOR ───────────────────────────────────────────────────── # «Existe» no es «sirve». Se exige que estén DEFINIDOS los símbolos exactos que firefox no # encontraba — copiados del fallo real de `atuq`, no inventados. Si esta receta sella sin ellos, # el navegador vuelve a morir igual y el artefacto habría dicho que todo bien. # ⚠ EL SÍMBOLO PUEDE VENIR VERSIONADO, Y LA PRIMERA VERSIÓN DE ESTE GUARDIÁN NO LO CONTEMPLABA. # `--disable-symvers` desactiva el versionado de **libstdc++**, pero `libgcc` tiene su PROPIO # `libgcc.map` y sigue versionando pase lo que pase. Así que `nm` los imprime como # `_Unwind_Backtrace@@GCC_3.3`, y un grep anclado al fin de línea NO los encuentra: el guardián # reportaba «no definen los símbolos» sobre una librería que los definía perfectamente. # Comprobado contra el `libgcc_s.so.1` DEL LAB —el que hoy satisface a firefox— y coinciden hasta # en la etiqueta de versión: `_Unwind_Backtrace@@GCC_3.3`, `__udivti3@@GCC_3.0`. falta="" for s in _ZNKSt5ctypeIcE13_M_widen_initEv _ZNSt13runtime_errorC1EPKc _ZNKSt9bad_alloc4whatEv; do nm -D --defined-only "$L"/libstdc++.so.6* 2>/dev/null | grep -qE " $s(@|\$)" || falta="$falta $s" done for s in _Unwind_Backtrace _Unwind_GetIP __udivti3; do nm -D --defined-only "$L"/libgcc_s.so.1* 2>/dev/null | grep -qE " $s(@|\$)" || falta="$falta $s" done test -z "$falta" || { echo "!! los .so están pero NO definen los símbolos que firefox pide:$falta" >&2 echo " (son los del fallo real de atuq; si cambiaron, revisá --disable-symvers)" >&2 exit 1 } echo "guardián: los 6 símbolos que firefox no encontraba están DEFINIDOS" # ── GUARDIÁN 3: EL LD SCRIPT NO PUEDE MENTIR ───────────────────────────────────────────────── # `libgcc_s.so` es un ld script que manda buscar `-lgcc`. Si esa librería no viaja con él, el # artefacto sella, reproduce, y la primera dylib que alguien enlace en una caja takana muere. Se # comprueba lo que el script PIDE contra lo que el artefacto TRAE, no la intención. if grep -q "GROUP" "$L/libgcc_s.so" 2>/dev/null; then grep -q -- "-lgcc" "$L/libgcc_s.so" && test -f "$L/libgcc.a" || { echo "!! $L/libgcc_s.so es un ld script que pide -lgcc y no hay libgcc.a al lado" >&2 cat "$L/libgcc_s.so" >&2; exit 1; } echo "guardián: el ld script pide -lgcc y libgcc.a viaja con él ($(stat -Lc%s "$L/libgcc.a") bytes)" fi target_bin: /usr/bin/gcc-libs expected_hash: b3:5ddfd93c4d475e708d77c0ca2fe08f74631d21fe44a2a90cda1f7c49722c4300 deps: build: - make - m4 - gmp - mpfr - mpc runtime: []