Changes between Version 69 and Version 70 of Archi-1-TP10


Ignore:
Timestamp:
Oct 26, 2021, 9:54:57 AM (3 years ago)
Author:
franck
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Archi-1-TP10

    v69 v70  
    179179
    180180
    181 1. En assembleur, vous utilisez les sections prédéfinies `.data` et `.text` pour placer respectivement les data et le code ou alors vous pouvez créer vos propres sections avec la directive `.section` (nous avons utilisé cette possibilité pour la section `.boot`). Il est aussi possible d'imposer ou de créer des sections en langage C avec le mot clé `__attribute__`. Ce mot clé du C permet de demander certains comportements au compilateur. Il y a en a beaucoup  (si cela vous intéresse vous pouvez regarder dans la [https://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Variable-Attributes.html doc de GCC sur les attributs]. En cours, nous avons vu un attribut permettant de désigner ou créer une section dans laquelle est mise la fonction concernée. Quelle était la syntaxe de cet attribut (regardez sur le slide 37).
    182 {{{#!protected ------------------------------------------------------------------------------------
    183 '''''''''''''''
    184 - `__attribute__ ((section (".crt0")))`\\Remarquez la syntaxe un peu curieuse avec les doubles underscore et les doubles parenthèses.
     1811. En assembleur, vous utilisez les sections prédéfinies `.data` et `.text` pour placer respectivement les data et le code ou alors vous pouvez créer vos propres sections avec la directive `.section` (nous avons utilisé cette possibilité pour la section `.boot`). Il est aussi possible d'imposer ou de créer des sections en langage C avec le mot clé `__attribute__`. Ce mot clé du C permet de demander certains comportements au compilateur. Il y a en a beaucoup  (si cela vous intéresse vous pouvez regarder dans la [https://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Variable-Attributes.html doc de GCC sur les attributs]. En cours, nous avons vu un attribut permettant de désigner ou créer une section dans laquelle est mise la fonction concernée. Quelle était la syntaxe de cet attribut (regardez sur le slide 38).
     182{{{#!protected ------------------------------------------------------------------------------------
     183'''''''''''''''
     184- `__attribute__ ((section (".start")))`\\Remarquez la syntaxe un peu curieuse avec les doubles underscore et les doubles parenthèses.
    185185'''''''''''''''
    186186}}}
     
    239239'''''''''''''''
    240240}}}
    241 1. Certaines parties du noyau sont en assembleur. Il y a au moins les toutes premières instructions du code de boot (démarrage de l'ordinateur) et l'entrée dans le noyau après l'exécution d'un syscall. Le gestionnaire de syscall est écrit en assembleur et il a besoin d'appeler une fonction écrite en langage C. Ce que fait le gestionnaire de syscall est:
     2411. Certaines parties du noyau sont en assembleur. Il y a au moins les toutes premières instructions du code de boot (démarrage de l'ordinateur) et l'entrée dans le noyau (kentry) après l'exécution d'un syscall. Le gestionnaire de syscall est écrit en assembleur et il a besoin d'appeler une fonction écrite en langage C. Ce que fait le gestionnaire de syscall est:
    242242 - trouver l'adresse de la fonction C qu'il doit appeler pour exécuter le service demandé;
    243243 - placer cette adresse dans un registre, par exemple `$2`;
     
    255255'''''''''''''''
    256256}}}
    257 1. Vous avez appris à écrire des programmes assembleur, mais parfois il est plus simple, voire nécessaire, de mélanger le code C et le code assembleur. Dans l'exemple ci-dessous, nous voyons comment la fonction `kinit()` procède pour entrer dans la fonction placée à l'adresse `__crt0` définie dans le fichier `kernel.ld`.\\Remarquez la syntaxe, ici `volatile` permet de dire au compilateur d'insérer le code tel que sans le modifier. Notez aussi l'absence de `,` entre les chaînes de caractères. Le premier argument de `__asm__` est une chaîne de caractères **unique** dans laquelle les instructions sont séparées par de `\n`. Il peut y avoir d'autres arguments, nous n'en aurons pas besoin.\\ \\Dans quelle section se trouve l'adresse `__crt0`? Combien vaut-elle? Est-ce que cette valeur est imposée par le processeur MIPS comme l'adresse de boot ou d'entrée dans le kernel? Quelle fonction est à cette adresse? Pourquoi doit-on écrire ce code en assembleur?
     2575. Vous avez appris à écrire des programmes assembleur, mais parfois il est plus simple, voire nécessaire, de mélanger le code C et le code assembleur. Dans l'exemple ci-dessous, nous voyons comment la fonction `syscall()` est écrite. Cette fonction utilise l'instruction `syscall`.\\Deux exemples d'usage de la fonction `syscall()` pris dans le fichier `tp2/4_libc/ulib/libc.c`
    258258{{{#!c
    259   9 void kinit (void)
    260  10 {
    261  11     kprintf (0, banner);
     259  1 int tty, char *fmt, ...)
     260  2 {
     261  3     int res;
     262  4     char buffer[PRINTF_MAX];
     263  5     va_list ap;
     264  6     va_start (ap, fmt);
     265  7     res = vsnprintf(buffer, sizeof(buffer), fmt, ap);
     266  8     res = syscall( tty, (int)buffer, 0, 0, SYSCALL_TTY_PUTS);
     267  9     va_end(ap);
     268 10     return res;
     269 11 }
    262270 12
    263  13     // put bss sections to zero. bss contains uninitialised global variables
    264  14     extern int __bss_origin;    // first int of bss section
    265  15     extern int __bss_end;       // first int of above bss section
    266  16     for (int *a = &__bss_origin; a != &__bss_end; *a++ = 0);
    267  17
    268  18     // this code allows to exit the kernel to go to user code
    269  19     __asm__ volatile (   "la     $26,    __text_origin  \n"     // get first address of user code
    270  20                          "mtc0   $26,    $14            \n"     // put it in c0_EPC
    271  21                          "li     $26,    0b00010010     \n"     // next status [UM,0,ERL,EXL,IE]
    272  22                          "mtc0   $26,    $12            \n"     // UM <- 1, IE <- 0, EXL <- 1
    273  23                          "la     $29,    __data_end     \n"     // define new user stack pointer
    274  24                          "eret                          \n");   // j EPC and EXL <- 0
    275  25 }
    276 }}}
    277 {{{#!protected ------------------------------------------------------------------------------------
    278 '''''''''''''''
    279 - L'adresse `__crt0` est la première adresse de la section `text` dans laquelle se trouve le code de l'application.
    280 - Elle vaut `0X7F400000`.
    281 - Cette adresse n'est pas imposée par le MIPS. C'est le choix des architectes du SoC. La seule condition est que cette adresse soit
    282   dans la partie accessible en mode user.
    283 - A cette adresse, on place la fonction `__start()`.
    284 - On est obligé d'écrire ce code en assembleur parce que la manière de changer de mode (de `kernel` à `user`) est propre à chaque processeur. Il n'y a aucun moyen de le faire en C.
    285 '''''''''''''''
    286 }}}
    287 1. Dans le code C de la question précédente, à quoi servent les lignes 14 à 16? Pourquoi faire des déclarations `extern`?
    288 {{{#!protected ------------------------------------------------------------------------------------
    289 '''''''''''''''
    290 - Ces lignes servent à mettre à 0 la zone des variables globales non initialisées explicitement par le programme.
    291 - Les déclarations `extern` permettent d'informer le compilateur que les adresses `__bss_orgin` et `__bss_end`
    292   existent ailleurs. De fait, elles sont définies dans le fichier `kernel.ld`.
     271 13 void exit (int status)
     272 14 {
     273 15     syscall( status, 0, 0, 0, SYSCALL_EXIT);        // never returns
     274 16 }
     275}}}
     276 
     277 Le code de cette fonction est dans le fichier `tp2/4_libc/ulib/crt0.c`
     278{{{#!c
     279  1 //int syscall (int a0, int a1, int a2, int a3, int syscall_code)
     280  2 __asm__ (
     281  3 ".globl syscall     \n"         
     282  4 "syscall:           \n"         
     283  5 "   lw  $2,16($29)  \n"         
     284  6 "   syscall         \n"         
     285  7 "   jr  $31         \n"         
     286  8 );
     287}}}
     288 Combien d'arguments a la fonction `syscall()`?
     289 Comment la fonction `syscall()` reçoit-elle ses arguments ?
     290 A quoi sert la ligne 3 de la fonction `syscall()` et que se passe-t-il si on la retire ?
     291 Expliquer la ligne 5 de la fonction `syscall()`.
     292 Aurait-il été possible de mettre le code de la fonction `syscall()` dans un fichier `.S` ?
     293{{{#!protected ------------------------------------------------------------------------------------
     294'''''''''''''''
     295- La fonction `syscall()` a 5 a arguments
     296- Elle reçoit ses 4 premiers arguments dans les registres $4 à $7 et le 5e dans la pile.
     297- La ligne 3 sert à dire que syscall est une étiquette utilisée dans un autre fichier. `globl` signifie **glob**al **l**abel. Si on la retire, il y aura un problème lors de l'édition de lien. `syscall()` ne sera pas trouvé par le linker.
     298- Le noyau attend le numéro de service dans `$2`. Or le numéro du service est le 5e arguments de la fonction `syscall()`. La ligne 5 permet d'aller le chercher dans la pile.
    293299'''''''''''''''
    294300}}}