Pourquoi un simple programme en C aurait-il besoin d’appels système?

Relatif à cette autre question. J’essaye de lancer ce programme simple en gem5:

int main() { int a=1, b=2; int c=a+b; return c; } 

Et cela échoue car gem5 n’a pas implémenté d’appels système.

Ma question est la suivante: pourquoi un programme simple comme celui-ci aurait-il besoin d’appels système? Cela devrait fonctionner en métal nu sans problème. Est-il possible de comstackr cela pour éviter les appels système? J’utilise arm-linux-gnueabi-gcc -static -DUNIX pour le comstackr.

Sans appels système, le programme ne peut pas quitter. La façon dont cela fonctionne est généralement quelque chose comme ceci:

 // Not how it's actually implemented... just a sketch. void _start() { char **argv = ...; int argc = ...; // ... other initialization code ... int retcode = main(argc, argv); exit(retcode); } 

Les détails exacts dépendent du système d’exploitation, mais exit() , qui termine le processus, doit généralement être un appel système ou doit être implémenté avec des appels système.

Notez que cela est vrai pour les implémentations C “hébergées”, pas pour les implémentations C “autonomes”, et dépend fortement du système d’exploitation. Il existe des implémentations C autonomes pouvant fonctionner sur du métal nu, mais les implémentations C hébergées nécessitent généralement un système d’exploitation.

Vous pouvez comstackr sans bibliothèques standard et sans le runtime, mais votre point d’entrée ne peut pas retourner … il n’y a rien vers lequel retourner, sans un runtime.

Créer un programme de baremetal

Il est généralement possible de comstackr des programmes capables d’exécuter baremetal.

  • Utilisez -ffreestanding . Cela fait que GCC génère un code qui ne suppose pas que la bibliothèque standard est disponible (et a d’autres effets).

  • Utilisez -nostdlib . Cela empêchera GCC de se lier à la bibliothèque standard. Notez que les memcmp , memset , memcpy et memmove peuvent être générés de toute façon, vous devrez donc peut-être les fournir vous-même.

À ce stade, vous pouvez écrire votre programme, mais vous devez généralement utiliser _start au lieu de main :

 void _start(void) { while (1) { } } 

Notez que vous ne pouvez pas revenir de _start ! Pensez-y … il n’y a nulle part où retourner. Lorsque vous comstackz un programme comme celui-ci, vous constatez qu’il n’utilise aucun appel système et n’a pas de chargeur.

 $ gcc -ffreestanding -nostdlib test.c

Nous pouvons vérifier qu’il ne charge aucune bibliothèque:

 $ ldd a.out                              
     lié statiquement
 $ readelf -d a.out 

 La section dynamic au décalage 0xf30 contient 8 entrées:
   Type de tag Nom / Valeur
  0x000000006ffffef5 (GNU_HASH) 0x278
  0x0000000000000005 (STRTAB) 0x2b0
  0x0000000000000006 (SYMTAB) 0x298
  0x000000000000000a (STRSZ) 1 (octets)
  0x000000000000000b (SYMENT) 24 (octets)
  0x0000000000000015 (DEBUG) 0x0
  0x000000006ffffffb (FLAGS_1) drapeaux: PIE
  0x0000000000000000 (NULL) 0x0

Nous pouvons également constater qu’il ne contient aucun code permettant d’effectuer des appels système:

 $ objdump -d a.out

 a.out: format de fichier elf64-x86-64


 Déassembly de la section .text:

 00000000000002c0 <_start>:
  2c0: eb fe jmp 2c0 <_start>

Ma question est la suivante: pourquoi un programme simple comme celui-ci aurait-il besoin d’appels système?

Le chargeur au moment de l’exécution, ld.so effectue des appels système. L’exécution C fait des appels système. Faites strace et voyez.

Il est possible que vous souhaitiez extraire certains parameters de gcc. Entre autres:

  • -représentant
  • -nostdlib
  • -nodefaultlibs

Ma question est la suivante: pourquoi un programme simple comme celui-ci aurait-il besoin d’appels système?

Parce que l’entrée principale et la sortie du programme sont basées sur des appels système.

Comstackr avec arm-unknown-linux-uclibcgnueabi a résolu le problème. Apparemment, l’implémentation uclibc n’utilise pas les appels système que gem5 n’a pas implémentés.