Protection mémoire pour eCos/ARM9

Auteur : Ivan Djelic <ivan.djelic@parrot.fr>
Dernière mise à jour : 16/08/2005


Introduction |  Installation  |  Utilisation  |  Implémentation  |  Détails de l'implémentation

1. Introduction

La protection mémoire est un package pour eCos qui permet d'exécuter du code sur cible avec des restrictions sur l'accès à la mémoire, en exploitant la MMU des processeurs ARM9. L'intérêt principal est de détecter des bugs typiques du C difficiles à trouver autrement (pointeurs fantômes, débordement de piles, corruption de mémoire, etc).
Note: Dans toute la suite, on entend par noyau l'ensemble du système eCos, c'est-à-dire non seulement le noyau eCos (kernel), mais également les librairies libc, libm, et plus généralement l'ensemble des paquets disponibles.
La protection mémoire possède les caractéristiques suivantes:

2. Installation

Pour installer la protection mémoire, il vous faut :
  1. Récupérer la dernière version d'eCos disponible sur le serveur CVS canari [192.168.1.209].
  2. Dans l'outil configtool, ajouter (menu Build → Packages) le paquet suivant : "Memory protection for ARM architectures with MMU" (memprot). Vous pouvez également importer directement un fichier de configuration pour eCos (voir la liste des configurations disponibles). Vous aurez éventuellement à cliquer sur Continuer dans la fenêtre de résolution automatique de conflits.

    Après installation, vous devriez voir les options suivantes dans le configtool:


  3. La protection mémoire est maintenant prête à être utilisée.

3. Utilisation

Lorsque la protection mémoire est activée, il faut pour l'utiliser associer à chaque module de code xxx.o un domaine d'exécution. Lors de la compilation, cette association est effectuée à l'aide d'une réécriture du code objet par la commande objcopy. Le script shell put_in_domain permet d'effectuer simplement cette association :

#!/bin/sh

# This script is used to assign an object file .o to a particular domain
# Usage : put_in_domain <domain> <file> [<file2>]
# Example : put_in_domain 01 java.o


domain=$( printf "%02d" "$1" )

if [ $domain = "00" ]; then
   echo "Invalid domain: domain 0 is implicit and needs no assignment."
   exit 1
fi

temp=$( tempfile )
out=$2

if [ ! -z "$3" ]; then
   out=$3
fi

# Prefix all sections
arm-elf-objcopy --prefix-sections=__memprot_vp${domain}_ $2 $temp

# Restore debug sections otherwise they'll be lost at link time
arm-elf-objcopy \
--rename-section __memprot_vp${domain}_.debug_abbrev=.debug_abbrev \
--rename-section __memprot_vp${domain}_.debug_macinfo=.debug_macinfo \
--rename-section __memprot_vp${domain}_.debug_loc=.debug_loc \
--rename-section __memprot_vp${domain}_.debug_ranges=.debug_ranges \
--rename-section __memprot_vp${domain}_.debug_info=.debug_info \
--rename-section __memprot_vp${domain}_.debug_line=.debug_line \
--rename-section __memprot_vp${domain}_.debug_str=.debug_str \
--rename-section __memprot_vp${domain}_.debug_frame=.debug_frame \
--rename-section __memprot_vp${domain}_.debug_aranges=.debug_aranges \
--rename-section __memprot_vp${domain}_.debug_pubnames=.debug_pubnames \
--rename-section __memprot_vp${domain}_.debug_pubtypes=.debug_pubtypes \
$temp $out

Ce script peut être utilisé indifféremment sur un module .o ou sur une librarie statique .a. Hormis l'utilisation de ce script, certaines options de compilation doivent être ajoutées :

# Options spécifiques à la protection mémoire
MEMPROT_CFLAGS = -fno-common -mlong-calls

L'option -fno-common est nécessaire pour séparer l'allocation de variables entre modules, l'option -mlong-calls est requise en raison des sauts d'adresses supérieurs à 24 Mo liés à l'utilisation de la mémoire virtuelle.

Exemple

Par exemple, supposons que l'on veuille associer le code et les variables de l'objet appli.o au domaine 4. Avec les variables de compilation classiques d'eCos, cela donne :
  1. Compilation de l'objet appli.o:
    $(XCC) -c $(CFLAGS) $(ECOS_GLOBAL_CFLAGS) $(MEMPROT_CFLAGS) appli.c

  2. Association de l'objet au domaine 04:
    put_in_domain 04 appli.o

  3. L'édition des liens se fait de manière identique, à ceci près que le script utilisé est différent: l'option -Ttarget.ld est remplacée par -Ttarget_mp.ld

Si l'on supprime l'étape 2, le code sera exécuté par défaut en domaine 0, c'est-à-dire en mode privilégié.

Utilisation dans un Makefile

Il y a plusieurs façons de procéder pour intégrer ces modifications dans un Makefile; en voici un exemple qui permet de compiler un programme avec ou sans protection mémoire (en changeant la valeur de la variable USE_MEMPROT).

Notes importantes

  1. L'utilisation de la protection mémoire entraîne quelques modifications dans la sémantique de certains appels système :

  2. Les déclarations multiples de variables globales sans utilisation du mot-clef extern sont interdites; ainsi, si vous déclarez par exemple la variable globale a dans deux fichiers séparés : vous obtiendrez une erreur du compilateur. La manière correcte de procéder est la suivante :

Utilisation d'un debugger

L'utilisation de la MMU ainsi que la surcharge des appels système introduisent quelques subtilités et pièges dans l'utilisation d'un debugger :

Messages d'erreur

Lorsqu'une violation de privilège a lieu, le système affiche un message d'erreur avant d'arrêter la thread fautive. L'exemple ci-dessous s'est produit lors d'une tentative d'accès à une adresse invalide:

memprot: data abort @ pc = 0x4510042c (page translation fault)
memprot: [trying to access 0x00074528 in domain 0]
memprot: killing thread "Bad Thread" (handle = 0x49165000, domain = 2)

A noter que la thread incriminée est simplement arrêtée, elle n'est pas détruite. Pour libérer les ressources qu'elle occupe (pile), il faut appeler cyg_thread_delete() depuis une autre thread.

4. Implémentation

L'implémentation se décompose en 2 parties:
eCos virtual memory map
5. Détails de l'implémentation

Patches

system_mode_mp.patch
Ce patch permet de faire tourner eCos en mode ARM SYSTEM à la place du mode SVC par défaut. L'intérêt du mode SYSTEM est qu'il partage la totalité de ses registres avec le mode USER, simplifiant ainsi les allers-retours entre les modes utilisateur et privilégié. Le patch modifie également le traitement des exceptions et le changement de contexte:

Packages