Pourquoi l’assembleur Solaris génère-t-il un code machine différent de celui de l’assembleur GNU?

J’ai écrit ce petit fichier d’assemblage pour amd64. Ce que le code fait n’est pas important pour cette question.

.globl fib fib: mov %edi,%ecx xor %eax,%eax jrcxz 1f lea 1(%rax),%ebx 0: add %rbx,%rax xchg %rax,%rbx loop 0b 1: ret 

Ensuite, j’ai assemblé puis désassemblé sur Solaris et Linux.

Solaris

 $ as -o yo -xarch=amd64 -V ys as: Sun Comstackr Common 12.1 SunOS_i386 Patch 141858-04 2009/12/08 $ dis yo disassembly for yo section .text 0x0: 8b cf movl %edi,%ecx 0x2: 33 c0 xorl %eax,%eax 0x4: e3 0a jcxz +0xa  0x6: 8d 58 01 leal 0x1(%rax),%ebx 0x9: 48 03 c3 addq %rbx,%rax 0xc: 48 93 xchgq %rbx,%rax 0xe: e2 f9 loop -0x7  0x10: c3 ret 

Linux

 $ as --64 -o yo -V ys GNU assembler version 2.22.90 (x86_64-linux-gnu) using BFD version (GNU Binutils for Ubuntu) 2.22.90.20120924 $ objdump -d yo yo: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 : 0: 89 f9 mov %edi,%ecx 2: 31 c0 xor %eax,%eax 4: e3 0a jrcxz 10  6: 8d 58 01 lea 0x1(%rax),%ebx 9: 48 01 d8 add %rbx,%rax c: 48 93 xchg %rax,%rbx e: e2 f9 loop 9  10: c3 retq 

Comment se fait-il que le code machine généré soit différent? Sun génère 8b cf pour mov %edi,%ecx alors que le gaz génère 89 f9 pour la même instruction. Est-ce à cause des différentes manières d’encoder la même instruction sous x86 ou est-ce que ces deux encodages ont vraiment une différence particulière?

Certaines instructions x86 ont plusieurs encodages qui font la même chose. En particulier, les registres peuvent être échangés dans toute instruction agissant sur deux registres et inversement dans l’instruction.

Le choix d’un assembleur / compilateur donné dépend simplement de ce que les auteurs de l’outil ont choisi.

Vous n’avez pas spécifié la taille de l’opérande pour les opérations mov , xor et add . Cela crée une certaine ambiguïté. Le manuel de l’assembleur GNU, i386 Mnemonics , mentionne ceci:

Si aucun suffixe n’est spécifié par une instruction, alors comme essaye de remplir le suffixe manquant basé sur l’opérande du registre de destination (le dernier par convention). […]. Notez que ceci est incompatible avec l’assembleur Unix d’AT & T qui suppose qu’un suffixe mnémonique manquant implique une taille d’opérande longue.

Cela implique que l’assembleur GNU choisit différemment – il choisira l’opcode avec l’octet R / M en spécifiant l’opérande cible (car la taille de la destination est connue / implicite) alors que l’AT & T choisit l’opcode où l’octet R / M spécifie la source opérande (car la taille de l’opérande est implicite).

J’ai cependant fait cette expérience et j’ai donné des tailles d’opérandes explicites dans votre source d’assemblage, et cela ne change pas la sortie de l’assembleur GNU. Il y a cependant l’autre partie de la documentation ci-dessus,

Différentes options d’encodage peuvent être spécifiées via un suffixe mnémonique facultatif. Le suffixe `.s permute 2 opérandes de registre en encodage lors du passage d’un registre à un autre.

que l’on peut utiliser; Le code source suivant, sous GNU as , me crée les codes d’opération de Solaris:

 .globl fib fib: movl.s %edi,%ecx xorl.s %eax,%eax jrcxz 1f leal 1(%rax),%ebx 0: addq.s %rbx,%rax xchgq %rax,%rbx loop 0b 1: ret