Aller au contenu

Placement d'un programme en mémoire

Pour qu’un µP puisse exécuter un programme informatique, il est indispensable de le charger préalablement dans la mémoire centrale. Son emplacement dans cette mémoire dépend naturellement fortement de l’architecture du système, du type de mémoires et de leur capacité. Un µP équipé d’une unité de gestion ou de protection de la mémoire (MMU/MPU - Memory Management Unit / Memory Protecting Unit) ainsi que le type du système d’exploitation mis en oeuvre ont également leur importance.

Layout d’un programme

Afin de permettre une gestion optimale de l’espace mémoire, le code et les différents types de données sont regroupés dans des sections distinctes (figure Layout d’un programme en mémoire).

Layout d'un programme en mémoire

Layout d'un programme en mémoire

Un programme distingue au minimum quatre sections principales:

  • La section .text, qui regroupe toutes les instructions du programme, le code.
  • La section .rodata, qui regroupe toutes les constantes ou variables du programme avec seulement un accès en lecture (non modifiable).
  • La section .data, qui regroupe toutes les données ou variables globales du programme qui sont initialisées avec une valeur différente de zéro (0).
  • La section .bss1, qui regroupe toutes les données ou variables globales du programme qui sont soit initialisée à zéro ou nul (0), ainsi que les variables non initialisées, lesquelles seront mises à zéro lors du lancement du programme.

La taille de ces quatre sections est déterminée lors de la génération du programme et découle directement du développement de l’application, c’est-à-dire du nombre de lignes de code ainsi que du nombre de constantes et de variables globales initialisées ou non.

A ces quatre sections, deux sections sont encore nécessaires pour l’exécution du programme par le µP:

  • La pile (stack) permet de stocker le contexte d’une fonction (adresse de retour, sauvegarde de certains registres du µP, etc.), les arguments ou paramètres d’appel de la fonction ainsi les variables locales de la fonction.
  • Le tas (heap) permet à l’application d’allouer dynamiquement des zones mémoires pour le stockage d’information ou la création d’objets.

La taille de la pile et du tas dépendent fortement du type de système déployé. Si le système met en oeuvre un système d’exploitation (OS - Operating System) ou d’un OS temps réel (RTOS - Real Time Operating System), ces tailles sont définies par l’OS ou le RTOS. Dans le cas d’application bare metal, c’est-à-dire sans OS/RTOS, la taille de la pile est généralement fixée statiquement lors de la génération du programme. Quant au tas, il occupe l’espace restant entre le sommet de la pile et le sommet de la section .bss.

L’éditeur de liens (linker) effectue le placement du code et des données dans ces différentes sections. Le fichier ci-dessous présente un exemple élémentaire d’un fichier de configuration de l’éditeur de liens.

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)

MEMORY {
    DDR0 : o = 0x80000000, l = 0x00f00000 
}

SECTIONS {
    .text : {
        *(.text_startup)
        *(.text*)   
    } > DDR0

    .rodata : { 
        . = ALIGN(0x100);
        *(.rodata*) 
    } >DDR0

    .data : { 
        . = ALIGN(0x100);
        *(.data*)
    } >DDR0

    .bss (NOLOAD): {
        . = ALIGN (0x100);
        __bss_start__ = . ;
        *(.bss*) 
        *(COMMON)
        __bss_end__ = .;
    } >DDR0

    _heap_bottom  = ALIGN(0x100);
    _stack_bottom = ORIGIN(DDR0) + LENGTH(DDR0) - 0x80000;
    _stack_top    = ORIGIN(DDR0) + LENGTH(DDR0);  
}

L’organisation du code et des données en différentes sections permettent également d’augmenter la robustesse et la fiabilité du logiciel. En effet, si le processeur dispose d’une MMU ou d’une MPU, il devient possible d’éviter des accès intempestifs dans des zones mémoires inappropriées, par exemple exécution de code dans une zone de données, modification du code, etc.

Organisation pour des “gros” systèmes embarqués (type Raspberry Pi)

Les systèmes embarqués disposent généralement d’un µP performant équipé d’une MMU et de suffisamment de mémoire Flash et RAM (plusieurs centaines de MiB, voire même de GiB). Les mémoires Flash sont principalement de type NAND, tandis que la RAM est de type DRAM. Ces ressources autorisent la mise en oeuvre d’un OS tel que GNU/Linux et d’un système de fichiers, par exemple ext4, sur la Flash permettant ainsi d’exécuter plusieurs applications sous forme de processus indépendants.

La mémoire Flash sert à stocker de façon permanente les applications et les données de configuration. Lors du lancement d’une application, l’OS copie l’application (contenu des sections .text, .rodata et .data) de la Flash dans la RAM (figure Organisation sur des systèmes embarqués). Cette opération terminée, le µP pourra finalement l’exécuter.

Organisation sur des systèmes embarqués

Organisation sur des systèmes embarqués

Organisation pour des systèmes sur puce (SoC)

Les systèmes sur puce (SoC) ne disposent généralement que de très peu de mémoire Flash et RAM (quelques dizaines de KiB). Sur les SoC, la Flash ainsi que la RAM sont intégrées directement dans la puce du µP. Pour ce faire, elles sont de type NOR pour la Flash et de type SRAM pour la RAM. Ces ressources limitées n’autorisent pas la mise en oeuvre d’OS tel que GNU/Linux. Par contre, le déploiement de RTOS, tel que MBedOS, FreeRTOS, Zephyr OS, et bien d’autres, est très courant. Ces RTOS permettent d’exécuter une seule et unique application, mais avec plusieurs tâches (threads).

Organisation sur des systèmes sur puce

La mémoire Flash sert à stocker de façon permanente les applications et les données de configuration. Les Flash de type NOR offrant un accès direct et aléatoire au contenu, le µP peut ainsi exécuter directement les instructions de l’application de la mémoire Flash sans devoir préalablement la copier dans la RAM. Pour cela, le code binaire de l’application est stocké directement dans les blocs de la Flash (section .text). Il en va de même des constantes (section .rodata) et des données avec une valeur initiale (section .data). Lors du lancement de l’application, le contenu de la section .data est copié de la Flash dans la RAM. Cette opération terminée, le µP poursuit l’exécution du programme.


  1. BSS (Block Started by Symbol) était une pseudo-opération introduite dans l’assembleur UA-SAP (United Aircraft Symbolic Assembly Program) pour l’IBM 704 dans les années 1950. Aujoud’hui, BSS n’est plus une pseudo-opération, mais une section de données dans les fichiers exécutables. Le nom BSS est resté pour des raisons historiques.