harness: wrapper linker-driver que quita -static en links -shared
Las extensiones Python (python.extension_module → shared_module) son .so que el intérprete del sandbox debe dlopen; intrínsecamente dinámicas. El harness inyecta LDFLAGS=-static para link=static, y meson/autotools lo aplican a TODOS los targets por igual ⇒ el .so sale ET_EXEC y dlopen da "Exec format error" (el muro de gobject-introspection: _giscanner.cpython-312.so no cargaba). Fix estructural (elegido sobre flip-a-dynamic): ensure_layout materializa en el rootfs dos wrappers (hammer-zig-cc/-cxx sobre zig cc/c++) que QUITAN -static SÓLO cuando el link lleva -shared. -static+-shared es contradictorio ⇒ correcto por construcción: transparente (byte-idéntico) para todo link normal, arregla TODA extensión Python futura, no sólo g-i. Hash-safe: el ArtifactHash se deriva de inputs (receta+deps), no de los bytes ⇒ no re-hashea ninguno de los 700+ sellados (cache-hit intacto); sólo cambia builds nuevos con .so dinámicos. Verificado: rota positional params, preserva orden y espacios; transparente sin -shared. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -417,8 +417,11 @@ impl Sandbox {
|
||||
// rompe la reproducibilidad — un no-determinismo que Stage 2 cazó (runbook §8c, SDD 09
|
||||
// §2). De paso cierra el SIGILL de AVX en qemu64 (runbook §7). Las rutas SIMD que sí
|
||||
// necesita el runtime (blake3/sha2) son asm con dispatch en runtime: siguen presentes.
|
||||
("CC", "zig cc -mcpu=baseline"),
|
||||
("CXX", "zig c++ -mcpu=baseline"),
|
||||
// Driver vía wrapper (ensure_layout lo materializa en /usr/local/bin): idéntico a
|
||||
// `zig cc -mcpu=baseline` salvo que quita `-static` en links `-shared` (extensiones
|
||||
// Python y demás `.so` dlopen-ables). Ver write_cc_wrappers.
|
||||
("CC", CC_WRAPPER),
|
||||
("CXX", CXX_WRAPPER),
|
||||
("AR", "zig ar"),
|
||||
("LC_ALL", "C"),
|
||||
("LANG", "C"),
|
||||
@@ -517,6 +520,61 @@ pub fn ensure_layout(rootfs: &Path, zig_dir: &Path) -> hammer_core::Result<()> {
|
||||
zig_dir.display()
|
||||
)));
|
||||
}
|
||||
write_cc_wrappers(rootfs)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Nombres (en `/usr/local/bin`, que está en el PATH del sandbox) del driver de compilador que
|
||||
/// usan CC/CXX por defecto. Ver [`write_cc_wrappers`].
|
||||
pub const CC_WRAPPER: &str = "hammer-zig-cc";
|
||||
pub const CXX_WRAPPER: &str = "hammer-zig-cxx";
|
||||
|
||||
/// Materializa en el rootfs dos wrappers de linker-driver (`hammer-zig-cc`/`-cxx`) que envuelven a
|
||||
/// `zig cc`/`zig c++`. Su ÚNICO efecto es QUITAR `-static` cuando el link también lleva `-shared`.
|
||||
///
|
||||
/// El harness inyecta `LDFLAGS=-static` para las recetas `link=static` (binario monolítico que pide
|
||||
/// la hidratación primaria). Pero meson/autotools aplican ese LDFLAGS a TODOS los targets por igual,
|
||||
/// incluidas las extensiones Python (`python.extension_module` → `shared_module`): un `.so` que el
|
||||
/// intérprete del sandbox debe `dlopen`. `-static` + `-shared` es contradictorio ⇒ el `.so` sale
|
||||
/// ET_EXEC y `dlopen` da `Exec format error` (el muro de gobject-introspection: `_giscanner.so`).
|
||||
///
|
||||
/// Quitar `-static` SÓLO cuando hay `-shared` es correcto por construcción: un objeto compartido
|
||||
/// nunca debe ser estático. Para todo link normal (ejecutable/`.a`) el wrapper es transparente ⇒
|
||||
/// byte-idéntico a `zig cc`. El ArtifactHash se deriva de inputs (receta+deps), no de los bytes ⇒
|
||||
/// este cambio NO re-hashea nada sellado; sólo destraba builds nuevos con extensiones dinámicas.
|
||||
fn write_cc_wrappers(rootfs: &Path) -> hammer_core::Result<()> {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let bindir = rootfs.join("usr/local/bin");
|
||||
std::fs::create_dir_all(&bindir).map_err(|e| {
|
||||
hammer_core::Error::Other(anyhow::anyhow!("no pude crear {}: {e}", bindir.display()))
|
||||
})?;
|
||||
// `case " $* "` detecta `-shared` como palabra completa; el bucle rota los positional params
|
||||
// (pop del frente, push al fondo salvo `-static`) ⇒ filtra preservando orden y seguro con espacios.
|
||||
let body = |driver: &str| {
|
||||
format!(
|
||||
"#!/bin/sh\n\
|
||||
case \" $* \" in\n\
|
||||
\x20 *\" -shared \"*)\n\
|
||||
\x20 n=$#\n\
|
||||
\x20 while [ \"$n\" -gt 0 ]; do\n\
|
||||
\x20 arg=$1; shift\n\
|
||||
\x20 [ \"$arg\" = \"-static\" ] || set -- \"$@\" \"$arg\"\n\
|
||||
\x20 n=$((n - 1))\n\
|
||||
\x20 done\n\
|
||||
\x20 ;;\n\
|
||||
esac\n\
|
||||
exec {driver} -mcpu=baseline \"$@\"\n"
|
||||
)
|
||||
};
|
||||
for (name, driver) in [(CC_WRAPPER, "zig cc"), (CXX_WRAPPER, "zig c++")] {
|
||||
let path = bindir.join(name);
|
||||
std::fs::write(&path, body(driver)).map_err(|e| {
|
||||
hammer_core::Error::Other(anyhow::anyhow!("no pude escribir {}: {e}", path.display()))
|
||||
})?;
|
||||
std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o755)).map_err(|e| {
|
||||
hammer_core::Error::Other(anyhow::anyhow!("no pude chmod {}: {e}", path.display()))
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user