dwarves llama a 15 `dwfl_*` distintas desde dwarf_loader.c, así que libdw sola
no alcanza. libdwfl SÍ arrastra argp (argp-std.c) y fts (linux-kernel-modules.c)
⇒ la receta declara `argp-standalone` y `musl-fts`. Esos objetos casi nunca se
enlazan (dwarves no usa dwfl_standard_argp ni dwfl_linux_kernel_*) pero tienen
que COMPILAR.
Dos cosas que costaron:
· libdwfl no se puede construir suelto: su Makefile lee los .manifest de otros
subdirs. Hay que seguir el orden de SUBDIRS del raíz:
lib → libelf → libcpu → backends → libebl → libdwelf → libdw → libdwfl.
· `rm -f compat/argp.h`: el shim de la canónica es sólo tipos/decls y no declara
`argp_failure`, que usa argp-std.c. Como esta variante trae argp de verdad, hay
que BORRAR el shim para que gane el /usr/include/argp.h real — `-I$PWD/compat`
va primero y si no lo tapa.
Tercera pieza del frente libdw/musl. `libdwfl` de elfutils usa fts en
linux-kernel-modules.c, y dwarves necesita libdwfl.
⚠ Sus símbolos casi nunca se ENLAZAN (dwarves no llama dwfl_linux_kernel_*, así
que ese objeto no sale del archive), pero el fichero tiene que COMPILAR: hace
falta un <fts.h> real con los campos y constantes que usa. Escribir ese header
a mano es fácil de hacer mal en silencio; traer la implementación real es más
honesto.
Se compila a mano como musl-obstack. Gotcha menor: fts.c hace
`#include "config.h"` con COMILLAS (obstack.c lo hace con ÁNGULOS).
Tercera pata del frente libdw/musl, para `dwarves`.
VARIANTE y no tocar `recipes/elfutils.toml`: de la canónica cuelgan los CUATRO
kernels (su tools/objtool enlaza -lelf) y moverle el hash los invalidaría.
Mismo patrón que openssl-threads.
⚠ La canónica avisa que «libdw/libdwfl arrastran argp/obstack/fts, lo
verdaderamente difícil en musl». Es cierto de `libdwfl` y de `src/`, pero NO de
`libdw` a secas, que es lo único que pide dwarves. Verificado sobre el fuente:
· `argp_`/`fts_open` → sólo en libdwfl/, CERO en libdw/.
· `obstack` → en libdw/ aparece UNA vez y es dentro de un COMENTARIO de
libdwP.h: elfutils lleva su propia «simplified thread-local reimplementation
of obstacks» y no incluye <obstack.h> en ningún sitio.
⇒ los stubs de compat/ que ya montaba la receta bastan; libdw compila tal cual.
Verificado: sella y libdw.a exporta 129 símbolos `dwarf_*` reales
(dwarf_begin, dwarf_begin_elf, dwarf_getelf…), más dwarf.h/libdw.h/known-dwarf.h.
Segunda pata del frente libdw/musl: `dwarves` hace `find_package(argp REQUIRED)`
y musl no trae argp. Es lo que Alpine empaqueta como `argp-standalone`.
Se compila a mano (sin autotools, como musl-obstack) y SÓLO los siete
`argp-*.c` que el propio Makefile.am pone en `libargp_a_SOURCES`. Los otros .c
del tarball (mempcpy/strchrnul/strndup/strcasecmp) son LIBOBJS de autoconf —
reemplazos para sistemas que NO los tienen— y musl SÍ los tiene, así que
incluirlos duplicaría símbolos de la libc.
Gotcha: `UNUSED` no es del código, lo define el `acinclude.m4` de autoconf. Sin
él las firmas `char* arg UNUSED` no parsean y salen `expected ')'` y varios
`undeclared identifier 'state'` — errores que parecen del código y son de
config. Va en el config.h a mano.
Primera pieza del frente libdw/musl (para `dwarves`). La necesitan DOS cosas:
`libdw` de elfutils (su libdwP.h mete `struct obstack` en el Dwarf) y el propio
`dwarves`, que hace `find_package(obstack REQUIRED)`. Es lo que Alpine
empaqueta como `musl-obstack`.
Se compila A MANO: el tarball trae Makefile.am pero no un `configure` generado,
y el corpus no tiene autoconf/automake/libtool. Son dos .c.
Dos gotchas resueltos:
· `obstack.c` hace `#include <config.h>` SIN guarda (fuera de _LIBC) ⇒ hay que
fabricarle uno. Lo único que mira es HAVE_LIBINTL_H, que musl no tiene, así
que el config.h mínimo vale.
· Es `<config.h>` con ÁNGULOS, no comillas ⇒ no basta con dejarlo en el cwd,
hace falta `-I.` en el compile.
El nombre importa: dwarves lo busca con `find_library(NAMES obstack)` en
/usr/lib ⇒ instala /usr/lib/libobstack.a y obstack.h.
Los añadí durante el diagnóstico y luego los verifiqué uno a uno quitándolos:
la receta construye igual sin ellos. Dejarlos era cargo-cult, y encima el
comentario afirmaba cosas que mis propias pruebas ya habían desmentido.
La causa real es UNA y las dos únicas piezas necesarias siguen ahí: quitar la
dep `linux-headers` (Linux 6.16 tapando los headers 7.1 del rootfs) y el
cherry-pick de upstream para el `io_uring_buf_reg` de 7.1.
Ambos descartes quedan escritos en la receta para que nadie los re-intente:
`--enable-bundled=yes` no puede ganar porque zig-cc impone sus headers por
delante de cualquier `-isystem`, y la versión de zig es irrelevante porque los
headers que mandan son los del ROOTFS.
Verificado tras la limpieza: sella, y `strace -c /bin/true` traza 30 syscalls.
Dos cambios, una causa: el sandbox sirve headers UAPI MÁS NUEVOS de lo que
strace 6.19 espera, y la receta traía una dep que los ensombrecía con otros
más VIEJOS.
1) Fuera `linux-headers` de [deps]. Esa receta instala Linux 6.16 en
/usr/include y TAPA los headers del rootfs (7.1). Con ella faltaban 35
constantes —18 de btrfs.h, 17 de input-event-codes.h— y como los xlat de
strace son `#unconditional` (sin guardas #ifdef), una constante ausente NO
degrada la traza: rompe la compilación. Comprobado que no aporta NI UN
header que el rootfs/zig no traigan ya (0 ficheros exclusivos).
2) `strace-io-uring-7.1.patch`: Linux 7.1 añadió `min_left` a
`struct io_uring_buf_reg` y cambió `__u64 resv[3]` por `__u32 resv[5]`, así
que `CHECK_TYPE_SIZE(arg.resv, sizeof(uint64_t)*3)` saltaba como
`static assertion failed`. El cambio es el de UPSTREAM (strace master ya lo
trae), mismo criterio que `linux-headers-7.0.patch`, que también es un
cherry-pick.
⚠ El fallo se destapa POR CAPAS: make abortaba en btrfs.o, así que las 17
constantes de evdev ni se compilaban, y al arreglar btrfs "aparecían" las de
evdev como si el arreglo no hubiera servido. Y sólo tras resolver las 35 se
llegó a compilar io_uring.c y salió el static_assert. Una causa, tres caras.
⚠ `--enable-bundled=yes` NO sirve acá aunque el compilador lo sugiera: zig-cc
impone sus headers por delante de cualquier `-isystem`, así que los bundled de
strace no pueden ganar. Se probó: la opción se aplica (`checking whether to use
bundled linux kernel headers... yes`) y el assert sigue.
Verificado: sella, 0 undeclared, 0 asserts, y el binario TRAZA de verdad —
`strace -c /bin/true` cuenta 30 syscalls.
El módulo es `github.com/yyyar/gobetween/main` y el paquete main está en la
raíz, así que `go install` —que nombra el binario por el último elemento de la
ruta del módulo— lo dejaba como `/usr/bin/main`. Un `/usr/bin/main` en una
distro choca con cualquier cosa y no dice qué es.
La fase `install` que genera hammer para Go es `true` (el binario ya lo puso
`go install` en GOBIN=/out/usr/bin), así que definirla sólo añade el renombrado.
Los `test -x` no son adorno: si `go install` cambiara el nombre, sin ellos el
`mv` fallaría y se sellaría un artefacto SIN binario — regla 3 del repo, que
falle ruidosamente.
Verificado: /usr/bin/gobetween (33 M), arranca e imprime su versión, y no queda
ningún fichero `main` en el artefacto.
`github.com/eric-lindau/udpfacade` (que importa src/server/udp) ya no existe:
GitHub da «Repository not found» y proxy.golang.org 404 para esa versión, así
que `go mod vendor` moría y la receta era INCONSTRUIBLE. No se arregla subiendo
de versión: master de gobetween pinea el mismo módulo.
Se redirige con `replace` al único superviviente, illarion/udpfacade.
⚠ Apunta a MASTER (031998cc71fa), no al commit pineado. El original
(d8c1c27add169599654f71a6c62d13cde1c51e50) SÍ existe como objeto en illarion
—los forks comparten almacén y la API de GitHub lo muestra— pero NO es
alcanzable desde ninguna ref, y `go` sólo resuelve pseudo-versiones alcanzables
desde una ref. Comprobado: `unknown revision d8c1c27add16`. (`git fetch <sha>`
sí lo trae; `go` no usa esa vía.)
⚠ SEGURO y comprobado, no supuesto: entre master y el commit pineado hay TRES
commits y el diff entero es `udp.go` +2 y `udp_test.go` +2 —sólo la línea
`// +build !windows`— más `udp_windows.go` nuevo con `// +build windows`. Para
GOOS=linux el código compilado es IDÉNTICO, y esto construye x86_64-linux-musl.
Va como PATCH y no como fase porque `vendor_go_deps` corre en el fetch
(lib.rs:277), antes de las fases; los patches se aplican antes (lib.rs:224).
Verificado: el patch aplica limpio sobre un checkout pristino, hammer sella, y
el binario arranca e imprime su versión.
samba.org borró `rsync-3.4.4.tar.gz` (404) y en ese directorio ya sólo queda
3.5.0. Tampoco estaba en nuestro mirror, así que la receta era INCONSTRUIBLE:
la única fuente MUERTA del corpus según docs/state/fuentes-vigia.json (1164
vivas / 3 muertas; las otras dos son un 502 transitorio de savannah).
⚠ NO es «cambiar el sha256 para tapar un 404» (lo prohibido por ADR 0013): el
tarball es OTRO, con su versión y su hash propios. Provenance verificada de
verdad, no asumida:
gpg --verify rsync-3.5.0.tar.gz.asc rsync-3.5.0.tar.gz
→ Good signature from "Andrew Tridgell <andrew@tridgell.net>"
RSA key 9FEF112DCE19A0DC7E882CB81BB24997A8535F6F
Clave traída de keys.openpgp.org; su fingerprint coincide con el `issuer` del
.asc. El aviso «User ID not certified» es esperable (no hay web-of-trust acá);
la firma en sí es válida.
Comprobado ANTES de subir: las diez opciones de configure siguen existiendo en
3.5.0, `rrsync.1` sigue en la raíz, y dont-use-nobody.patch aplica limpio
(`patch -p1 --dry-run` sin rechazos).
Verificado: sella, artefacto de 3,3 M con rsync/rrsync/rsync-ssl, y el binario
responde `rsync version 3.5.0 protocol version 32`.
CPython abortaba `_ssl` con «OPENSSL_THREADS is not defined, Python requires
thread-safe OpenSSL», y la receta lo daba por imposible sin rehacer openssl.
CAUSA RAÍZ, probada: no es openssl, es `link = "static"`.
`hammer-build/src/lib.rs:285` inyecta `LDFLAGS=-static`, y el Configure de
openssl (línea 1514) hace:
if (grep { $_ =~ /(?:^|\s)-static(?:\s|$)/ } @{$config{LDFLAGS}}) {
disable('static', 'pic', 'threads');
}
⇒ el propio `-static` apaga los threads en silencio. Añadir `threads` explícito
NO sirve (el disable() va después y gana): el configuration.h salía byte a byte
idéntico. Dentro del sandbox `configdata.pm` decía `thread_scheme => pthreads`
pero `THREADS_DEFINE=0`; fuera del sandbox, los mismos flags —y hasta con el
perl del corpus— SÍ definían el macro. La diferencia era el entorno.
`openssl-threads`: misma receta con `link = "dynamic"` (mantiene `no-shared`,
sigue emitiendo .a). Verificado: OPENSSL_THREADS definido y 36 símbolos
pthread_ reales en libcrypto.a.
VARIANTE y no tocar `recipes/openssl.toml`: de la canónica cuelgan los CUATRO
kernels (linux, linux-generic, linux-metal, linux-metal-dual) y la reproducción
bit a bit del 6.16.12 es un resultado cerrado; moverle el hash lo invalidaría,
más 125 sellados.
Además `ssl` entra en la asserción del install. Sin ese guardián se volvería a
sellar un intérprete mudo — que es justo como esto estuvo escondido meses.
Verificado: `checking for stdlib extension module _ssl... yes`,
_ssl.cpython-312-x86_64-linux-musl.so instalada, y el propio install imprime
`modulos opcionales: OK` importando ssl.
⚠ Mueve el hash de python3: 251 dependientes directos, ~369 sellados en los
cinco grafos (161 en KDE). Es el coste aceptado de la decisión.
Con gtk4 ya sin objetivos compartidos, estas tres dejaron de fallar por PIC
(0 errores) y pasaron a morir en el link con `unable to find static system
library 'lzma'`.
Misma causa que appstream: declaran `appstream` y `libxmlb` pero no `xz`, y
`libxmlb.pc` trae `Requires: gio-2.0, liblzma, libzstd`. Al enlazar appstream
estática necesitan la liblzma igual. Patrón `.pc Requires`→[deps]; `xz` va
junto a `zstd`, que viene del mismo .pc.
Con esto cierra la clausura ENTERA de gtk4 en el grafo del corpus, las 6 que
colgaban de él: gtk4-hello, libadwaita, gtksourceview, adwaita-hello,
sourceview-hello y hammer-edit — todas SELLADAS y con binario real.
`gtk/meson.build` declara dos hermanos con llamadas explícitas —`static_library('gtk')`
y `shared_library('gtk-4')`— y por ser explícita, `-Ddefault_library=static`
NO la suprime: no hay flag. Esa .so era el único objetivo compartido que se
enlazaba, arrastraba CINCO archives sin PIC (libtiff, libfreetype,
libfontconfig, libjpeg, libpng16) y moría con 13930 `relocation … recompile
with -fPIC`. Un ejecutable puede enlazar una .a no-PIC; una .so no.
Se apagan con `build_by_default: false` + `install: false` vía dos seds
acotados por rango (tocan 1 de 10 y 1 de 3 `install: true` respectivamente).
⚠ `build_by_default: false` NO BASTA SOLO: ninja construye igual lo que algo
NECESITA. El primer intento falló exactamente así — `shared_module('printbackend-file')`
depende de `libgtk_dep` y el fuente avisa «The 'file' print backend cannot be
disabled» (está fuera de los `if` de cpdb/cups), así que devolvía la .so al
grafo y re-aparecían los 13930 errores. Por eso se apagan LOS DOS.
En todo el árbol hay 5 objetivos compartidos: libgtk-4.so, printbackend-{cpdb,
cups,file} y media-gstreamer; las flags ya quitaban tres, estos seds los otros dos.
No se borra la declaración de libgtk: `pkg_config.generate(libgtk)` la necesita
para emitir el .pc con `-lgtk-4`, que es el nombre del archive GORDO que fabrica
la fase install. Nada la enlaza: los usuarios de `libgtk_dep` son demos, tests,
testsuite, examples y los backends de impresión/media, todos desactivados.
Verificado: sella, 0 errores PIC, 0 .so enlazadas, targets 997→993. Artefacto
con gtk4.pc `Libs: -lgtk-4`, libgtk-4.a de 808 objetos y `gtk_window_new` como
símbolo T definido. Las colas wlr/gnome tienen sus PROPIAS recetas de gtk4.
Quinta de la familia (appstream, cargo-deb, cargo-make, dprint). Sin `[deps]`
el link muere con `unable to find static system library 'lzma'`. Pendiente de
verificar en la próxima parada; el patrón está verificado tres veces.
Falla con las DOS a la vez: `unable to find static system library 'lzma'` y
`… 'bz2'`. La receta no tenía `[deps]`.
Cuarta de la misma familia esta noche (appstream, cargo-deb, cargo-make): el
lab trae el toolchain de Rust pero NO las libs C que los crates `*-sys`
esperan del sistema. El arreglo va en [deps], NUNCA engordando el rootfs
(regla de rootfs-laptop-worker-divergen).
NO se añade en bloque a las 220 recetas Cargo sin [deps]: la mayoría construye
bien y re-hashearlas invalidaría las ya selladas. Se arregla la que falla.
⚠ Pendiente de verificar como cargo-make en su momento; el patrón está
verificado tres veces (appstream, cargo-deb, cargo-make selladas tras el mismo
arreglo). Se verifica en la próxima parada.
`unable to find static system library 'bz2' using strategy 'no_fallback'`. La
receta no tenía `[deps]`: el lab trae el toolchain de Rust pero no las libs C
que los crates `*-sys` esperan del sistema. bzip2 está sellado y aporta
/usr/lib/libbz2.a.
Tercera de la misma familia esta noche: appstream (lzma vía el .pc Requires de
libxmlb), cargo-deb (lzma vía lzma-sys) y ésta (bz2). El síntoma del linker es
idéntico en las tres y el arreglo también.
⚠ PENDIENTE DE VERIFICAR: las otras dos se construyeron y sellaron antes de
commitear; ésta no, para no parar el driver una vez por receta. Se verifica en
la próxima tanda de reanudación. Si fallara, el patrón —no la receta— es lo que
habría que revisar.
La receta NO tenía `[deps]` en absoluto. El lab trae el toolchain de Rust pero
no las libs C que los crates `*-sys` esperan del sistema, así que el link moría
con `unable to find static system library 'lzma' using strategy 'no_fallback'`.
El log lo confirma: vendoriza y compila `lzma-sys v0.1.20`.
Mismo fallo y mismo arreglo que `appstream.toml` de hace un rato, por vías
distintas: allá liblzma entraba por el `.pc Requires` de libxmlb, acá por un
sys-crate. El síntoma del linker es idéntico.
Verificado: sella en 3m02s, artefacto de 4,6 M con un ELF real en
/usr/bin/cargo-deb. Blast radius nulo: en deuda en los cinco grafos con 0
dependientes sellados.