- Wild es un enlazador que apunta a enlaces muy rápidos durante el desarrollo iterativo; aunque todavía no implementa enlace incremental, ya es rápido incluso solo con enlace no incremental
- El punto de partida de Wild es que mold, el enlazador rápido existente, no hace enlace incremental ni tiene planes de hacerlo; Wild está escrito en Rust y busca manejar la complejidad del enlace incremental
- Las plataformas compatibles actualmente son x86-64, ARM64, RISC-V, LoongArch64 y PPC64LE en Linux; admite binarios estáticos, static-PIE, binarios con enlace dinámico, objetos compartidos, proc-macros de Rust, información de depuración, etc.
- Puede usarse como drop-in replacement, invocado igual que un enlazador existente desde GCC o Clang, y puede integrarse con Cargo, sistemas de compilación C/C++ y wild-action para CI
- Todavía no están implementados el enlace incremental, linker scripts más complejos, Mach-O ni soporte para Windows; para verificar si realmente se enlazó con Wild, hay que revisar con
readelf o strings la cadena Linker: Wild version... en el binario
Objetivo y posición de Wild
- Wild es un enlazador orientado a lograr enlaces muy rápidos durante el desarrollo iterativo
- El plan a largo plazo es implementar enlace incremental, pero actualmente todavía no está implementado
- Indica que, incluso sin enlace incremental, el rendimiento del enlace no incremental ya es rápido
- mold ya es muy rápido, pero no hace enlace incremental, y su autor declaró que no tiene planes de hacerlo; esa es la razón por la que Wild se creó como un enlazador separado
- Wild está escrito en Rust, con la expectativa de poder manejar en Rust la complejidad del enlace incremental
Instalación
- El tarball de release se puede descargar desde la página de releases; basta con descomprimirlo y copiar el binario
wild a una ubicación dentro del PATH
- Si tienes
cargo-binstall, puedes instalarlo con el siguiente comando
cargo binstall wild-linker
- El comando de instalación con Homebrew es el siguiente
brew install wild-linker/wild/wild
- Para compilar la release más reciente de crates.io, usa el siguiente comando
cargo install --locked wild-linker
- Para instalar el código no publicado más reciente de Git, usa el siguiente comando
cargo install --locked --bin wild --git https://github.com/wild-linker/wild.git wild-linker
- La versión estable de Wild en Nixpkgs se puede usar mediante
pkgs.useWildLinker pkgs.stdenv
- Para la revisión Git inestable más reciente, hay que consultar el documento
nix/nix.md
Cómo usarlo como enlazador predeterminado
- Wild está diseñado como drop-in replacement y puede usarse de la forma en que lo invocan GCC o Clang
- Las opciones son las siguientes
- La opción exclusiva de Clang
--ld-path=wild
-fuse-ld=wild en GCC 16.1 o superior y en Clang
- Clang requiere un binario
ld.wild o un enlace simbólico
- La opción generalmente compatible
-B <path>
<path> es el directorio que contiene un ld que apunta a wild
-
Rust y Cargo
- En
~/.cargo/config.toml se puede usar Clang junto con --ld-path=wild
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-Clink-arg=--ld-path=wild"]
- O se puede usar
-fuse-ld=wild
[target.x86_64-unknown-linux-gnu]
# linker = "clang" # Uncomment this line if your GCC is older than version 16.
rustflags = ["-Clink-arg=-fuse-ld=wild"]
-
Sistemas de compilación C/C++
- En autotools, CMake, meson, etc., normalmente basta con configurar
LDFLAGS
export LDFLAGS="${LDFLAGS} -fuse-ld=wild"
- En versiones antiguas de GCC, puedes crear un enlace simbólico
ld que apunte a wild y pasar ese directorio con -B
ln -s /usr/bin/wild /tmp/ld
export CFLAGS="${CFLAGS} -B/tmp"
export CXXFLAGS="${CXXFLAGS} -B/tmp"
export LDFLAGS="${LDFLAGS} -B/tmp"
- Como estos sistemas de compilación pueden ser complejos, se recomienda verificar con
readelf si Wild se usó realmente para enlazar el binario
-
Configuración de Cargo para Illumos
- En Illumos hay que especificar la ruta absoluta de Clang y también pasar Wild con ruta absoluta
- Si no se proporciona la ruta absoluta de Wild, podría delegar silenciosamente en GNU ld o Sun ld
[target.x86_64-unknown-illumos]
# Absolute path to clang - on OmniOS this is likely something like /opt/ooce/bin/clang.
linker = "/usr/bin/clang"
rustflags = [
# Will silently delegate to GNU ld or Sun ld unless the absolute path to Wild is provided.
"-Clink-arg=-fuse-ld=/absolute/path/to/wild"
]
Estado de soporte y elementos no compatibles
- Las plataformas y arquitecturas compatibles actualmente son las siguientes
-
x86-64 on Linux
-
ARM64 on Linux
-
RISC-V(riscv64gc) on Linux
- LoongArch64 on Linux
- Está en estado de soporte inicial
- PPC64LE on Linux
- Está en estado de soporte inicial
- Las funciones operativas son las siguientes, aunque se aclara que puede haber bugs
- Salida de binarios non-relocatable enlazados estáticamente
- Salida de binarios position-independent enlazados estáticamente, es decir, static-PIE
- Salida de binarios con enlace dinámico
- Salida de archivos de objeto compartido
.so
- Proc-macros de Rust enlazadas con Wild
- Supera las pruebas de la mayoría de los crates más descargados de crates.io
- Información de depuración
- Soporte para GNU jobserver
- Soporte parcial para linker scripts
- Para más detalles, consultar la matriz de soporte de linker scripts
- Linker plugin LTO
- Hay problemas conocidos
- Los elementos grandes que aún no están implementados son los siguientes, en un orden cercano a la prioridad actual
-
Enlace incremental
- Linker scripts más complejos
- Soporte para Mach-O
- Soporte para Windows
Cómo verificar si se usó Wild
readelf se puede instalar desde el paquete binutils; con el siguiente comando se revisan las cadenas de la sección .comment
readelf --string-dump .comment my-executable
- Si aparece una línea como la siguiente, es un binario enlazado con Wild
Linker: Wild version 0.1.0
strings también puede usarse desde el paquete binutils, y también se puede verificar con el siguiente comando
strings my-executable | grep 'Linker:'
Benchmarks y objetivos de prueba
- El objetivo de Wild es convertirse finalmente en un enlazador muy rápido mediante enlace incremental
- También busca que el enlace no incremental y el enlace inicial cuando esté activado el enlace incremental sean lo más rápidos posible
- Todos los benchmarks se ejecutan con salida a tmpfs
- La forma de ejecutar los benchmarks está documentada en BENCHMARKING.md
- Los sistemas usados para los benchmarks son los siguientes
- Los benchmarks de ejemplo incluyen tiempo de enlace y uso de memoria de Chromium, tiempo de enlace de
librustc-driver, tiempo de enlace de Wild en sí y tiempo de enlace de rust-analyzer en Raspberry Pi 5
Ejemplos de enlace de código Rust e información del proyecto
- Un ejemplo para compilar y probar un crate usando Wild en
cargo test es el siguiente
- Este comando se ejecutó correctamente en varios crates populares como
ripgrep, serde, tokio, rand y bitflags
- El binario
wild debe estar en el PATH, y como GCC no permite usar un enlazador arbitrario, se necesita tener Clang instalado
RUSTFLAGS="-Clinker=clang -Clink-args=--ld-path=wild" cargo test
- Si el enlace simbólico
ld.wild apunta a wild, también se puede usar este método
RUSTFLAGS="-Clinker=clang -Clink-args=-fuse-ld=wild" cargo test
- Para usar Wild como enlazador de código Rust en CI, consultar wild-action
- La información para contribuir está en CONTRIBUTING.md, y la descripción general de diseño de alto nivel está en DESIGN.md
- Las conversaciones sobre Wild se llevan a cabo en el servidor de Zulip
- En el blog de David hay muchos artículos que tratan distintos aspectos del enlazador Wild
- La licencia puede elegirse entre Apache License 2.0 o MIT license
1 comentarios
Opiniones de Hacker News
Después de que mold fuera relicenciado de AGPL a MIT (como parte del lanzamiento de mold 2.0), pensé que a nivel mundial se había reducido mucho la necesidad de crear otro linker rápido, así que no esperaba que apareciera un proyecto así.
Además, que en algunos casos ya sea 2 veces más rápido que mold me tomó todavía más por sorpresa; voy a seguir de cerca cómo evoluciona y le deseo suerte al autor.
El linker de Microsoft trae linking incremental por defecto desde hace décadas, y da un poco de vergüenza que Linux todavía no tenga un linker incremental listo para producción.
Viendo los issues de GitHub, parece que el autor al principio intentó dar soporte, luego movió el soporte de Windows al linker sold, pero sold fue archivado recientemente; entonces no sé si al final no hay soporte para Windows o si entendí mal la secuencia.
Lo revisé antes, pero todavía no sé si está listo para usarse en producción.
Según el README, parece que no, así que sigo usando mold.
Si usas macOS, Apple sacó un linker nuevo hace más o menos 1 o 2 años, y por eso el autor de mold dejó de trabajar en la versión para macOS.
Para usarlo en Rust, puedes poner esto en
config.toml:[target.aarch64-apple-darwin]rustflags = ["-C","link-arg=-fuse-ld=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld","-C","link-arg=-ld_new",]Instalé las herramientas de línea de comandos y solo tengo
/usr/bin/ldy/usr/bin/ld-classic.Sería interesante que los compiladores de C/C++ eliminaran el paso intermedio del linking y construyeran todo el programa como una sola unidad.
Si el compilador pudiera ver todo el programa desde el principio, LTO en sí no sería necesario.
Para builds incrementales habría que guardar algunos artefactos de build, pero no en forma de archivos objeto; se necesitarían metadatos que sepan de dónde viene el código generado y cuáles son sus dependencias, para poder reemplazar solo las partes correctas.
Hoy en día las bibliotecas externas casi siempre se linkean dinámicamente, así que no hace falta compilarlas desde el código fuente, y eliminar el linker tampoco generaría problemas con dependencias privadas.
Si aun así no alcanza, el compilador podría aceptar también archivos objeto para manejar casos legacy o especiales que deban linkearse estáticamente en el binario.
SQLite3 concatena todo para convertirlo en una sola unidad de compilación.
Probablemente más gente de la que uno cree ya usa este enfoque.
https://sqlite.org/amalgamation.html
El interés por los linkers rápidos aumentó mucho por la adopción y popularidad de Rust.
Incluso un binario Rust con linking estático de tamaño moderado puede tardar varios minutos en la etapa de linking de una compilación en modo release (usando mold).
No es un problema exclusivo de Rust; en general es el resultado de la combinación de linking estático estricto, optimizaciones avanzadas en tiempo de linking como LTO y BOLT que ofrece LLVM, y las quejas de la comunidad Rust por los tiempos de compilación.
Por su fuerte relación con LLVM, en la práctica una dependencia, Rust se convirtió en el lenguaje donde la magia de LLVM en tiempo de linking se adoptó más ampliamente; en C++ se puede sufrir el mismo problema, pero en ese caso es más probable que se culpe al toolchain que al lenguaje.
He estado siguiendo wild durante un tiempo por su potencial de convertirse en un linker incremental con optimizaciones, pero sinceramente no tengo ninguna motivación para probarlo hasta que realmente pueda hacer linking incremental.
Además, el ecosistema C/C++ tiene una cultura de aceptar bibliotecas binarias, así que muchas veces, al clonar un repositorio o cambiar a una rama de desarrollo, solo necesitas compilar tu propia aplicación, no recompilar el mundo entero.
Si haces que el shell externo de la aplicación llame a la lógica de negocio en Wasm, solo tienes que recompilar la lógica interna, y ni siquiera necesitas reiniciar el shell de la app externa.
2008: gold, un nuevo linker creado para ser más rápido que GNU ld
Alrededor de 2015: lld, un reemplazo drop-in al menos 2 veces más rápido que gold
2021: mold, un nuevo linker varias veces más rápido que lld
2025: el nuevo linker wild...
Relacionado con esto, hay un libro viejo pero bueno: Linkers and Loaders, de John Levine.
Es el último libro de la lista de abajo.
https://www.johnlevine.com/books.phtml
Lo leí hace unos años y me pareció bastante interesante; es el libro de referencia en esta área.
Como se ve en el enlace, también escribió otros libros populares de computación.
Se ve bastante prometedor.
Está hecho en Rust desde el principio, y el objetivo es que sea rápido y soporte linking incremental.
Para usarlo con Rust, probablemente también se pueda usar gcc como driver del linker.
En el
.cargo/config.tomldel proyecto:[target.x86_64-unknown-linux-gnu]rustflags = ["-C", "link-arg=-fuse-ld=wild"]Es un detalle secundario, pero ¿por qué Rust tiene que engancharse aquí con gcc o clang? ¿Le falta alguna función?
Lamentablemente, gcc no acepta cualquier enlazador con la bandera
-fuse-ld=Los enlazadores que acepta son bfd, gold, lld y mold
Se puede hacer que gcc invoque a wild como enlazador, pero por ahora hay que crear un directorio que contenga el enlazador wild, renombrar el binario o el enlace simbólico a
"ld"y luego pasarle a gcc-B/path/to/directory/containing/wildLa razón por la que Rust no invoca directamente al enlazador y lo hace mediante gcc o clang es que el compilador de C conoce las banderas de enlazado necesarias para enlazar con libc y el runtime de C en la plataforma actual
Por ejemplo, cosas como
Scrt1.o,crti.o,crtbeginS.o,crtendS.o,crtn.oPorque el compilador de Rust genera bytecode IR, no código máquina
Me da curiosidad: ¿cuál sería la razón teórica por la que esto podría ser más rápido que mold en el caso no incremental?
“Porque es Rust” es una explicación válida para varias cosas, pero no explica una ventaja de rendimiento esperada
Si fuera “porque hay oportunidades de paralelismo de baja dificultad que Rust facilita aprovechar”, sería interesante, pero no está dicho ni insinuado
Una pista es que escuché que Mold se acelera alrededor de un 10% si usa el asignador más rápido mimalloc
Probé mimalloc en Wild, pero no hubo una mejora de velocidad medible
Eso sugiere que Mold usa el asignador más que Wild, y en Wild claramente se hizo un esfuerzo por optimizar la cantidad de asignaciones en el heap
En general, lo veo como una diferencia en decisiones de diseño
En cuanto a cómo podría relacionarse esto con Rust, estoy seguro de que si Wild se portara de Rust a C o C++, el rendimiento sería casi el mismo
Pero gracias al borrow checker, en Rust hay patrones de código aceptables que en C o C++ podrían convertirse en trampas y ser difíciles de mantener
Cuando antes programaba en C++, escribía de forma más defensiva aunque tuviera que aceptar un pequeño costo de rendimiento; en Rust, sabiendo que el compilador me cubre la espalda, puedo escribir con mucha más audacia
Qué coincidencia
Hace una hora comparé el rendimiento de wild, mold y ld en un proyecto en C en el que estoy trabajando
Tiene 23 mil líneas y 172 archivos; con
gcc+ldtomó unos 23.4 segundos de tiempo de usuario, congcc+mold22.5 segundos y congcc+wild21.8 segundosAsí que creo que, si un proyecto está bien estructurado, el tiempo de enlazado quizá no sea un problema tan grande
En ese caso, la mayor parte del tiempo se va en compilar código, no en enlazar
La ventaja de un enlazador rápido es mayor en el desarrollo iterativo
Es decir, cuando haces un cambio pequeño en el código, vuelves a compilar y ejecutas el resultado; normalmente el compilador tiene muy poco que hacer, pero el enlazado igual se ejecuta desde cero, por lo que suele convertirse en el tiempo dominante
ld.lld?“Este benchmark se ejecutó en la laptop de David Lattimore, una System76 Lemur Pro de 2020, con 4 núcleos (8 hilos) y 42GB RAM”
https://news.ycombinator.com/item?id=33330499
Como referencia, no estoy intentando decir que wild sea pesado
Si hay un problema, está en el software que se desarrolla con eso y en las computadoras de las personas que usarán ese software
https://news.ycombinator.com/item?id=42896619
“... tiene 16GB de RAM y no se puede actualizar...”
Medio en broma, pero si alguien programa en Rust, imagino que ya tiene 32GB RAM
Personalmente, sacrificar literalmente todo lo demás para actualizar mi laptop a 64GB fue casi una gran decisión
Digo casi porque no debí apostar todo a la RAM, sino gastar en RAM y también en la pantalla
La única desventaja es que limpiar las pestañas abiertas una vez por semana se convirtió en una tarea que toma toda la noche