1# SDD 31 — El compilador de Rust: de «lo presta el lab» a «es nuestro»
2
3> «sin compilador de rust, takana no tiene sentido» — el usuario, 2026-09-14.
4>
5> Tiene razón por una vía que no es de gusto, y conviene escribirla entera antes de discutir cómo.
6
7## 1. El hueco, medido
8
9| pregunta | respuesta (2026-09-14) |
10|---|---|
11| ¿cuánto de takana es Rust? | **12 crates, 42 555 líneas** — todo el instrumental |
12| recetas `cargo-*` en el corpus | **44** |
13| recetas de `rustc` o `cargo` | **0** |
14| ¿de dónde sale el `rustc` que construye todo eso? | del **LAB** (`.dev-fs/alpine`), un rootfs ajeno |
15
16Y el lab **entra en el `ArtifactHash`**. O sea: la distro depende de bytes de otro para poder
17reconstruir su propio instrumental. No se ve porque funciona — es la misma forma de
18`subcomando-sin-driver`, a escala de toolchain: 44 subcomandos de `cargo` y ningún `cargo`.
19
20## 2. Los tres escalones
21
221. **`rust-toolchain-bin`** — el tarball oficial de rust-lang, verificado por su sha256 y sellado
23 tal cual con `foreign = true` (bytes ajenos: clase `ajeno`, fuera del recuento del corpus).
24 **Hecho y probado en la caja**: `rustc 1.97.0`, `cargo 1.97.0`, y un binario compilado que corre.
252. **`rust` desde fuente** — con `llvm21` del corpus y el stage0 del lab. *En curso.*
263. **La cadena `mrustc → 1.90 → 1.91.0 → 1.91.1`** del selfhost (`docs/10-roadmap.md`,
27 `docs/11-bootstrap.md`), que quita hasta el escalón 1. Pendiente.
28
29## 3. Lo que costó el escalón 1, y no estaba en el grafo
30
31El artefacto sella (799 M) y **no ejecuta** hasta tener dos cosas que la distro publicaba por
32separado — las dos `sealed`, las dos en el perfil equivocado:
33
34- **El cargador musl.** `rustc` es PIE dinámico (`NEEDED librustc_driver-*.so`, `NEEDED libc.so`).
35 En el worker ni arranca (`cannot execute: required file not found`, que es como se ve la falta del
36 intérprete); en la caja sí, porque ahí está `musl-shared`.
37- **`libgcc_s.so.1`** (`_Unwind_Resume: symbol not found`). Lo publica `gcc-libs`, que estaba
38 declarado **sólo en los cuatro perfiles de escritorio**.
39
40**Y no hace falta un `cc`.** El primer `rustc hola.rs` falla con `linker \`cc\` not found`, y la
41respuesta no es traer un compilador de C: el toolchain **trae su propio `lld`**
42(`rustlib/<target>/bin/rust-lld`). Con `-C linker-flavor=ld.lld -C link-self-contained=yes` enlaza y
43el binario corre. Una distro sin compilador de C puede compilar Rust igual.
44
45## 4. Lo que costó el escalón 2 — cinco muros, todos con su mensaje engañoso
46
471. **`llvm18` no sirve.** El bootstrap de rustc lo dice en una línea:
48 `panic!("bad LLVM version: {version}, need >=21")`. Mínimos medidos leyendo
49 `src/bootstrap/src/core/build_steps/llvm.rs`: **1.87 → ≥18 · 1.90 → ≥19 · 1.93/1.95 → ≥20 ·
50 1.97 → ≥21**. Y bajar de rustc no es salida: el stage0 de rustc *N* es *N-1* o *N*, y lo que
51 tenemos es 1.97. Por eso existe `llvm21` — y `llvm18` se queda, porque lo usa `mesa-llvmpipe` y
52 mesa 24.0.9 no soporta 21. Dos consumidores con rangos incompatibles ⇒ dos artefactos.
532. **El toolchain musl oficial NO puede producir proc-macros.** Está compilado con
54 `crt-static = true`, y los proc-macros son dylibs:
55
56 error: cannot produce proc-macro for `clap_derive` as the target
57 `x86_64-unknown-linux-musl` does not support these crate types
58
59 …compilando el **propio bootstrap de rustc**, que usa `clap` con derive. O sea: el prebuilt
60 sirve para compilar programas y **no** para arrancar el build de rustc. El stage0 es el `rustc`
61 del LAB, que Alpine construye con soporte de dylib. (Y por eso el rustc que salga de acá lleva
62 `crt-static = false`: con `true` heredaría el mismo defecto y no podría compilar ninguna de las
63 44 recetas `cargo-*` que usan `derive`.)
643. **x.py DESCARGA el stage0 si no le nombras el local.** Sin `build.rustc`/`build.cargo`, en un
65 sandbox sin red muere con `RuntimeError: failed verification` en `download_toolchain()` — un
66 mensaje que no menciona ni la red ni el stage0.
674. **El triple de Alpine no es el canónico.** Sin fijar `build`, x.py detecta
68 `x86_64-alpine-linux-musl` y entonces una sección `[target.x86_64-unknown-linux-musl]` **no
69 aplica a nada**: ignora el `llvm-config` externo y se pone a construir LLVM él solo. Y al forzar
70 el canónico, el stage0 del lab **no tiene la std de ese triple** (`can't find crate for std`).
71 Se usa el de Alpine en todo, que además es con el que ya se compila el corpus entero.
725. **cmake + zig = «compiler broken».** Los build scripts nativos (jemalloc, compiler-rt) usan
73 cmake, y con el wrapper de zig la prueba de ABI falla. `CC=gcc CXX=g++`, igual que en `llvm21`
74 y `cmake`.
75
76## 4.bis El SEXTO muro, que la granja encontró sola *(2026-09-15)*
77
78`rust` entró a la cola del worker y llegó **mucho más lejos**: compiló las **386 crates del stage1** y
79murió justo al empezar `Building stage1 library artifacts (stage1 -> stage1)`:
80
81 error: error loading target specification: could not find specification for target
82 "x86_64-alpine-linux-musl"
83 = help: did you mean `x86_64-unknown-linux-musl`?
84
85**Es la otra mitad del muro 4, y sólo se ve en el stage 2.** Usar el triple de Alpine funciona
86mientras el compilador que manda es el del LAB —Alpine le PARCHEA esa especificación a su rustc— y
87deja de funcionar en el instante en que toma el relevo **el rustc que acabamos de construir**, que es
88upstream puro y no la conoce. O sea: el triple que hace falta para arrancar el build es el que el
89producto del build no tiene.
90
91Las salidas, por orden de honestidad:
92
931. **Parchear `rustc_target` para registrar `x86_64-alpine-linux-musl`**, que es literalmente lo que
94 hace Alpine en su APKBUILD. Un fichero de spec copiado de `x86_64_unknown_linux_musl.rs` y una
95 línea en `supported_targets!`. El triple queda DENTRO del compilador, así que vale en todos los
96 stages.
972. `RUST_TARGET_PATH` + un `.json` generado por el propio stage0
98 (`--print target-spec-json`) — más barato, pero un target por JSON como **host** es terreno que
99 rustc no soporta bien: la std del host se construye con el spec built-in.
1003. Volver al triple canónico y resolver el «can't find crate for std» del stage0 por otro lado. Hoy
101 no hay candidato: el prebuilt oficial trae esa std pero **no puede compilar proc-macros** (muro 2),
102 así que no sirve de stage0 para el bootstrap.
103
104⇒ La 1 es la que Alpine ya demostró que funciona, y es la que sigue.
105
106## 4.ter ✅ EL ESCALÓN 2 SELLÓ *(2026-09-16 00:18)*
107
108**`rust` 1.97.0 desde fuente: `b3:015a07fb…`, 353 M.** Construido por la granja, en el worker, desde
109`llvm21` del corpus y el stage0 del lab.
110
111Los controles, corridos en la caja (que sí tiene el cargador musl; el worker no):
112
113 $ rustc --version
114 rustc 1.97.0 (2d8144b78 2026-07-07) (built from a source tarball) ← NUESTRO
115 $ rustc --print target-list | grep alpine
116 x86_64-alpine-linux-musl ← EL PARCHE, DENTRO
117 $ cargo --version
118 cargo 1.97.0 (c980f4866 2026-06-30) (built from a source tarball)
119
120Y compila: `--emit=obj` da un objeto de 3,5 K y `--crate-type=rlib` una biblioteca de 6,4 K, las dos
121sin tocar el enlazador.
122
123### Los dos muros de esta tanda
124
1256. **El triple de Alpine no existía en el producto.** Resuelto con `rust-alpine-target.patch`, que lo
126 registra en `rustc_target` — un spec copiado del canónico con `crt_static_default = false` (sin
127 eso no hay dylibs y sin dylibs no hay proc-macros) y una línea en `supported_targets!`. Es lo que
128 hace Alpine. **El control no es que el build pase: es que `--print target-list` lo liste.**
1297. **`openssl-sys`, a la hora y media de build.** `tools = ["cargo"]` lo arrastra. La dep que entró es
130 **`openssl-threads` y no la canónica**: el `Configure` de openssl apaga los threads en cuanto ve
131 `-static` en LDFLAGS, y cargo es masivamente multihilo. Más `OPENSSL_STATIC=1`/`OPENSSL_DIR=/usr`,
132 porque el lab publica `.a` y ningún `.so`.
133
134### 🧱 Muro 8, ya medido: el compilador no trae con qué ENLAZAR
135
136En una caja sin `cc` —o sea, en cualquier takana que no sea un hub— el enlace falla:
137
138 $ rustc hola.rs -C linker-flavor=ld.lld
139 error: linker `lld` not found
140 $ rustc hola.rs -C linker=/usr/bin/ld -C link-self-contained=yes
141 /usr/bin/ld: cannot find -lgcc
142
143La causa está en el log del propio build, en una línea que parece informativa:
144
145 skipping llvm-tools (x86_64-alpine-linux-musl): external LLVM
146
147Al usar un LLVM externo —que es lo que queremos, porque `llvm21` es del corpus— **x.py se salta las
148herramientas de LLVM, y `rust-lld` es una de ellas**. El prebuilt oficial sí lo trae, y por eso el
149escalón 1 enlazaba y éste no. Y `llvm21` tampoco sirve de repuesto: **no publica ningún `lld`**
150(medido: 1,6 G de artefacto, cero binarios `lld`).
151
152⇒ Las salidas son dos, y hay que elegir: **(a)** `[rust] lld = true` en el `bootstrap.toml`, que
153construye `rust-lld` desde el `llvm-project` vendoreado en el tarball —el toolchain queda completo y
154autosuficiente—; o **(b)** publicar `lld` desde `llvm21` y fijar el linker en un
155`/etc/cargo/config.toml` del sitio. La (a) es la que hace que «tener compilador de Rust» signifique
156lo que parece.
157
158⚠ Y lo que esto enseña del método: **el artefacto selló, reproduce y no enlaza**. Es la familia de
159[[subcomando-sin-driver]] en el corazón del toolchain — un compilador que compila y no produce un
160ejecutable. Un `build ok` no es la prueba; la prueba es el objeto en el disco y el binario corriendo.
161
162## 4.quater ✅ EL TOOLCHAIN COMPILA, ENLAZA Y CORRE *(2026-09-16)*
163
164 $ cargo build --offline
165 Compiling prueba v0.1.0
166 Finished `dev` profile in 0.44s
167 $ ./target/debug/prueba
168 cargo, rustc y ld: los tres nuestros
169
170**En una caja sin compilador de C.** `cargo` y `rustc` son los que construyó la granja; el enlazador
171es el `ld` de nuestro `binutils`. El binario sale estático (880 K el de `rustc` a secas) y corre.
172
173### El enlazador no era `lld`: era el `ld` que ya teníamos
174
175El muro 8 se cerró **sin** `rust-lld` y sin `lld21`. La cadena de intentos, que es la parte útil:
176
177| intento | resultado |
178|---|---|
179| `-C linker-flavor=ld.lld` | `linker \`lld\` not found` — el build con LLVM externo **no genera** `rust-lld` |
180| `rust.lld = true` | el bootstrap lo RECHAZA: «Cannot enable LLD … when using external llvm-config» |
181| `lld21` (receta nueva) | selló, y **crasheaba con cualquier entrada** (ver abajo) |
182| `lld21` dinámico + zlib | el zlib **no se puede encender desde acá**: `LLVMConfig.cmake` dice `LLVM_ENABLE_ZLIB 0` y el build standalone lo hereda |
183| **`-C linker-flavor=ld` + `+crt-static`** | **funciona** |
184
185Lo que faltaba no era un enlazador: era **decirle a rustc que enlace en estático**. Con
186`crt-static`, rustc usa su propio `libunwind`/`compiler_builtins` y **deja de pedir `-lgcc`** — que
187es lo que rompía el intento con `ld`, porque la distro se construye con zig (compiler-rt) y libgcc
188no existe. Estático es además el modo natural de musl.
189
190⇒ La config del sitio (el pendiente del §5) ya está escrita: `scripts/servidor/cargo-config.toml`,
191instalada en `/etc/cargo/config.toml`, con las tres flags y el porqué de cada una.
192
193### 🧨 Lo que `lld21` destapó de paso: las 79 herramientas de `llvm21` están ROTAS
194
195`lld21` selló y se moría en cuanto hacía algo: `--version` y `--help` bien, y **cualquier** enlace
196—incluso uno que sólo debía dar un error— en SIGSEGV con un «Stack dump:» vacío. No era la receta:
197
198 $ llc --version (del artefacto llvm21, sellado hace días)
199 PLEASE submit a bug report … Stack dump: ← también crashea, y también en el worker
200
201Salen **`static-pie`**, y el enlace estático descarta los constructores globales de los que dependen
202los registros de LLVM (`cl::opt`, `TargetRegistry`): funciona lo que no los necesita (`llvm-ar`,
203`llvm-config`) y revienta lo que sí. **Nadie lo había visto porque `rust` usa `llvm-config` y las
204`.a`, nunca una herramienta.** Con `link = "dynamic"`, `lld21` pasa a dar errores limpios.
205
206⚠ **Queda como deuda DECLARADA, no arreglada**: encender zlib en `llvm21` —lo que haría a `lld21`
207usable— **re-hashea `llvm21` y con él `rust`**, o sea dos builds largos. Y arreglar las 79
208herramientas es la misma operación. Hoy no bloquea nada: el toolchain enlaza con `ld`.
209
210## 5. Lo que falta decidir
211
212- **Dónde vive el toolchain.** Hoy `rust-toolchain-bin` está en `perfil.servidor` (799 M; un
213 escritorio no compila nada). El de fuente irá donde se decida — y probablemente sustituya al
214 prebuilt en ese perfil en vez de sumarse.
215- **La config del sitio**: que las flags del linker (`-C linker-flavor=ld.lld`) sean el default en
216 un `/etc/cargo/config.toml`, para que `cargo build` funcione sin recordarlas.
217- **El escalón 3**: cuándo se conecta la cadena del selfhost al corpus. Es lo único que quita la
218 dependencia de bytes ajenos, y es el trabajo más caro de los tres.
31-compilador-de-rust.md
SDD 31 — El compilador de Rust: de «lo presta el lab» a «es nuestro»
«sin compilador de rust, takana no tiene sentido» — el usuario, 2026-09-14.
Tiene razón por una vía que no es de gusto, y conviene escribirla entera antes de discutir cómo.
1. El hueco, medido
| pregunta | respuesta (2026-09-14) |
| ¿cuánto de takana es Rust? | 12 crates, 42 555 líneas — todo el instrumental |
recetas cargo-* en el corpus | 44 |
recetas de rustc o cargo | 0 |
¿de dónde sale el rustc que construye todo eso? | del LAB (.dev-fs/alpine), un rootfs ajeno |
Y el lab entra en el ArtifactHash. O sea: la distro depende de bytes de otro para poder
reconstruir su propio instrumental. No se ve porque funciona — es la misma forma de
subcomando-sin-driver, a escala de toolchain: 44 subcomandos de cargo y ningún cargo.
2. Los tres escalones
rust-toolchain-bin — el tarball oficial de rust-lang, verificado por su sha256 y sellado
tal cual con foreign = true (bytes ajenos: clase ajeno, fuera del recuento del corpus).
Hecho y probado en la caja: rustc 1.97.0, cargo 1.97.0, y un binario compilado que corre.
rust desde fuente — con llvm21 del corpus y el stage0 del lab. En curso.
- La cadena
mrustc → 1.90 → 1.91.0 → 1.91.1 del selfhost (docs/10-roadmap.md,
docs/11-bootstrap.md), que quita hasta el escalón 1. Pendiente.
3. Lo que costó el escalón 1, y no estaba en el grafo
El artefacto sella (799 M) y no ejecuta hasta tener dos cosas que la distro publicaba por
separado — las dos sealed, las dos en el perfil equivocado:
- El cargador musl.
rustc es PIE dinámico (NEEDED librustc_driver-*.so, NEEDED libc.so).
En el worker ni arranca (cannot execute: required file not found, que es como se ve la falta del
intérprete); en la caja sí, porque ahí está musl-shared.
libgcc_s.so.1 (_Unwind_Resume: symbol not found). Lo publica gcc-libs, que estaba
declarado sólo en los cuatro perfiles de escritorio.
Y no hace falta un cc. El primer rustc hola.rs falla con linker \cc` not found, y la respuesta no es traer un compilador de C: el toolchain **trae su propio lld** (rustlib/<target>/bin/rust-lld). Con -C linker-flavor=ld.lld -C link-self-contained=yes` enlaza y
el binario corre. Una distro sin compilador de C puede compilar Rust igual.
4. Lo que costó el escalón 2 — cinco muros, todos con su mensaje engañoso
-
llvm18 no sirve. El bootstrap de rustc lo dice en una línea:
panic!("bad LLVM version: {version}, need >=21"). Mínimos medidos leyendo
src/bootstrap/src/core/build_steps/llvm.rs: 1.87 → ≥18 · 1.90 → ≥19 · 1.93/1.95 → ≥20 ·
1.97 → ≥21. Y bajar de rustc no es salida: el stage0 de rustc N es N-1 o N, y lo que
tenemos es 1.97. Por eso existe llvm21 — y llvm18 se queda, porque lo usa mesa-llvmpipe y
mesa 24.0.9 no soporta 21. Dos consumidores con rangos incompatibles ⇒ dos artefactos.
-
El toolchain musl oficial NO puede producir proc-macros. Está compilado con
crt-static = true, y los proc-macros son dylibs:
error: cannot produce proc-macro for `clap_derive` as the target
`x86_64-unknown-linux-musl` does not support these crate types
…compilando el propio bootstrap de rustc, que usa clap con derive. O sea: el prebuilt
sirve para compilar programas y no para arrancar el build de rustc. El stage0 es el rustc
del LAB, que Alpine construye con soporte de dylib. (Y por eso el rustc que salga de acá lleva
crt-static = false: con true heredaría el mismo defecto y no podría compilar ninguna de las
44 recetas cargo-* que usan derive.)
-
x.py DESCARGA el stage0 si no le nombras el local. Sin build.rustc/build.cargo, en un
sandbox sin red muere con RuntimeError: failed verification en download_toolchain() — un
mensaje que no menciona ni la red ni el stage0.
-
El triple de Alpine no es el canónico. Sin fijar build, x.py detecta
x86_64-alpine-linux-musl y entonces una sección [target.x86_64-unknown-linux-musl] no
aplica a nada: ignora el llvm-config externo y se pone a construir LLVM él solo. Y al forzar
el canónico, el stage0 del lab no tiene la std de ese triple (can't find crate for std).
Se usa el de Alpine en todo, que además es con el que ya se compila el corpus entero.
-
cmake + zig = «compiler broken». Los build scripts nativos (jemalloc, compiler-rt) usan
cmake, y con el wrapper de zig la prueba de ABI falla. CC=gcc CXX=g++, igual que en llvm21
y cmake.
4.bis El SEXTO muro, que la granja encontró sola (2026-09-15)
rust entró a la cola del worker y llegó mucho más lejos: compiló las 386 crates del stage1 y
murió justo al empezar Building stage1 library artifacts (stage1 -> stage1):
error: error loading target specification: could not find specification for target
"x86_64-alpine-linux-musl"
= help: did you mean `x86_64-unknown-linux-musl`?
Es la otra mitad del muro 4, y sólo se ve en el stage 2. Usar el triple de Alpine funciona
mientras el compilador que manda es el del LAB —Alpine le PARCHEA esa especificación a su rustc— y
deja de funcionar en el instante en que toma el relevo el rustc que acabamos de construir, que es
upstream puro y no la conoce. O sea: el triple que hace falta para arrancar el build es el que el
producto del build no tiene.
Las salidas, por orden de honestidad:
- Parchear
rustc_target para registrar x86_64-alpine-linux-musl, que es literalmente lo que
hace Alpine en su APKBUILD. Un fichero de spec copiado de x86_64_unknown_linux_musl.rs y una
línea en supported_targets!. El triple queda DENTRO del compilador, así que vale en todos los
stages.
RUST_TARGET_PATH + un .json generado por el propio stage0
(--print target-spec-json) — más barato, pero un target por JSON como host es terreno que
rustc no soporta bien: la std del host se construye con el spec built-in.
- Volver al triple canónico y resolver el «can't find crate for std» del stage0 por otro lado. Hoy
no hay candidato: el prebuilt oficial trae esa std pero no puede compilar proc-macros (muro 2),
así que no sirve de stage0 para el bootstrap.
⇒ La 1 es la que Alpine ya demostró que funciona, y es la que sigue.
4.ter ✅ EL ESCALÓN 2 SELLÓ (2026-09-16 00:18)
rust 1.97.0 desde fuente: b3:015a07fb…, 353 M. Construido por la granja, en el worker, desde
llvm21 del corpus y el stage0 del lab.
Los controles, corridos en la caja (que sí tiene el cargador musl; el worker no):
$ rustc --version
rustc 1.97.0 (2d8144b78 2026-07-07) (built from a source tarball) ← NUESTRO
$ rustc --print target-list | grep alpine
x86_64-alpine-linux-musl ← EL PARCHE, DENTRO
$ cargo --version
cargo 1.97.0 (c980f4866 2026-06-30) (built from a source tarball)
Y compila: --emit=obj da un objeto de 3,5 K y --crate-type=rlib una biblioteca de 6,4 K, las dos
sin tocar el enlazador.
Los dos muros de esta tanda
- El triple de Alpine no existía en el producto. Resuelto con
rust-alpine-target.patch, que lo
registra en rustc_target — un spec copiado del canónico con crt_static_default = false (sin
eso no hay dylibs y sin dylibs no hay proc-macros) y una línea en supported_targets!. Es lo que
hace Alpine. El control no es que el build pase: es que --print target-list lo liste.
openssl-sys, a la hora y media de build. tools = ["cargo"] lo arrastra. La dep que entró es
openssl-threads y no la canónica: el Configure de openssl apaga los threads en cuanto ve
-static en LDFLAGS, y cargo es masivamente multihilo. Más OPENSSL_STATIC=1/OPENSSL_DIR=/usr,
porque el lab publica .a y ningún .so.
🧱 Muro 8, ya medido: el compilador no trae con qué ENLAZAR
En una caja sin cc —o sea, en cualquier takana que no sea un hub— el enlace falla:
$ rustc hola.rs -C linker-flavor=ld.lld
error: linker `lld` not found
$ rustc hola.rs -C linker=/usr/bin/ld -C link-self-contained=yes
/usr/bin/ld: cannot find -lgcc
La causa está en el log del propio build, en una línea que parece informativa:
skipping llvm-tools (x86_64-alpine-linux-musl): external LLVM
Al usar un LLVM externo —que es lo que queremos, porque llvm21 es del corpus— x.py se salta las
herramientas de LLVM, y rust-lld es una de ellas. El prebuilt oficial sí lo trae, y por eso el
escalón 1 enlazaba y éste no. Y llvm21 tampoco sirve de repuesto: no publica ningún lld
(medido: 1,6 G de artefacto, cero binarios lld).
⇒ Las salidas son dos, y hay que elegir: (a) [rust] lld = true en el bootstrap.toml, que
construye rust-lld desde el llvm-project vendoreado en el tarball —el toolchain queda completo y
autosuficiente—; o (b) publicar lld desde llvm21 y fijar el linker en un
/etc/cargo/config.toml del sitio. La (a) es la que hace que «tener compilador de Rust» signifique
lo que parece.
⚠ Y lo que esto enseña del método: el artefacto selló, reproduce y no enlaza. Es la familia de
[[subcomando-sin-driver]] en el corazón del toolchain — un compilador que compila y no produce un
ejecutable. Un build ok no es la prueba; la prueba es el objeto en el disco y el binario corriendo.
4.quater ✅ EL TOOLCHAIN COMPILA, ENLAZA Y CORRE (2026-09-16)
$ cargo build --offline
Compiling prueba v0.1.0
Finished `dev` profile in 0.44s
$ ./target/debug/prueba
cargo, rustc y ld: los tres nuestros
En una caja sin compilador de C. cargo y rustc son los que construyó la granja; el enlazador
es el ld de nuestro binutils. El binario sale estático (880 K el de rustc a secas) y corre.
El enlazador no era lld: era el ld que ya teníamos
El muro 8 se cerró sin rust-lld y sin lld21. La cadena de intentos, que es la parte útil:
| intento | resultado |
-C linker-flavor=ld.lld | linker \lld` not found— el build con LLVM externo **no genera**rust-lld` |
rust.lld = true | el bootstrap lo RECHAZA: «Cannot enable LLD … when using external llvm-config» |
lld21 (receta nueva) | selló, y crasheaba con cualquier entrada (ver abajo) |
lld21 dinámico + zlib | el zlib no se puede encender desde acá: LLVMConfig.cmake dice LLVM_ENABLE_ZLIB 0 y el build standalone lo hereda |
-C linker-flavor=ld + +crt-static | funciona |
Lo que faltaba no era un enlazador: era decirle a rustc que enlace en estático. Con
crt-static, rustc usa su propio libunwind/compiler_builtins y deja de pedir -lgcc — que
es lo que rompía el intento con ld, porque la distro se construye con zig (compiler-rt) y libgcc
no existe. Estático es además el modo natural de musl.
⇒ La config del sitio (el pendiente del §5) ya está escrita: scripts/servidor/cargo-config.toml,
instalada en /etc/cargo/config.toml, con las tres flags y el porqué de cada una.
🧨 Lo que lld21 destapó de paso: las 79 herramientas de llvm21 están ROTAS
lld21 selló y se moría en cuanto hacía algo: --version y --help bien, y cualquier enlace
—incluso uno que sólo debía dar un error— en SIGSEGV con un «Stack dump:» vacío. No era la receta:
$ llc --version (del artefacto llvm21, sellado hace días)
PLEASE submit a bug report … Stack dump: ← también crashea, y también en el worker
Salen static-pie, y el enlace estático descarta los constructores globales de los que dependen
los registros de LLVM (cl::opt, TargetRegistry): funciona lo que no los necesita (llvm-ar,
llvm-config) y revienta lo que sí. Nadie lo había visto porque rust usa llvm-config y las
.a, nunca una herramienta. Con link = "dynamic", lld21 pasa a dar errores limpios.
⚠ Queda como deuda DECLARADA, no arreglada: encender zlib en llvm21 —lo que haría a lld21
usable— re-hashea llvm21 y con él rust, o sea dos builds largos. Y arreglar las 79
herramientas es la misma operación. Hoy no bloquea nada: el toolchain enlaza con ld.
5. Lo que falta decidir
- Dónde vive el toolchain. Hoy
rust-toolchain-bin está en perfil.servidor (799 M; un
escritorio no compila nada). El de fuente irá donde se decida — y probablemente sustituya al
prebuilt en ese perfil en vez de sumarse.
- La config del sitio: que las flags del linker (
-C linker-flavor=ld.lld) sean el default en
un /etc/cargo/config.toml, para que cargo build funcione sin recordarlas.
- El escalón 3: cuándo se conecta la cadena del selfhost al corpus. Es lo único que quita la
dependencia de bytes ajenos, y es el trabajo más caro de los tres.