|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Note : traduction par David Brunet. Sautez dans le grand bain et compilez quelque chose sans attendre Vous pouvez copier et coller ce qui suit, mais il est probablement plus rapide d'entrer ces deux lignes ci-dessous dans un éditeur de texte :
Enregistrer le fichier sous le nom "shorty.s". .text est une directive de l'assembleur, nous en reparlerons plus tard. blr est une des nombreuses instructions de branchement. Celle-ci signifie "branch to link register" (branchement au registre de lien). Lorsqu'un programme se termine, c'est la dernière instruction à être exécutée et elle provoque le retour de la séquence d'exécution des instructions au programme ou à l'environnement appelant (Shell, Ambient, etc.). Les sous-programmes peuvent également se terminer avec cette instruction, mais nous en reparlerons plus tard. C'est le moment idéal pour vous demander si vous avez déjà installé vbcc. Si ce n'est pas le cas, faites-le, téléchargez vbcc sur sun.hasenbraten.de/vbcc/. Une fois vbcc installé, ouvrez une fenêtre Shell et changez le répertoire où "shorty.s" a été sauvegardé. Ensuite, entrez :
Ceci indique à vasm de générer un fichier objet au format ELF appelé "shorty.o" dans "ram:" à partir du fichier source "shorty.s". Hello ? Le programme MorphOS le plus court du monde - ou pas ? Il y a des fois où un éditeur de liens est nécessaire pour traiter un fichier objet afin de générer un exécutable - ce n'est pas le cas dans cet exemple. Entrez ceci dans la fenêtre Shell :
Vous aurez l'impression que rien ne se passe - mais au moins, cela se passe très vite. Cependant, quelque chose s'est passé. "shorty.o" a été identifié comme un programme exécutable et chargé en mémoire où la séquence d'exécution des instructions du processeur lui a été transmise. Comme nous l'avons mentionné plus haut, l'unique instruction "blr" de ce programme a fait en sorte que la séquence d'exécution des instructions soit renvoyée directement à l'endroit d'où elle a été appelée - dans ce cas, le Shell. Il peut être intéressant de savoir que toutes les instructions PowerPC sont longues de quatre octets. Dans la nomenclature PowerPC, cette longueur est appelée un "word" (mot). Mais prenons un moment pour jeter un coup d'oeil à la taille de "shorty.o". Entrez ceci dans la fenêtre Shell :
2664 octets ! Mais ceci est typique pour les fichiers objets car ils contiennent beaucoup d'informations supplémentaires qui sont utilisées dans le processus de débogage et de lien. L'édition de liens, en particulier avec l'option "-s" (suppression de tous les symboles), génère un exécutable sensiblement plus petit. Entrez ceci dans la fenêtre Shell :
L'exécutable nouvellement généré fait 328 octets. C'est beaucoup plus petit que son fichier objet, mais que font ces 324 autres octets ? Ils constituent le "conteneur" ELF qui contient l'instruction unique de quatre octets de shorty. "ELF" signifie "Executable and Linkable Format" (format exécutable et liable) et est utilisé par MorphOS, AmigaOS 4 et AROS - sans oublier Linux, Unix, BSD et même des consoles de jeux. Il existe de nombreuses informations sur ce format de fichier en ligne mais ce sujet sera abordé un peu plus loin dans cet article, lorsque l'utilisation du programme objdump sera abordée. Le nouveau et l'ancien En général, pour créer un programme qui fait quelque chose d'utile ou qui produit au moins un résultat observable, il est nécessaire d'utiliser les fonctions des bibliothèques du système d'exploitation. Malgré la famille de processeurs nettement différente sur laquelle tourne MorphOS par rapport à la famille 68k utilisée par AmigaOS Classic, MorphOS demeure largement compatible avec AmigaOS. Cette compatibilité se reflète dans une API très similaire partagée avec AmigaOS et étendue à l'utilisation d'une structure de système MorphOS comme s'il s'agissait des registres de données et d'adresses internes d'un processeur 68k réel. Cette structure s'appelle EmulHandle et elle est toujours disponible via le registre PowerPC GPR2. De même, comme pour AmigaOS 68k, l'adresse de la base de la bibliothèque Exec se trouve toujours à l'emplacement mémoire 4. De nombreux aspects de l'API d'AmigaOS correspondent fidèlement aux caractéristiques disponibles dans le processeur 68k et la comparaison de code suivante devrait illustrer la façon dont elle est reprise dans l'API de MorphOS PowerPC. Voici une tâche courante effectuée lors de l'initialisation de nombreux programmes : l'ouverture de la bibliothèque DOS. Il s'agit d'un exemple particulièrement simple d'utilisation de cette bibliothèque, mais la plupart des fonctions suivent cette forme. 68k
PowerPC
Les attributs @h, @ha et @l sont nécessaires pour spécifier les demi-mots de poids fort et de poids faible des valeurs 32 bits immédiates, car la taille fixe de 32 bits des instructions PowerPC ne laisse pas suffisamment d'espace pour un code opération d'instruction et 32 bits supplémentaires de données. La différence entre @h et @ha est due à l'utilisation courante de l'instruction addi (add immediate - addition immédiate), qui ne prend qu'une valeur immédiate signée, pour fournir les 16 bits inférieurs d'une constante de 32 bits - telle qu'une adresse. Parfois, ces 16 bits inférieurs correspondent à une valeur négative et c'est alors que l'utilisation préalable de l'attribut @ha entraîne l'ajustement de sa valeur immédiate pour compenser. Ces détails ne sont résolus qu'avant l'exécution, lorsque le programme est chargé. L'attribut @h spécifie simplement le demi-mot supérieur d'une constante 32 bits sans tenir compte de ce qui suit. Pendant que je m'attarde sur de telles subtilités, la signification de zéro dans "load word" et de zéro dans l'instruction lwz est que sur les processeurs PowerPC 64 bits, cette instruction chargera une valeur 32 bits de la mémoire dans le mot inférieur d'un GPR 64 bits, puis effacera (ou mettra à zéro) son mot supérieur. C'est quelque chose à considérer pour MorphOS tournant sur les ordinateurs de la classe PowerPC G5. On pourrait être excusé de penser que l'extrait de code PowerPC a l'air un peu disgracieux comparé au premier. Alors que chaque instruction PowerPC fait quatre octets, les instructions 68k peuvent faire de deux à dix octets. Une seule instruction 68k peut charger une valeur à partir d'une adresse mémoire 32 bits spécifiée par un opérande d'instruction et la stocker à une autre adresse mémoire 32 bits spécifiée par le second opérande d'instruction. Le 68k peut également effectuer d'autres opérations que de simples chargements et stockages directement sur la mémoire. Ou du moins le laisser croire... En réalité, la mémoire d'un ordinateur ne fait qu'envoyer et recevoir des données, aucun autre traitement de données (comme l'addition, la soustraction, etc.) ne se produit en mémoire. Alors que l'instruction 68k add.l #$12345678,(a0) semble ajouter la valeur immédiate de son premier opérande à ce qui peut déjà être stocké à l'adresse indiquée par a0, le contenu de cette adresse est en fait chargé depuis la mémoire dans un registre de travail privé, l'addition est effectuée et le résultat est ensuite stocké à nouveau au même emplacement mémoire. Cette instruction effectue donc deux accès à la mémoire alors qu'il pourrait sembler qu'il n'y en ait qu'un seul. Comparez cela à la programmation en assembleur PowerPC où les chargements et les stockages en mémoire sont tous effectués de manière explicite. Avant que les données puissent être utilisées, elles doivent être chargées de la mémoire dans un GPR (General Purpose Register), zéro (dans le cas d'une simple copie de la mémoire) ou plusieurs opérations peuvent alors être effectuées et les données peuvent être stockées en mémoire. A moins que vous ne soyez tout à fait nouveau en assembleur PowerPC, vous savez déjà que le PowerPC possède 32 GPR - r0 à r31. Mais saviez-vous que l'utilisation souhaitée de ces registres est définie dans un document appelé ABI System V.4 auquel MorphOS adhère ?
L'ABI System V.4 définit également une manière particulière pour les programmes et les sous-routines d'organiser leurs cadres de pile. Lorsqu'un programme est chargé en mémoire et que la séquence d'exécution des instructions lui est transmise, le pointeur de pile pointe toujours vers le cadre de pile du programme appelant et il y a également une adresse importante stockée dans le registre de lien à ce moment-là. À moins que ce programme ne soit très simple (comme shorty), il doit enregistrer l'adresse du registre de lien et, très probablement, créer son propre cadre de pile. De manière assez pratique, il existe une position spéciale dans la pile de l'appelant dans laquelle un programme appelé peut sauvegarder le contenu du registre de lien. Voici un exemple assez courant des toutes premières instructions d'un programme ou d'une sous-routine :
"Euh ?" en effet... Essayons de visualiser la mémoire utilisée comme espace de pile.
Notez que stwu r1,-8(r1) crée un cadre de pile de deux mots qui est la plus petite pile qu'un programme ou une sous-routine peut avoir si, à son tour, il appelle un autre programme, une sous-routine ou une fonction de bibliothèque. Le plus souvent, un cadre de pile plus grand est créé, bien que stwu r1,-4(r1) puisse être utilisé par un programme ou un sous-programme pour créer un cadre de pile d'un mot, mais cela serait redondant et un tel programme ne doit appeler aucun autre programme, sous-programme ou fonction de bibliothèque. Un tel programme n'a pas besoin de stocker la valeur dans le registre de lien pour éviter qu'elle ne soit écrasée par des appels ultérieurs et peut se terminer et retourner à son appelant avec une simple instruction blr, tout comme le fait shorty. Une autre remarque concernant les cadres de pile plus grands et les tailles de pile appropriées : pour des raisons liées à l'architecture PowerPC, il est judicieux de choisir des tailles de cadre de pile multiples de 16 octets. Voici un exemple moins courant des premières instructions d'un programme, mais il peut mieux illustrer la façon d'utiliser les cadres de pile :
Examinons maintenant les dernières instructions impliquées dans la fin du programme, qui annuleraient les instructions précédentes :
Moins de parole, plus d'action s'il vous plaît Il est temps de compiler un autre programme : celui-ci fera vraiment quelque chose. Mais attendez... Pas un autre programme "Hello World"... J'en ai bien peur. Cette fois, le copier-coller est probablement plus rapide. Vous pouvez également télécharger cette archive source entièrement commentée. Notez que cet exemple n'utilise pas la trousse de développement de MorphOS. Au lieu de cela, quelques méthodes "rapides et grossières" sont utilisées pour des raisons de simplicité et de lisibilité :
Enregistrez ce fichier source sous le nom de "HelloWorld.s" et ouvrez une fenêtre Shell. Changez de répertoire pour aller là où HelloWorld.s vient d'être sauvegardé et entrez :
Une fois de plus, en raison de la simplicité de ce programme, l'édition de liens n'est pas nécessaire, il suffit donc d'entrer ce qui suit dans la fenêtre Shell :
Êtes-vous impressionné ? N'hésitez pas à modifier, expérimenter et améliorer ce code source. Un défi pas trop difficile pourrait être de changer ce programme pour qu'il affiche les arguments qui lui sont donnés dans la fenêtre Shell - un certain nombre de petits changements, mais significatifs, seraient nécessaires pour y parvenir. Conseil : regardez à nouveau l'utilisation des registres de l'ABI System V.4 ci-dessus. La version commentée de HelloWorld.s dans l'archive source donne une brève description de la raison pour laquelle la paire d'instructions mtctr (move to count register - déplacement vers le registre compteur) et bctrl (branch to count register and link - branchement au registre compteur et lien) est utilisée de préférence à mtlr (move to link register - déplacement vers le registre de lien) et blrl (branch to link register and link - branchement au registre de lien et lien) étant donné que cette dernière paire peut dégrader les optimisations de performance de certains processeurs PowerPC. Cependant, l'utilisation principale du registre compteur est un compteur de boucle 32 bits qui peut être automatiquement décrémenté par certaines instructions de branchement. Lorsque vous choisissez les registres à utiliser dans vos propres programmes, sachez que l'utilisation de r0 dans les opérandes de certaines instructions ne fonctionnera pas toujours comme prévu. Dans ces instructions, le contenu réel de r0 est ignoré et le résultat est basé, à la place, sur la valeur constante zéro. Par exemple, imaginez que r0 contient la valeur 100 lorsque cette instruction est exécutée : addi r0,r0,50. Il semble que le résultat devrait être r0 = r0 + 50 = 150. Le résultat sera en fait r0 = 0 + 50 = 50. Cela peut sembler étrange mais, tant que le programmeur en est conscient, ce comportement peut être utile. Un bon manuel de référence des instructions PowerPC expliquera cela, et bien d'autres choses, de manière beaucoup plus détaillée. Il y a beaucoup de ces documents de référence disponibles en ligne - celui-ci est l'un d'entre eux : MPCFPE32B.pdf. Trousse de développement de MorphOS, Objdump, ELF et Sections En plus d'installer vbcc, il est également recommandé d'installer la trousse de développement de MorphOS et le compilateur MorphOS vbcc. Le script d'installation de ce dernier s'attend à ce qu'il y ait une assignation préexistante pour include:. Si votre système assigne déjà include: quelque part, veuillez passer la partie suivante. Ce qui suit est mon fichier S:user-startup après l'installation de vbcc, de sa cible de compilateur MorphOS et d'une assignation supplémentaire que j'ai faite précédere de ce commentaire :
A ce stade, il serait utile d'avoir un certain nombre de fichiers objets et d'exécutables fraîchement générés à regarder avec objdump bien que n'importe quel ELF puisse être utilisé dans ce but. Notez que si les fichiers système de MorphOS 2.x/3.x sont compilés en tant que ELF, ils sont également signés pour empêcher leur utilisation sur MorphOS 1.x où ils pourraient ne pas fonctionner. Cette signature a également pour effet que objdump ne les reconnaît pas comme des ELF. objdump peut être utilisé pour révéler beaucoup d'informations intéressantes sur un ELF, dont la liste de tous les symboles et sections présents, ainsi que le désassemblage. Soyez conscient que, lorsqu'ils sont désassemblés, des programmes apparemment petits peuvent produire plus d'informations que le tampon d'historique de la fenêtre Shell ne peut en contenir ; redirigez donc la sortie vers un fichier ou spécifiez des adresses de début et de fin pour limiter la sortie. Cela dit, aucun des fichiers générés jusqu'à présent dans ce tutoriel ne risque de submerger le tampon d'historique de l'interpréteur de commandes lorsqu'il est désassemblé. Comme nous l'avons mentionné juste avant l'exemple de code source précédent, certaines méthodes rapides et grossières ont été utilisées pour des raisons de simplicité et de lisibilité, et cela concerne la manière dont les décalages des fonctions de bibliothèque ont été établis. L'approche utilisée deviendra rapidement préjudiciable à mesure que d'autres décalages seront ajoutés. Il existe un certain nombre de solutions à ce problème, mais nous n'en présenterons qu'une seule ici. Elle consiste à supprimer le caractère de soulignement (_) en tête des noms de fonctions, de sorte que, par exemple, li r3,_LVOOpenLibrary devienne li r3,LVOOpenLibrary. Supprimez ou commentez les trois premières directives .set qui définissent les décalages des fonctions de la bibliothèque et assemblez avec vasm comme précédemment mais notez que le fichier objet résultant n'est plus exécutable. Pour rendre cet objet exécutable, vlink est nécessaire.
Dans la commande Shell ci-dessus, -lamiga fait référence au fichier libamiga.a que vlink sait rechercher dans vlibmos: Ce fichier contient des informations utilisées pour résoudre de nombreux noms de fonctions de bibliothèque en valeurs numériques. Notez que tout exécutable généré par la commande vlink ci-dessus contiendra beaucoup d'informations sur les symboles qui, bien que potentiellement utiles, augmentent la taille de l'exécutable. Ceci peut être évité avec quelques ajouts à la commande ci-dessus.
-s supprime tous les symboles du fichier de sortie. -P<symbole> préserve ce symbole. Pour plus d'informations, veuillez vous reporter à la documentation de vlink et des autres programmes installés avec vbcc.
|