14 points par GN⁺ 2025-05-06 | Aucun commentaire pour le moment. | Partager sur WhatsApp
  • 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 comme printf
  • 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 printf avec sortie UART, entrée via scanf et allocation mémoire dynamique fonctionnelles

Abstractions logicielles et bibliothèque standard C

  • Dans un OS classique, un appel à printf active 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

  • printf est 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=medany sont utilisées
  • medany est 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
  • _sbrk fonctionne en étendant le heap depuis _end jusqu’à _stack_bottom

Exemple d’application : entrée et sortie

  • Dans la fonction main, printf et scanf peuvent être utilisés, et les valeurs saisies sont correctement traitées
  • L’echo n’est pas pris en charge, mais scanf permet 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 _end jusqu’avant la pile
  • La pile a une taille fixe de 64 KB, et son adresse maximale est 0x80000000 + 64MB
  • Une instruction ASSERT empêche les collisions entre heap et pile

Le moment « gotcha »

  • Lors de la configuration de la toolchain, il faut utiliser --with-cmodel=medany pour 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.ld permettent 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.log permet 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.

Aucun commentaire pour le moment.