2009-02-13 9 views
7

En la escuela, hemos estado usando un programa de arranque para ejecutar programas independientes sin un sistema operativo. He estado estudiando este programa y cuando está habilitado el modo protegido, se ejecuta un salto lejano ensamblando directamente el código de operación y los operandos como datos dentro del programa. Esto era para el ensamblador GNU:¿Alguien puede explicar este código de operación J86 JMP ensamblado directamente?


     /* this code immediately follows the setting of the PE flag in CR0 */

.byte 0x66, 0xEA 
.long TARGET_ADDRESS 
.word 0x0010   /* descriptor #2, GDT, RPL=0 */ 

En primer lugar, ¿por qué uno quiere hacer esto (en lugar de la tecla de acceso de instrucciones)?

He estado mirando los manuales de Intel, pero todavía estoy un poco confundido por el código. Específicamente en el Volumen 2A, página 3-549, hay una tabla de códigos de operación. La entrada pertinente:

 
EA *cp* JMP ptr16:32 Inv. Valid Jump far, absolute, address given in 
operand 

el código de operación real es obvia, pero el primer byte, 0x66, me tiene confundido. En referencia a la tabla en el manual de Intel, el cp aparentemente significa que seguirá un operando de 6 bytes. Y obviamente 6 bytes siguen en las siguientes dos líneas. 0x66 codifica un 'Prefijo de anulación de tamaño de operando'. ¿Qué tiene esto que ver con el CP en la tabla? Esperaba que hubiera algún valor hexadecimal para el CP, pero en su lugar existe este prefijo de anulación. ¿Puede alguien aclarar esto por mí?

Aquí es un vertedero de OD:

 
c022 **ea66 0000 0001 0010** ba52 03f2 c030 

TARGET_ADDRESS se definió como 0x00010000.

También estoy un poco confundido por la importancia de los dos últimos bytes. Sin embargo, esa parece ser otra pregunta por completo. Se está haciendo bastante tarde, y he estado mirando el código y los manuales de Intel durante horas, así que espero haber aclarado mi punto.

¡Gracias por mirar!

+0

La gente usa códigos de operación (en lugar de instrucciones) por 2 razones.La primera razón es cuando el ensamblador es "menos que adecuado" y no brinda soporte para las instrucciones que necesitan (esto es/era común cuando se agregan nuevas instrucciones y los ensambladores más antiguos no las admiten aún). La segunda razón es cuando el ensamblador admite las instrucciones que necesitan, pero el programador no sabe cómo convencer al ensamblador para que lo genere. Básicamente, son malas herramientas (incluidas herramientas antiguas, una sintaxis confusa y/o mala documentación) o programadores incorrectos. – Brendan

+0

Nota: Mi comentario anterior es "en general" y se aplica a todos los ensambladores. No uso GAS, y no tengo idea de si es compatible con la instrucción de "salto de 32 bits en código de 16 bits" o no (o qué tan buena/mala es la documentación). – Brendan

Respuesta

12

El 0x66 indica que el JMP (0xEA) se refiere a seis bytes. El valor predeterminado se refiere a 64K (16 bits) en modo real o a 32 bits en modo protegido (si recuerdo bien). Al aumentarlo, también incluye el descriptor de segmento, el índice del segmento en el GDT o el LDT, lo que significa que este código está haciendo lo que tradicionalmente se llama un "salto de longitud": un salto que cruza más allá de los segmentos en el arquitectura x86. El segmento, en este caso, apunta a la segunda entrada en el GDT. Si mira antes en ese programa, probablemente verá cómo se define el GDT en términos de la dirección y longitud de inicio del segmento (consulte el manual de Intel para estudiar las tablas GDT y LDT, entrada de 32 bits que describe cada segmento).

+0

Ah, esto tiene sentido ahora. Antes, cuando se define el GDT, la primera entrada es nula (como dice el manual), pero el segundo es el segmento de código. Después de volver a leer algunas partes del manual, veo cómo funciona esto. Gracias por aclarar esto. –

+0

Por otra parte, todavía tengo curiosidad de por qué el autor eligió hacer esto en lugar de usar los mnemónicos. –

+0

Es el prefijo de tamaño de operando, pero lo cambia de ['jmp ptr16: 16' a' jmp ptr16: 32'] (http://felixcloutier.com/x86/JMP.html). Esta respuesta afirma que la versión sin prefijo sería 'jmp rel16' o' jmp rel32', pero ese es un código de operación diferente, 'E9' no' EA'. 'EA' siempre es un jmp lejano con desplazamiento y segmento inmediato. –

2

Me encuentro con esto un poco. Algunos ensambladores solo saltarán a una ETIQUETA. En este caso, la persona quiere hacer un salto absoluto a un desplazamiento codificado duro específico. jmp TARGET_ADDRESS no funcionará. Estoy adivinando, por lo que solo lo ponen como bytes para evitar este problema.

0

0x66 especifica la anulación del tamaño del operando del tamaño del segmento del código actual. Suponiendo que el tamaño del código actual es de 16 bits, el nuevo puntero de instrucción será de 32 bits, no de 16 bits. Si el tamaño del segmento de código actual es de 32 bits, el 0x66 convertirá el puntero de instrucción de destino en 16 bits. El atributo de tamaño de código actual depende del selector CS en uso y sus atributos cargados desde la tabla GDT/LDT. En modo real, el tamaño del segmento de código suele ser de 16 bits, excepto en los casos especiales del modo "irreal".

Cuestiones relacionadas