
A prototype turns the executable format into a queryable SQLite database
Developer Farid Zakaria has built SELF, an experimental format that stores a program and its libraries in one SQLite database, replacing readelf, ldd and even strip with SQL.
Software developer Farid Zakaria has published a prototype that replaces the ELF binary format with SQLite. The project, called SELF — Structured Executable & Linkable Format — produces an ordinary SQLite file that is stamped executable and runs, with SQL tooling taking the place of binutils utilities. The code is on GitHub under the selfdb repository.
ELF is a database that refuses to admit it
Zakaria's argument is that ELF already is a database, implemented by hand: string tables are string interning, .gnu.hash is a hand-rolled index, the section header table is a table of tables, and offsets into the string table act as foreign keys. The format is dense, has no self-describing schema, and every tool that parses it reimplements the same parser. His earlier tool, sqlelf, exposed ELF files as SQL virtual tables so developers could run SELECT statements instead of combining readelf with grep.
What a SELF file contains
Two tables are enough to execute: self_meta stores the ELF header as key/value pairs, and segments holds one row per program header with the segment bytes in a BLOB column. A single symbols table with one b-tree index replaces several ELF sections and the hand-rolled .gnu.hash lookup structure, and symbol versions become an ordinary column. The tables for sections, notes and dynamic entries exist only for tooling: deleting them still leaves a runnable program, which means strip is a DELETE followed by VACUUM, patchelf is an UPDATE, and ldd becomes a view over a join.
How the kernel runs it
SQLite reserves a four-byte application_id field at offset 68 of its header; SELF stamps it with the characters SELF so an ordinary database never matches. Registration through Linux's binfmt_misc then tells the kernel to invoke a small interpreter, self-exec, which maps the loadable segments, relocates them and jumps to the entry point. The interpreter itself must remain an ELF file, because one that also matched the magic would recurse until the kernel returns ELOOP.
Costs and a whole userland in one file
Dynamic linking was explored twice: first by keeping ld.so and answering library lookups through glibc's rtld-audit interface with SQL, then with a prototype linker that performs the binding in SQL. A SELF file is roughly twice the size of the equivalent ELF, but most of that is optional tooling data — stripped, a coreutils binary measured 1,794,048 bytes against 1,768,632 for the ELF version, within one percent. Startup costs a fixed ~5 ms plus a copy proportional to image size, and since bytes are copied out of the b-tree rather than mapped, two running instances do not share text pages.
Packing a full userland into one database gave the most striking result: 723 executables pulling in 400 distinct shared libraries became 1,123 objects and 346,386 symbols in a single 611.9 MiB file, smaller than the 644.4 MiB of ELF files it came from. LD_PRELOAD becomes a row in a table, so toggling it is a transaction.
SiTech — AI-powered web development
We build fast, modern websites and bring AI into real business workflows. Have a project or a question? We'd love to help.