# LLD 21.1.2 sobre zlib-rs — la PRUEBA DE CONSUMIDOR DINÁMICO del paso 8 del plan. # # ══ POR QUÉ ESTA VARIANTE, Y POR QUÉ lld ES EL CASO QUE FALTABA ════════════════════════════════ # `pigz-zlibrs` probó el enlace ESTÁTICO (`libz.a` + `-lunwind`). Este prueba el otro, que es el que # de verdad puede fallar callado: **lld enlaza `libz.so.1` DINÁMICAMENTE** —`link = "dynamic"` en su # receta, por una razón medida que está escrita allá— así que acá lo que se pone a prueba no es sólo # compilar, es que el `NEEDED` grabado sea el correcto y que el cargador lo resuelva en ejecución. # # ⚠ **Y ahí apareció el defecto que el caso estático no podía mostrar: la `.so` de zlib-rs no # declaraba SONAME.** `readelf -d` sobre la `libz_rs.so` que sale de cargo mostraba `NEEDED` y ningún # `SONAME`, mientras la `zlib-shared` del corpus declara `libz.so.1`. Un consumidor enlazado contra una # `.so` sin soname graba la RUTA con la que se enlazó, no el nombre de la librería ⇒ sella, reproduce # y no corre ([[needed-colgante-libstdcxx]]). Arreglado en `recipes/zlib-rs.toml` con # `-C link-arg=-Wl,-soname,libz.so.1`; esta receta existe para comprobarlo de punta a punta. # # ⚠ **lld USA zlib de verdad, no de adorno**: `LLVM_ENABLE_ZLIB` está encendido porque los objetos # `crt*` del sysroot de rust traen las secciones de depuración comprimidas # (`ELFCOMPRESS_ZLIB`) y un enlazador sin zlib no puede ni leerlos. O sea que si zlib-rs no # descomprimiera bien, el fallo aparecería enlazando, no en un `--version`. # # Lo único que cambia frente a `recipes/lld21.toml`: la dep (`zlib-rs` en vez de `zlib`). No hace # falta `-lunwind` —y eso es un hallazgo del caso dinámico—: la `.so` de zlib-rs trae # `NEEDED libgcc_s.so.1`, que es quien provee los `_Unwind_*` en ejecución, así que el consumidor no # tiene que sumar nada. La reserva del unwinder era del enlace ESTÁTICO. name = "lld21-zlibrs" version = "21.1.2" license = "Apache-2.0 WITH LLVM-exception" [source] # El mismo tarball que `llvm21`, byte por byte: la URL es locator y el sha256 es la identidad # (ADR 0013), así que compartir fuente entre dos recetas no cuesta nada. tarball = "https://github.com/llvm/llvm-project/releases/download/llvmorg-21.1.2/llvm-project-21.1.2.src.tar.xz" sha256 = "1a417d1c8faf8d93e73fec1cbb76d393ed3218974c2283c7bac9672d3d47c54b" [build] compiler = "zig-cc" target = "x86_64-linux-musl" # ⚠ `dynamic` NO es una preferencia: con `static` el binario SELLA, arranca y **se muere en cuanto # hace algo** (medido 2026-09-16). `ld.lld --version` y `--help` contestan bien; cualquier enlace # —incluso uno que sólo debería dar un error, como un fichero de entrada inexistente— termina en # SIGSEGV con el banner de LLVM y un «Stack dump:» vacío. La firma es la de los registros estáticos # de LLVM: `cl::opt`, `TargetRegistry` y compañía se inicializan desde constructores globales que # viven en objetos de las `.a`, y un enlace estático agresivo los DESCARTA por no estar # referenciados — lo que funciona es lo que no los necesita (`--version`), y lo que no, revienta. # Es la misma familia que `link-static-mentira-libtool`: el flag global de la receta decide cosas # que la receta no dice. link = "dynamic" strip_debug = true [deps] # `llvm21` NO es un adorno: el build standalone de lld necesita su `lib/cmake/llvm/LLVMConfig.cmake` # y sus `.a`. Es la misma dep que el paso `Lld` del bootstrap de rustc habría usado. build = ["llvm21", "cmake", "samurai", "python3", "pkgconf", "zlib-rs"] [build.phases] # ⚠ `LLVM_ENABLE_ZLIB` va ENCENDIDO —al revés que en `llvm21`— y lo decidió un error, no el gusto: # # lld: error: …/self-contained/crtn.o:(.debug_line) is compressed with ELFCOMPRESS_ZLIB, # but lld is not built with zlib support # # Los objetos `crt*` que rust trae en su sysroot llevan las secciones de depuración COMPRIMIDAS, así # que un enlazador sin zlib no puede ni leerlos. Para `llvm21` apagarlo era gratis; para el # ENLAZADOR no lo es. # # `g++` con los runtimes estáticos, igual que `llvm21` y `cmake`: C++ a escala con zig sigue siendo # terreno minado, y además LLD tiene que casar ABI con el LLVM contra el que enlaza — que se # construyó exactamente así. configure = ''' CC=gcc CXX='g++ -static-libstdc++ -static-libgcc' \ cmake -S lld -B build -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr \ -DLLVM_DIR=/usr/lib/cmake/llvm \ -DLLVM_INCLUDE_TESTS=OFF \ -DLLVM_ENABLE_ZSTD=OFF \ -DLLVM_ENABLE_LIBXML2=OFF ''' compile = "ninja -C build" # ⚠ LOS CINCO `lld` SON EL MISMO BINARIO, Y cmake LOS INSTALA COMO CINCO COPIAS: medido, 5 × 96 M = # **478 M de artefacto para 97 M de enlazador**. `lld` despacha por `argv[0]`, así que los cuatro # alias son symlinks por diseño de upstream — instalarlos como copias es sólo cómo quedó su cmake. # En una caja cuya raíz son 5,8 G, 380 M de duplicado no son un detalle. install = "DESTDIR=/out ninja -C build install && cd /out/usr/bin && for f in ld.lld ld64.lld lld-link wasm-ld; do ln -sf lld \"$f\"; done"