Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ADR-049 — Runtime Air : TCB, TLS, démarrage (crt0), errno, environ — fondation *-linux-air

Statut : Accepté (2026-06-28, ratifié par le BDFL ; option (a) *-linux-air complète, cf. amendement en fin). Document fondateur du runtime userland d’Air.

⚠️ CORRECTION D’ARCHITECTURE (2026-06-29, ADR-052). Le placement d’air-rt posé par cet ADR est OBSOLÈTE : D8 (« nouveau crate runtime reposant sur la couche 0 ») est faux au regard de la doctrine de layering d’Air — seule la couche 1 consomme la couche 0. air-rt est en réalité un objet couche 1 (AirRuntime) composé de briques couche 1 (ThreadControlBlock, ThreadLocalStorage, outils de relocation). Voir ADR-052, qui fait autorité. Le fond technique des décisions D1–D9 (TCB, TLS Variant I/II, crt0, errno-in-TCB, self-relocation static-PIE, spawn std-free, panic=abort, no_std) reste valide ; seul l’étage (couche 0 → couche 1) et le consommateur des syscalls changent. Companion d’ADR-046/ADR-047 (libc) et d’ADR-048 (couche 0 std-free). Réalise l’étape « produire TCB/TLS » de la feuille de route BDFL (couche 1 → TCB/TLS → libc). Aboutissement des bloqueurs convergents : spawn std-free (volet 2 air-thread), environ/OsString (air-process/filesystem/config), état par-thread de la libc.

Catégorie : Architecture (fondateur, runtime). Toute évolution structurante = RFC (ADR-015).

Contexte

Plusieurs chantiers ont buté sur le même socle manquant, et tous le désignent :

  • air-thread spawn std-free (volet 2, #151) : faire tourner une closure Rust dans un thread spawné exige un TCB correct (errno, canari %fs:0x28, DTV, état par-thread) — impossible via l’ABI interne glibc.
  • air-process/air-filesystem/air-config no_std (#153) : besoin de environ (capturé au démarrage) et d’un équivalent OsString.
  • La libc (ADR-046/047) : errno thread-local, état par-thread, démarrage des programmes C.

Le déblocage commun = le runtime userland d’Air : notre TCB, notre TLS, notre crt0/démarrage. C’est la fondation de la cible *-linux-air, construite par nous (pas d’ABI glibc).

Décision (proposée — à arbitrer/ratifier)

D1 — TCB propre à Air (au registre TLS)

Air définit son Thread Control Block placé au registre TLS (%fs x86_64 / tpidr_el0 aarch64). Champs : self-pointer, errno (le canal POSIX, thread-local, ADR-046 D4), canari de pile, DTV (Dynamic Thread Vector), métadonnées du thread (tid, base/taille de pile, mot futex de join), crochet d’état par-thread (futur cache malloc). Aucune compat ABI glibc : on possède le layout.

D2 — TLS ELF standard

Variant II (x86_64) / Variant I (aarch64). Image .tdata/.tbss copiée/zéroée à la création de thread ; #[thread_local] résout via le registre TLS. Mise en place : crt0 (thread principal) / clone3 + CLONE_SETTLS (threads spawnés, via air-thread).

D3 — errno dans le TCB

errno = champ du TCB, thread-local, lu/écrit par la libc via le registre TLS. Zéro globale (ADR-046 D4).

D4 — Démarrage (crt0/_start)

L’entrée capture argc/argv/envp/auxv depuis la pile initiale (ABI System V), met en place le TCB/TLS du thread principal, exécute l’init, appelle l’entrée du programme. auxv (AT_RANDOM pour le canari, AT_PAGESZ, AT_HWCAP…).

D5 — Accès environ

Le envp capturé en D4 est exposé par une API runtime (équivalent getenv/environ) → débloque le no_std d’air-process/air-filesystem/air-config.

D6 — Type chaîne-OS (octets)

Type octets possédé (réutiliser air-base-lib::AirPath/un newtype) en remplacement d’OsString dans les API couche 1 concernées (air-config) — cohérent avec la doctrine « chemins/env = octets ».

D7 — Spawn std-free (déblocage volet 2 air-thread)

air-thread::spawn : alloue pile + image TLS + TCB, câble CLONE_SETTLS, l’entrée enfant initialise son contexte (registre TLS → son TCB) puis exécute la closure, nettoie (munmap), exit ; join via CHILD_CLEARTID/futex (couche 0).

D8 — Emplacement & dépendances ⚠️ (corrigé par ADR-052)

Nouveau crate runtime (air-rt ?) reposant sur la couche 0OBSOLÈTE. air-rt est un objet couche 1 (AirRuntime) qui ne consomme que la couche 1 (air-thread pour clone3/futex/join, air-memory pour mmap, air-process pour exit_group, et les nouveaux objets couche 1 ThreadControlBlock/ThreadLocalStorage/relocation). Il ne dépend jamais de air-sys-syscall (la surface kernel) ; air-sys-types (types transverses : AirError/Errno, Pid/Tid… — ADR-052 D6) reste autorisé. air-thread (spawn), la libc et le futur std de *-linux-air reposent sur AirRuntime. no_std + alloc conservé. Cf. ADR-052 D1–D6.

D9 — Modèle panic / destructeurs TLS

panic = abort partout (cohérent ADR-045) ; pas d’unwind cross-thread. Destructeurs thread_local! (Drop en fin de thread) : supportés via une liste enregistrée dans le TCB, exécutée à la sortie du thread.

Conséquences

  • Débloque tout le reste de la couche 1 no_std (les 3 crates env-gated) et le spawn std-free d’air-thread.
  • Pose la fondation *-linux-air : le std de cette cible reposera sur ce runtime + la libc.
  • Introduit de l’asm (entrée _start, mise en place TLS par arch) — chaque bloc // SAFETY:, audité, minimal (ADR-021).

Alternatives écartées

  • Réutiliser le TCB glibc/musl : rejeté (ABI interne non maîtrisée, contraire à « pas de C non maîtrisé »).
  • Bricoler #[thread_local] nightly : insuffisant pour l’interop runtime complet ; on veut stable.
  • Rester lié à glibc pour le spawn : rejeté (unsound, cf. #151).

Questions ouvertes (décisions BDFL)

  1. Relation avec std Rust : (a) cible *-linux-air avec un std portant sur ce runtime (gros, propre), ou (b) rester no_std+libc pour les userlands C/C++ d’abord, std-Rust-sur-Air plus tard ? Recommandation : (b) d’abord.
  2. Nom/forme du crate runtime (air-rt) et frontière avec air-thread (le spawn migre-t-il dans air-rt ?).
  3. set_tid/threads multiples au démarrage, gestion at_exit/init-array C (pour les userlands C).
  4. Canari : source (AT_RANDOM de l’auxv) et politique.

Amendement / ratification — 2026-06-28 (BDFL)

Statut : Accepté. Question ouverte n°1 tranchée : option (a) — cible *-linux-air complète avec std complet sur air-rt, construite comme un tout cohérent.

Justification technique décisive : le TCB/TLS d’air-rt ne peut pas être validé en isolation dans un binaire lié à glibc — mêler le TLS d’Air et celui de glibc-std est unsound (cf. #151, volet 2 air-thread). Le runtime n’a de sens que sur la cible *-linux-air entière (zéro glibc). Conséquence : air-rt, la spec rustc-target et le portage du sys/pal de std forment un seul programme cohérent (pas des pièces testées séparément sur glibc).

Programme *-linux-air (phases) : (1) spec rustc-target *-linux-air (sans glibc, +crt-static ou crt Air) ; (2) air-rt (TCB/TLS/crt0/environ/spawn, ADR-049 D1–D9) ; (3) portage std::sys (pal) sur air-rt + couche 0/1 (thread/TLS/fs/net/env/time/process) ; (4) bootstrap d’un binaire Rust *-linux-air (hello-world → tests) ; (5) libc C-ABI (ADR-046/047) sur la même fondation, pour les userlands C (OpenSSH). C’est un programme multi-sessions.