Tu ejecutable puede ser una base de datos SQLite

Tu ejecutable ahora es una base de datos SQLite y corre igual

Un hola mundo en C, empaquetado como archivo SQLite de 1,7 MiB, se ejecuta nativamente en Linux sin cambio alguno. Farid Zakaria, ingeniero detrás del proyecto selfdb, demostró que el formato ELF —el que usa el kernel para cargar cada binario desde hace décadas— puede reemplazarse por una base de datos SQLite completa sin sacrificar rendimiento. La base de datos se convierte en el ejecutable.

El proyecto llamó la atención de Simon Willison y de la comunidad técnica porque introduce una idea provocadora: el formato ELF ya es una base de datos que se niega a admitirlo, con string interning manual, índices tipo bloom filter y tablas de secciones implementadas a mano. SQLite, en cambio, es un formato auto-descriptivo y estable desde hace más de dos décadas. ¿Qué pasa si se usa SQLite directamente como contenedor de un ejecutable?

Por qué un ELF parece una base de datos que se resiste a serlo

Zakaria, durante su doctorado, mappeó cada mecanismo de ELF a su equivalente en bases de datos:

👥 ¿Quieres ir más allá de la noticia?

En nuestra comunidad discutimos las tendencias, compartimos oportunidades y nos ayudamos entre emprendedores. Sin humo, solo acción.

👥 Unirme a la comunidad
  • .strtab y .dynstr funcionan como interning de strings.
  • .hash y .gnu.hash son índices estilo CREATE INDEX.
  • La tabla de cabeceras de sección equivale a sqlite_schema, la tabla de tablas.
  • sh_offset y sh_size reproducen el layout de páginas b-tree.
  • objcopy --strip-debug se reduce a DELETE + VACUUM.
  • El cache de ldconfig y debuginfod son índices out-of-band.

Cada herramienta — readelf, nm, objcopy, ldconfig — reimplementa el mismo parser en serie. El formato es increíblemente terso, pensado cuando el espacio en disco escaseaba, explica Zakaria en su publicación del 23 de agosto. Modificarlo es frágil: hay que zerar secciones y añadir otras nuevas, sin un esquema que se autodescriba.

SELF: cuando SQLite se vuelve ejecutable

El formato que propone Zakaria se llama SELF (Structured Executable & Linkable Format) y se reduce a dos tablas obligatorias más algunas opcionales:

  • self_meta: cabecera ELF como pares clave/valor.
  • segments: imagen de carga con un registro por cabecera de programa, con los bytes en un BLOB.
  • symbols: tabla única de símbolos con versiones tipo GLIBC_2.2.5 y un índice b-tree real.
  • Tablas opcionales para sections, notes, dynamic_entries: borrarlas y el programa sigue corriendo. strip(1) se vuelve una transacción.

Para que el kernel acepte el archivo como ejecutable, SELF usa el campo application_id de 4 bytes que SQLite reserva en el offset 68 del header, estampado con el magic SELF (0x53454c46). Un SQLite común nunca matchea ese patrón.

Cómo el kernel aprende a ejecutar archivos SQLite

El truco se apoya en binfmt_misc, el subsistema de Linux para registrar binarios personalizados. Registrando el magic de SQLite más el SELF en el offset 68, el kernel lanza automáticamente el intérprete self-exec cada vez que ve un archivo con ese patrón.

En NixOS la configuración cabe en pocas líneas. En cualquier otra distribución, una variante del registro se reduce a:

printf '%s\n' ':self:M:68:SELF::/usr/local/bin/self-exec:' \
  > /proc/sys/fs/binfmt_misc/register

self-exec es un programa en C enlazado contra libsqlite3 que lee los program headers y la tabla de símbolos desde la base de datos, mapea los segmentos en memoria, aplica relocaciones y salta al entry point. Funciona como ld.so, pero alimentado por queries.

Closures: binarios autosuficientes con SQL

La idea más potente no es ejecutar un archivo SELF suelto, sino empaquetar un binario y todas sus dependencias transitivas en un solo SQLite. La herramienta self closure toma un ejecutable y produce una base de datos con tablas objects y needs, donde resolved_path actúa como foreign key hacia la ruta exacta de cada librería.

El ejemplo de Zakaria con ls:

libgmp.so.10          rfabfsmwq02sn94mb3qg
libacl.so.1           x0zgiss9hdzcsll3cswg
libattr.so.1          08nfpyc4qhzdkc37nznv
libc.so.6             8kvxvr3pmsypxiypa4g8

Seis objetos, segmentos y todo, en un único archivo de 4,8 MiB. ldd se vuelve un JOIN.

Llevado al extremo, empaquetar los 723 ejecutables y 400 librerías distintas del PATH en una sola base de datos da como resultado 611,9 MiB, más pequeño que los 644,4 MiB de ELF originales según las cifras publicadas por Zakaria. 1.123 objetos, 346.386 símbolos y 3.808 aristas de dependencia, todo consultable con SELECT. Sin deduplicación explícita, en modo AppImage, esos mismos 723 programas pesarían 5,53 GiB.

El bonus inesperado: LD_PRELOAD transaccional

SELF incluye una tabla preload(ord, path) con los objetos a mapear al final, de modo que sus exports ganan. Activar o desactivar LD_PRELOAD es INSERT + DELETE dentro de una transacción —sin variables de entorno, sin relink, sin reinicio. Esto permite hacer cosas como interponer un malloc instrumentado en todo el userland y luego ROLLBACK para volver al estado anterior, algo imposible con la mecánica clásica.

Qué significa esto para tu startup

No se trata de migrar mañana a SELF. Se trata de tres ideas que sí puedes aplicar hoy:

  • Inspecciona binarios con SQL en vez de readelf + grep. La herramienta previa de Zakaria, sqlelf, expuesta en su paper arXiv:2405.03883, monta tablas virtuales sobre ELF. Sustituye pipelines frágiles de análisis por queries reproducibles. Útil para auditorías de dependencias y triage de supply chain.
  • Explora binfmt_misc para runtimes propios. El mecanismo que SELF usa para que el kernel lance SQLite es el mismo que Rust usa para .wasm o Java para .class. Si tu producto corre sobre un runtime custom, registrarlo en binfmt_misc evita wrappers y scripts de shell en cada deploy.
  • Piensa en closures para artefactos reproducibles. El patrón SELF (un archivo, dependencias resueltas como FK, sin ambigüedad de soname) es el mismo principio detrás de Nix store paths, OCI image layers y hasta de bun build --compile. Cuando lances binarios a producción, replica el closure y evita el clásico "en mi máquina funciona" por una librería mal resuelta.

El coste de SELF hoy —según el benchmark publicado— es de unos 5 ms de overhead fijo para abrir la base de datos y arrancar el intérprete, más una copia proporcional a la imagen del programa. Los binarios stripped quedan dentro del 1 % del tamaño ELF original. No es un reemplazo para producción esta semana, pero sí la mejor evidencia de que el formato binario de Linux llevaba años pidiendo ser una base de datos.

Fuentes

👥 ¿Quieres ir más allá de la noticia?

En nuestra comunidad discutimos las tendencias, compartimos oportunidades y nos ayudamos entre emprendedores. Sin humo, solo acción.

👥 Unirme a la comunidad

Daily Shot: Tu ventaja táctica

Lo que pasó en las últimas 24 horas, resumido para que tú no tengas que filtrarlo.

Suscríbete para recibir cada mañana la curaduría definitiva del ecosistema startup e inversionista. Sin ruido ni rodeos, solo la información estratégica que necesitas para avanzar:

  • Venture Capital & Inversiones: Rondas, fondos y movimientos de capital.
  • IA & Tecnología: Tendencias, Web3 y herramientas de automatización.
  • Modelos de Negocio: Actualidad en SaaS, Fintech y Cripto.
  • Propósito: Erradicar el estancamiento informativo dándote claridad desde tu primer café.

📡 El Daily Shot Startupero

Noticias del ecosistema startup en 2 minutos. Gratis, todos los días.

Share to...