web-dev-qa-db-fra.com

Différence entre movq et movabsq en x86-64

Je suis un nouveau venu ici et je commence tout juste à étudier le langage de l'Assemblée. Veuillez donc me corriger si je me trompe, ou si ce message n'a aucun sens, je le supprimerai.

Je parle des instructions de déplacement des données dans l'architecture Intel x86-64. J'ai lu que l'instruction movq régulière ne peut avoir que des opérandes source immédiats qui peuvent être représentés comme des nombres complémentaires de deux bits 32 bits, tandis que l'instruction movabsq peut avoir une valeur immédiate arbitraire 64 bits comme son opérande source et ne peut avoir qu'un registre comme destination.

Peux tu développer ta pensée à ce propos? Cela signifie-t-il que je peux déplacer la valeur immédiate 64 bits en utilisant uniquement l'instruction movabsq? Et seulement de la valeur immédiate au registre? Je ne vois pas comment je peux déplacer une valeur immédiate 64 bits en mémoire. Ou peut-être que je me suis trompé sur quelque chose d'important ici.

22
IgNite

Dans la syntaxe NASM/Intel, mov r64, 0x... Choisit n encodage MOV basé sur la constante. Vous pouvez choisir parmi quatre opérandes immédiats:

  • 5 octets mov r32, imm32. ( extension zéro pour remplir le registre 64 bits comme toujours ). AT&T: mov/movl
  • 6+ octets mov r/m32, imm32. utile uniquement pour les destinations mémoire. AT&T: mov/movl
  • 7+ octets mov r/m64, sign-extended-imm32. Peut stocker 8 octets en mémoire , ou définir un registre 64 bits sur une valeur négative. AT&T: mov/movq
  • 10 octets mov r64, imm64. (Il s'agit de la version REX.W = 1 du même opcode sans ModRM que mov r32, imm32) AT&T: mov/movq/movabs

(Le nombre d'octets est uniquement pour les destinations de registre ou les modes d'adressage qui n'ont pas besoin d'un octet SIB ou disp8/disp32: juste opcode + ModR/M + imm32.)

Certains assembleurs de syntaxe Intel (mais pas GAS) optimisent les constantes 32 bits comme mov rax, 1 En 5 octets mov r32, imm32 (NASM le fait), tandis que d'autres (comme YASM) utilisent 7 octets mov r/m64, sign-extended-imm32. Ils choisissent tous les deux le codage imm64 uniquement pour les grandes constantes, sans avoir à utiliser un mnémonique spécial.

Ou avec une constante equ, YASM utilisera malheureusement la version 10 octets même avec de petites constantes.


Dans GAS avec la syntaxe AT&T

movabsq signifie que l'encodage du code machine contiendra une valeur 64 bits: soit une constante immédiate, soit une adresse mémoire absolue. (Il existe un autre groupe de formes spéciales de mov qui chargent/stockent al/ax/eax/rax depuis/vers une adresse absolue, et la version 64 bits utilise une adresse absolue 64 bits, pas relative La syntaxe AT&T appelle cela aussi movabs, par exemple movabs 0x123456789abc0, %eax).

Même si le nombre est petit, comme movabs $1, %rax, Vous obtenez toujours la version 10 octets.

Une partie de cela est mentionnée dans ce ce qui est nouveau dans le guide x86-64 en utilisant la syntaxe AT&T.


Cependant, le mnémonique mov (avec ou sans suffixe de taille d'opérande q) choisira entre mov r/m64, imm32 Et mov r64, imm64 En fonction de la taille de l'immédiat. (Voir Quelle est la différence entre les instructions x86-64 AT&T movq et movabsq? , un suivi qui existe parce que la première version de cette réponse a mal deviné ce que GAS a fait avec les grandes constantes de temps d'assemblage pour movq.)

Mais les adresses des symboles ne sont pas connues avant l'heure du lien, elles ne sont donc pas disponibles lorsque l'assembleur sélectionne un encodage. Au moins lors du ciblage d'un objet ELF Linux fichiers, GAS suppose que si vous n'avez pas utilisé movabs, vous vouliez un absolu 32 bits. (YASM fait de même pour mov rsi, string Avec une relocalisation R_X86_64_32, mais NASM par défaut à movabs, produisant une relocalisation R_X86_64_64.)

Si, pour une raison quelconque, vous souhaitez utiliser un nom de symbole comme un absolu immédiat (au lieu d'un LEA relatif au RIP normalement meilleur), vous avez besoin de movabs

(Sur des cibles comme Mach-O64 sous OS X, movq $symbol, %rax Peut toujours choisir le codage imm64, car les adresses absolues 32 bits ne sont jamais valides. Il y a quelques questions/réponses sur MacOS sur SO où je pense que les gens ont dit que leur code fonctionnait avec movq pour mettre une adresse de données dans un registre.)


Exemple sur Linux/ELF avec un $symbol Immédiat

mov    $symbol, %rdi     # GAS assumes the address fits in 32 bits
movabs $symbol, %rdi     # GAS is forced to use an imm64


lea    symbol(%rip), %rdi  # 7 byte RIP-relative addressing, normally the best choice for position-independent code or code loaded outside the low 32 bits

mov    $symbol, %edi    # optimal in position-dependent code

Assemblé avec GAS dans un fichier objet (avec .bss; symbol:), Nous obtenons ces délocalisations. Notez la différence entre R_X86_64_32S (Signé) vs R_X86_64_32 (Non signé) vs R_X86_64_PC32 (Relatif au PC) relocalisations 32 bits.

0000000000000000 <.text>:
   0:   48 c7 c7 00 00 00 00    mov    $0x0,%rdi        3: R_X86_64_32S .bss
   7:   48 bf 00 00 00 00 00 00 00 00   movabs $0x0,%rdi        9: R_X86_64_64  .bss
  11:   48 8d 3d 00 00 00 00    lea    0x0(%rip),%rdi        # 18 <.text+0x18>  14: R_X86_64_PC32       .bss-0x4
  18:   bf 00 00 00 00          mov    $0x0,%edi        19: R_X86_64_32 .bss

Relié à un exécutable non PIE (gcc -no-pie -nostdlib foo.s), Nous obtenons:

4000d4:       48 c7 c7 f1 00 60 00      mov    $0x6000f1,%rdi
4000db:       48 bf f1 00 60 00 00 00 00 00   movabs $0x6000f1,%rdi
4000e5:       48 8d 3d 05 00 20 00      lea    0x200005(%rip),%rdi     # 6000f1 <__bss_start>
4000ec:       bf f1 00 60 00            mov    $0x6000f1,%edi

Et bien sûr, cela ne sera pas lié à un exécutable PIE, en raison des relocalisations absolues 32 bits. movq $symbol, %rax Ne fonctionnera pas avec le gcc foo.S Normal sur les distributions Linux modernes . les adresses absolues 32 bits ne sont plus autorisées sous Linux x86-64? . (Souvenez-vous, la bonne solution est un LEA relatif au RIP, ou un exécutable statique, n'utilisant pas réellement movabs).


movq est toujours au format 7 octets ou 10 octets, donc n'utilisez pas mov $1, %rax À moins que vous ne souhaitiez une instruction plus longue à des fins d'alignement (au lieu de compléter avec des NOP plus tard. Quelles méthodes peuvent être utilisées pour étendre efficacement la longueur des instructions sur les x86 modernes? ). Utilisez mov $1, %eax Pour obtenir le formulaire de 5 octets.

Notez que movq $0xFFFFFFFF, %rax Ne peut pas utiliser le formulaire à 7 octets, car il n'est pas représentable avec un signe-étendu 32 bits immédiat, et nécessite soit le codage imm64 soit le %eax Encodage de destination. GAS ne fera pas cette optimisation pour vous, vous êtes donc coincé avec le codage de 10 octets. Vous voulez vraiment mov $0xFFFFFFFF, %eax.

movabs avec une source immédiate est toujours la forme imm64.

(movabs peut également être le encodage MOV avec une adresse absolue 64 bits et RAX comme source ou destination: comme REX.W + A3MOV moffs64, RAX).


Je ne vois pas comment je peux déplacer une valeur immédiate 64 bits en mémoire.

C'est une question distincte, et la réponse est: vous ne pouvez pas. entrée manuelle de la référence insn pour MOV le montre clairement: le seul formulaire qui a un opérande immédiat imm64 n'a qu'une destination de registre, pas r/m64.

Si votre valeur tient dans un immédiat 32 bits étendu par signe, movq $0x123456, 32(%rdi) fera un stockage de 8 octets en mémoire . La limitation est que les 32 bits supérieurs doivent être des copies du bit 31, car il doit être encodable en tant que signe-étendu-imm32.

20
Peter Cordes