把723个可执行程序、400个共享库,连同所有的依赖关系,全部塞进一个 SQLite 数据库文件里。
你猜结果怎么着?
这个庞大的“数据库”,体积居然比原来散落的 ELF 文件还要小。
这绝非赛博朋克科幻小说。这是最近科技圈一个极其硬核的极客实验。
拒绝承认身份的数据库
作者 fzakaria 在博士期间就盯上了一个看起来有点“离经叛道”的目标:用 SQLite 替换 ELF,作为 Linux 的可执行文件格式。
说实话,刚听到这个想法,个人觉得有点扯。ELF(Executable and Linkable Format)可是 Unix/Linux 世界的基石,稳如老狗。
但作者指出了一个极其尖锐的事实:ELF 本质上就是一个数据库,只不过它是一个拒绝承认自己身份的数据库。
它诞生于磁盘空间和带宽极度匮乏的年代。为了省空间,格式被设计得极其紧凑。
想改点东西?难如登天。你往往得把原有区块清零,再硬塞新区块。更糟的是,它没有自描述的 schema。
结果就是,内核、动态链接器、binutils、readelf……所有人都在一遍又一遍地重复手写解析器。大家都在给一个手工拼凑的草台班子打补丁。
把外科手术变成搭积木
既然 ELF 是个手工拼凑的草台班子,那为什么不直接用一个真正的、成熟的、自描述的关系型数据库呢?
于是,SELF(Structured Executable & Linkable Format) 诞生了。
在 SELF 的世界里,ELF 的 header 变成了 self_meta 表,程序段变成了 segments 表,符号表变成了 symbols 表。
有意思的是,以前那些让人头秃的命令行工具,全被降维成了 SQL 查询。
想查看依赖?不用敲 ldd,直接 SELECT soname FROM ldd。
想剥离调试信息?不用搞脆弱的偏移量手术,strip 命令直接变成了 DELETE FROM sections; VACUUM;。
这简直是把外科手术变成了搭积木。任何修改 ELF 文件的工具,现在都变成了数据库里的事务操作。
整个用户态,装进一个文件
如果故事只到这里,那不过是个极客的自嗨。真正的反转在于系统级闭包。
单个 SELF 文件因为 SQLite 的 B-tree 索引开销,体积大概是 ELF 的两倍。看起来是个劣势,对吧?
但作者做了一个疯狂的举动:他把系统 PATH 下的 723 个可执行文件,以及它们拉取的 400 个共享库,全部打包进了一个 SQLite 文件。
1123 个对象,34万多个符号,近 4000 条依赖边。
奇迹发生了。当对象数量足够大时,B-tree 的开销被无限摊薄。
611.9 MiB 的数据库,比原来 644.4 MiB 的 ELF 文件还要小!
整个 Linux 用户态,变成了一个可查询的单一文件。
更绝的是,以前靠环境变量 LD_PRELOAD 搞的库拦截,现在直接变成了数据库里的一行记录。想全局开启拦截?INSERT 一行。想关闭?DELETE 一行。
原子级的全局拦截,只需一个数据库事务。 这操作,老实讲,有点帅。
无法回避的物理硬伤
但别急着欢呼,技术世界没有免费的午餐。
评论区的大佬们一眼就看穿了死穴。有人直接甩出结论:
The copied vs. mapped memory situation is the only deal breaker in this experiment.
正常的 ELF 文件,多个进程运行同一个程序时,文本页是在内存中共享的(mmap)。
但 SELF 文件里的字节是从 B-tree 里“复制”出来的,没法直接映射共享。这意味着,两个进程跑同一个 SELF 程序,内存占用会实打实地翻倍。
加上固定 5ms 的 SQLite 打开延迟,启动速度也变慢了。
这点我不太认同作者那种“一切皆可探索”的乐观态度。在系统级底层,内存和延迟的损耗往往是致命的。轮子确实可以重造,但物理定律不买账。
下一个被颠覆的传统是什么
抛开工程上的瑕疵,这个实验的价值在于打破了思维定势。
我们习惯了 ELF 的紧凑,却忘了它已经在这个时代显得笨重。当 Nix 这样的工具允许我们“重建世界”时,也许我们真的该跳出舒适区。
如果连可执行文件都可以是一个数据库,那下一个被颠覆的“传统”,又会是什么?
【锐评】:把系统打包成数据库的想法很性感,但无法共享内存页的物理硬伤,注定它只能是个极客玩具,成不了 Linux 的新基石。
参考链接:
https://fzakaria.com/2026/08/23/your-executable-is-a-sqlite-database