5. Conventions d'appel

Contenu de cette section

5.1 Linux

Edition de liens avec GCC

C'est la solution la plus pratique. Consultez la documentation de gcc et prenez exemple sur les sources du noyau Linux (fichiers .S qui sont utilisés avec gas, non pas as86).

Les arguments 32 bits sont empilés dans la pile vers le bas dans l'ordre inverse de l'ordre syntaxique (c'est-à-dire qu'on accède aux arguments ou les dépile dans l'ordre syntaxique), au-dessus de l'adresse de retour 32 bits. %ebp, %esi, %edi, %ebx doivent être conservés par l'appelé, les autres registres peuvent être détruits; %eax doit contenir le résultat, ou %edx:%eax pour des résultats sur 64 bits.

Pile virgule flottante: je ne suis pas sûr, mais je pense que le résultat se trouve dans st(0), la pile étant à la discrétion de l'appelé.

Notez que GCC possède certaines options pour modifier les conventions d'appel en réservant certains registres, en mettant les arguments dans des registres, en supposant que l'on ne possède pas de FPU, etc. Consultez les pages .info concernant le i386.

Il faut prendre garde à déclarer l'attribut cdecl pour une fonction qui suit la convention standard GCC (je ne sais pas exactement ce que cela produit avec des conventions modifiées). Consultez la documentation GCC dans la section: C Extensions::Extended Asm::

Problèmes ELF et a.out

Certains compilateurs C ajoutent un underscore avant tout symbole, alors que d'autres ne le font pas.

En particulier, la version GCC a.out effectue ce genre d'ajouts, alors que la version ELF ne le fait pas.

Si vous êtes confronté à ce problème, regardez comment des paquetages existants traitent le problèmes. Par exemple, récupérer une ancienne arborescence des sources de Linux, Elk, les qthreads ou OCAML...

Vous pouvez également redéfinir le renommage implicite de C en assembleur en ajoutant les instructions suivantes:


        void truc asm("machin") (void);

pour s'assurer que la fonction C truc sera réellement appelée machin en assembleur.

Remarquez que l'outil objcopy, du paquetage binutils, devrait vous permettre de transformer vos fichiers objets a.out en objets ELF et peut-être inversement dans certains cas. D'une manière plus générale, il vous permet d'effectuer de nombreuses conversions de formats de fichiers.

Appels systèmes directs

Il n'est absolument pas recommandé d'effectuer de tels appels par ce que leurs conventions peuvent changer de temps en temps, ou d'un type de noyau à un autre (cf L4Linux), de plus, ce n'est pas portable, difficile à écrire, redondant avec l'effort entrepris par libc, et enfin, cela empêche les corrections et les extensions effectuées à travers la libc, comme par exemple avec le programme zlibc qui réalise une décompression à la volée de fichiers compressés avec gzip. La manière standard et recommendée d'effectuer des appels systèmes est et restera de passer par la libc.

Les objets partagés devraient réduire l'occupation mémoire des programmes, et si vous souhaitez absolument avoir de petits exécutables, utilisez #! avec un interpréteur qui contiendra tout ce que vous ne voulez pas mettre dans vos binaires.

Maintenant, si pour certaines raisons, vous ne souhaitez pas effectuer une édition des liens avec la libc, récupérez-la et essayez de comprendre comment elle fonctionne! Après tout, vous prétendez bien la remplacer non?

Vous pouvez aussi regarder comment eforth 1.0c le fait.

Les sources de Linux sont fort utiles, en particulier le fichier d'en-tête asm/unistd.h qui décrit comment sont effectués les appels système...

Le principe général est d'utiliser l'instruction int $0x80 avec le numéro de l'appel système __NR_machin (regarder dans asm/unistd.h) dans %eax, et les paramètres (jusqu'à cinq) dans %ebx, %ecx, %edx, %esi, %edi. Le résultat est renvoyé dans %eax avec un résultat négatif étant l'erreur dont l'opposé est tranféré par la libc dans errno. La pile utilisateur n'est pas modificée donc n'avez pas besoin d'en avoir une correcte lors de l'appel.

Entrées/sorties sous Linux

Si vous souhaitez effectuer des entrées/sorties directement sous Linux, soit il s'agit de quelque chose de très simple qui n'a pas besoin de spécificités du système et dans ce cas là, consultez le mini-HOWTO IO-Port-Programming, ou alors vous devez créer un nouveau gestionnaire de périphérique et vous devriez alors lire quelques documents sur les méandres du noyau, le développement de gestionnaires de périphériques, les modules du noyau, etc. Vous trouverez d'excellents HOWTO ou autres documents du projet LDP.

Plus particulièrement, si vous souhaitez réaliser des programmes graphiques, rejoignez le projet GGI: http://synergy.caltech.edu/~ggi/ http://sunserver1.rz.uni-duesseldorf.de/~becka/doc/scrdrv.html

Dans tous les cas, vous devriez plutôt utiliser l'assembleur en ligne de GCC avec les macros provenant des fichiers linux/asm/*.h que d'écrire des sources en assembleur pur.

Accéder aux gestionnaires 16 bits avec Linux/i386

De telles choses sont théoriquement possibles (preuve: voir comment DOSEMU permet à des programmes d'accéder au port série), et j'ai entendu des rumeurs que certaines personnes le font (avec le gestionnaire PCI? Accès aux cartes VESA? PnP ISA? Je ne sais pas). Si vous avez de plus amples précisions à ce sujet, soyez les bienvenus. Le bon endroit à regarder est les sources du noyau, les sources de DOSEMU (et des autres programmes se trouvant dans le répertoire DOSEMU ), ainsi que les sources d'autres programmes bas niveaux (peut-être GGI s'il gère les cartes VESA).

En fait, vous devez utiliser soit le mode protégé 16 bits, soit le mode vm86.

Le premier est plus simple à configurer mais il ne fonctionne qu'avec du code ayant un comportement propre qui n'effectue pas d'arithmétique de segments ou d'adressage absolu de segment (en particulier pour l'adressage du segment 0), à moins que par chance tous les segments utilisés peuvent être configuré à l'avance dans le LDT.

La seconde possiblité permet d'être plus "compatibles" avec les environnements 16 bits mais il nécessite une gestion bien plus compliquée.

Dans les deux cas, avant de sauter sur le code 16 bits, vous devez:

Encore une fois, lisez attentivement les codes sources situés dans le répertoire de DOSEMU et consorts, en particulier ces mini-émulateurs permettant de faire tourner des programmes ELKS et/ou des .COM assez simples sous Linux/i386.

5.2 DOS

La plupart des émulateurs DOS sont livrés avec certaines interfaces d'accès aux services DOS. Lisez leur documentation à ce sujet, mais bien souvent, ils ne font que simuler int $0x21 et ainsi de suite, donc c'est comme si vous étiez en mode réel (je doute qu'ils aient de possibilités de fonctionner avec des opérandes 32 bits: ils ne font que réfléchir l'interruption dans le mode réel ou dans le gestionnaire vm86).

Certaines documentations concernant DPMI (ou ses variantes peuvent) être trouvées sur ftp://x2ftp.oulu.fi/pub/msdos/programming/

DJGPP est livré avec son propre sous-ensemble, dérivé, ou remplacement (limité) de la glibc.

Il est possible d'effectuer une compilation croisée de Linux vers DOS. Consultez le répertoire devel/msdos/ de votre miroir FTP de sunsite.unc.edu. Voir également le dos-extender MOSS du projet Flux d'utah.

D'autres documentations et FAQ sont plus consacrés à DOS. Nous déconseillons le développement sous DOS.

5.3 Windauberies...

Heu, ce document ne traite que de libre logiciel. Téléphonez-moi lorsque Windaube le deviendra ou du moins ses outils de développement!

En fait, après tout, cela existe: Cygnus Solutions a développé la bibliothèque cygwin32.dll pour que les programmes GNU puissent fonctionner sur les machines MicroMerdiques. Donc, vous pouvez utiliser GCC, GAS et tous les outils GNU ainsi que bon nombre d'applications Unix. Consultez leur site Web. Je (Faré) ne souhaite pas m'étendre sur la programmation sous Windaube, mais je suis sûr que vous trouverez tout un tas d'informations partout...

5.4 Votre propre système d'exploitation

Le contrôle sur le système étant ce qui attire de nombreux programmeurs vers l'assembleur, une prémisse ou un corollaire naturel de son utilisation est la volonté de développer son propre système d'exploitation. Remarquons tout d'abord que tout système permettant son auto-développement pourrait être qualifié de système d'exploitation, combien même tournerait-il au-dessus d'un autre système sur lequel il se déchargerait de la gestion du multitâche (Linux sur Mach) ou des entrées/sorties (OpenGenera sur Digital Unix), etc. Donc, pour simplifier le débogage, vous pouvez souhaiter développer votre système d'exploitation comme étant un processus fonctionnant sous Linux (au prix d'un certain ralentissement), puis, utiliser le Flux OS kit (qui permet l'utilisation des drivers Linux et BSD dans votre propre système d'exploitation) pour le rendre indépendant. Lorsque votre système est stable, il est toujours temps d'écrire vos propres gestionnaires de matériels si c'est vraiment votre passion.

Ce HowTo ne couvrira pas des sujets comme le code de chargement du système, le passage en mode 32 bits, la gestion des interruptions, les bases concernant les horreurs des processeurs Intel (mode protégé, V86/R86), la définition de votre format d'objets ou de vos conventions d'appel. L'endroit où vous pourrez trouver le plus d'informations concernant tous ces sujets est le code source de système déjà existants.

Un grand nombre de pointeurs se trouvent dans la page: http://www.eleves.ens.fr:8080/home/rideau/Tunes/Review/OSes.html


Chapitre suivant, Chapitre Précédent

Table des matières de ce chapitre, Table des matières générale

Début du document, Début de ce chapitre