From 1ab53a650ffb00e83016acc17012f9324f222ba2 Mon Sep 17 00:00:00 2001 From: sergio Date: Fri, 12 Jun 2026 03:04:45 -0400 Subject: [PATCH] =?UTF-8?q?selfhost-verify:=20m=C3=B3dulos=20del=20kernel?= =?UTF-8?q?=20booteado=20+=20build=20a=20stderr=20(E2BIG)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dos bugs latentes que sólo aparecían en un build real (in-VM); en el host quedan tapados porque todo sale de caché. 1. scripts/selfhost-verify.sh: los módulos se sacaban de /lib/modules/$(uname -r) pero la VM bootea $KERNEL (/boot/vmlinuz-linux), que puede ser otra versión. Mismatch de version-magic ⇒ overlay.ko no carga ⇒ bwrap muere "No such device". Ahora la versión se deriva del bzImage booteado (file -bL "$KERNEL"; fallback uname -r). 2. crates/hammer-build/src/sandbox.rs: spawn_pump teeaba el stdout del build al stdout del padre. rebuild-stage1 hace PRIME=$(hammer ... bootstrap stage1); en un build real son miles de líneas de configure/make capturadas en $PRIME ⇒ el paso siguiente --rootfs "$PRIME" exec con un arg gigante ⇒ E2BIG (Argument list too long). Ahora el tee va a stderr; stdout queda para el hash legible-por-máquina. Con esto el verify corre end-to-end in-VM: los 4 componentes reproducen bit a bit, pero of_tree(stage1) diverge (host vs VM vs baseline) — no-determinismo del ensamblado a cazar (SDD 09 §2). Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/hammer-build/src/sandbox.rs | 25 ++++++++++++++----------- scripts/selfhost-verify.sh | 10 ++++++++-- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/crates/hammer-build/src/sandbox.rs b/crates/hammer-build/src/sandbox.rs index 7e385680..6c07098a 100644 --- a/crates/hammer-build/src/sandbox.rs +++ b/crates/hammer-build/src/sandbox.rs @@ -90,8 +90,8 @@ impl Sandbox { let tail = Arc::new(Mutex::new(VecDeque::::with_capacity(TAIL_LINES))); let out = child.stdout.take().expect("stdout piped"); let err = child.stderr.take().expect("stderr piped"); - let h_out = spawn_pump(out, tail.clone(), false); - let h_err = spawn_pump(err, tail.clone(), true); + let h_out = spawn_pump(out, tail.clone()); + let h_err = spawn_pump(err, tail.clone()); let status = child .wait() @@ -201,12 +201,19 @@ impl Sandbox { } } -/// Lanza un thread que lee `reader` línea a línea, la reenvía al stream del padre -/// (`to_stderr` decide cuál) y la acumula en el ring `tail`. Devuelve el handle para join. +/// Lanza un thread que lee `reader` línea a línea, la reenvía a stderr del padre y la +/// acumula en el ring `tail`. Devuelve el handle para join. +/// +/// El tee va SIEMPRE a stderr (tanto el stdout como el stderr del build): stdout del proceso +/// queda reservado para el resultado legible-por-máquina (el content-hash que imprime la CLI, +/// p. ej. `bootstrap stage1`). Antes el stdout del build se teeaba al stdout del padre y se +/// colaba en capturas tipo `PRIME=$(hammer … bootstrap stage1)`; con builds reales (no cacheados, +/// p. ej. el rebuild in-VM) eso son miles de líneas de `configure`/`make` ⇒ el argumento crece y +/// la fase de verificación siguiente (`--rootfs "$PRIME"`) revienta el exec con E2BIG +/// (Argument list too long). Va a stderr, junto a los logs de tracing. Ver runbook §8c. fn spawn_pump( reader: R, tail: Arc>>, - to_stderr: bool, ) -> std::thread::JoinHandle<()> { std::thread::spawn(move || { let buf = BufReader::new(reader); @@ -215,12 +222,8 @@ fn spawn_pump( Ok(l) => l, Err(_) => break, }; - // Tee en vivo al padre (sin bufferizar de más: el build puede tardar). - if to_stderr { - let _ = writeln!(std::io::stderr(), "{line}"); - } else { - let _ = writeln!(std::io::stdout(), "{line}"); - } + // Tee en vivo al padre por stderr (sin bufferizar de más: el build puede tardar). + let _ = writeln!(std::io::stderr(), "{line}"); if let Ok(mut g) = tail.lock() { push_tail(&mut g, line, TAIL_LINES); } diff --git a/scripts/selfhost-verify.sh b/scripts/selfhost-verify.sh index dd255aa0..ff25ab83 100755 --- a/scripts/selfhost-verify.sh +++ b/scripts/selfhost-verify.sh @@ -83,8 +83,14 @@ fi # 3) Inyectar los módulos del kernel DE ESTA máquina al toolchain (el builder los carga: overlay para # el sandbox bwrap, e1000 para la red del vendoring). Deben matchear el kernel que bootea la VM. -say "inyectar overlay.ko + e1000.ko (kernel $(uname -r)) al toolchain" -moddir="/lib/modules/$(uname -r)" +# La versión de los módulos DEBE matchear el kernel que bootea la VM ($KERNEL), no el del host +# corriendo el script ($(uname -r)) — en máquinas con varios kernels instalados difieren y el +# insmod falla con "version magic mismatch" → sin overlayfs, bwrap revienta. Sacamos la versión +# del propio bzImage; si no se puede, caemos a uname -r. +KVER="$(file -bL "$KERNEL" 2>/dev/null | grep -oE 'version [0-9][^ ]*' | awk '{print $2}')" +[[ -n "$KVER" && -d "/lib/modules/$KVER" ]] || KVER="$(uname -r)" +say "inyectar overlay.ko + e1000.ko (kernel $KVER) al toolchain" +moddir="/lib/modules/$KVER" for m in overlay e1000; do src="$(find "$moddir" -name "$m.ko*" 2>/dev/null | head -1)" if [[ -n "$src" ]]; then