Registre de conformité — libc C-ABI d’Air
Registre central de la doctrine de conformité (roadmap
roadmap-libc-openssh-fr.md§3). Chaque symbole exporté par la libc C-ABI d’Air y figure avec son étiquette de conformité, sa déviation éventuelle (listée + justifiée) et l’ADR de référence.Source de vérité = le
///sur chaqueextern "C"(roadmap §4). Ce fichier agrège ces étiquettes. Cohérence outillée : le testair-libc-capi/tests/conformance_labels.rséchoue si un symboleextern "C"défini dansair-libc-capi/src/(errno, crt, shimsmalloc) n’a pas d’étiquette dans son///ou n’apparaît pas ici. Lesmem*/str*sont définis dansair-libc-strmem(ré-exportés parair_libc_capi::string) et y portent leurs étiquettes ; ils figurent au registre ci-dessous.
Mise à jour M2 (2026-07-04) : familles I/O fichier fd brut (fcntl.h/
unistd.h), métadonnées (sys/stat.h, struct stat ABI Air) et répertoires
(dirent.h, DIR opaque) — logique mesurée air-libc-fileio, shims
air-libc-capi::fileio, frontière variadique open/openat/fcntl via
fileio_shim.c (option A, comme printf). Cf. section « M2 » et déviations
D-M2.1..D-M2.5. Précédent :
Dernière mise à jour : 2026-07-04 (jalon M1-d — frontière variadique printf
(option A) : famille printf/fprintf/sprintf/snprintf + v* via un mince shim
C va_arg et la glue Rust mesurée air-libc-printf, au-dessus du moteur fuzzé
air-libc-fmt ; sur M1-a/M1-b/M1-c). Architecture « logique / shims » étendue :
logique stdio dans air-libc-stdio, exit/atexit/getenv dans air-libc-stdlib,
glue printf dans air-libc-printf (crates mesurées) ; shims extern "C"
minces dans air-libc-capi::{stdio,stdlib} (exclus du gate) ; 1ʳᵉ exception « zéro C »
= le shim C va_arg (air-libc-c/csrc/printf_shim.c, cf. EXCEPTIONS.md). Six crates
de logique libc mesurées : air-libc-fmt, air-libc-alloc, air-libc-stdio,
air-libc-stdlib, air-libc-printf.
Taxonomie (roadmap §3)
| Étiquette | Sens |
|---|---|
POSIX | Pile conforme C/POSIX : comportement, valeurs de retour, errno identiques à la norme. |
POSIX-PARTIEL | Fonctionnellement POSIX, avec déviations documentées (erreur plus stricte, zéro troncature muette, borne explicite). Chaque déviation listée + justifiée. |
NON-POSIX-DÉLIBÉRÉ | On choisit de dévier / de fournir un symbole hors POSIX — raison obligatoire + renvoi ADR. |
AIR-EXT | Extension Air (air_*) — hors POSIX, valeur ajoutée. |
M0 — Fondations (air-libc-capi) : errno + crt
Fonctions
| Symbole | Étiquette | Déviation / justification | ADR |
|---|---|---|---|
__errno_location | POSIX | Sémantique conforme : rend l’lvalue errno par-thread, pointeur stable pour la vie du thread, jamais NULL. Nuance de nom : __errno_location n’est pas dans le texte POSIX (qui spécifie la macro errno) — c’est la convention ABI Linux/glibc, fournie à l’identique pour que le code C et les <errno.h> du système marchent sans modification. Socle : air_runtime::AirRuntime::errno_location() (canal unique, zéro globale, partagé libc ⇄ PAL). | ADR-046 (D4), ADR-049 (D3), ADR-052 |
__libc_start_main | NON-POSIX-DÉLIBÉRÉ | Non-POSIX assumé : ABI de démarrage Linux/glibc (pas un symbole POSIX). Air en fournit une variante minimale à 4 arguments (main, argc, argv, envp) là où la glibc en prend 7 (init/fini/rtld_fini/stack_end) — le crt appelant est celui d’Air (bootstrap, couche 1), pas celui de gcc ; les crochets .init_array/atexit/ld.so sont hors M0 (M1 pour atexit/flush stdio). Élargissement à la signature 7-args = additif d’un jalon ultérieur si besoin réel. | ADR-046, ADR-050 |
air_main | AIR-EXT | Bridge air_* interne à la chaîne de démarrage d’Air (_start → bootstrap → air_main → __libc_start_main) — TARGET-ONLY (cfg(target_env = "air")), non destiné à l’appel par du code C applicatif. Convertit les types Air (usize/*const *const u8) vers la signature C-runtime. | ADR-052, ADR-049 (D4) |
Types
| Symbole | Étiquette | Note | ADR |
|---|---|---|---|
AirMainFn | POSIX (support) | typedef int (*AirMainFn)(int, char **, char **) — type du main C (ABI System V). Support de type pour __libc_start_main. | ADR-046 |
Constantes E* (numéros errno Linux)
Étiquette : POSIX (toutes). Valeurs conformes à asm-generic/errno{,-base}.h
(identiques x86_64 / aarch64). Source de vérité unique = couche 0
(air_sys_types::Errno) : les #define du header sont des littéraux
croisés-vérifiés contre la couche 0 (test errno_constants_match_layer0), jamais
inventés. Périmètre M0 = les errno déjà ancrés en couche 0 ; d’autres seront
ajoutés par famille (jalons M1+), au fil des besoins réels d’OpenSSH.
EPERM(1) · ENOENT(2) · ESRCH(3) · EINTR(4) · E2BIG(7) · ENOEXEC(8) ·
EBADF(9) · ECHILD(10) · EAGAIN(11) · EWOULDBLOCK(11, alias EAGAIN) ·
ENOMEM(12) · EACCES(13) · EFAULT(14) · EBUSY(16) · EEXIST(17) ·
EXDEV(18) · ENODEV(19) · ENOTDIR(20) · EINVAL(22) · ENFILE(23) ·
EMFILE(24) · ENOTTY(25) · ENOSPC(28) · ESPIPE(29) · EPIPE(32) ·
ERANGE(34) · ENOSYS(38) · ELOOP(40) · ETIME(62) · EPROTO(71) ·
EBADMSG(74) · EMSGSIZE(90) · EOPNOTSUPP(95) · ECONNRESET(104) ·
ENOBUFS(105) · ETIMEDOUT(110) · EALREADY(114) · ECANCELED(125).
Code vs artefact (air-libc-capi / air-libc-c, ADR-029)
Les symboles ci-dessus sont définis dans la crate de code air-libc-capi
(rlib pur — leur source de vérité /// + header committé y vivent). L’artefact
partagé libair_c.so est produit par la crate sœur air-libc-c (cdylib,
host-only), qui ne définit aucun symbole propre : elle se contente de
ré-exporter (pub use) et d’ancrer (#[used]) ceux d’air-libc-capi pour
forcer leur export dans la .so. Aucune entrée de registre supplémentaire n’est
donc requise pour air-libc-c (surface ABI identique, zéro symbole ajouté).
M1 — tranche string/mem (air-libc-strmem ; ré-export air-libc-capi::string)
Primitives mem*/str* sans allocation : #[no_mangle] extern "C" définis
dans la crate air-libc-strmem (isolée pour porter #![no_builtins], cf. le split)
et ré-exportés par le module air_libc_capi::string (chemin stable + visibilité
cbindgen via parse.include/extra_bindings). Shims minces sur une logique
sûre (tranches bornées / marches de pointeurs bornées par n, zéro indexation
paniquante, zéro UB — roadmap §5.①). Toutes POSIX : mêmes
valeurs de retour, mêmes pointeurs, comparaisons en unsigned char conformes à
la norme. Aucune déviation de comportement ; la seule garantie plus forte
(sûreté, non observable côté C) est le court-circuit n == 0 (pointeurs nuls
tolérés là où from_raw_parts/copy de Rust exigent du non-nul) — pas une
déviation POSIX. strdup (allocation) est différé à la tranche stdlib.
| Symbole | Étiquette | Note | ADR |
|---|---|---|---|
memcpy | POSIX | Régions disjointes exigées (norme) ; rend dst. | ADR-046 |
memmove | POSIX | Chevauchement géré (core::ptr::copy) ; rend dst. | ADR-046 |
memset | POSIX | c réduit en unsigned char (sans as lossy) ; rend s. | ADR-046 |
memcmp | POSIX | Comparaison en unsigned char ; signe conforme. | ADR-046 |
memchr | POSIX | Rend void * (non const, convention norme) ou NULL. | ADR-046 |
strlen | POSIX | Longueur non bornée (marche de pointeur). | ADR-046 |
strnlen | POSIX | POSIX.1-2008 ; ne lit jamais au-delà de maxlen. | ADR-046 |
strcpy | POSIX | Non borné (contrat C) ; rend dst. | ADR-046 |
strncpy | POSIX | Piège normé documenté : pas de NUL si strlen(src) ≥ n ; padding NUL sinon. Bornage strict ≤ n octets. | ADR-046 |
strcat | POSIX | Non borné (contrat C) ; rend dst. | ADR-046 |
strncat | POSIX | Toujours NUL-terminé (≤ n+1 octets ajoutés). | ADR-046 |
strcmp | POSIX | Comparaison en unsigned char. | ADR-046 |
strncmp | POSIX | Bornée par n, comparaison unsigned char. | ADR-046 |
strchr | POSIX | Première occurrence, NUL terminal compris ; rend char *. | ADR-046 |
strrchr | POSIX | Dernière occurrence, NUL terminal compris. | ADR-046 |
strstr | POSIX | Naïf O(n·m), sans allocation ; aiguille vide ⇒ haystack. | ADR-046 |
strtok_r | POSIX | Réentrant (état dans *saveptr, zéro global) ; modifie s in place. | ADR-046 |
strerror | POSIX | Table statique errno → message (standard Linux). Chaîne immuable statique (jamais invalidée, contrairement au buffer partagé glibc). Logique mesurée air-libc-strerror. | ADR-076 |
strerror_r | POSIX | Variante POSIX/musl (int, Air ABI-musl) : copie bornée dans le buffer, ERANGE si trop petit (tronqué + NUL). | ADR-076 |
__xpg_strerror_r | POSIX | Alias XSI de strerror_r (nom émis par std/glibc/musl pour la variante int) : même logique mesurée, copie bornée, ERANGE si trop petit (tronqué + NUL). | ADR-076 |
M1-b — famille malloc (logique air-libc-alloc ; shims air-libc-capi::malloc)
Famille allocation de stdlib.h. Split « logique / shims C-ABI » : la
LOGIQUE (malloc_impl/free_impl/…) vit dans la crate air-libc-alloc (Rust pur,
mesurée à 100 %, aucun symbole #[no_mangle]), au-dessus de l’arène globale
scellée d’air-alloc (verrou futex, fusion O(1), échec ⇒ null déterministe,
ADR-056) ; les shims extern "C" minces qui délèguent vivent dans
air_libc_capi::malloc (exclus du gate de couverture, car leurs #[no_mangle]
surchargent le runtime de couverture LLVM). La logique porte le modèle d’en-tête
borné (placé devant chaque bloc) qui rend free/realloc possibles sans que
le C repasse taille/alignement. Sécurité #1 (roadmap §5.①) :
overflow calloc checked (checked_mul), zéro as lossy (try_from /
checked_* partout), zéro double-free/UAF introduit (chaque bloc libéré une
fois via son en-tête ; realloc recopie puis libère l’ancien après succès du
neuf). strdup/strndup allouent ⇒ ici, pas dans string.
| Symbole | Étiquette | Note / déviation | ADR |
|---|---|---|---|
malloc | POSIX | malloc(0) rend un pointeur unique, non-NULL, libérable (la norme autorise NULL ou un pointeur unique — on choisit le plus sûr). Échec ⇒ NULL + errno = ENOMEM. | ADR-056 |
free | POSIX | free(NULL) = no-op. Double-free/UAF = UB (contrat C, comme toute libc). | ADR-056 |
calloc | POSIX | Produit nmemb * size checked (débordement ⇒ NULL + errno = ENOMEM — anti heap-overflow). Mémoire entièrement à zéro. calloc(0, x)/calloc(x, 0) ⇒ pointeur unique libérable. | ADR-056 |
realloc | POSIX-PARTIEL | realloc(NULL, n) == malloc(n) ; realloc(p, 0) == free(p) puis rend NULL (comportement glibc historique ; C17 implementation-defined — déviation gravée). Échec (size ≠ 0) ⇒ NULL + errno = ENOMEM, ancien bloc conservé. | ADR-056 |
aligned_alloc | POSIX-PARTIEL | alignment puissance de deux non nulle sinon NULL + errno = EINVAL. Contrainte C11 « size multiple de alignment » relâchée (comme glibc) — déviation documentée (plus permissif, jamais moins sûr). | ADR-056 |
posix_memalign | POSIX | alignment puissance de deux et multiple de sizeof(void *) sinon EINVAL (sans toucher errno ni *memptr). Succès ⇒ 0 + *memptr. Échec ⇒ ENOMEM (ne pose pas errno, contrat POSIX). | ADR-056 |
strdup | POSIX-PARTIEL | POSIX.1-2008. Durcissement : strdup(NULL) rend NULL (au lieu du déréférencement de NULL indéfini — sécurité #1). Échec d’allocation ⇒ NULL + errno = ENOMEM. | ADR-056 |
strndup | POSIX-PARTIEL | POSIX.1-2008. Résultat toujours NUL-terminé (≤ n+1 octets). Durcissement : strndup(NULL, …) rend NULL (sécurité #1). Échec ⇒ NULL + errno = ENOMEM. | ADR-056 |
Moteur de format (crate air-libc-fmt — aucun symbole extern "C")
Le cœur du rendu printf ([air_libc_fmt], extrait dans sa propre crate
pour être mesuré à 100 % par llvm-cov) est du Rust pur, sans unsafe, sans
allocation, agnostique de la source des arguments (FormatArgs). Il
n’exporte aucun symbole C (donc aucune entrée de registre requise, et rien
à parser pour cbindgen) : le câblage variadique printf/snprintf/vfprintf
(option A, extern "C", dans air-libc-capi)
est le jalon M1-d. C’est LA surface d’attaque « format-string », isolée et
fuzzée (cible fuzz_libc_format, cf. fuzz/). Invariants garantis par
construction + prouvés au fuzz : zéro panic, zéro débordement du puits borné
(snprintf-exact : écrit min(longueur théorique, capacité)), zéro boucle
infinie/DoS (remplissages bornés par la capacité, compteur saturating_add), et
%n REJETÉ — rendu littéral, aucun argument consommé, jamais une
écriture mémoire (vecteur d’exploitation format-string classique neutralisé). Cf.
déviation D-M1b.5 ci-dessous.
M1-c — stdio.h (FILE* bufferisé) : air-libc-stdio (logique) ; shims air-libc-capi::stdio
Famille stdio.h. Split « logique / shims C-ABI » : la LOGIQUE (FILE
bufferisé, registre des flux, flush-at-exit) vit dans air-libc-stdio (Rust pur,
mesurée, aucun symbole #[no_mangle]), au-dessus d’AirFile/air-stdio
(couche 1) ; les shims extern "C" minces vivent dans air_libc_capi::stdio
(exclus du gate). Bufferisation bloc/ligne/sans (_IOFBF/_IOLBF/_IONBF),
verrou par-FILE (thread-safety POSIX, façon flockfile), boucle sur écriture
courte + retry EINTR dans le toit (ADR-021 conv.2). Sécurité #1 : bornage
strict des tampons (get/copy_from_slice, jamais d’indexation), zéro as lossy,
overflow size*nmemb checked (fread/fwrite). Parseur de mode fopen
fuzzé (fuzz_libc_stdio_mode) ; bufferisation fuzzée (fuzz_libc_stdio_buffer,
backend mémoire).
| Symbole | Étiquette | Note / déviation | ADR |
|---|---|---|---|
fopen | POSIX | Perm. de création 0666 masquées par l’umask ; modes tolérants façon glibc (b no-op Linux, x=O_EXCL avec w, octets inconnus ignorés) ; mode invalide ⇒ NULL+EINVAL. | ADR-046 |
fclose | POSIX | Flush + fermeture + désenregistrement + destruction du FILE. | ADR-046 |
fread | POSIX | Produit size*nmemb checked (débordement ⇒ 0). Rend le nb d’éléments complets. | ADR-046 |
fwrite | POSIX | Produit size*nmemb checked. Rend le nb d’éléments complets. | ADR-046 |
fflush | POSIX | fflush(NULL) vide tous les flux ouverts (registre process-global). | ADR-046 |
fseek | POSIX | Position logique corrigée de la bufferisation (pré-lecture / octets non flushés). | ADR-046 |
ftell | POSIX | Position logique (offset descripteur ± tampon). | ADR-046 |
rewind | POSIX | fseek(f,0,SEEK_SET) + clearerr. | ADR-046 |
fgetc / getc | POSIX | Lit un octet (0..=255) ou EOF. getc = fonction (pas seulement macro). | ADR-046 |
getchar | POSIX | getc(stdin). | ADR-046 |
fgets | POSIX | Au plus size-1 octets, s’arrête au \n (inclus)/EOF, NUL-termine. Jamais d’écriture au-delà de size (bornage strict). Verrou tenu sur toute la ligne (atomicité). | ADR-046 |
fputc / putc | POSIX | Écrit un octet (réduit en unsigned char). | ADR-046 |
putchar | POSIX | putc(c, stdout). | ADR-046 |
fputs | POSIX | Écrit la C-string (sans le NUL). | ADR-046 |
puts | POSIX | C-string + \n sur stdout (contrat C). | ADR-046 |
feof / ferror / clearerr | POSIX | Indicateurs EOF/erreur par-FILE. | ADR-046 |
setvbuf | POSIX-PARTIEL | Déviation de sécurité (D-M1c.1) : le tampon fourni par l’appelant est ignoré (jamais d’aliasing d’une mémoire C au cycle de vie incontrôlé) ; seuls mode/size honorés. Mode invalide ⇒ -1+EINVAL. | ADR-046/032 |
setbuf | POSIX-PARTIEL | ≡ setvbuf (mêmes déviations ; buf nul ⇒ _IONBF, sinon _IOFBF). | ADR-046/032 |
fileno | POSIX | Descripteur brut sous-jacent (POSIX.1, hors C standard). | ADR-046 |
Données (FILE *)
| Symbole | Étiquette | Note | ADR |
|---|---|---|---|
stdin / stdout / stderr | POSIX | Trois FILE pré-ouverts (static, sémantique OS). stdout = ligne sur terminal sinon bloc ; stderr = sans tampon (_IONBF). Symboles de données (FILE *), hors périmètre du scanner conformance_labels (qui ne lit que les extern "C" fn). | ADR-046 |
Constantes <stdio.h>
Étiquette POSIX (toutes, littéraux ABI standard) : EOF(-1) · SEEK_SET(0) ·
SEEK_CUR(1) · SEEK_END(2) · _IOFBF(0) · _IOLBF(1) · _IONBF(2) · BUFSIZ(8192).
M1-c — stdlib.h : exit/atexit/abort + getenv (air-libc-stdlib ; shims air-libc-capi::stdlib)
Split « logique / shims » : le registre atexit (borné), la séquence de
nettoyage (perform_exit_cleanup) et le cache getenv vivent dans air-libc-stdlib
(mesurée) ; les shims — dont exit/_Exit/_exit/abort qui divergent
(-> !, insondables par le gate, comme air-process/src/exit.rs) — vivent dans
air_libc_capi::stdlib. Séquence exit : handlers atexit (ordre inverse) →
flush tous les FILE → air_process::exit_process.
| Symbole | Étiquette | Note / déviation | ADR |
|---|---|---|---|
exit | POSIX | Handlers atexit + flush stdio + exit_group. Ne revient jamais. | ADR-046 |
_Exit / _exit | POSIX | Sortie immédiate (ni handlers, ni flush). | ADR-046 |
abort | POSIX | SIGABRT (air-signal, coredump par défaut), aucun flush (contrat). Durcissement : sortie forcée 128+SIGABRT si le signal ne termine pas. | ADR-046/064 |
atexit | POSIX | Registre borné (≥ 32, ATEXIT_MAX), ordre inverse. Durcissement (D-M1c.3) : atexit(NULL) rend une valeur non nulle. | ADR-046 |
getenv | POSIX-PARTIEL | Nuance de durée de vie gravée (D-M1c.2) : pointeur stable pour la vie du processus via un cache process-global ; un getenv répété rend le même pointeur. setenv/unsetenv invalident l’entrée de cache (lecture fraîche ensuite) en fuyant l’ancienne valeur (les pointeurs déjà rendus restent valides). | ADR-046 |
setenv | POSIX | overwrite==0 + variable existante ⇒ no-op. Nom invalide ⇒ EINVAL, pointeur nul ⇒ EFAULT. Sur AirEnvironmentManager (couche 1, ADR-077). | ADR-046/077 |
unsetenv | POSIX | Nom vide / contenant = ⇒ EINVAL, pointeur nul ⇒ EFAULT. Sur AirEnvironmentManager. | ADR-046/077 |
M1-d — frontière variadique printf (shim C va_arg + glue air-libc-printf)
Famille printf (stdio.h). Décision BDFL — option (A) (roadmap §6) :
Rust-stable ne peut pas définir de fonction C variadique (c_variadic = nightly).
Retenu : parseur + moteur de rendu 100 % Rust fuzzé (air-libc-fmt, la surface
d’attaque « format-string » N°1) + un mince shim C (air-libc-c/csrc/ printf_shim.c) faisant le seul va_arg mécanique. C’est la 1ʳᵉ exception
nommée à « zéro C » (cf. docs/EXCEPTIONS.md).
Architecture. Le shim ne parse rien (zéro logique de format en C) : il
(a) demande le plan des types d’args à la glue Rust mesurée air-libc-printf
(air_fmt_arg_kinds), (b) fait le va_arg(ap, <type promu>) dicté par le plan,
(c) re-délègue au moteur Rust (air_fmt_render_buf/_stream). Le plan et le
rendu passent par le même parseur (air_libc_fmt::format) ⇒ zéro divergence.
Sécurité #1 : %n rejeté (rendu littéral, aucun argument tiré, jamais
d’écriture-arrière — cf. déviation D-M1b.6) ; puits snprintf borné (jamais
de débordement) ; promotions C par défaut respectées. Fuzzé : moteur
(fuzz_libc_format) et glue (fuzz_libc_printf). Preuve d’exécution :
harnais hello-world C (air-libc-c/tests/hello_world.rs, « Hello, Air 42 »).
Symboles fournis par le shim C (déclarés dans air_c.h, exportés dans
libair_c.so — nm -D) :
| Symbole | Étiquette | Note / déviation | ADR |
|---|---|---|---|
printf | POSIX | stdout, bufferisé (flush-at-exit). %n rejeté (D-M1b.6). Rend le nb d’octets écrits (négatif sur erreur d’écriture). | ADR-046 |
fprintf | POSIX | Vers un FILE *. | ADR-046 |
sprintf | POSIX | Buffer non borné (contrat C ; préférer snprintf). NUL-terminé. | ADR-046 |
snprintf | POSIX | Écrit au plus size octets (NUL inclus — jamais de débordement) ; rend la longueur théorique (C99). | ADR-046 |
vprintf / vfprintf / vsprintf / vsnprintf | POSIX | Variantes va_list (mêmes sémantiques). | ADR-046 |
Conversions supportées (moteur M1-b) : d i u x X o c s p % ; drapeaux - + espace 0 # ; largeur/précision (dont */.*) ; longueurs hh h l ll z t j. Différés
(hors scope) : flottants e f g a (rendus littéralement tant qu’air-libm n’est pas
câblé — aucun argument tiré) ; positions %n$ ; locale numérique. %n : rejeté
(NON-POSIX-DÉLIBÉRÉ, sécurité — cf. D-M1b.6).
Points d’entrée de support de la glue Rust (air-libc-printf, #[no_mangle],
exportés dans libair_c.so mais hors ABI libc standard — consommés uniquement
par le shim ; non scannés par conformance_labels, hors air-libc-capi/src) :
| Symbole | Étiquette | Note |
|---|---|---|
air_fmt_arg_kinds | AIR-EXT (support interne) | Parse fmt, écrit le plan des kinds d’args (borné), rend le compte ou -1. Ne tire aucun va_arg. |
air_fmt_render_buf | AIR-EXT (support interne) | Rendu borné (snprintf) : ≤ cap octets, rend la longueur théorique. |
air_fmt_render_stream | AIR-EXT (support interne) | Rendu stream : pousse la sortie via un callback (fwrite/copie), rend la longueur théorique ou -1. |
M2 — I/O fichier fd brut + métadonnées + répertoires (air-libc-fileio ; shims air-libc-capi::fileio)
Familles fcntl.h/unistd.h (fd brut), sys/stat.h (métadonnées) et
dirent.h (répertoires). Split « logique / shims C-ABI » : la LOGIQUE
(open_impl/read_impl/stat_impl/opendir_impl/readdir_impl/mkstemp_impl/…,
le remplissage de la struct stat ABI Air, la bufferisation getdents64 derrière
DIR) vit dans air-libc-fileio (Rust pur, mesurée, aucun symbole libc
#[no_mangle] standard), au-dessus de la couche 0 (openat2/read/write/
pread/pwrite/close/lseek/statx/faccessat/getdents64/dup_fd/dup3/
pipe2/getrandom/getcwd/chdir) et de la couche 1 (canonicalize →
realpath) ; les shims extern "C" minces vivent dans air_libc_capi::fileio
(exclus du gate). Sécurité #1 : chemins via C-strings bornées (octets, zéro
présomption UTF-8) ; buffers getdents64/readdir bornés (get, jamais
d’indexation) ; zéro as lossy sur les champs stat (try_from/wrapping_shl) ;
fd empruntés sans double-close.
Sémantique fd brut : le C gère la vie du fd (open rend un int désolidarisé,
close reconstruit l’OwnedFd et le ferme une fois ; les autres empruntent). EINTR
remonté (jamais de retry auto — ADR-021 conv.2). O_CLOEXEC : couche 0 le force
toujours ; open/dup/dup2 effacent FD_CLOEXEC si l’appelant ne l’a pas
demandé, rétablissant la sémantique POSIX (fd héritable par exec) — D-M2.1.
struct stat/dirent/DIR = ABI Air (largeur fixe, arch-indépendant, distinct
de la glibc — comme FILE) : les programmes compilés contre air_c.h voient notre
layout (D-M2.2). Le parseur getdents64 → dirent (données kernel externes) est
fuzzé (fuzz_libc_dirent).
Fonctions non variadiques (shims air-libc-capi::fileio)
| Symbole | Étiquette | Note / déviation | ADR |
|---|---|---|---|
creat | POSIX | ≡ open(path, O_CREAT|O_WRONLY|O_TRUNC, mode) (non variadique). | ADR-046 |
read | POSIX | Lecture partielle possible ; EINTR remonté. | ADR-046 |
write | POSIX | Écriture partielle possible ; EINTR remonté. | ADR-046 |
pread | POSIX | N’altère pas l’offset ; offset < 0 ⇒ EINVAL. | ADR-046 |
pwrite | POSIX | N’altère pas l’offset ; offset < 0 ⇒ EINVAL. | ADR-046 |
readv | POSIX | Lecture vectorisée (iovcnt buffers). Sur AirFileManager::read_vectored (couche 0 readv, sans additif). fd<0 ⇒ EBADF, iovcnt<0 ⇒ EINVAL, iov nul ⇒ EFAULT. | ADR-077 |
writev | POSIX | Écriture vectorisée. Sur AirFileManager::write_vectored. | ADR-077 |
preadv | POSIX | readv à l’offset absolu (n’altère pas la position) ; offset<0 ⇒ EINVAL. Sur AirFileManager::read_vectored_at. | ADR-077 |
pwritev | POSIX | writev à l’offset absolu ; offset<0 ⇒ EINVAL. Sur AirFileManager::write_vectored_at. | ADR-077 |
splice | Linux | Transfert kernel fd_in→fd_out (au moins un pipe) ; off_in/off_out nuls ⇒ position courante, sinon départ explicite mis à jour. fd<0 ⇒ EBADF, *off<0 ⇒ EINVAL. Sur AirFileManager::splice (couche 0 splice, sans additif). | ADR-077 |
close | POSIX | Ferme une fois (reconstruit l’OwnedFd). | ADR-046 |
lseek | POSIX | SEEK_SET/CUR/END + extensions Linux SEEK_DATA/SEEK_HOLE. | ADR-046 |
dup | POSIX | Non close-on-exec (efface FD_CLOEXEC, D-M2.1). | ADR-046 |
dup2 | POSIX | oldfd == newfd valide ⇒ no-op ; non close-on-exec (D-M2.1). | ADR-046 |
dup3 | POSIX | Seul O_CLOEXEC valide ; oldfd == newfd ⇒ EINVAL. | ADR-046 |
pipe | POSIX | Non close-on-exec (D-M2.1). | ADR-046 |
pipe2 | POSIX | O_CLOEXEC/O_NONBLOCK/O_DIRECT ; bit inconnu ⇒ EINVAL. | ADR-046 |
stat | POSIX-PARTIEL | struct stat layout ABI Air (D-M2.2). Suit les liens. | ADR-046 |
fstat | POSIX-PARTIEL | Idem stat sur un fd (AT_EMPTY_PATH). Layout ABI Air (D-M2.2). | ADR-046 |
lstat | POSIX-PARTIEL | Idem, sans suivre le lien final. Layout ABI Air (D-M2.2). | ADR-046 |
fstatat | POSIX-PARTIEL | flags AT_* ; layout ABI Air (D-M2.2). | ADR-046 |
access | POSIX | R_OK/W_OK/X_OK/F_OK, suit les liens. | ADR-046 |
faccessat | POSIX | Honore AT_SYMLINK_NOFOLLOW/AT_EACCESS (bascule faccessat2). | ADR-046 |
opendir | POSIX | O_RDONLY|O_DIRECTORY ; DIR opaque. | ADR-046 |
fdopendir | POSIX | Prend possession du fd (fermé par closedir). | ADR-046 |
readdir | POSIX | dirent * interne réutilisé (écrasé au prochain appel) ; fin ⇒ NULL sans errno. Nom > NAME_MAX sauté (D-M2.5). | ADR-046/032 |
closedir | POSIX | Ferme le fd + libère le DIR. | ADR-046 |
rewinddir | POSIX | lseek(0) + vidage du tampon. | ADR-046 |
mkstemp | POSIX | O_CREAT|O_EXCL, 0600, entropie kernel (getrandom). Patron invalide ⇒ EINVAL ; espace épuisé ⇒ EEXIST. | ADR-046 |
mkdtemp | POSIX | mkdir 0700, entropie kernel ; rend template. | ADR-046 |
realpath | POSIX-PARTIEL | Sur canonicalize (couche 1). realpath(path, NULL) (forme GNU malloc) différée ⇒ EINVAL (D-M2.4). Canonique ≥ PATH_MAX ⇒ ENAMETOOLONG. | ADR-046 |
getcwd | POSIX-PARTIEL | getcwd(NULL, 0) (forme GNU) différée ⇒ EINVAL (D-M2.4) ; buffer trop petit ⇒ ERANGE. | ADR-046 |
chdir | POSIX | Change le répertoire de travail. | ADR-046 |
fchdir | POSIX | Idem par descripteur de répertoire. | ADR-046 |
mkdir | POSIX | Crée un répertoire (mode filtré par umask). Sur AirFileManager (couche 1, ADR-077). | ADR-046/077 |
rmdir | POSIX | Supprime un répertoire vide (unlinkat+AT_REMOVEDIR). Sur AirFileManager. | ADR-046/077 |
unlink | POSIX | Supprime fichier/lien (no-follow). Sur AirFileManager. | ADR-046/077 |
ftruncate | POSIX | Fixe la taille d’un fd (tronque/étend). length < 0 ⇒ EINVAL. Sur AirFileManager. | ADR-046/077 |
fsync | POSIX | Flush données et métadonnées d’un fd. Sur AirFileManager. | ADR-046/077 |
fdatasync | POSIX | Flush données d’un fd. Sur AirFileManager. | ADR-046/077 |
rename | POSIX | Renomme/déplace (renameat2 sans drapeau). Sur AirFileManager. | ADR-046/077 |
link | POSIX | Lien physique (linkat, lien final non suivi). Sur AirFileManager. | ADR-046/077 |
symlink | POSIX | Lien symbolique (symlinkat). Sur AirFileManager. | ADR-046/077 |
readlink | POSIX | Cible d’un lien (readlinkat), sans NUL ; tronqué à bufsiz ; bufsiz==0 ⇒ EINVAL. Sur AirFileManager. | ADR-046/077 |
chmod | POSIX | Permissions (fchmodat, suit les liens). Sur AirFileManager. | ADR-046/077 |
chown | POSIX | Propriétaire/groupe (fchownat, suit les liens). Sur AirFileManager. | ADR-046/077 |
lchown | POSIX | Comme chown mais no-follow (AT_SYMLINK_NOFOLLOW). Sur AirFileManager. | ADR-046/077 |
dirfd | POSIX | fd sous-jacent d’un DIR (possédé, ne pas fermer). Accès direct au flux. | ADR-046 |
flock | POSIX | Verrou consultatif (LOCK_SH/EX/UN ± NB). Sur AirFileManager. | ADR-046/077 |
mkfifo | POSIX | Tube nommé (mknodat+S_IFIFO). Sur AirFileManager. | ADR-046/077 |
utimensat | POSIX | Horodatages atime/mtime (UTIME_NOW/UTIME_OMIT ; times nul ⇒ now). tv_nsec hors plage ⇒ EINVAL. Sur AirFileManager. | ADR-046/077 |
fchmod | POSIX | Permissions par fd (additif couche 0 fchmod, ADR-085). fd < 0 ⇒ EBADF. Sur AirFileManager::change_mode_by_fd. | ADR-085 |
fchown | POSIX | Propriétaire/groupe par fd (additif couche 0 fchown, ADR-085). (uid,gid)=(−1,−1) ⇒ inchangé ; fd < 0 ⇒ EBADF. Sur AirFileManager::change_owner_by_fd. | ADR-085 |
futimens | POSIX | Horodatages par fd (additif couche 0 futimens = utimensat(fd, NULL, …), ADR-085). times nul ⇒ now ; tv_nsec hors plage ⇒ EINVAL ; fd < 0 ⇒ EBADF. Sur AirFileManager::set_times_by_fd. | ADR-085 |
sendfile | POSIX | Transfert zéro-copie kernel in_fd→out_fd (additif couche 0 sendfile, ADR-085). offset nul ⇒ position courante de in_fd ; sinon départ explicite mis à jour. Rend les octets transférés ; fd < 0 ⇒ EBADF, *offset < 0 ⇒ EINVAL. Sur AirFileManager::send_file. | ADR-085 |
Fonctions variadiques (shim C fileio_shim.c, déclarées air_c.h — trailer)
| Symbole | Étiquette | Note / déviation | ADR |
|---|---|---|---|
open | POSIX | 3e arg mode lu si O_CREAT. Non close-on-exec par défaut (D-M2.1). | ADR-046 |
openat | POSIX | Comme open, relatif à dirfd. | ADR-046 |
fcntl | POSIX-PARTIEL | Commandes à argument int (F_DUPFD/F_DUPFD_CLOEXEC/F_GETFD/F_SETFD/F_GETFL/F_SETFL). F_GETFL sans mode d’accès (D-M2.3) ; verrous F_*LK différés ⇒ EINVAL. | ADR-046 |
Points d’entrée de support (glue variadique, air-libc-fileio, #[no_mangle])
Exportés dans libair_c.so (consommés uniquement par le shim C fileio_shim.c ;
hors ABI libc standard, non scannés par conformance_labels — hors air-libc-capi/src).
| Symbole | Étiquette | Note |
|---|---|---|
air_libc_open | AIR-EXT (support interne) | Point d’entrée non-variadique d’open (le shim a fait le va_arg du mode). |
air_libc_openat | AIR-EXT (support interne) | Idem openat. |
air_libc_fcntl | AIR-EXT (support interne) | Point d’entrée non-variadique de fcntl (arg entier déjà tiré). |
Constantes <fcntl.h>/<unistd.h>/<sys/stat.h>/<dirent.h>
Étiquette POSIX (toutes, littéraux ABI Linux). O_* : O_RDONLY(0)…O_CLOEXEC
(0o2000000) ; O_DIRECTORY/O_NOFOLLOW arch-dépendants (trailer #ifdef).
F_* : F_DUPFD(0)…F_DUPFD_CLOEXEC(1030), FD_CLOEXEC(1). AT_* : AT_FDCWD
(-100), AT_SYMLINK_NOFOLLOW(0x100), AT_REMOVEDIR/AT_EACCESS(0x200),
AT_NO_AUTOMOUNT(0x800), AT_EMPTY_PATH(0x1000). Accès : F_OK(0)/X_OK(1)/
W_OK(2)/R_OK(4). S_* : S_IFMT…S_IXOTH (+ macros S_IS* au trailer). DT_* :
DT_UNKNOWN(0)…DT_SOCK(12).
Types (struct stat/timespec/struct dirent/DIR)
| Symbole | Étiquette | Note | ADR |
|---|---|---|---|
struct stat | POSIX-PARTIEL | Layout ABI Air (largeur fixe, arch-indépendant ; D-M2.2). Champs st_dev/st_ino/st_nlink/st_mode/st_uid/st_gid/st_rdev/st_size/st_blksize/st_blocks/st_atim/st_mtim/st_ctim. | ADR-046 |
struct timespec | POSIX | tv_sec/tv_nsec (i64). | ADR-046 |
struct dirent | POSIX-PARTIEL | Layout ABI Air ; d_name[256] borné (NAME_MAX+1). | ADR-046 |
DIR | POSIX | Type opaque (typedef struct DIR DIR;), comme la norme. | ADR-046 |
M3 — signaux (signal.h) : air-libc-signal ; shims air-libc-capi::signal
Famille signaux. Split « logique / shims C-ABI » : la LOGIQUE
(sigaction_impl/kill_impl/sigprocmask_impl/…, la table process-globale de
dispositions, la traduction struct sigaction↔Disposition) vit dans
air-libc-signal (Rust pur, mesurée, aucun #[no_mangle]), au-dessus de la
couche 1 (air-signal : install_handler async non-faute, masque, envoi,
pending ; air-thread::raw_futex pour le sigsuspend interruptible) — jamais
air-sys-syscall (empilement strict ADR-066). Les shims extern "C" minces vivent
dans air_libc_capi::signal (exclus du gate).
LE POINT DE SÉCURITÉ (ADR-064). sigaction/signal sur les 4 fautes
(SIGSEGV/SIGBUS/SIGFPE/SIGILL) ACCEPTENT (retour 0) et mémorisent la
disposition (illusion d’API : les getters rendent la valeur posée) mais
N’INSTALLENT RIEN côté kernel — une faute prend toujours l’action par défaut
(coredump). Déviation NON-POSIX-DÉLIBÉRÉE majeure (D-M3.1). Les signaux
gérables (non-faute) sont, eux, installés en async réel (le kernel appelle le
handler C, ADR-066).
| Symbole | Étiquette | Note / déviation | ADR |
|---|---|---|---|
sigemptyset | POSIX | Vide sigset_t. | ADR-046 |
sigfillset | POSIX | Peuple sigset_t (__bits[0] = tous). | ADR-046 |
sigaddset | POSIX | signo hors [1, 64] ⇒ EINVAL. | ADR-046 |
sigdelset | POSIX | Idem sigaddset. | ADR-046 |
sigismember | POSIX | 1/0 ; signo invalide ⇒ EINVAL. | ADR-046 |
sigaction | POSIX (gérables) / NON-POSIX-DÉLIBÉRÉ (fautes) | Non-fautes : install async réel (air-signal). Fautes : mémorisé mais jamais installé → coredump (D-M3.1). SIGKILL/SIGSTOP ⇒ EINVAL. | ADR-064/066 |
signal | POSIX (gérables) / NON-POSIX-DÉLIBÉRÉ (fautes) | Sémantique glibc (SA_RESTART, masque vide). Rend le handler précédent ou SIG_ERR. Fautes idem sigaction (D-M3.1). | ADR-064/066 |
sigprocmask | POSIX | Masque per-thread (air-signal). how invalide ⇒ EINVAL. | ADR-046 |
pthread_sigmask | POSIX | Idem, convention pthread_* (rend le code errno). | ADR-046 |
sigpending | POSIX | air-signal::pending. set nul ⇒ EFAULT. | ADR-046 |
sigsuspend | POSIX-PARTIEL | Composé replace_mask + futex_wait interruptible ; petite fenêtre de course vs rt_sigsuspend atomique (D-M3.3). Rend toujours -1/EINTR. | ADR-066 |
kill | POSIX-PARTIEL | pid > 0 seulement ; pid <= 0 (groupe/diffusion) ⇒ EINVAL (D-M3.2). signum == 0 = test d’existence. | ADR-046 |
killpg | POSIX | Signal à tout un groupe de processus (pgrp == 0 = groupe de l’appelant ; pgrp < 0 ⇒ EINVAL). Sur l’additif couche 0 kill_process_group (ADR-085) via air-signal::send_signal_to_process_group. Lève la restriction « groupe différé » de D-M3.2. | ADR-085 |
raise | POSIX | Envoie au thread appelant. | ADR-046 |
sigaltstack | POSIX | Installe/interroge la pile de signal alternative du thread (pour un SIGSEGV de débordement de pile, usage std). Additif couche 0 sigaltstack (ADR-085) + type AltStack (stack_t) → AirSignalManager::set_alt_stack. Pile trop petite ⇒ ENOMEM/EINVAL. | ADR-085 |
pause | POSIX | Suspend jusqu’à un signal capté ; rend toujours -1/EINTR. Composé sur AirSignalManager::pause = ppoll(0 fd, ∞, masque courant) (sans additif couche 0). Chemin bloquant = exception de couverture (non injectable, comme le EINTR d’air-poll). | ADR-085 |
Types & constantes <signal.h> (ABI Air, trailer hand-written)
sigset_t (128 octets, parité glibc ; seul __bits[0] porté), struct sigaction
(union sa_handler/sa_sigaction, sa_mask, sa_flags, sa_restorer — layout
ABI Air, D-M3.7), siginfo_t (128 octets, champs si_signo/si_code/si_addr/
si_pid/si_uid/si_status), sighandler_t (void (*)(int)). Constantes
(POSIX) : SIG_DFL(0)/SIG_IGN(1)/SIG_ERR(-1) ; SIG_BLOCK(0)/SIG_UNBLOCK(1)/
SIG_SETMASK(2) ; SIGHUP(1)…SIGSYS(31), SIGRTMIN/SIGRTMAX ; SA_*
(SA_NOCLDSTOP=1…SA_RESTART=0x10000000…SA_RESETHAND=0x80000000, identiques
x86_64/aarch64). Écrits à la main dans le trailer cbindgen.toml (union +
arch-vérifiés), exclus de la génération cbindgen.
M3 — threads (pthread.h) : air-libc-thread ; shims air-libc-capi::thread
Famille pthread. Split « logique / shims » : la LOGIQUE
(pthread_mutex_lock_impl/pthread_cond_wait_impl/…) vit dans air-libc-thread
(Rust pur, mesurée), bâtie sur air-thread::raw_futex opérant sur le mot futex
dans la mémoire opaque du pthread_*_t fourni par l’appelant (jamais un
AirMutex qui posséderait son stockage — ADR-066). Types opaques ABI Air à
largeur fixe (parité taille glibc), zéro-initialisables
(PTHREAD_MUTEX_INITIALIZER = {{0}}). Empilement strict : consomme uniquement
air-thread (jamais air-sys-syscall). Le cycle de vie (pthread_create/join/
…) et le TLS reposent sur le spawn/std::thread d’air-thread — hôte
seulement (cfg(not(target_env = "air"))), comme air_thread::thread.
Synchronisation (bâtie sur futex, exportée partout)
| Symbole | Étiquette | Note | ADR |
|---|---|---|---|
pthread_mutex_init / pthread_mutex_destroy | POSIX | Type via attribut ; destroy d’un mutex verrouillé ⇒ EBUSY. | ADR-066 |
pthread_mutex_lock / pthread_mutex_trylock / pthread_mutex_unlock | POSIX | Mutex futex 3 états ; NORMAL/RECURSIVE/ERRORCHECK (owner = Tid) ; EDEADLK/EBUSY/EAGAIN/EPERM. | ADR-066 |
pthread_mutex_timedlock | POSIX | Échéance absolue CLOCK_REALTIME ; ETIMEDOUT. | ADR-066 |
pthread_mutexattr_init / destroy / settype / gettype | POSIX | type NORMAL/RECURSIVE/ERRORCHECK. | ADR-066 |
pthread_cond_init / destroy / signal / broadcast | POSIX | Condition futex par compteur de séquence ; broadcast = FUTEX_WAKE ALL. | ADR-066 |
pthread_cond_wait / pthread_cond_timedwait | POSIX | Déverrouille/attend/ré-acquiert ; échéance absolue (REALTIME/MONOTONIC selon attribut) ; ETIMEDOUT. | ADR-066 |
pthread_condattr_init / destroy / setclock / getclock | POSIX | Horloge REALTIME (défaut) / MONOTONIC. | ADR-066 |
pthread_rwlock_init / destroy | POSIX | destroy d’un verrou détenu ⇒ EBUSY. | ADR-066 |
pthread_rwlock_rdlock / tryrdlock / wrlock / trywrlock / unlock | POSIX-PARTIEL | Mot d’état lecteurs/écrivain sur futex ; lecteur-préférentiel (famine d’écrivain possible sous charge continue — D-M3.9) ; EBUSY/EPERM. Variantes timed* différées. | ADR-066 |
pthread_rwlockattr_init / destroy | POSIX | No-op (aucun attribut honoré en M3). | ADR-066 |
pthread_once | POSIX | Exécute init_routine exactement une fois (état futex 3 valeurs). | ADR-066 |
pthread_attr_init / destroy / setdetachstate / getdetachstate / setstacksize / getstacksize | POSIX | Attributs honorés : état detach + taille de pile (autres différés). | ADR-066 |
Cycle de vie + TLS (hôte uniquement, cfg(not(target_env = "air")))
| Symbole | Étiquette | Note / déviation | ADR |
|---|---|---|---|
pthread_create | POSIX | Sur le spawn d’air-thread. Honore detach/pile ; ENOMEM à l’échec. pthread_t = Tid noyau (D-M3.4). | ADR-066 |
pthread_join | POSIX | Récupère la valeur de retour ; ESRCH/EINVAL. Thread paniqué ⇒ retour NULL. | ADR-066 |
pthread_detach | POSIX | Rend non joignable ; ESRCH/EINVAL. | ADR-066 |
pthread_self | POSIX | Tid noyau du thread courant (air_thread::runtime_primitives::current_tid, D-M3.4). Export cible aussi (target_vendor="air", ADR-086). | ADR-066 |
pthread_getattr_np | POSIX-PARTIEL | Initialise l’attribut avec les bornes de pile du thread principal (air_runtime::main_thread_stack, ADR-086). Threads spawnés différés. Export cible-only. | ADR-086 |
pthread_attr_getstack | POSIX | Lit (stackaddr, stacksize) d’un attribut renseigné par pthread_getattr_np. | ADR-086 |
pthread_attr_getguardsize | POSIX | Lit la taille de garde de pile d’un attribut renseigné par pthread_getattr_np. | ADR-086 |
pthread_equal | POSIX | Égalité de pthread_t. | ADR-066 |
pthread_exit | NON-POSIX-DÉLIBÉRÉ | Additif REMONTÉ (D-M3.5) : pas de sortie de thread propre en couche 1 + panic = "abort". Rejoue les destructeurs TLS puis sommeil indéfini du thread appelant (ne revient jamais, sans abort du processus). | ADR-064/066 |
pthread_key_create / pthread_key_delete | POSIX | Registre de clés borné (PTHREAD_KEYS_MAX) ; EAGAIN si épuisé. | ADR-066 |
pthread_setspecific / pthread_getspecific | POSIX | Valeurs par-thread. Hôte : std::thread_local! (destructeurs via Drop). Cible : #[thread_local] natif (target_tls, ADR-086) ; rejeu des destructeurs à la sortie d’un thread spawné = crochet couche 1 différé (thread principal réclamé à la sortie du processus). | ADR-066 |
Types & constantes <pthread.h> (ABI Air, trailer hand-written)
Types opaques largeur/alignement FIXES (D-M3.7) : pthread_mutex_t (40),
pthread_cond_t (48), pthread_rwlock_t (56), pthread_once_t (4),
pthread_attr_t (56), pthread_mutexattr_t/pthread_condattr_t (4),
pthread_rwlockattr_t (8) ; pthread_t = unsigned long (parité glibc, encode le
Tid), pthread_key_t = unsigned int. Initialiseurs (POSIX) :
PTHREAD_MUTEX_INITIALIZER/PTHREAD_COND_INITIALIZER/PTHREAD_RWLOCK_INITIALIZER
(= {{0}}), PTHREAD_ONCE_INIT (= 0). Constantes : PTHREAD_MUTEX_NORMAL(0)/
RECURSIVE(1)/ERRORCHECK(2)/DEFAULT(0) ; PTHREAD_CREATE_JOINABLE(0)/
DETACHED(1) ; CLOCK_REALTIME(0)/CLOCK_MONOTONIC(1) ; struct timespec
(partagée avec M2). Écrits à la main dans le trailer cbindgen.toml, exclus de
la génération cbindgen.
Liste exhaustive des symboles M3 (référence du gate conformance_labels)
Chaque extern "C" ci-dessous porte son étiquette dans le /// de son shim
(air-libc-capi::{signal,thread}) et figure verbatim ici (le test vérifie la
présence de chaque nom) :
sigemptyset, sigfillset, sigaddset, sigdelset, sigismember, sigaction,
signal, sigprocmask, pthread_sigmask, sigpending, sigsuspend, kill,
raise, pthread_mutex_init, pthread_mutex_destroy, pthread_mutex_lock,
pthread_mutex_trylock, pthread_mutex_timedlock, pthread_mutex_unlock,
pthread_mutexattr_init, pthread_mutexattr_destroy, pthread_mutexattr_settype,
pthread_mutexattr_gettype, pthread_cond_init, pthread_cond_destroy,
pthread_cond_signal, pthread_cond_broadcast, pthread_cond_wait,
pthread_cond_timedwait, pthread_condattr_init, pthread_condattr_destroy,
pthread_condattr_setclock, pthread_condattr_getclock, pthread_rwlock_init,
pthread_rwlock_destroy, pthread_rwlock_rdlock, pthread_rwlock_tryrdlock,
pthread_rwlock_wrlock, pthread_rwlock_trywrlock, pthread_rwlock_unlock,
pthread_rwlockattr_init, pthread_rwlockattr_destroy, pthread_once,
pthread_attr_init, pthread_attr_destroy, pthread_attr_setdetachstate,
pthread_attr_getdetachstate, pthread_attr_setstacksize,
pthread_attr_getstacksize, pthread_create, pthread_join, pthread_detach,
pthread_self, pthread_equal, pthread_exit, pthread_key_create,
pthread_key_delete, pthread_setspecific, pthread_getspecific.
Journal des déviations (roadmap §8)
| # | Symbole | Type de déviation | Justification | ADR |
|---|---|---|---|---|
| D-M0.1 | __libc_start_main | Signature 4-args (vs 7-args glibc) | crt d’Air (pas gcc) ; crochets init/fini hors M0 | ADR-046/050 |
| D-M0.2 | __errno_location | Nom = convention ABI Linux (hors texte POSIX) | Compat ABI C indispensable pour OpenSSH ; sémantique POSIX conforme | ADR-046 |
| D-M1b.1 | malloc / calloc | malloc(0)/calloc(0,x) rendent un pointeur unique non-NULL libérable | Choix POSIX-autorisé, plus sûr : free() du retour toujours valide | ADR-056 |
| D-M1b.2 | realloc | realloc(p, 0) == free(p) puis rend NULL | Comportement glibc historique (C17 implementation-defined) — gravé et documenté | ADR-056 |
| D-M1b.3 | calloc | Produit nmemb * size checked ⇒ débordement = NULL + ENOMEM | Défense anti heap-overflow (sécurité #1) — un calloc naïf débordant ouvrirait un overflow | ADR-056 |
| D-M1b.4 | aligned_alloc | Contrainte C11 « size multiple de alignment » relâchée | Aligné sur glibc — plus permissif, jamais moins sûr | ADR-056 |
| D-M1b.5 | strdup / strndup | strdup(NULL)/strndup(NULL, …) rendent NULL (durci) | Évite le déréférencement d’un NULL indéfini (sécurité #1) | ADR-056 |
| D-M1b.6 | moteur de format (interne) | %n rejeté — rendu littéral, aucune écriture mémoire | Vecteur d’exploitation format-string classique (écriture-arrière) neutralisé par construction (sécurité #1) | ADR-046/047 |
| D-M1c.1 | setvbuf / setbuf | Tampon fourni par l’appelant IGNORÉ (seuls mode/size honorés) | Sécurité #1 : ne jamais aliaser une mémoire C au cycle de vie incontrôlé (classe de bugs stdio) ; Air gère toujours son tampon interne | ADR-032/046 |
| D-M1c.2 | getenv | Pointeur rendu stable pour la vie du processus (au lieu de « jusqu’au prochain setenv ») | Environ scellé lecture seule en M1-c (pas de setenv) ⇒ aucune invalidation possible ; cache à pointeur figé, race-safe vis-à-vis du futur setenv | ADR-046 |
| D-M1c.3 | atexit | atexit(NULL) rend une valeur non nulle (durci) | Évite l’appel indéfini d’un handler nul (sécurité #1) | ADR-046 |
| D-M2.1 | open/openat/creat/dup/dup2/pipe | Le fd rendu n’est pas close-on-exec par défaut (POSIX) — on efface FD_CLOEXEC que la couche 0 force | La couche 0 force O_CLOEXEC (posture Air) ; POSIX/OpenSSH exigent des fd héritables par exec. La fenêtre cloexec transitoire est un durcissement | ADR-021/046 |
| D-M2.2 | struct stat/dirent/DIR | Layout ABI Air (largeur fixe, arch-indépendant), distinct de la glibc | C’est notre ABI (comme FILE) : les programmes compilent contre air_c.h. Évite le layout glibc arch-dépendant/padding | ADR-046 |
| D-M2.3 | fcntl (F_GETFL) | Ne restitue pas le mode d’accès (O_RDONLY/WRONLY/RDWR) ; verrous F_*LK différés ⇒ EINVAL | La couche 0 get_status_flags ne modélise que les drapeaux de statut ; le round-trip F_GETFL|O_NONBLOCK→F_SETFL (cas OpenSSH) reste correct. Verrous = additif si besoin réel | ADR-046 |
| D-M2.4 | realpath/getcwd | Forme GNU allouante (realpath(_,NULL)/getcwd(NULL,0)) différée ⇒ EINVAL | Pas d’allocation malloc dans la crate mesurée (le buffer appelant suffit au cas courant OpenSSH). Additif possible via le shim | ADR-046 |
| D-M2.5 | readdir | Un nom d’entrée > NAME_MAX (255) est sauté (jamais tronqué en silence) | ADR-032 (zéro troncature muette) : un nom débordant d_name[256] ne peut être rendu intègre ⇒ omis plutôt que tronqué | ADR-046/032 |
| D-M3.1 | sigaction/signal (fautes) | Les 4 fautes (SIGSEGV/SIGBUS/SIGFPE/SIGILL) acceptent (retour 0) et mémorisent la disposition, mais aucun handler n’est installé côté kernel — une faute prend toujours l’action par défaut (coredump) | Doctrine « fail-fast » d’Air : aucun code utilisateur dans un contexte corrompu (un handler de crash est une surface d’exploitation). Illusion d’API maintenue pour la compat source | ADR-064 |
| D-M3.2 | kill | pid <= 0 (groupe de processus / diffusion) non supporté ⇒ EINVAL | La couche 1 send_signal_to_process modélise un Pid positif ; groupe/diffusion = additif si besoin réel OpenSSH | ADR-046 |
| D-M3.3 | sigsuspend | Petite fenêtre de course (masque posé puis attente, non atomiques) vs rt_sigsuspend kernel | Pas d’rt_sigsuspend atomique en couche 1 (escalade additif) ; composition replace_mask + futex_wait interruptible, correcte hors fenêtre minuscule | ADR-066 |
| D-M3.4 | pthread_t | pthread_t = Tid noyau (élargi en unsigned long), non un pointeur opaque de TCB glibc | Identité de thread stable, obtenue via air_thread::runtime_primitives::current_tid (couche 1) ; pthread_self/equal/join cohérents. Réutilisation de Tid après mort de thread = caveat pthread standard | ADR-066 |
| D-M3.5 | pthread_exit | Ne réalise pas une sortie de thread propre : rejoue les destructeurs TLS puis endort indéfiniment le thread appelant (valeur non récupérable par join) | air-thread n’expose aucune sortie de thread sans unwind, et l’artefact est panic = "abort" (pas d’unwind) → additif couche 1 REMONTÉ, sans invention de mécanisme ; comportement confiné au thread appelant (ni abort du processus, ni corruption) | ADR-064/066 |
| D-M3.6 | pthread_setspecific/getspecific (destructeurs) | Destructeurs TLS rejoués à la sortie de thread via std (hôte) ; absents sur la cible *-linux-air | air-thread n’expose pas de crochet de destruction TLS à la sortie de thread → additif couche 1 REMONTÉ ; sur l’hôte mesuré, la voie std (thread_local! Drop) les rejoue réellement | ADR-066 |
| D-M3.7 | sigset_t/struct sigaction/pthread_*_t | Layout ABI Air (largeur fixe, arch-indépendant, disposition interne propre), distinct de la glibc bit-à-bit (parité taille seulement) | C’est notre ABI (comme FILE/struct stat) : les programmes compilent contre air_c.h. Évite le layout glibc arch-dépendant | ADR-046/066 |
| D-M3.8 | pthread_cond_timedwait (broadcast) | broadcast réveille tous les waiters (FUTEX_WAKE ALL, troupeau) plutôt que FUTEX_REQUEUE | Correction avant performance (Principe 5) ; zéro réveil perdu, zéro deadlock. requeue = optimisation additive si mesure le justifie | ADR-066 |
| D-M3.9 | pthread_rwlock_* | Verrou lecteur-préférentiel (famine d’écrivain possible sous charge de lecture continue) ; variantes timed* différées | Implémentation correcte et simple sur futex (Principe 5) ; verrou d’équité = additif si un besoin réel émerge | ADR-066 |
Additifs couche 1 v1.x déclenchés par la libc (roadmap §8)
| Additif | Crate couche 1 | Raison (libc) | Statut |
|---|---|---|---|
AirRuntime::errno_location() -> *mut i32 | air-runtime | Socle de __errno_location() : l’errno scellé n’exposait que errno()/set_errno() (par valeur) ; C exige l’adresse (lvalue). | Additif v1.x — RFC de descellement additif requis (ADR-062). Premier additif révélé par la libc (roadmap §6). |
air_env::set_var/unset_var (mutation d’environ) | air-env | setenv/putenv/unsetenv de la libc — M1-c ne fournit que getenv (environ lecture seule). Le design du cache getenv (pointeur figé) est déjà compatible avec un setenv futur. | Additif v1.x à poser (délégation B) si un besoin OpenSSH réel émerge — getenv seul suffit à la lecture d’$HOME/$PATH/… (le cas courant). Signalé par M1-c. |
Sortie de thread propre (air-thread) | air-thread | pthread_exit(retval) doit terminer le thread appelant sans unwind (artefact panic = "abort") en rendant sa valeur récupérable par join. La couche 1 n’expose aucune telle primitive → M3 endort le thread (D-M3.5). | Additif à poser — révélé par M3 (roadmap §8). Le spawn Air-natif (air-runtime, cible) devra exposer une sortie de thread propre. |
Crochet de destruction TLS à la sortie de thread (air-thread) | air-thread | Destructeurs pthread_key_create à rejouer à la fin de chaque thread, y compris sur cible *-linux-air (M3 ne les rejoue que via std, hôte — D-M3.6). | Additif à poser — révélé par M3. Lié au spawn Air-natif. |
rt_sigsuspend atomique (couche 0/1) | air-signal | sigsuspend POSIX exige masque + attente atomiques ; M3 compose replace_mask + futex_wait (petite fenêtre de course, D-M3.3). | Additif à poser si la course s’avère problématique — révélé par M3. |
kill groupe de processus / diffusion (air-signal) | air-signal | send_signal_to_process prend un Pid positif ; kill(pid<=0, …) (groupe/diffusion, killpg) non modélisé (D-M3.2). | Additif à poser si un besoin OpenSSH réel émerge — révélé par M3. |
Sommeil non-retry (air-base-core) | air-base-core | nanosleep POSIX doit rendre EINTR + le temps restant à l’appelant, sans retry auto ; or air-base-core::sleep réessaie en interne sur EINTR. | Additif à poser — révélé par M4. Exige un sommeil couche 1 qui remonte EINTR+reste (base de nanosleep/clock_nanosleep/select/poll timeouts). |
clock_getres (couche 1) | air-base-core | clock_getres (résolution d’horloge) — non exposé par la couche 1. | Additif à poser si un besoin réel émerge — révélé par M4. |
Base tz + calendrier (strftime/localtime_r) | air-base-lib (icu4x) | strftime/localtime_r/gmtime_r exigent la base tz + le calendaire (roadmap i18n, icu4x). | Différé — dépend du pipeline données i18n (ADR-059). Révélé par M4. |
M4 — temps (time.h/sys/time.h) : air-libc-time ; shims air-libc-capi::time
Lectures d’horloge de la libc, bâties exclusivement sur la couche 1
(air-base-core : AirSystemTime = CLOCK_REALTIME, AirInstant =
CLOCK_MONOTONIC) — jamais air-sys-syscall (empilement strict). La valeur
monotone brute est reconstruite via l’API publique
(AirInstant::now().duration_since(AirInstant::from_monotonic_micros(0, 0))),
sans additif couche 1. Le sommeil (nanosleep), la résolution (clock_getres)
et le formatage tz (strftime/localtime_r) sont remontés comme additifs
(section suivante).
Fonctions
| Symbole | Étiquette | Notes | ADR |
|---|---|---|---|
clock_gettime | POSIX | CLOCK_REALTIME/CLOCK_MONOTONIC ; horloge inconnue ⇒ EINVAL ; tp nul ⇒ EFAULT. Validation en amont (horloge avant pointeur). | ADR-046 |
gettimeofday | POSIX | Obsolescent. tv murale (s + µs, troncature ns→µs conforme) ; tz ignoré (obsolète, jamais déréférencé) ; tv nul ⇒ EFAULT. | ADR-046 |
time | POSIX | Secondes Unix murales ; time(NULL) valide (rend sans écrire) ; (time_t)-1 + errno sur échec (inatteignable). | ADR-046 |
Types
timespec (POSIX, tag dédupliqué avec air-libc-fileio par cbindgen),
timeval (POSIX), time_t/suseconds_t/clockid_t (POSIX) : largeur
fixe arch-indépendante — tv_sec/tv_nsec/tv_usec = int64_t (64 bits sur
x86_64 ET aarch64), clockid_t = int. Constantes CLOCK_REALTIME/
CLOCK_MONOTONIC déjà émises par M3 (pthread_cond).
M4 — comptes (pwd.h) : air-libc-pwd ; shims air-libc-capi::pwd
Variantes réentrantes (_r) interrogeant /etc/passwd, lu via la couche 1
(AirFileSystem::read_to_bytes, bornée 1 MiB) — jamais air-sys-syscall. Parseur
pur mesuré + empaquetage borné dans le buffer appelant (sécurité : getpwnam_r =
vecteur d’overflow ; buffer trop petit ⇒ ERANGE, jamais de dépassement). Convention
*_r : rendent l’errno directement (0 = trouvé/absent ; >0 = erreur), sans
toucher l’errno global. Les formes non-réentrantes getpwnam/getpwuid
(stockage statique, shims air-libc-capi::pwd) enveloppent les _r : elles
rendent un struct passwd * (écrasé au prochain appel), NULL si absent, NULL +
errno global sur erreur — non thread-safe par contrat POSIX.
Fonctions
| Symbole | Étiquette | Notes | ADR |
|---|---|---|---|
getpwnam_r | POSIX | Entrée par nom ; *result=&pwd (trouvée) / NULL (absente), retour 0 ; ERANGE (buffer trop petit) ; EINVAL (pointeur nul) ; errno de lecture propagé. Empaquetage borné. | ADR-046 |
getpwuid_r | POSIX | Comme getpwnam_r, recherche par UID. | ADR-046 |
getpwnam/getpwuid | POSIX | Formes non-réentrantes (requises par OpenSSH) : struct passwd * vers un stockage statique (NSS_BUFLEN_PASSWD = 1024), NULL si absent, NULL+errno global (ERANGE) sur erreur. Non thread-safe — variantes _r en concurrence. | ADR-046 |
Types
struct passwd (POSIX : pw_name/pw_passwd/pw_uid/pw_gid/pw_gecos/pw_dir/pw_shell, char * dans le buffer appelant), uid_t/gid_t (POSIX, unsigned int 32 bits x86_64 ET aarch64).
M4 — groupes (grp.h) : air-libc-grp (binder) sur air-account
Variantes réentrantes (_r) interrogeant /etc/group via la brique couche 1
air-account (ADR-067) — la libc ne réimplémente rien, elle bind + empaquette
l’ABI C. Empaquetage borné du buffer appelant, y compris le tableau gr_mem
(char **, NULL-terminé, aligné pointeur — sécurité). Convention *_r : errno
rendu directement (0 = trouvé/absent ; ERANGE/EINVAL).
Fonctions
| Symbole | Étiquette | Notes | ADR |
|---|---|---|---|
getgrnam_r | POSIX | Entrée name de /etc/group empaquetée dans buf (dont gr_mem aligné/NULL-terminé) ; *result=&grp/NULL (retour 0) ; ERANGE ; EINVAL (pointeur nul). | ADR-046, ADR-067 |
getgrgid_r | POSIX | Comme getgrnam_r, recherche par GID. | ADR-046, ADR-067 |
getgrnam/getgrgid | POSIX | Formes non-réentrantes (requises par OpenSSH) : struct group * vers un stockage statique (4096 octets, gr_mem inclus), NULL si absent, NULL+errno global (ERANGE) sur erreur. Non thread-safe — variantes _r en concurrence. | ADR-046, ADR-067 |
Types
struct group (POSIX : gr_name/gr_passwd/gr_gid/gr_mem) — char * et char **gr_mem pointent dans le buffer appelant. gid_t (POSIX, unsigned int 32 bits, déjà émis par M4 comptes).
M4 — shadow (shadow.h) : air-libc-shadow (binder) sur air-account
Variante réentrante getspnam_r interrogeant /etc/shadow (root-only) via la brique couche 1 air-account (ADR-067). SENSIBLE : le hachage transite par air-account où il est zeroïsé (ShadowHash, écriture volatile) et jamais journalisé (Debug redacté) ; la comparaison temps-constant relève d’air-crypto. Le codec decode seul (l’écriture de /etc/shadow est différée, Principe 5).
Fonctions
| Symbole | Étiquette | Notes | ADR |
|---|---|---|---|
getspnam_r | NON-POSIX-DÉLIBÉRÉ | shadow.h = extension Linux/glibc (hors POSIX), fournie car requise par OpenSSH (auth mot de passe). *result=&sp/NULL (retour 0) ; ERANGE ; EINVAL ; EACCES en non-privilégié (root-only). | ADR-046, ADR-067 |
Types
struct spwd (NON-POSIX-DÉLIBÉRÉ, extension shadow : sp_namp/sp_pwdp/sp_lstchg/sp_min/sp_max/sp_warn/sp_inact/sp_expire/sp_flag) — champs de vieillissement long (-1 = non défini) ; sp_namp/sp_pwdp pointent dans le buffer appelant.
M4 — identifiants (unistd.h/grp.h) : air-libc-id sur air-process::credentials (ADR-068)
Getters/setters d’identifiants du processus courant, bindant la surface fine air-process::credentials (getters + set_resuid/set_resgid + groupes) sur la couche 0 existante — aucun additif couche 0. drop_privileges reste la voie recommandée du largage réel ; ces primitives sont les 1:1 POSIX. initgroups combine air_account::groups_of_user + setgroups.
Fonctions
| Symbole | Étiquette | Notes | ADR |
|---|---|---|---|
getuid/geteuid/getgid/getegid | POSIX | Infaillibles ; réel/effectif via getresuid/getresgid. | ADR-068 |
setuid/setgid | POSIX | Sémantique conditionnelle au privilège (privilégié ⇒ r/e/s ; sinon effectif seul si uid ∈ {real,saved}, sinon EPERM). 0/−1+errno. | ADR-068 |
setresuid/setresgid | NON-POSIX-DÉLIBÉRÉ | Extension Linux/BSD requise par OpenSSH ; -1 = ne pas changer. | ADR-068 |
setgroups | NON-POSIX-DÉLIBÉRÉ | Extension ; EINVAL si size > NGROUPS_MAX/list nul ; EPERM non privilégié. | ADR-068 |
initgroups | NON-POSIX-DÉLIBÉRÉ | Extension BSD/glibc ; groupes de /etc/group (+ group) → setgroups. | ADR-068 |
getgroups | POSIX | Groupes supplémentaires courants (requise par OpenSSH uidswap.c : sauvegarde/restauration autour d’un changement d’UID). size == 0 ⇒ rend le nombre sans écrire ; sinon écrit jusqu’à size entrées et rend le nombre, ou −1+EINVAL si size insuffisant. Logique mesurée (air-libc-id). | ADR-068 |
Chantier B — famille processus (unistd.h/sched.h) : air-libc-process sur air-process::AirTaskManager (ADR-077)
Identité et ordonnancement du processus courant, bindant l’objet médiateur
AirTaskManager (couche 1) sur la couche 0 existante — aucun additif couche 0.
Lot process-1 : symboles infaillibles (aucun errno). Contrôle (setpgid/setsid),
attente (waitpid/waitid) et exec/spawn élargiront la famille (lots suivants).
| Symbole | Étiquette | Note / déviation | ADR |
|---|---|---|---|
getpid | POSIX | PID courant. Infaillible. Sur AirTaskManager::current_pid. | ADR-077 |
getppid | POSIX | PID parent (0 si réattribué à l’init). Infaillible. Sur AirTaskManager::parent_pid. | ADR-077 |
sched_yield | POSIX | Cède le CPU ; rend 0 (ne peut échouer sous Linux). Sur AirTaskManager::yield_now. | ADR-077 |
getpgid | POSIX | Groupe de processus de pid (0 = courant). −1+errno (ESRCH…). Sur AirTaskManager::process_group. | ADR-077 |
getsid | POSIX | Session de pid (0 = courant). −1+errno. Sur AirTaskManager::session_id. | ADR-077 |
setpgid | POSIX | Place pid (0 = courant) dans le groupe pgid (0 = son PID). 0/−1+errno. Sur AirTaskManager::set_process_group. | ADR-077 |
waitpid | POSIX | Attente d’un enfant (pid >0/-1/0/<-1) ; encode le wstatus (macros W*) via waitid couche 0. WNOHANG/WUNTRACED/WCONTINUED. 0 si WNOHANG sans événement ; −1+errno. Sur AirTaskManager::wait. | ADR-077 |
sched_getaffinity | POSIX/Linux | Affinité CPU de pid (0 = tâche courante) écrite dans mask (au plus cpusetsize octets, reste à zéro). mask nul ⇒ EFAULT ; cpusetsize nul ⇒ EINVAL. Sur AirTaskManager::cpu_affinity (couche 0 get_cpu_affinity, sans additif syscall ; type CpuSet gagne as_mut_bytes). | ADR-077 |
sched_setaffinity | POSIX/Linux | Fixe l’affinité CPU de pid depuis mask. Masque vide ⇒ EINVAL ; mask nul ⇒ EFAULT. Sur AirTaskManager::set_cpu_affinity. | ADR-077 |
execve | POSIX | Remplace l’image du processus par path (argv/envp = tableaux char* NULL-terminés). Ne retourne pas en succès ; échec ⇒ -1+errno (path nul ⇒ EFAULT, ENOENT/EACCES/ENOEXEC…). Sur AirTaskManager::exec (couche 0 execve, sans additif). | ADR-077 |
execveat | POSIX | Comme execve mais path relatif à dirfd (AT_FDCWD = cwd ; AT_EMPTY_PATH = exécuter dirfd, socle fexecve) + flags. dirfd<0 hors AT_FDCWD ⇒ EBADF. Sur AirTaskManager::exec_at. | ADR-077 |
execv | POSIX | Comme execve mais avec l’environnement courant implicite (bloc environ reflétant setenv, via AirEnvironmentManager::environ_block). path nul ⇒ EFAULT. Sur AirTaskManager::exec. | ADR-077 |
execvp | POSIX | Comme execv mais recherche file dans PATH (sauf s’il contient / ; dir vide = cwd ; défaut confstr si PATH absent). file nul ⇒ EFAULT. Cœur exec_resolving sur AirTaskManager::exec. | ADR-077 |
execvpe | NON-POSIX (GNU) | Comme execvp mais envp explicite. Cœur exec_resolving. | ADR-077 |
chroot | NON-POSIX (Unix/SVr4) | Change le répertoire racine du processus (confinement, CAP_SYS_CHROOT ; EPERM sinon = exception PRIVILEGE). path nul ⇒ EFAULT. Sur additif couche 0 chroot (ADR-085) → AirTaskManager::change_root. | ADR-085 |
posix_spawn | POSIX | Lance path (direct). Traduit les types opaques (posix_spawn_file_actions_t/posix_spawnattr_t, disposition ABI musl) → air_process::spawn_process (couche 1, fork+file-actions+exec async-signal-safe). Rend le numéro d’erreur (0 = succès), *pid reçoit le PID. | ADR-077 |
posix_spawnp | POSIX | Comme posix_spawn mais recherche PATH si file sans / (candidats pré-résolus, l’enfant essaie en ordre). | ADR-077 |
posix_spawn_file_actions_init | POSIX | Initialise une liste d’actions vide. | ADR-077 |
posix_spawn_file_actions_destroy | POSIX | Libère la liste (chemins possédés inclus) ; idempotent. | ADR-077 |
posix_spawn_file_actions_addopen | POSIX | Ajoute open(path, oflag, mode) → installé sur fildes. Chemin copié (POSIX). | ADR-077 |
posix_spawn_file_actions_addclose | POSIX | Ajoute close(fildes). | ADR-077 |
posix_spawn_file_actions_adddup2 | POSIX | Ajoute dup2(fildes, newfildes). | ADR-077 |
posix_spawn_file_actions_addchdir_np | NON-POSIX (GNU) | Ajoute chdir(path) dans l’enfant. | ADR-077 |
posix_spawnattr_init | POSIX | Attributs à zéro (rien d’appliqué). | ADR-077 |
posix_spawnattr_destroy | POSIX | No-op (sans état alloué). | ADR-077 |
posix_spawnattr_setflags | POSIX | Fixe POSIX_SPAWN_{SETPGROUP,SETSIGDEF,SETSIGMASK} (autres non supportés). | ADR-077 |
posix_spawnattr_getflags | POSIX | Lit les drapeaux dans *flags. | ADR-077 |
posix_spawnattr_setpgroup | POSIX | Groupe de processus de l’enfant (0 = le sien). | ADR-077 |
posix_spawnattr_setsigmask | POSIX | Masque de signaux de l’enfant (1er mot du sigset_t = signaux 1..=64). | ADR-077 |
posix_spawnattr_setsigdefault | POSIX | Signaux remis à l’action par défaut dans l’enfant. | ADR-077 |
Chantier B — famille système (unistd.h) : air-libc-system sur air-system::AirSystemManager (ADR-077)
Fonctions
| Symbole | Conformité | Sémantique | Réf |
|---|---|---|---|
gethostname | POSIX | Écrit le nom d’hôte (terminé NUL) dans name (≤ len octets). name nul ⇒ EFAULT ; buffer trop court ⇒ ENAMETOOLONG. Sur AirSystemManager::hostname (couche 0 gethostname via uname, sans additif). | ADR-077 |
sethostname | POSIX/Linux | Fixe le nom d’hôte aux len octets de name. EPERM sans CAP_SYS_ADMIN (chemin succès = exception PRIVILEGE, runner CI root) ; NUL interne ⇒ EINVAL ; name nul ⇒ EFAULT. Sur AirSystemManager::set_hostname. | ADR-077 |
sysconf | POSIX | Config système : _SC_PAGESIZE=4096 (x86_64/Pi4 4 Kio, à dériver d’AT_PAGESZ), _SC_CLK_TCK=100, _SC_OPEN_MAX=1024, _SC_ARG_MAX/_SC_NGROUPS_MAX/_SC_GET{PW,GR}_R_SIZE_MAX statiques ; _SC_NPROCESSORS_{ONLN,CONF} réels via AirSystemManager::online_processor_count (sched_getaffinity). name inconnu ⇒ -1. | ADR-077 |
getauxval | POSIX-PARTIEL | Entrée du vecteur auxiliaire (AT_PAGESZ/AT_HWCAP/AT_RANDOM…) via air_runtime::process_context::auxval (capturé au bootstrap, ADR-086). Entrée absente ⇒ 0. Export cible-only (target_vendor="air" ; l’auxv n’existe qu’on-target). | ADR-086 |
syscall | POSIX-PARTIEL | Trappe générique long syscall(long, ...) que le pal std exige (futex/gettid en direct). EXCEPTION assumée temporaire (docs/EXCEPTIONS.md, ADR-087) : seul shim tapant la couche 0 (raw_syscall). Convention __syscall_ret (-errno→-1+errno). Export cible-only. À retirer avec l’équipe Rust. | ADR-087 |
Chantier B — famille mémoire (sys/mman.h) : air-libc-mem sur air-memory::AirMemoryManager (ADR-077 / ADR-085)
Fonctions
| Symbole | Conformité | Sémantique | Réf |
|---|---|---|---|
mmap | POSIX | Mapping anonyme (MAP_ANONYMOUS, fd < 0) ou fichier-adossé de length octets. Rend l’adresse, ou MAP_FAILED ((void *)-1) + errno. MAP_FIXED non supporté ⇒ EINVAL (jamais un placement silencieux, ADR-032) ; offset négatif ⇒ EINVAL. Sur AirMemoryManager::map_anonymous/map_file. | ADR-085 |
mprotect | POSIX | Modifie les protections de [addr, addr+len). 0/−1+errno (addr nul/non aligné ⇒ EINVAL). Sur AirMemoryManager::protect. | ADR-085 |
munmap | POSIX | Libère [addr, addr+len) via l’additif couche 0 munmap_raw (ADR-085). 0/−1+errno (addr nul/non aligné ⇒ EINVAL). Sur AirMemoryManager::unmap. | ADR-085 |
M4 — famille réseau (arpa/inet.h, ADR-069 Lot 3)
Codec d’adresses et byte-order. Logique mesurée dans air-libc-socketio
(errno-aware) et codec pur/fuzzé air-libc-socket ; shims extern "C" minces dans
air-libc-capi::socket (exclus du gate de couverture).
| Symbole | Étiquette | Notes |
|---|---|---|
inet_pton | POSIX | Grammaire stricte (pas le legacy inet_aton) ; EAFNOSUPPORT sur famille inconnue. |
inet_ntop | POSIX | Sortie canonique RFC 5952 (IPv6) ; ENOSPC si size insuffisant. |
htons | POSIX | Hôte → réseau (16 bits), fonction pure. |
ntohs | POSIX | Réseau → hôte (16 bits), fonction pure. |
htonl | POSIX | Hôte → réseau (32 bits), fonction pure. |
ntohl | POSIX | Réseau → hôte (32 bits), fonction pure. |
Aucune déviation. Familles fd-level (socket/bind/connect/…) : lots 3.b–3.d.
M4 — cycle de vie des sockets (sys/socket.h, ADR-069 Lot 3.b)
Logique mesurée air-libc-socketio (errno-aware) sur le moteur air-socket ;
shims air-libc-capi::socket. Types : struct sockaddr, socklen_t.
| Symbole | Étiquette | Notes |
|---|---|---|
socket | POSIX | SOCK_CLOEXEC forcé ; SOCK_NONBLOCK honoré ; domaine inconnu EAFNOSUPPORT. |
bind | POSIX | sockaddr décodé via le codec couche 0 (ADR-070). |
connect | POSIX | EINPROGRESS remonté (non bloquant). |
listen | POSIX | backlog négatif traité comme 0. |
accept | POSIX | accept4(flags=0) ; adresse du pair tronquée à *addrlen. |
accept4 | POSIX | extension Linux (POSIX de fait) ; SOCK_CLOEXEC/SOCK_NONBLOCK. |
getsockname | POSIX | longueur réelle dans *addrlen (signale la troncature). |
getpeername | POSIX | idem getsockname. |
shutdown | POSIX | SHUT_RD/SHUT_WR/SHUT_RDWR ; how inconnu EINVAL. |
socketpair | POSIX | paire connectée écrite dans sv[2]. |
Aucune déviation. send/recv/sendmsg : lot 3.c ; setsockopt/getsockopt : lot 3.d.
M4 — transfert de données socket (ADR-069 Lot 3.c)
Logique mesurée air-libc-socketio::transfer sur le moteur air-socket ; shims
air-libc-capi::socket.
| Symbole | Étiquette | Notes |
|---|---|---|
send | POSIX | socket connecté ; MSG_NOSIGNAL ajouté par la couche 0. |
recv | POSIX | 0 = pair fermé. |
sendto | POSIX | datagramme adressé (ou pair connecté si dest nul). |
recvfrom | POSIX | écrit l adresse source dans src (si non nul). |
Aucune déviation. sendmsg/recvmsg (msghdr + SCM_RIGHTS) et setsockopt/getsockopt : lots suivants.
M4 — résolution inverse numérique (netdb.h, ADR-069 Lot 4.a)
Logique mesurée air-libc-socketio::nameinfo. Numérique uniquement au jalon M4.
| Symbole | Étiquette | Notes |
|---|---|---|
getnameinfo | POSIX-PARTIEL | numérique ; adresse + port en clair ; EAI_OVERFLOW si buffer court. |
gai_strerror | POSIX | message statique pour un code EAI_*. |
Déviation D-M4.2 : DNS inverse et résolution de service par nom non supportés.
getaddrinfo/freeaddrinfo : lot 4.b.
M4 — résolution nom → adresses (netdb.h, ADR-069 Lot 4.b)
Logique mesurée air-libc-socketio::addrinfo. Liste possédée (une alloc Box par nœud).
| Symbole | Étiquette | Notes |
|---|---|---|
getaddrinfo | POSIX-PARTIEL | service numérique ; nom numérique ou résolu ; filtre ai_family ; AI_PASSIVE. |
freeaddrinfo | POSIX | libère la liste. |
Déviation D-M4.3 : ai_canonname non rempli.
M4 — multiplexage evenements (poll.h, ADR-069 Lot 5.a)
Logique mesuree air-libc-socketio::poll sur air-poll (ppoll). Zero copie (struct pollfd reinterprete en PollFd couche 0, revents ecrits en place).
| Symbole | Etiquette | Notes |
|---|---|---|
poll | POSIX | timeout < 0 = infini ; EINTR remonte ; poll(NULL,0,t) = veille bornee. |
Aucune deviation. select/ppoll : lots suivants.
M4 — multiplexage historique (sys/select.h, ADR-069 Lot 5.b)
Logique mesuree air-libc-socketio::select, AU-DESSUS de poll (fd_set projetes en PollFd puis reconstruits depuis revents).
| Symbole | Etiquette | Notes |
|---|---|---|
select | POSIX | via poll ; nfds hors 0..=FD_SETSIZE => EINVAL ; timeout NULL = infini. |
Macros FD_ZERO/FD_SET/FD_CLR/FD_ISSET fournies au header (trailer).
M4 — messages vectorises + passage de fd (sys/socket.h, ADR-069)
Logique mesuree air-libc-socketio::message sur le moteur air-socket. Parse/build des cmsg SCM_RIGHTS (buffer de controle C), sans fuite de fd (les non-livres sont fermes).
| Symbole | Etiquette | Notes |
|---|---|---|
sendmsg | POSIX | iovec + adresse + fds SCM_RIGHTS extraits du msg_control C. |
recvmsg | POSIX | ecrit adresse source + fds recus (cmsg SCM_RIGHTS) ; CTRUNC si troncature. |
Macros CMSG_* + FD_* fournies au header. Cle de la privsep OpenSSH.
Chantier B (std) — routines Unwind* (stubs)
Sonde build-std=std : std réfère 82 symboles libc absents de libair (surface pour lier std). Lot 0 = les 11 _Unwind_*, fournis en stubs (cible panic = abort ⇒ jamais invoqués ; présents pour le lien). Crate air-libc-capi::unwind (gaté not(test)).
| Symbole | Étiquette | Notes |
|---|---|---|
_Unwind_Backtrace .. _Unwind_SetIP (11) | STUB | panic=abort ; avorte si atteint (bug). Pour le lien de std uniquement. |