El lenguaje de programación Rust ha ganado una gran popularidad en los últimos años debido a su seguridad de memoria, rendimiento y capacidades para sistemas embebidos y de bajo nivel. Tradicionalmente, muchas aplicaciones escritas en Rust dependen en gran medida de la biblioteca Libc, una capa clave que brinda acceso a las API del sistema operativo en entornos Unix y Linux. Sin embargo, existe un proyecto experimental llamado Mustang, impulsado por sunfishcode, que redefine esta relación al permitir la creación de programas completos escritos enteramente en Rust sin usar Libc, crt1.o o ningún código en C. El objetivo de Mustang es ofrecer un entorno en el que Rust pueda interactuar directamente con el sistema operativo mediante llamadas al sistema (syscalls) sin pasar por la capa de compatibilidad que ofrece Libc.
Esto abre puertas interesantes para quienes buscan una integración minimalista y totalmente en Rust del programa con el entorno de ejecución, manteniendo compatibilidad con el estándar Rust y con las arquitecturas x86-64, x86 y aarch64. Para entender la profundidad del proyecto Mustang, primero es necesario conocer cómo se comunican las aplicaciones con el sistema operativo en Linux. En general, las llamadas al sistema conforman el puente imprescindible para que una aplicación pueda realizar operaciones como leer archivos, escribir en la pantalla, gestionar memoria o crear procesos. Estas llamadas normalmente se realizan a través de Libc que actúa como intermediaria. Mustang, sin embargo, basa estas interacciones en rustix, una biblioteca Rust que ofrece una API similar a syscalls y puede funcionar directamente por medio de syscalls Linux crudos o, de forma opcional, a través de Libc.
Rustix ha sido el núcleo motivador del proyecto Mustang. Su desarrollo ha permitido experimentar con nuevos conceptos de seguridad de entrada/salida y gestión requerida por Wasmtime, una máquina virtual para ejecución de WebAssembly, además de impulsar implementaciones que requieren un rendimiento bajo muy ajustado, todo mientras se mantienen ciertas garantías de seguridad y estilo idiomático en Rust. Mustang aprovecha Rustix para realizar llamadas al sistema sin los gastos generales ni la dependencia de Libc, una decisión que se traduce en menos capas entre el código Rust y el kernel del sistema operativo. Otro aspecto crucial para ejecutar programas sin Libc es el proceso de inicio o arranque del programa. Tradicionalmente, en Linux, crt1.
o es responsable de realizar la configuración inicial del entorno, la preparación del stack y la transferencia de control desde el sistema operativo al programa escrito en C o C++. Este componente ha sido históricamente complejo por su mezcla de código en ensamblador, condiciones preprocesadas y soporte para funciones avanzadas como el enlazado dinámico. Mustang redefine esta etapa en el crate llamado origin, que ofrece un muy pequeño fragmento de código en ensamblador que traduce de forma directa el estado inicial del proceso —establecido por el sistema operativo— al convenio de llamada utilizado por Rust. A partir de ahí, se calcula argc, argv y envp, se ejecutan las funciones de inicialización y finalmente se llama a main, todo dentro de Rust. Para evitar que el enlazador incluya crt1.
o, Mustang define destinos de compilación personalizados con sufijos "-mustang" que pasen la bandera -nostdlib, dejando claro que no se desea enlazar con el sistema estándar de C. Gracias a la integración con el modo build-std de Cargo, la herramienta oficial para Rust, Mustang puede compilar la biblioteca estándar personalizada para estos objetivos, sin necesidad de alterar el código base de Rust. Pero eliminar completamente Libc significa enfrentarse a otro reto importante: muchas partes del lenguaje y la biblioteca estándar dependen de funciones y convenciones definidas en Libc, que incluyen desde funciones para gestionar memoria hasta operaciones sobre cadenas o acceso a memoria virtual. En vez de intentar reescribir toda la biblioteca estándar o crear un runtime desde cero, Mustang opta por crear una capa mínima llamada c-scape que proporciona las funciones libc compatibles necesarias para que std pueda funcionar normalmente. Esta capa ofrece implementaciones básicas pero funcionales para funciones comunes como write, strlen, memcpy, mmap y otras, todas construidas usando internamente rustix para realizar las llamadas al sistema.
De este modo, el programa Rust puede usar la biblioteca estándar sin que ésta dependa directamente de una implementación completa de Libc, sino de esta capa ligera en Rust. Un desafío adicional en entornos sin Libc es la asignación global de memoria. Rust permite a los desarrolladores definir su asignador global mediante el atributo global_allocator. El asignador predeterminado utiliza malloc basado en libc, lo cual no es viable cuando se elimina esta dependencia. Por ello, Mustang integra wee_alloc, un asignador simple y portable diseñado originalmente para WebAssembly, que encaja de forma rápida y sencilla con #[global_allocator].
Wee_alloc no ofrece optimizaciones complejas, pero es suficiente para que programas básicos funcionen sin problemas en este contexto sin libc. El crate mustang reúne todas estas piezas y hace posible que los desarrolladores compilen y ejecuten programas escritos enteramente en Rust y usando std, sin la intervención de código C o Libc. Aunque el proyecto Mustang es experimental y tiene limitaciones importantes, es altamente educativo y puede inspirar nuevas formas de abordar la seguridad, la gestión de procesos y las llamadas al sistema con Rust. Muchas de sus partes relativamente maduras pueden beneficiar otros proyectos reales y prácticas donde se prefiera minimizar la dependencia de código C tradicional. Por otro lado, trabajar sin la capa de libc trae consigo una cuota elevada de código inseguro y falta de robustez en comparación con las bibliotecas C maduras.
Mustang no busca reemplazar completamente el ecosistema de libc ni la compatibilidad con el sistema actual, sino explorar nuevas maneras para mejorar la interacción entre Rust y el sistema operativo. A largo plazo, este enfoque podría abrir la puerta a redefinir cómo se tratan algunos elementos cruciales como los argumentos en línea de comandos, las variables de entorno o el manejo seguro de códigos de error, buscando modelos de interacción más seguros y ergonomía nativa para Rust. Para quienes desarrollan sistemas operativos, herramientas embebidas o programas que requieren un control absoluto y conocimientos profundos del proceso de ejecución, Mustang es una inspiración de cómo el ecosistema Rust puede continuar creciendo para cubrir casos que antes sólo eran posibles en C o ensamblador. Además, la comunidad alrededor de Mustang y rustix es accesible y abierta, permitiendo a desarrolladores contribuir en la evolución de estas ideas o aprender de su diseño. Aquellos interesados en conocer más sobre Mustang pueden explorar su documentación oficial, participar en chats o seguir el desarrollo en sus repositorios públicos.
En resumen, escribir programas en Rust sin depender de libc es un reto fascinante que Mustang encara con una combinación de pragmatismo y experimentación. Al evitar libc y crt1.o, este sistema ofrece un camino alternativo para que Rust controle cada etapa desde el inicio del proceso hasta la interacción sistemática, sin sacrificar la compatibilidad con programas que usan std. Aunque aún en fase experimental, Mustang abre las puertas a nuevas posibilidades en desarrollo de software de bajo nivel con Rust, apuntando a un futuro donde el lenguaje pueda ser aún más autónomo y seguro para programación de sistemas y aplicaciones críticas.