2010-04-14 9 views
12

que estoy tratando de hacer un salto relativo en ensamblador x86, sin embargo no puedo conseguir que funcione. Parece que por alguna razón mi salto sigue siendo reescrito como un salto absoluto o algo así.GCC/X86, Problemas con saltos relativos

Un programa sencillo ejemplo de lo que estoy tratando de hacer es esto:

.global main 

main: 
    jmp 0x4 
    ret 

Desde la instrucción JMP es de 4 bytes de longitud y un salto relativo está desplazado de la dirección del salto + 1, este debería ser una fantasía no-operativa. Sin embargo, compilar y ejecutar este código provocará un error de segmentación.

El verdadero rompecabezas para mí es que al compilarlo al nivel del objeto y luego desensamblar el archivo del objeto muestra que parece que el ensamblador está haciendo correctamente un salto relativo, pero después de compilarlo el enlazador lo está cambiando a otro tipo de salto.

Por ejemplo, si el código anterior estaba en un archivo llamado asmtest.s:

$gcc -c asmtest.s 
$objdump -D asmtest.o 

... Some info from objdump 
00000000 <main>: 
    0: e9 00 00 00 00   jmp 5 <main+0x5> 
    5: c3      ret 

Esto se ve como el ensamblador hizo correctamente un salto relativo, aunque es sospechoso que la instrucción JMP se llena de 0s.

Luego utiliza gcc para enlazar entonces desmontado y tiene esto:

$gcc -o asmtest asmtest.o 
$objdump -d asmtest 

...Extra info and other disassembled functions 
08048394 <main>: 
8048394:  e9 6b 7c fb f7  jmp 4 <_init-0x8048274> 
8048399:  c3     ret 

Esto me parece que el enlazador volvió a escribir la declaración JMP, o sustituido en el 5 por otra dirección.

Así que mi pregunta se reduce a, ¿qué estoy haciendo mal?

Soy yo que especifica el desplazamiento de forma incorrecta? ¿No estoy entendiendo cómo funcionan los saltos relativos? ¿GCC está tratando de asegurarse de que no haga cosas peligrosas en mi código?

Respuesta

15

En realidad, el ensamblador pensó que estaba tratando de hacer un salto absoluto. Sin embargo, el código de operación jmp es, en el nivel del metal, relativo. Por lo tanto, el ensamblador no podría saber qué escribir después del byte 0xe9, porque el ensamblador no sabe en qué dirección terminará su código.

El ensamblador no se conoce, pero el vinculador. Entonces, el ensamblador escribió en los encabezados asmtest.o en alguna parte una solicitud del enlazador, algo que dice así: "cuando sepa en qué dirección se cargará el código, ajuste esos bytes justo después del 0xe9 para que sean apropiados para un salto desde ese punto (con direccionamiento relativo) a la dirección absoluta '4' ". El enlazador hizo exactamente eso. Se vio que el 0xE9 fue en la dirección 0x08048394, y el siguiente código de operación en 0x08048399, y se calcula: pasar de 0x08048399 a 0x00000004, uno tiene que restar 0x08048395, lo que equivale a añadir (en máquinas de 32 bits) 0xf7fb7c6b. De ahí su secuencia "6b 7c fb f7" en el binario resultante.

Usted puede codificar un salto relativo "manual" de esta manera:

.global main 
main: 
    .byte 0xe9 
    .long 0x4 
    ret 

Por lo tanto, el ensamblador no se dará cuenta de que su 0xE9 es realmente un jmp, y no va a tratar de ser más astuto que usted. En el binario, obtendrá la secuencia 'e9 04 00 00 00' que desee y ninguna interacción del enlazador.

Tenga en cuenta que el código anterior se puede bloquear, porque el desplazamiento relativo se cuenta desde la dirección de inmediatamente después de el desplazamiento (es decir, la dirección de la siguiente código de operación, aquí ret). Esto saltará en los no-man-land 4 bytes después del ret y parece probable un segfault o algo extraño.

+0

Saltando a una etiqueta llamada es cómo los saltos se hacen normalmente en el montaje. Esto es muy básico. Supongo que el OP ya lo sabe y realmente desea codificar manualmente un salto relativo por algún motivo no especificado. –

+0

Sospecho que el comentario fue en respuesta al mío. Lo eliminé mientras movía mi comentario a una respuesta. –

+0

Gracias por la ayuda, esto funciona exactamente como yo lo necesito también. También gracias por señalar el problema con el salto demasiado lejos, no estaba seguro de exactamente de dónde se calculaba la instrucción. –

5

Creo que el ensamblador toma una dirección absoluta y calcula el desplazamiento de dirección para usted. Los ceros en el primer caso probablemente estén allí porque forman parte de una tabla de corrección y el desplazamiento se calcula en la fase de enlace.

Mis habilidades de lenguaje ensamblador son un poco oxidado, pero creo que usted podría hacer esto:

.global main 

main: 
    jmp getouttahere 
getouttahere: 
    ret 

O si realmente quiere que se vea relativa:

.global main 

main: 
    jmp .+5 
    ret 

favor ser suave si Me equivoco; Ha sido un largo tiempo.

+0

Eso tiene sentido que está tratando de calcular una dirección absoluta . Desafortunadamente, por diversas razones, no puedo usar una etiqueta para hacer el salto, y $ + 5 no es una sintaxis válida. Gracias por la ayuda. –

+0

@Ian Kelly: la sintaxis del ensamblador puede variar. Creo que ". + 5" podría funcionar. –

14

Si está utilizando ensamblador GAS de GCC que utiliza AT & sintaxis T por defecto, la sintaxis de direccionamiento relativo uses the dot ('.') to represent the current address being assembled (al igual que el pseudo-símbolo $ se utiliza en la sintaxis de montaje de Intel/MASM). Usted debe ser capaz de obtener su salto relativo usando algo como:

jmp . + 5 

Por ejemplo, la siguiente función:

void foo(void) 
{ 
    __asm__ (
     "jmp . + 5\n\t" 
     "nop\n\t" 
     "nop\n\t" 
     "nop\n\t" 
     "nop\n\t" 
     "nop\n\t" 

    ); 
} 

obtiene montado a:

71 0000 55   pushl %ebp 
    72 0001 89E5   movl %esp, %ebp 
    74    LM2: 
    75    /APP 
    76 0003 EB03   jmp . + 5 
    77 0005 90   nop 
    78 0006 90   nop 
    79 0007 90   nop 
    80 0008 90   nop 
    81 0009 90   nop 
    82      
    84    LM3: 
    85    /NO_APP 
    86 000a 5D   popl %ebp 
    87 000b C3   ret