tawasuyu

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1531Shell 196Rust 108Markdown 983686 Sutichaykuna

musl-shared: el corpus publica su propio CARGADOR — 23 binarios dejan de ser inertes

La imagen del perfil servidor traía 23 binarios de 726 que no podían ejecutarse:

    $ python3 -c "print(1)"
    sh: python3: not found        <- y `command -v python3` decía /usr/bin/python3

No faltaban: eran ELF dinámicos pidiendo `/lib/ld-musl-x86_64.so.1`, y **ningún artefacto del corpus
publicaba ese fichero**. Funcionaban en el hub sólo porque el rootfs Alpine del LAB lo presta — la
misma fuga que documentan `zlib-shared` y `expat-shared`, un piso más abajo, y peor: el lab no entra
en `hash_inputs`, así que la dependencia era invisible para el store.

Los 23 son la suite binutils entera + perl, python3, sqlite3, flex y nft. Entre ellos, TODO el
instrumental del proyecto, que es Python.

**Variante y no `--enable-shared` en la canónica**: `musl` es componente de Stage 1 y su `of_tree` es
el baseline de `selfhost-verify`. Re-hashearlo obliga a rehacer ese baseline a propósito, y eso es su
propia unidad de trabajo. Con el patrón `*-shared` (ya hay 23 en el corpus) la canónica no se mueve —
control: `takana hash recipes/musl.toml` sigue dando `b3:ce952f72…`, el mismo sellado de Stage 1. Y
además `musl` no es dep de NADIE (medido: sólo de sí misma; el enlace estático lo resuelve el musl de
zig), así que esto suma sin mover un solo ArtifactHash del corpus.

**⚠ `compiler = "gcc"` y no `zig-cc`, medido.** Con zig cc el `libc.so` sale con 1586 símbolos
dinámicos contra los 1654 de Alpine, y los 68 que faltan son EXACTAMENTE los que musl implementa en
ensamblador x86_64 (`memset memcpy memmove memcmp strlen` y toda la familia matemática) más los
`__stack_chk_*`. No es que no se compilen —en el `libc.a` canónico están—: zig los resuelve con su
propio musl y los deja `FUNC LOCAL HIDDEN` de tamaño 0, fuera de la tabla dinámica. El síntoma es un
cargador que arranca, reloca y muere con `memset: symbol not found`, que se lee como "el binario está
roto" y no como "a la libc le faltan símbolos". Con gcc: **1651 símbolos**, `memset` y `ceil`
presentes, y `python3 3.12.10` CORRE con el cargador del corpus.

De paso: el comentario de `musl.toml` decía que el artefacto aportaba «el loader, para uso dinámico
futuro». Era falso —`--disable-shared` no construye ninguno— y es justo la etiqueta que hizo que
nadie buscara el agujero. Corregido (los comentarios no entran en `hash_inputs`; verificado).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RomoxEGZUhaT4pob1QSX5x

7e864597388a2705d1cab27661791ff74ba97fae · ruwaq Sergio · (1 killa ñawpaq) · tayta ef6164861 · kay commitpi sach'ata qhaway

Semántico sayk'uy

Mana ima sutichaypas t'ikrakurqanchu: qillqalla, yachachiylla utaq willaylla t'ikrakurqan.

3 qillqakuna · +85 −3

diff --git a/docs/state/targets.toml b/docs/state/targets.toml
index 5dfcc287..237b4391 100644
--- a/docs/state/targets.toml
+++ b/docs/state/targets.toml
@@ -32,6 +32,14 @@ paquetes = [
   "iproute2", "iputils", "dhcpcd", "e2fsprogs", "dosfstools", "parted", "rsync",
   "sed", "tzdata", "vim", "dbus", "ca-certificates", "gnupg", "lsof", "strace",
   "pciutils", "usbutils", "mandoc",
+  # `musl-shared` (2026-09-11): EL CARGADOR DINÁMICO. Va en `base` y no en un perfil concreto porque
+  # sin él cualquier binario dinámico de la clausura queda INERTE — presente y sin poder ejecutarse.
+  # Medido en la imagen del perfil `servidor`: **23 binarios de 726** pedían
+  # `/lib/ld-musl-x86_64.so.1` y NINGÚN artefacto del corpus lo publicaba; funcionaban en el hub sólo
+  # porque el rootfs Alpine del LAB lo presta. Entre ellos, `python3` y `perl` — o sea TODO el
+  # instrumental del proyecto. El síntoma era `sh: python3: not found` con `command -v python3`
+  # contestando `/usr/bin/python3`: no falta, no arranca. Ver SDD 28 §6.4.
+  "musl-shared",
 ]
 
 [perfil.cli]
diff --git a/recipes/musl-shared.toml b/recipes/musl-shared.toml
new file mode 100644
index 00000000..f9056956
--- /dev/null
+++ b/recipes/musl-shared.toml
@@ -0,0 +1,68 @@
+# musl-shared 1.2.5 — variante DINÁMICA de musl: el CARGADOR y `libc.so`.
+#
+# ── POR QUÉ EXISTE ──────────────────────────────────────────────────────────────────────────────
+# El musl canónico (`recipes/musl.toml`) es `--disable-shared`: emite headers, `libc.a` y los
+# `crt*.o`, que es lo que quiere el enlace estático del corpus. Su comentario decía que el artefacto
+# aportaba «el loader, para uso dinámico futuro» — y era FALSO: con `--disable-shared` musl no
+# construye ni `libc.so` ni `ld-musl-x86_64.so.1`. Corregido allá; acá está el loader de verdad.
+#
+# ── EL AGUJERO QUE TAPA, MEDIDO (2026-09-10, SDD 28 §6.4) ───────────────────────────────────────
+# La imagen del `perfil.servidor` traía **23 binarios que no podían correr**:
+#
+#     $ python3 -c "print(1)"
+#     sh: python3: not found        ← y `command -v python3` decía /usr/bin/python3
+#
+# No faltaban: eran ELF DINÁMICOS pidiendo `/lib/ld-musl-x86_64.so.1`, y **ningún artefacto del
+# corpus publicaba ese fichero**. Funcionaban en el hub sólo porque el rootfs Alpine del LAB lo
+# presta (`.dev-fs/alpine/lib/ld-musl-x86_64.so.1`) — la misma fuga de soberanía que documentan
+# `zlib-shared` y `expat-shared`, un piso más abajo, y con el agravante de que el lab NO entra en
+# `hash_inputs`: la dependencia era invisible para el store.
+#
+# Los 23: la suite `binutils` entera (ar as ld nm objcopy objdump ranlib readelf strip addr2line
+# c++filt elfedit gprof size ld.bfd) más `perl`, `python3`, `sqlite3`, `flex` y `nft`. Entre ellos
+# está TODO el instrumental del proyecto, que es Python.
+#
+# ── POR QUÉ UNA VARIANTE Y NO `--enable-shared` EN LA CANÓNICA ──────────────────────────────────
+# `musl` es componente de **Stage 1** (musl + busybox + hammerd + arje-zero) y su `of_tree` es el
+# baseline de `selfhost-verify.sh`. Re-hashearlo obliga a rehacer ese baseline A PROPÓSITO, y eso es
+# su propia unidad de trabajo, no algo que se arrastra de rebote. El patrón `*-shared` deja la
+# canónica intacta: el nombre es DISTINTO, así que conviven en el mismo store y cada consumidor toma
+# la que necesita. Mismo criterio que zlib-shared / expat-shared / ncurses-shared.
+#
+# ⚠ Y no es dep de nadie: medido, `musl` no aparece en el `[deps]` de ninguna receta del corpus
+# (sólo la suya). El enlace estático lo resuelve el musl que trae zig. Por eso esta variante suma
+# sin mover un solo ArtifactHash.
+
+name    = "musl-shared"
+version = "1.2.5"
+license = "MIT"
+
+[source]
+tarball = "https://musl.libc.org/releases/musl-1.2.5.tar.gz"
+sha256  = "a9a118bbe84d8764da0ea0d28b3ab3fae8477fc7e4085d90102b8596fc7c75e4"
+
+[build]
+# ⚠ `gcc` Y NO `zig-cc`, y es MEDIDO. Con zig cc el `libc.so` sale con 1586 símbolos dinámicos
+# contra los 1654 del musl de Alpine, y los 68 que faltan son EXACTAMENTE los que musl implementa en
+# ensamblador x86_64 —`memset`, `memcpy`, `memmove`, `memcmp`, `strlen` y toda la familia matemática
+# (`ceil floor sqrt fmod exp log sin cos tan fma round trunc`)— más los `__stack_chk_*`.
+#
+# No es que no se compilen: en el `libc.a` del musl canónico están. Lo que pasa es que zig los
+# RESUELVE CON SU PROPIO musl y los deja ocultos — medido: `memset` aparece como
+# `FUNC LOCAL HIDDEN` de tamaño 0, así que no entra en la tabla dinámica. El resultado es un
+# cargador que arranca, empieza a relocar y muere con `memset: symbol not found`, que se lee como
+# "el binario está roto" y no como "a la libc le faltan símbolos".
+compiler = "gcc"
+target   = "x86_64-linux-musl"
+link     = "dynamic"
+
+[deps]
+build = ["make"]
+
+[build.phases]
+# `--enable-shared` produce `libc.so`; `make install` lo pone en `/usr/lib/libc.so` y crea el
+# cargador en `$syslibdir` (`/lib/ld-musl-x86_64.so.1`), que es literalmente el mismo fichero: en
+# musl el loader Y la libc son el mismo objeto.
+configure = "./configure --prefix=/usr --syslibdir=/lib --enable-shared --disable-static"
+compile   = "make -j\"$(nproc)\""
+install   = "make DESTDIR=/out install"
diff --git a/recipes/musl.toml b/recipes/musl.toml
index 6eb2807e..92fb8c6d 100644
--- a/recipes/musl.toml
+++ b/recipes/musl.toml
@@ -1,8 +1,14 @@
 # musl libc 1.2.5 — libc del target para el userland mínimo del Stage 1 (SDD 11 §3).
 #
-# Se compila estática con `zig cc` cross al target. El artefacto aporta headers + libc.a (y el
-# loader, para uso dinámico futuro) al rootfs sellado. Tarball release pinned por sha256: igual de
-# fuerte que un commit git para reproducibilidad (ADR 0006).
+# Se compila estática con `zig cc` cross al target. El artefacto aporta headers + libc.a + los
+# `crt*.o`. Tarball release pinned por sha256: igual de fuerte que un commit git para
+# reproducibilidad (ADR 0006).
+#
+# ⚠ ANTES ESTE COMENTARIO DECÍA «y el loader, para uso dinámico futuro». Era FALSO y costó caro: con
+# `--disable-shared` musl NO construye `libc.so` ni `ld-musl-x86_64.so.1`, así que el corpus no
+# publicaba ningún cargador dinámico y los 23 binarios dinámicos de una imagen quedaban INERTES
+# (SDD 28 §6.4). El loader vive ahora en `recipes/musl-shared.toml`; esta receta sigue siendo
+# estática pura, que es lo que Stage 1 necesita.
 
 name    = "musl"
 version = "1.2.5"