Diferencia entre movq y movabsq en x86-64

Resuelto IgNite asked hace 7 años • 1 respuestas

Me refiero a instrucciones de movimiento de datos en la arquitectura Intel x86-64. He leído que la movqinstrucción normal solo puede tener operandos de origen inmediatos que pueden representarse como números en complemento a dos de 32 bits, mientras que la movabsqinstrucción puede tener un valor inmediato arbitrario de 64 bits como operando de origen y solo puede tener un registro como destino. .

¿Podría dar más detalles sobre esto? ¿Eso significa que puedo mover un valor inmediato de 64 bits usando movabsqsolo la instrucción? ¿Y sólo del valor inmediato al registro? No veo cómo puedo mover un valor inmediato de 64 bits a la memoria. O tal vez me equivoqué en algo importante aquí.

IgNite avatar Oct 29 '16 10:10 IgNite
Aceptado

A menos que su valor de 64 bits pueda codificarse como un inmediato extendido con signo de 32 bits, primero debe moverlo a un registro y luego almacenarlo. (O haga dos almacenes separados de 32 bits u otra solución peor para obtener los bytes donde los desea).


En la sintaxis NASM/Intel, mov r64, 0x...selecciona una codificación MOV basada en la constante. Hay cuatro para elegir con operandos inmediatos:

  • 5 bytes mov r32, imm32. ( cero extendido para llenar el registro de 64 bits como siempre ). AT&T: mov/movl
  • 6+ bytes mov r/m32, imm32. Sólo es útil para destinos de memoria. AT&T: mov/movl
  • 7+ bytes mov r/m64, sign-extended-imm32. Puede almacenar 8 bytes en la memoria o establecer un registro de 64 bits en un valor negativo. AT&T: mov/movq
  • 10 bytes mov r64, imm64. (Esta es la versión REX.W=1 del mismo código de operación sin ModRM que mov r32, imm32) AT&T: movabso mov/ movqcon una constante amplia.

(Los recuentos de bytes solo son para destinos de registro o modos de direccionamiento que no necesitan un byte SIB o disp8/disp32: solo código de operación + ModR/M + imm32 como mov dword [rdi], 123)

Algunos ensambladores de sintaxis Intel (pero no GAS a menos que use as -Oso gcc -Wa,-Os) optimizarán constantes de 32 bits como mov rax, 15 bytes mov r32, imm32(NASM hace esto), mientras que otros (como YASM) usarán 7 bytes mov r/m64, sign-extended-imm32. Ambos eligen la codificación imm64 sólo para constantes grandes, sin tener que utilizar una mnemónica especial.

O con una equconstante, desafortunadamente, YASM a veces usará la versión de 10 bytes incluso con constantes pequeñas.


En GAS con sintaxis de AT&T

movabsqsignifica que la codificación del código de máquina contendrá un valor de 64 bits: ya sea una constante inmediata o una dirección de memoria absoluta. (Hay otro grupo de formas especiales de movesa carga/almacenamiento al/ax/eax/rax desde/hacia una dirección absoluta, y la versión de 64 bits usa una dirección absoluta de 64 bits, no relativa. La sintaxis de AT&T llama a eso movabscomo bueno, por ejemplo movabs 0x123456789abc0, %eax).

Incluso si el número es pequeño, como movabs $1, %rax, seguirás obteniendo la versión de 10 bytes.

Algo de esto se menciona en esta guía de novedades en x86-64 usando la sintaxis de AT&T.


Sin embargo, el movmnemotécnico (con o sin qsufijo de tamaño de operando) elegirá entre el tamaño del inmediato mov r/m64, imm32y dependiendo del mismo. mov r64, imm64(Consulte ¿ Cuál es la diferencia entre las instrucciones movq y movabsq de AT&T x86-64? , un seguimiento que existe porque la primera versión de esta respuesta adivinó mal lo que hizo GAS con grandes constantes de tiempo de ensamblaje para movq).

Pero las direcciones de los símbolos no se conocen hasta el momento del enlace, por lo que no están disponibles cuando el ensamblador selecciona una codificación. Al menos cuando apunta a archivos objeto ELF de Linux, GAS supone que si no usó movabs, pretendía 32 bits absolutos. (YASM hace lo mismo mov rsi, stringcon una reubicación R_X86_64_32, pero NASM tiene por defecto movabs, lo que produce una reubicación R_X86_64_64).

Si por alguna razón desea utilizar un nombre de símbolo como inmediato absoluto (en lugar de un LEA relativo a RIP que normalmente es mejor), necesitamovabs

(En objetivos como Mach-O64 en OS X, movq $symbol, %raxsiempre se puede elegir la codificación imm64, porque las direcciones absolutas de 32 bits nunca son válidas. Hay algunas preguntas y respuestas de MacOS sobre SO en las que creo que la gente dijo que su código funcionaba movqpara poner una dirección de datos en un registro.)


Ejemplo en Linux/ELF con un $symbolinmediato

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

Ensamblados con GAS en un archivo objeto (con .bss; symbol:), obtenemos estas reubicaciones. Tenga en cuenta la diferencia entre reubicaciones de 32 bits R_X86_64_32S(firmadas) , ( R_X86_64_32sin firmar) y (relativas a la PC).R_X86_64_PC32

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

Vinculado a un ejecutable que no es PIE ( gcc -no-pie -nostdlib foo.s), obtenemos:

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

Y, por supuesto, esto no se vinculará a un ejecutable PIE, debido a las reubicaciones absolutas de 32 bits. movq $symbol, %raxno funcionará con normalidad gcc foo.Sen las distribuciones modernas de Linux . ¿Ya no se permiten direcciones absolutas de 32 bits en Linux x86-64? . (Recuerde, la solución correcta es LEA relativa a RIP, o crear un ejecutable estático, sin utilizar realmente movabs).


movqes siempre el formato de 7 bytes o 10 bytes, así que no lo use mov $1, %raxa menos que desee una instrucción más larga para fines de alineación (en lugar de rellenar con NOP más adelante. ¿Qué métodos se pueden usar para extender de manera eficiente la longitud de las instrucciones en x86 moderno? ). Úselo mov $1, %eaxpara obtener el formato de 5 bytes.

Tenga en cuenta que movq $0xFFFFFFFF, %raxno se puede utilizar el formato de 7 bytes, porque no se puede representar con un inmediato de 32 bits con extensión de signo%eax y necesita la codificación imm64 o la codificación de destino. GAS no hará esta optimización por usted, por lo que quedará atrapado con la codificación de 10 bytes. Definitivamente quieres mov $0xFFFFFFFF, %eax.

movabscon una fuente inmediata es siempre la forma imm64.

( movabsTambién puede ser la codificación MOV con una dirección absoluta de 64 bits y RAX como origen o destino: me gusta REX.W + A3 MOV moffs64, RAX).


No veo cómo puedo mover un valor inmediato de 64 bits a la memoria.

Esa es una pregunta aparte y la respuesta es: no puedes. La entrada manual insn ref para MOV deja esto claro: el único formulario que tiene un operando inmediato imm64 solo tiene un destino de registro, no r/m64.

Si su valor cabe en un inmediato de 32 bits con signo extendido, movq $0x123456, 32(%rdi)realizará un almacenamiento de 8 bytes en la memoria . La limitación es que los 32 bits superiores deben ser copias del bit 31, porque debe poder codificarse como un signo-imm32 extendido.

Relacionado:

  • ¿Por qué no podemos mover un valor inmediato de 64 bits a la memoria? - arquitectura de computadora / razones de diseño ISA.
  • Cómo cargar la dirección de una función o etiqueta en el registro (use 5 bytes mov r32, imm32como optimización o LEA relativa a RIP en cualquier caso, excepto en un modelo de memoria grande donde un símbolo puede estar a más de 2 GiB de distancia).
Peter Cordes avatar Oct 29 '2016 05:10 Peter Cordes