Implémenter `printf` sans OS — Utiliser la bibliothèque standard C dans un environnement Bare Metal
(popovicu.com)- Présentation d’une méthode permettant d’utiliser les fonctions standard du C, y compris
printf, même sans système d’exploitation, en s’appuyant sur la bibliothèque Newlib - Dans un environnement Bare Metal basé sur l’architecture RISC-V, implémentation directe d’un driver UART et de fonctions d’allocation mémoire connectés à Newlib
- L’implémentation minimale d’appels système comme
_write,_sbrk,_close, etc. suffit pour utiliser des fonctions avancées commeprintf - Explication de la création d’une toolchain basée sur Newlib avec la toolchain GCC RISC-V, ainsi que des scripts de build automatique et de linkage
- Au final, mise en place réussie d’un environnement
printfavec sortie UART, entrée viascanfet allocation mémoire dynamique fonctionnelles
Abstractions logicielles et bibliothèque standard C
- Dans un OS classique, un appel à
printfactive diverses couches d’abstraction comme les appels système du noyau, la couche terminal ou le rendu de polices - Dans un environnement Bare Metal, il faut contrôler directement les entrées/sorties sans système d’exploitation, ce qui nécessite d’implémenter soi-même les drivers
- Newlib fournit une configuration extensible où seules les fonctions minimales sont implémentées, plutôt qu’une bibliothèque standard C complète
Concept de Newlib
printfest implémenté en interne à partir de fonctions primitives simples comme_write- Dans Newlib, toutes les fonctions sont au départ définies comme des stubs, et il suffit d’implémenter les parties nécessaires pour utiliser les autres avec leurs comportements par défaut
- Le développeur peut ainsi utiliser les fonctionnalités de la bibliothèque C de manière flexible en n’implémentant que ce qui est nécessaire
Toolchain de cross-compilation
- Pour faire de la cross-compilation de x86_64/Linux vers RISC-V, il faut construire GCC directement depuis les sources
- En configurant une toolchain avec Newlib comme bibliothèque C par défaut, on peut générer des binaires pour RISC-V
Détails de la toolchain
- Lors de la construction de la toolchain, les options
--prefix,--enable-multilib,--disable-gdb,--with-cmodel=medanysont utilisées medanyest un réglage RISC-V qui permet d’accéder à des zones mémoire situées à des adresses élevées- Une fois la compilation terminée, le cross-compiler et les bibliothèques Newlib sont disponibles dans
/opt/riscv-newlib
Implémentation des briques mémoire et UART
- L’émission et la réception de caractères sont implémentées en accédant directement à l’adresse matérielle du UART 16550A dans l’environnement QEMU
- Des fonctions de remplacement d’appels système comme
_write,_sbrk,_close, etc. sont implémentées puis reliées à Newlib _sbrkfonctionne en étendant le heap depuis_endjusqu’à_stack_bottom
Exemple d’application : entrée et sortie
- Dans la fonction
main,printfetscanfpeuvent être utilisés, et les valeurs saisies sont correctement traitées - L’echo n’est pas pris en charge, mais
scanfpermet de lire puis d’afficher une chaîne - Un runtime séparé initialise la pile, remplit de zéros la section BSS, puis appelle
main
Script de linkage
- L’adresse de départ d’exécution est
0x80000000, et le code du runtime est placé à cet emplacement - La mémoire est organisée dans l’ordre
.text,.rodata,.data,.bss, et le heap est défini de_endjusqu’avant la pile - La pile a une taille fixe de 64 KB, et son adresse maximale est
0x80000000 + 64MB - Une instruction
ASSERTempêche les collisions entre heap et pile
Le moment « gotcha »
- Lors de la configuration de la toolchain, il faut utiliser
--with-cmodel=medanypour générer des instructions machine capables de gérer des adresses supérieures à0x80000000 - Si la bibliothèque C et le code applicatif n’utilisent pas le même modèle d’adressage, des erreurs d’édition de liens se produisent
Exécution de l’application
- Un Makefile permet d’automatiser la cross-compilation et l’exécution dans QEMU
- Les options
-specs=nosys.specs,-nostartfiles,-T link.ldpermettent d’utiliser la configuration minimale de Newlib avec un runtime personnalisé - Avec
make debug, les entrées et sorties via UART fonctionnent normalement dans la console QEMU qemu_debug.logpermet de consulter la trace réelle des instructions exécutées
Conclusion
- Une architecture permettant d’utiliser
printf,scanf,malloc, etc. sans système d’exploitation a été implémentée avec Newlib - La stratégie clé consiste à n’implémenter que le strict nécessaire en s’appuyant sur la structure en briques de Newlib
- Il sera ensuite possible d’ajouter des fonctions comme un système de fichiers ou une gestion mémoire plus avancée, tout en conservant la compatibilité avec la bibliothèque dans un environnement Bare Metal
- Le projet complet pèse environ 220 KB, ce qui reste relativement compact et efficace
Code source GitHub : popovicu/bare-metal-cstdlib
Aucun commentaire pour le moment.