2010-04-14 5 views
12

J'essaie de faire un saut relatif dans l'assemblage x86, mais je n'arrive pas à le faire fonctionner. Il semble que pour une raison quelconque, mon saut ne cesse de se réécrire comme un saut absolu ou quelque chose comme ça.GCC/X86, Problèmes avec les sauts relatifs

Un exemple de programme simple pour ce que je suis en train de faire est la suivante:

.global main 

main: 
    jmp 0x4 
    ret 

Depuis l'instruction jmp est de 4 octets et un saut relatif est décalé par rapport à l'adresse du saut + 1, cette devrait être un no-op de fantaisie. Cependant, compiler et exécuter ce code provoquera une erreur de segmentation. Le vrai casse-tête est de le compiler au niveau de l'objet puis de désassembler le fichier objet. Il semble que l'assembleur fasse un saut relatif, mais après que le fichier soit compilé, le lieur le change en un autre. type de saut.

Par exemple, si le code ci-dessus était dans un fichier appelé 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 

Cela ressemble à l'assembleur fait correctement un saut relatif, bien qu'il soit suspect que l'instruction jmp est rempli de 0s.

J'ai ensuite utilisé gcc pour relier ensuite démontées et obtenu ceci:

$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 

Ce me ressemble l'éditeur de liens réécrit l'instruction jmp ou substitué 5 dans une autre adresse.

Donc, ma question se résume à, qu'est-ce que je fais mal? Est-ce que je ne spécifie pas le décalage correctement?

Suis-je en train de mal comprendre comment fonctionnent les sauts relatifs? Est-ce que gcc essaie de m'assurer que je ne fais pas de choses dangereuses dans mon code?

Répondre

15

En fait, l'assembleur pensait que vous essayiez de faire un saut absolu . Cependant, l'opcode jmp est, au niveau du métal, relatif. Par conséquent, l'assembleur ne pouvait pas savoir quoi écrire après l'octet 0xe9, car l'assembleur ne sait pas à quelle adresse votre code aboutira.

L'assembleur ne le sait pas, mais l'éditeur de liens le sait. Donc, l'assembleur a écrit quelque part dans les en-têtes asmtest.o une requête pour l'éditeur de liens, comme suit: "Quand vous savez à quelle adresse le code sera chargé, ajustez ces octets juste après le 0xe9 afin qu'ils soient appropriés pour un saut de ce point (avec un adressage relatif) à l'adresse absolue '4' ". L'éditeur de liens l'a fait. Il a vu que le 0xe9 était à l'adresse 0x08048394, et l'opcode suivant à 0x08048399, et il calculé: pour aller de 0x08048399 à 0x00000004, on doit soustraire 0x08048395, ce qui équivaut à ajouter (sur les machines 32 bits) 0xf7fb7c6b. D'où votre séquence "6b 7c fb f7" dans le binaire résultant.

Vous pouvez encoder un saut relatif « manuellement » comme ceci:

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

Ainsi, l'assembleur ne remarquera pas que votre 0xE9 est vraiment un jmp, et il ne sera pas essayer de vous montrer plus malin. Dans le binaire, vous obtiendrez la séquence 'e9 04 00 00 00' que vous souhaitez, et aucune interaction de linker.

Notez que le code ci-dessus peut se bloquer, parce que le décalage relatif est comptée à partir de l'adresse immédiatement après l'offset (à savoir l'adresse du opcode, ici ret). Cela va sauter dans le no-man's-land 4 octets après le ret et un segfault ou quelque chose d'étrange semble probable.

+0

Saut à une étiquette nommée comment les sauts sont normalement effectués dans l'assemblage. C'est très basique. Je suppose que l'OP le sait déjà et souhaite vraiment coder à la main un saut relatif pour une raison quelconque. –

+0

Je suppose que ce commentaire était en réponse à la mienne. Je l'ai supprimé car j'ai déplacé mon commentaire à une réponse à la place. –

+0

Merci pour l'aide, cela fonctionne exactement comme j'en ai besoin aussi. Merci aussi de m'avoir signalé le problème en sautant trop loin, je ne savais pas exactement d'où l'instruction était calculée. –

5

Je pense que l'assembleur prend une adresse absolue et calcule le décalage d'adresse pour vous. Les zéros dans le premier cas sont probablement là parce que cela fait partie d'une table de corrections et le décalage est calculé dans la phase de liaison.

Mes compétences en langage assembleur sont un peu rouillé, mais je pense que vous pourriez faire:

.global main 

main: 
    jmp getouttahere 
getouttahere: 
    ret 

Ou si vous voulez vraiment à regarder par rapport:

.global main 

main: 
    jmp .+5 
    ret 

S'il vous plaît soyez doux si J'ai tort; Ça fait longtemps.

+0

Cela a du sens d'essayer de calculer une adresse absolue . Malheureusement, pour diverses raisons, je ne peux pas utiliser une étiquette pour faire le saut, et la syntaxe $ + 5 n'est pas valide. Merci pour l'aide. –

+0

@Ian Kelly: La syntaxe de l'assembleur peut varier. Je pense que ". + 5" pourrait fonctionner. –

14

Si vous utilisez l'assembleur de GAZ de GCC qui utilise AT & T syntaxe par défaut, la syntaxe pour l'adressage relatif uses the dot ('.') to represent the current address being assembled (un peu comme le pseudo-symbole $ est utilisé dans la syntaxe de montage Intel/MASM). Vous devriez être en mesure d'obtenir votre saut par rapport à l'aide quelque chose comme:

jmp . + 5 

Par exemple, la fonction suivante:

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

    ); 
} 

obtient assemblé:

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 
Questions connexes