assembly x86 gas objdump

assembly - ¿Qué es el registro% eiz?



x86 gas (3)

En el siguiente código de ensamblaje que objdump usando objdump :

lea 0x0(%esi,%eiz,1),%esi

¿Qué es el registro %eiz ? ¿Qué significa el código anterior?


(Muy tarde para el juego, pero esto parecía una adición interesante): no es un registro en absoluto, es una peculiaridad de la codificación de instrucciones de Intel. Cuando se utiliza un byte ModRM para cargar desde la memoria, hay 3 bits usados ​​para el campo de registro para almacenar 8 registros posibles. Pero el lugar donde "ESP sería" sería "ESP (el puntero de la pila) es interpretado por el procesador como" un byte SIB sigue esta instrucción "(es decir, es un modo de direccionamiento ampliado, no una referencia a ESP). Por razones que solo conocen los autores, el ensamblador GNU siempre ha representado este "cero donde un registro sería de otra manera" como un registro "% eiz". La sintaxis de Intel simplemente lo deja caer.


Andy Ross ofrece mucho más del razonamiento subyacente, pero desafortunadamente está equivocado o al menos confunde los detalles técnicos. Es cierto que una dirección efectiva de just (%esp) no se puede codificar con solo el byte ModR / M ya que en lugar de decodificarse como (%esp) , se usa para indicar que también se incluye un byte SIB. Sin embargo, el %eiz no siempre se utiliza con un byte SIB para representar que se utilizó un byte SIB.

El byte SIB (escala / índice / base) tiene tres partes: el índice (un registro como %eax o %ecx que se aplica la escala), la escala (una potencia de dos de 1 a 8 que el el índice de registro se multiplica por) y la base (otro registro que se agrega al índice escalado). Esto es lo que permite instrucciones como add %al,(%ebx,%ecx,2) (código máquina: 00 04 4b - opcode, modr / m, sib (note no% eiz register aunque se utilizó el byte SIB) )) (o en la sintaxis de Intel, "agregar BYTE PTR [ecx * 2 + ebx], al").

Sin embargo, %esp no se puede usar como el registro de índice en un byte SIB. En lugar de permitir esta opción, Intel en su lugar agrega una opción para usar el registro base como está sin escalar o indexar. Por lo tanto, para desambiguar entre el caso de add %al,(%ecx) (código de máquina: 00 01 - opcode, modr / m) y add %al,(%ecx) (código de máquina: 00 04 21 - opcode, modr / m, sib), la sintaxis alternativa add %al,(%ecx,%eiz,1) se usa en su lugar (o para la sintaxis Intel: add BYTE PTR [ecx+eiz*1],al ).

Y como se explica en el artículo enlazado por Sinan, esta instrucción específica ( lea 0x0(%esi,%eiz,1),%esi ) simplemente se usa como un nop multi-byte (equivalente a esi = &*esi ) para que solo se debe ejecutar una instrucción estilo nop en lugar de múltiples instrucciones nop.


Ver ¿Por qué GCC LEA EIZ? :

Aparentemente %eiz es un pseudo-registro que simplemente evalúa a cero en todo momento (como r0 en MIPS).

...

Finalmente encontré una publicación de la lista de correo del gurú de binutils, Ian Lance Taylor, que revela la respuesta. A veces, GCC inserta las instrucciones NOP en la secuencia de código para garantizar una alineación adecuada y cosas así. La instrucción NOP toma un byte, por lo que podría pensar que podría agregar tantas como sea necesario. Pero según Ian Lance Taylor, es más rápido para el chip ejecutar una instrucción larga que muchas instrucciones cortas. Entonces, en lugar de insertar siete instrucciones NOP, en su lugar usan un LEA bizarro, que utiliza hasta siete bytes y es semánticamente equivalente a un NOP.