wandres.dev
MAGO · Freestanding y no-libc

Freestanding: C sin libc

El C que corre sin sistema operativo debajo: -ffreestanding, -nostdlib, escribir tu propio _start y runtime. La base de kernels, firmware y bare metal.

⏱ 15 min

Todo lo que has escrito hasta ahora asumía un sistema operativo y una libc debajo. Pero C puede correr sin nada de eso: en el arranque de un kernel, en un microcontrolador, en firmware. Ese es el entorno freestanding, y entenderlo te lleva al metal desnudo.

🎯 Al terminar esta lección sabrás
  • Hosted vs freestanding.
  • Qué headers sobreviven sin libc.
  • -nostdlib y el punto de entrada _start.
  • Cuándo y por qué escribir código sin libc.

Hosted vs freestanding

El estándar C define dos entornos:

🏠

Hosted (alojado)

Hay un sistema operativo y la libc completa. Tu programa empieza en main, con printf, malloc, archivos… Es lo que has usado.

🌌

Freestanding (autónomo)

No hay SO ni libc completa. Solo unos pocos headers. El programa arranca donde tú digas. Es el mundo de kernels, bootloaders y firmware.

gcc -ffreestanding -nostdlib ...   # dile al compilador que no hay libc hosted

Los headers que sobreviven

En freestanding, casi toda la librería estándar desaparece. Solo quedan los headers que no dependen del sistema — los que definen tipos y límites, no funciones:

#include <stddef.h>    // size_t, nullptr_t, offsetof
#include <stdint.h>    // enteros de ancho fijo
#include <stdbool.h>   // bool (aunque en C23 es palabra clave)
#include <stdarg.h>    // argumentos variádicos
#include <limits.h>    // límites de los tipos
#include <stdalign.h>  // alineación

Nada de stdio.h, stdlib.h ni string.h: si quieres printf o malloc, los implementas tú (o hablas directo con el hardware).

Sin libc: _start y tu runtime

En un programa normal, antes de main corre código de arranque de la libc (el “crt”: prepara la pila, los argumentos, las variables globales, y al final llama a main). Con -nostdlib ese andamiaje desaparece: el punto de entrada real es _start, y lo escribes tú.

// -nostdlib: sin arranque de la libc; el kernel salta a _start
void _start(void) {
    // aquí no hay printf ni exit: solo syscalls directas
    const char msg[] = "hola sin libc\n";
    // write(1, msg, 14) y exit(0) mediante ensamblador de syscall
    __asm__ volatile ("syscall" :: "a"(1), "D"(1), "S"(msg), "d"(14));
    __asm__ volatile ("syscall" :: "a"(60), "D"(0));  // exit
    __builtin_unreachable();
}
gcc -ffreestanding -nostdlib -static -o hola hola.c
./hola      # "hola sin libc" — sin depender de NADA
Aquí tocas el metal de verdad

Este es uno de los momentos más reveladores del track. Sin libc, ves lo que de verdad hay: un punto de entrada, una pila, y syscalls crudas al kernel. printf era una comodidad de decenas de miles de líneas; write es una instrucción syscall con el número correcto en un registro. Este es el nivel en el que trabaja el arranque de un kernel (antes de que exista una libc), el firmware de un dispositivo (donde no hay SO), y el “no_std” de Rust. Entender freestanding desmitifica por completo qué es un programa: no necesita un SO ni una librería para existir — solo código, memoria y una forma de hablar con el hardware o el kernel. Es la base sobre la que se construye el track del kernel de Linux que viene después.

💡
Freestanding en la práctica

No escribirás _start a mano a diario, pero saber que puedes cambia tu comprensión. Es la base de: kernels (arrancan freestanding y luego montan su propio entorno), firmware y embebido (microcontroladores sin SO), bootloaders, y binarios ultraminimalistas. Cuando en el track de Rust veas #![no_std], o en el del kernel veas que no hay libc, recordarás este nivel: es el mismo mundo, el del código que se sostiene solo.

⚔️ Corre sin red
  1. Compila un programa con -ffreestanding -nostdlib que escriba un mensaje con una syscall directa y salga.
  2. Comprueba con ldd que no depende de ninguna librería.
  3. Lista los headers freestanding e identifica por qué esos sí sobreviven (definen tipos, no funciones).
  4. Reflexiona: ¿qué tendrías que implementar tú para tener un malloc en este entorno?