Je n'essaie pas de provoquer une guerre Intel vs AT&T (point discutable de toute façon, maintenant qu'ils prennent tous les deux en charge la syntaxe Intel) ou de demander laquelle est "meilleure" en soi, je veux juste connaître les différences pratiques dans le choix de l'un ou de l'autre .
Fondamentalement, lorsque je prenais un assemblage x86 de base il y a quelques années, j'ai utilisé le NASM sans autre raison que le livre que je lisais aussi - ce qui m'a mis fermement mais involontairement dans le camp NASM. Depuis lors, j'ai eu très peu de raisons d'utiliser Assembly, je n'ai donc pas eu l'occasion d'essayer GAS.
Sachant qu'ils prennent tous les deux en charge la syntaxe Intel (que je préfère personnellement) et devraient, théoriquement au moins, produire le même binaire (je sais qu'ils ne le seront probablement pas, mais la signification ne doit pas être modifiée), quelles sont les raisons de privilégier un ou l'autre?
S'agit-il d'options de ligne de commande? Des macros? Mots-clés non mnémoniques? Ou autre chose?
Merci :)
NASM utilise en fait sa propre variante de la syntaxe Intel, différente de la syntaxe MASM utilisée dans la documentation officielle d'Intel. Les noms d'opcode et les ordres d'opérande sont les mêmes que dans Intel, donc les instructions se ressemblent à première vue, mais tout programme significatif aura des différences. Par exemple avec MASM, l'instruction utilisée par MOV ax, foo
dépend du type de foo
, tandis que NASM n'a pas de types et cela s'assemble toujours à une instruction immédiate de déplacement. Lorsque la taille d'un opérande ne peut pas être déterminée implicitement, MASM nécessite quelque chose comme DWORD PTR
à utiliser lorsque NASM utilise DWORD
pour signifier la même chose. La plupart de la syntaxe au-delà des mnémoniques d'instructions et du format et de l'ordre des opérandes de base est différente.
En termes de fonctionnalités, NASM et GAS sont à peu près les mêmes. Les deux ont des macro-assembleurs, bien que les MSNA soient plus étendus et plus matures. De nombreux fichiers de code source GAS utilisent le préprocesseur C au lieu de la prise en charge des macros par GAS.
La plus grande différence entre les deux assembleurs est leur prise en charge du code 16 bits. GAS ne prend pas en charge la définition des segments x86. Avec GAS, vous êtes limité à la création de simples images binaires 16 bits à un seul segment, essentiellement des secteurs de démarrage et des fichiers .COM. NASM prend entièrement en charge les segments et prend en charge les fichiers objets au format OMF que vous pouvez utiliser avec un éditeur de liens approprié pour créer des exécutables segmentés 16 bits.
En plus du format de fichier objet OMF, NASM prend en charge un certain nombre de formats que GAS ne prend pas en charge. GAS ne prend normalement en charge que le format natif de la machine sur laquelle il fonctionne, essentiellement ELF, PE-COFF ou MACH-O. Si vous souhaitez prendre en charge un format différent, vous devez créer une version de "compilation croisée" de GAS pour ce format.
Une autre différence notable est que GAS prend en charge la création d'informations de déroulement DWARF et Windows 64 bits (la dernière exigée par Windows x64 ABI) tandis qu'avec NASM, vous devez créer les sections et remplir les données vous-même.
Intel Syntax: mov eax, 1 (destination de l'instruction, source)
Syntaxe AT&T: movl $ 1,% eax (source d'instruction, destination)
La syntaxe Intel est assez explicite. Dans l'exemple ci-dessus, la quantité de données qui est déplacée est déduite de la taille du registre (32 bits dans le cas de eax). Le mode d'adressage utilisé est déduit des opérandes eux-mêmes.
Il y a quelques bizarreries en ce qui concerne la syntaxe AT&T. Tout d'abord, notez le suffixe l
à la fin de l'instruction mov
, cela signifie long
et signifie 32 bits de données. Les autres suffixes d'instruction incluent w
pour un Word (16 bits - pas à confondre avec la taille de Word de votre CPU!), q
pour un quad-Word ( 64 bits) et b
pour un seul octet. Bien que cela ne soit pas toujours nécessaire, vous verrez généralement un code d'assemblage qui utilise la syntaxe AT&T indiquer explicitement la quantité de données utilisées par l'instruction.
Il faut plus d'explication en ce qui concerne le mode d'adressage utilisé sur l'opérande source et destination. $
signifie immediate
l'adressage, comme dans l'utilisation de la valeur dans l'instruction elle-même. Dans l'exemple ci-dessus, s'il a été écrit sans ce $
, direct
l'adressage serait utilisé, c'est-à-dire que le CPU essaierait de récupérer la valeur à l'adresse mémoire 1 (ce qui entraînerait très probablement un défaut de segmentation). Le %
signifie register
adressage, si vous ne l'avez pas inclus dans l'exemple ci-dessus eax
serait traité comme un symbol
c'est-à-dire une adresse mémoire étiquetée, ce qui serait plus que probable entraîner un undefined reference
au moment de la liaison. C'est donc obligatoire que vous êtes explicite sur le mode d'adressage utilisé sur les deux l'opérande source et destination.
La façon dont les opérandes de mémoire sont spécifiés est également différente:
Intel: [registre de base + index * taille de l'index + décalage]
AT&T: offset (registre de base, index, taille de l'index)
La syntaxe Intel permet un peu plus clair quel calcul est en cours pour trouver l'adresse mémoire. Avec la syntaxe AT&T, le résultat est le même mais vous devez connaître le calcul en cours.
devrait, théoriquement au moins, produire le même binaire
Cela dépend entièrement de votre chaîne d'outils.
quelles sont les raisons de privilégier l'un ou l'autre?
Bien entendu, selon moi, il s'agit de la syntaxe avec laquelle vous vous sentez plus à l'aise lorsque vous vous adressez à la mémoire. Préférez-vous l'explicitation forcée de la syntaxe AT&T? Ou préférez-vous que votre assembleur trouve pour vous cette minutie de bas niveau?
S'agit-il d'options de ligne de commande? Des macros? Mots-clés non mnémoniques?
Cela a à voir avec l'assembleur (GAS, NASM) lui-même. Encore une fois, préférence personnelle.