logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·02.11.26
← Back to news

Executable Is a SQLite Database

Original article
fzakaria.com|368 points|73 comments|by setheron|Aug 24, 2026

The Executable as a SQLite Database

By Farid Zakaria (Rewritten)

Over the last few years, my intellectual curiosity has been consumed by two primary pursuits: utilizing Nix to experiment with world-rebuilding concepts and the ambitious goal of replacing the ELF format substituting SQLite as the standard for executable files.

It is evident that these two trajectories are complementary.

I initially pursued this during my doctoral studies, but the feedback I received was largely uninspiring. Selling a radical departure from the norm is difficult because you are fighting the massive inertia of established industry standards.

One tangible outcome of that research was sqlelf, a utility designed to let users explore ELF files through declarative SQL queries. I documented this in a paper (arXiv:2405.03883) and a subsequent blog post. Instead of the tedious process of readelf | grep, one could simply run:

SELECT name FROM elf_symbols

While this was a refreshing way to interact with ELF files via virtual tables, I knew a more fundamental shift was possible. With the advent of modern LLMs, I felt compelled to revisit the core question: Can we replace ELF with SQLite as the actual executable format?

To be clear: I am not talking about a database that describes a binary, but a file that you actually chmod +x and execute.

The Vision in Practice

Imagine a world where the file command returns this:

$ file hello
hello: SQLite 3.x database, application id 0x53454c46, user version 1

$ ./hello
Hello, world!

$ sqlite3 hello 'SELECT soname FROM ldd'
libc.so.6

To test this, I built a prototype called SELF (Structured Executable Linkable Format). The results revealed several fascinating insights.


🧩 ELF: The Database in Denial

During my PhD, I realized that ELF is essentially a database that refuses to admit its identity. It manually implements various database primitives and complex data structures (like bloom filters for symbol lookups) to maintain performance.

Comparison of Mechanisms

ELF MechanismDatabase Primitive Equivalent
.strtab / .dynstrString Interning
.hash / .gnu.hashCREATE INDEX
Section Header Tablesqlite_schema (Table of tables)
st_name →\rightarrow→ .strtab offsetForeign Key (implemented manually)
sh_offset / sh_sizeB-Tree page record layout
.gnu.version_rA database column
objcopy --strip-debugDELETE + VACUUM
ldconfig / debuginfodOut-of-band indexes

Because ELF is so terse—designed for an era of extreme scarcity in disk space and bandwidth—it is incredibly rigid. Modifying it often requires "surgery," such as zeroing out sections to make room for new ones. Every tool that interacts with it (ld.so, binutils, LIEF, readelf) ends up re-implementing the same parser.

The Transition Logic


🛠️ The Architecture of SELF

To make a SELF file runnable, we only need a few core components.

Essential Requirements

  • self_meta table: Stores the ELF header as key/value pairs.
  • segments table: Stores the load image.
  • symbols table: Replaces multiple ELF sections and the .gnu.hash index.

1. The Segments Table

Each row represents a program header, with the actual binary data stored as a BLOB.

CREATE TABLE segments (
    id INTEGER PRIMARY KEY, -- Original phdr index
    type TEXT NOT NULL,     -- 'load', 'tls', 'stack', etc.
    offset INTEGER NOT NULL,
    vaddr INTEGER NOT NULL,
    filesz INTEGER NOT NULL,
    memsz INTEGER NOT NULL,
    r INTEGER, w INTEGER, x INTEGER,
    align INTEGER NOT NULL DEFAULT 4096,
    BLOB -- The actual segment bytes
);

2. The Symbols Table

Instead of a complex web of sections, we use a single table with a proper B-Tree index.

CREATE TABLE symbols (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    version TEXT,           -- e.g., 'GLIBC_2.2.5'
    value INTEGER,
    size INTEGER,
    type TEXT,              -- 'func', 'object', etc.
    bind TEXT,              -- 'global', 'weak', 'local'
    defined INTEGER NOT NULL,
    exported INTEGER NOT NULL
);
CREATE INDEX idx_symbols_name ON symbols (name, version);

What is eliminated?

By using SQLite, we can discard several legacy ELF structures:

  • .dynstr: Gone, because SQLite handles TEXT and string interning natively.
  • .gnu.hash: Replaced by a standard B-Tree index (whereas ELF uses a bloom filter + bucket chains to avoid touching the chain on a miss).
  • .gnu.version_r: Simplified into a standard column.

Conceptual Diagram of SELF


🚀 The Result: Tooling as Queries

In the SELF ecosystem, every traditional binary tool becomes a simple SQL operation.

Querying Metadata:

# Equivalent to ldd(1)
$ sqlite3 hello 'SELECT soname FROM ldd'
libc.so.6

# Equivalent to nm -D --undefined
$ sqlite3 hello 'SELECT name,version FROM imports LIMIT 3'
__libc_start_main|GLIBC_2.34
_ITM_deregisterTMCloneTable|
puts|GLIBC_2.2.5

# Equivalent to readelf -l
$ sqlite3 hello "SELECT type,vaddr,memsz,r,w,x FROM segments WHERE type='load'"
load|0|1744|1|0|0
load|4096|361|1|0|1

Modifying the Binary: The strip(1) utility, which is usually a fragile process of removing sections, becomes a simple atomic transaction:

-- strip(1) is now just:
DELETE FROM sections; 
DELETE FROM notes; 
VACUUM;

The result is a smaller file that still executes perfectly: $ ./hello →\rightarrow→ Hello, world!

By treating the executable as a database, we replace "offset surgery" with structured transactions. The application ID 0x53454c460x53454c460x53454c46 marks the beginning of a new way to think about linkable formats.