veilletech.fr
25 août Feed du jour
#08 SQLITE Article

Votre exécutable est une base SQLite

Vingt ans de format binaire, relus comme un schéma de base.

Un prototype nommé SELF remplace le format ELF par une base SQLite exécutable : le fichier se lance normalement et s'interroge en SQL. La démonstration de fond est que le format ELF réimplémente déjà, à la main, la plupart des primitives d'une base de données.

3 min de lecturevidéo 1:21
Partager
Sommaire5 sections
  1. Ce qui se passe
  2. L'argument central
  3. Ce que ça donne concrètement
  4. Les limites
  5. À retenir

Ce qui se passe

Farid Zakaria publie SELF (Structured Executable & Linkable Format), un prototype où le binaire que vous lancez est une base SQLite. Pas une base qui décrit un binaire : le fichier lui-même.

Code
$ 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

L'idée vient de sa thèse, où elle avait reçu un accueil tiède, et qu'il ressort aujourd'hui — un précédent outil du même auteur, sqlelf, permettait déjà d'explorer un fichier ELF en SQL au lieu de combiner readelf et grep.

L'argument central

Le constat qui porte tout le travail : ELF est déjà une base de données qui refuse de l'admettre, et qui réimplémente ses primitives à la main.

Mécanisme ELF Primitive de base réinventée
.strtab / .dynstr internement de chaînes
.hash / .gnu.hash un index, avec filtre de Bloom maison
table des en-têtes de sections un catalogue de tables
st_name → position dans .strtab une clé étrangère écrite à la main
objcopy --strip-debug un DELETE suivi d'un VACUUM
cache ldconfig, debuginfod des index hors bande

À cela s'ajoutent les défauts d'un format pensé quand l'octet coûtait cher : chaque outil réécrit le même analyseur, chaque producteur réécrit le même sérialiseur, le format est trop compact pour être modifié sans réécrire des sections entières, et il n'a aucun schéma auto-descriptif.

Ce que ça donne concrètement

Deux tables suffisent à faire tourner un programme : l'en-tête ELF stocké en paires clé-valeur, et une table de segments — une ligne par en-tête de programme, les octets rangés dans un champ binaire.

La table des symboles est plus parlante encore. Une seule table, un seul index (name, version), et plusieurs mécanismes disparaissent : la table de chaînes n'a plus lieu d'être puisque SQLite interne déjà les chaînes, le versionnement de symboles devient une colonne au lieu des sections dédiées, et l'index est un vrai arbre B maintenu par le moteur plutôt qu'un filtre de Bloom écrit à la main.

Les tables restantes — sections, notes, entrées dynamiques — n'existent que pour l'outillage. Supprimez-les : le programme fonctionne toujours. Autrement dit, dépouiller un binaire devient une transaction, avec ce que cela implique d'atomicité et de possibilité d'annulation.

Bénéfice collatéral : les commandes d'inspection deviennent des requêtes. ldd, nm -D --undefined, readelf -l s'expriment en SELECT.

Les limites

L'auteur ne vend pas un remplaçant. C'est un prototype, l'idée n'a pas convaincu lors de sa thèse, et l'inertie d'un format installé depuis trente ans ne se renverse pas avec un dépôt public. Restent ouvertes les questions de démarrage par le noyau, de performance de chargement à grande échelle et de compatibilité avec toute la chaîne de compilation existante.

À retenir

Même sans jamais exécuter une base SQLite, l'exercice change la lecture : les formats binaires que nous manipulons implémentent des index, des catalogues et des clés étrangères — mal, et sans le dire. La prochaine fois que vous lirez une sortie de readelf, vous saurez que vous interrogez une base de données à la main.

Source : Farid Zakaria