Utilisation de BX dans le code Thumb pour appeler une fonction Thumb ou pour passer à une instruction Thumb dans une autre fonction

StackOverflow https://stackoverflow.com/questions/9368360

  •  28-10-2019
  •  | 
  •  

Question

J'essaie d'acquérir des compétences utiles en modding de firmware (pour lesquelles je n'ai pas de code source) Ces questions concernent l'utilisation de BX à partir du code de pouce pour sauter ou appeler un autre code de pouce existant.

  1. Comment utiliser BX pour sauter au code THUMB du micrologiciel existant, à partir de mon code THUMB.
  2. Comment utiliser BX pour APPELER une fonction THUMB existante (doit d'abord définir LR), à partir de mon code THUMB.

D'après ce que je comprends, le processeur regarde le bit lsb (bit 0) et je dois m'assurer qu'il est réglé sur 1 afin de garder l'état du processeur sur "état du pouce". Donc je suppose que je dois AJOUTER 1, pour mettre le bit lsb à 1.

Alors ... disons que je veux juste sauter à 0x24000 (au milieu d'un code THUMB existant)

LDR R6, =0x24000
ADD R6, #1       @ (set lsb to 1)
BX R6

Je pense que c'est correct?

Dites maintenant que je veux APPELER une fonction de pouce existante, en utilisant BX, et que je veux qu'elle me revienne, donc je dois définir LR là où je veux qu'elle revienne.

Disons que la fonction que je veux appeler est à 0x24000 C'était m'a suggéré d'utiliser:

ldr r2, =0x24000
mov lr, pc
bx r2

Voici ce que je ne comprends pas:

  1. l'adresse dans R2 n'a pas de bit lsb défini ... donc ne générera pas le mode de basculement du code-code au mode ARM ??

  2. Le LR .. Le PC a l'adresse de (début de l'instruction en cours, + 4), m'a-t-on dit. Dans Thumb et Arm, toute adresse d'instruction doit être alignée (16 bits ou 32 bits), donc le bit LSB ne sera pas défini sur 1. Seuls les nombres impairs ont le bit lsb mis à 1.

Donc, dans le code ci-dessus, je règle LR sur (PC), une adresse qui n'a PAS non plus le bit 1 de lsb. Donc, quand la fonction que j'ai appelée arrive à son épilogue, et que bx r2, ... uhmmm .. comment cela peut-il fonctionner pour revenir à mon code THUMB? Je dois manquer quelque chose ...

Normalement, BL est utilisé pour appeler des fonctions. Le manuel dit que l'instruction BL définit le LR sur la ligne de code suivante ... Cela signifie-t-il qu'une instruction BX LR THUMB (normalement utilisée) définit automatiquement le LR sur BL?

Était-ce utile?

La solution

Wow, merci de m'avoir appelé sur celui-ci. Je sais que j'ai essayé le code qemu dans http://github.com/dwelch67/yagbat et j'ai pensé XPUT32 qui appelle PUT32 de la manière que vous décrivez, et cela a fonctionné. Mais cela ne semble PAS fonctionner. J'ai créé un certain nombre d'expériences et je suis assez surpris, ce n'est pas ce à quoi je m'attendais. Maintenant, je vois pourquoi l'éditeur de liens gnu fait ce qu'il fait. Désolé c'est une longue réponse mais je pense que c'est très précieux. C'est un sujet déroutant, je sais que je me trompe depuis des années en pensant que le PC traîne un peu le mode, mais ce n'est pas le cas.

Avant de commencer les expériences ci-dessous, si vous comptez faire ceci:

LDR R6, =0x24000
ADD R6, #1       @ (set lsb to 1)
BX R6

parce que vous savez que 0x24000 est un code de pouce, faites-le à la place:

LDR R6, =0x24001
BX R6

Et oui, c'est ainsi que vous vous connectez au code du pouce depuis le bras ou le pouce si vous savez que cette adresse codée en dur 0x24000 est une instruction de pouce que vous envoyez avec un registre contenant l'adresse plus un.

si vous ne connaissez pas l'adresse mais connaissez le nom de l'adresse

ldr r6,=something
bx r6

La bonne chose à ce sujet est que quelque chose peut être une adresse de bras ou de pouce et le code ci-dessus fonctionne. Eh bien, cela fonctionne si l'éditeur de liens sait correctement quel type d'étiquette est le bras ou le pouce, si cela est foiré, cela ne fonctionnera pas comme vous pouvez le voir ici

.thumb
ping:
    ldr r0,=pong
    bx r0
.code 32
pong:
    ldr r0,=ping
    bx r0


d6008148 <ping>:
d6008148:   4803        ldr r0, [pc, #12]   ; (d6008158 <pong+0xc>)
d600814a:   4700        bx  r0

d600814c <pong>:
d600814c:   e59f0008    ldr r0, [pc, #8]    ; d600815c <pong+0x10>
d6008150:   e12fff10    bx  r0

d6008158:   d600814c    strle   r8, [r0], -ip, asr #2
d600815c:   d6008148    strle   r8, [r0], -r8, asr #2

cela n'a pas fonctionné pong voulait extraire une adresse de pouce de 0xD600815C mais a obtenu une adresse de bras.

ce sont tous des trucs d'assembleur gnu btw, pour d'autres outils, vous devrez peut-être faire autre chose. Pour le gaz, vous devez mettre .thumb_func avant une étiquette que vous voulez déclarer comme une étiquette de pouce (le terme func impliquant une fonction est trompeur, ne vous inquiétez pas de ce que .thumb_func signifie qu'il s'agit simplement d'un jeu assembleur / éditeur de liens).

.thumb
.thumb_func
ping:
    ldr r0,=pong
    bx r0
.code 32
pong:
    ldr r0,=ping
    bx r0

et maintenant nous obtenons ce que nous voulions

d6008148 <ping>:
d6008148:   4803        ldr r0, [pc, #12]   ; (d6008158 <pong+0xc>)
d600814a:   4700        bx  r0

d600814c <pong>:
d600814c:   e59f0008    ldr r0, [pc, #8]    ; d600815c <pong+0x10>
d6008150:   e12fff10    bx  r0

d6008158:   d600814c    strle   r8, [r0], -ip, asr #2
d600815c:   d6008149    strle   r8, [r0], -r9, asr #2

0xD600815C a ce lsbit défini pour que vous n'ayez à faire aucun travail. Le compilateur s'occupe de tout cela lorsque vous faites des appels à des fonctions C par exemple. Pour l'assembleur, vous devez utiliser cette .thumb_func (ou une autre directive s'il y en a une) pour faire savoir à gas qu'il s'agit d'une étiquette de pouce et définir le lsbit pour vous.

L'expérience ci-dessous a donc été réalisée sur un mpcore qui est un ARM11 mais j'ai aussi essayé les fonctions testthumb 1 à 4 sur un ARM7TDMI et qemu avec les mêmes résultats.

.globl testarm
testarm:
    mov r0,pc
    bx lr

armbounce:
    mov r0,lr
    bx lr

.thumb
.thumb_func
.globl testthumb1
testthumb1:
    mov r0,pc
    bx lr
    nop
    nop
    nop
bounce:
    bx lr
.thumb_func
.globl testthumb2
testthumb2:
    mov r2,lr
    mov r0,pc
    bl bounce
    bx r2
    nop
    nop
    nop
.thumb_func
.globl testthumb3
testthumb3:
    mov r2,lr
    mov lr,pc
    mov r0,lr
    bx r2
    nop
    nop
    nop
.thumb_func
.globl testthumb4
testthumb4:
    push {lr}
    ldr r2,=armbounce
    mov r1,pc  ;@ -4
    add r1,#5  ;@ -2
    mov lr,r1  ;@ +0
    bx r2      ;@ +2
    pop {r2}   ;@ +4
    bx r2
.thumb_func
.globl testthumb5
testthumb5:
    push {lr}
    ldr r2,=armbounce
    mov lr,pc
    bx r2
    pop {r2}
    bx r2
.thumb_func
.globl testthumb6
testthumb6:
    push {lr}
    bl testthumb6a
.thumb_func
testthumb6a:
    mov r0,lr
    pop {r2}
    bx r2

.thumb_func
.globl testthumb7
testthumb7:
    push {lr}
    bl armbounce_thumb
    pop {r2}
    bx r2

.thumb_func
.globl testthumb8
testthumb8:
    push {lr}
    bl armbounce_thumb_two
    pop {r2}
    bx r2

.align 4
armbounce_thumb:
    ldr r1,[pc]
    bx r1
.word armbounce

nop
.align 4
armbounce_thumb_two:
    bx pc
    nop
.code 32
    b armbounce

qui devient

d60080b4 <testarm>:
d60080b4:   e1a0000f    mov r0, pc
d60080b8:   e12fff1e    bx  lr

d60080bc <armbounce>:
d60080bc:   e1a0000e    mov r0, lr
d60080c0:   e12fff1e    bx  lr

d60080c4 <testthumb1>:
d60080c4:   4678        mov r0, pc
d60080c6:   4770        bx  lr
d60080c8:   46c0        nop         ; (mov r8, r8)
d60080ca:   46c0        nop         ; (mov r8, r8)
d60080cc:   46c0        nop         ; (mov r8, r8)

d60080ce <bounce>:
d60080ce:   4770        bx  lr

d60080d0 <testthumb2>:
d60080d0:   4672        mov r2, lr
d60080d2:   4678        mov r0, pc
d60080d4:   f7ff fffb   bl  d60080ce <bounce>
d60080d8:   4710        bx  r2
d60080da:   46c0        nop         ; (mov r8, r8)
d60080dc:   46c0        nop         ; (mov r8, r8)
d60080de:   46c0        nop         ; (mov r8, r8)

d60080e0 <testthumb3>:
d60080e0:   4672        mov r2, lr
d60080e2:   46fe        mov lr, pc
d60080e4:   4670        mov r0, lr
d60080e6:   4710        bx  r2
d60080e8:   46c0        nop         ; (mov r8, r8)
d60080ea:   46c0        nop         ; (mov r8, r8)
d60080ec:   46c0        nop         ; (mov r8, r8)

d60080ee <testthumb4>:
d60080ee:   b500        push    {lr}
d60080f0:   4a15        ldr r2, [pc, #84]   ; (d6008148 <armbounce_thumb_two+0x8>)
d60080f2:   4679        mov r1, pc
d60080f4:   3105        adds    r1, #5
d60080f6:   468e        mov lr, r1
d60080f8:   4710        bx  r2
d60080fa:   bc04        pop {r2}
d60080fc:   4710        bx  r2

d60080fe <testthumb5>:
d60080fe:   b500        push    {lr}
d6008100:   4a11        ldr r2, [pc, #68]   ; (d6008148 <armbounce_thumb_two+0x8>)
d6008102:   46fe        mov lr, pc
d6008104:   4710        bx  r2
d6008106:   bc04        pop {r2}
d6008108:   4710        bx  r2

d600810a <testthumb6>:
d600810a:   b500        push    {lr}
d600810c:   f000 f800   bl  d6008110 <testthumb6a>

d6008110 <testthumb6a>:
d6008110:   4670        mov r0, lr
d6008112:   bc04        pop {r2}
d6008114:   4710        bx  r2

d6008116 <testthumb7>:
d6008116:   b500        push    {lr}
d6008118:   f000 f80a   bl  d6008130 <armbounce_thumb>
d600811c:   bc04        pop {r2}
d600811e:   4710        bx  r2

d6008120 <testthumb8>:
d6008120:   b500        push    {lr}
d6008122:   f000 f80d   bl  d6008140 <armbounce_thumb_two>
d6008126:   bc04        pop {r2}
d6008128:   4710        bx  r2
d600812a:   46c0        nop         ; (mov r8, r8)
d600812c:   46c0        nop         ; (mov r8, r8)
d600812e:   46c0        nop         ; (mov r8, r8)

d6008130 <armbounce_thumb>:
d6008130:   4900        ldr r1, [pc, #0]    ; (d6008134 <armbounce_thumb+0x4>)
d6008132:   4708        bx  r1
d6008134:   d60080bc            ; <UNDEFINED> instruction: 0xd60080bc
d6008138:   46c0        nop         ; (mov r8, r8)
d600813a:   46c0        nop         ; (mov r8, r8)
d600813c:   46c0        nop         ; (mov r8, r8)
d600813e:   46c0        nop         ; (mov r8, r8)

d6008140 <armbounce_thumb_two>:
d6008140:   4778        bx  pc
d6008142:   46c0        nop         ; (mov r8, r8)
d6008144:   eaffffdc    b   d60080bc <armbounce>
d6008148:   d60080bc            ; <UNDEFINED> instruction: 0xd60080bc
d600814c:   e1a00000    nop         ; (mov r0, r0)

Et les résultats de l'appel et de l'impression de toutes ces fonctions

D60080BC testarm
D60080C8 testthumb1
D60080D6 testthumb2
D60080E6 testthumb3
D60080FB testthumb4
         testthumb5 crashes
D6008111 testthumb6
D600811D testthumb7
D6008127 testthumb8

Alors, qu'est-ce que tout cela fait et qu'est-ce que cela a à voir avec votre question. Cela a à voir avec les appels en mode mixte depuis le mode pouce (et aussi depuis arm qui est plus simple)

Je programme le mode ARM et le mode pouce à ce niveau depuis de nombreuses années, et je me suis toujours trompé depuis le début. Je pensais que le compteur de programme maintenait toujours le mode dans ce lsbit, je sais comme vous le savez que vous voulez qu'il soit défini ou non lorsque vous effectuez une instruction bx.

Program counter Register 15 is the Program Counter (PC). It can be used in most
      instructions as a pointer to the instruction which is two instructions after 
      the instruction being executed...

Voyons donc ce que cela signifie vraiment, est-ce que cela signifie en mode armement deux instructions, 8 octets d'avance? et en mode pouce, deux instructions en avance, ou 4 octets en avance?

Donc, testarm vérifie que le compteur du programme est en avance de 8 octets. qui est également deux instructions.

testthumb1 vérifie que le programme a 4 octets d'avance, ce qui dans ce cas est également deux instructions.

testthumb2

d60080d2:   4678        mov r0, pc
d60080d4:   f7ff fffb   bl  d60080ce <bounce>
d60080d8:   4710        bx  r2

si le compteur de programme était composé de deux têtes "d'instructions", nous aurions 0xD60080D8 mais nous obtiendrions à la place 0xD60080D6 qui a quatre octets d'avance, ce qui est beaucoup plus logique. Mode d'armement 8 octets d'avance, mode pouce 4 octets ah

ead, pas de problème avec les instructions de décodage (ou les données) qui sont en avance sur le code en cours d'exécution, ajoutez simplement 4 ou 8.

testthumb3 était un espoir que mov lr, pc était spécial, ce n'est pas le cas.

si vous ne voyez pas encore le modèle, le lsbit du compteur de programme n'est PAS défini, et je suppose que cela a du sens pour les tables de branches par exemple. Donc mov lr, pc en mode pouce ne configure PAS le registre de lien droit pour un retour.

testthumb4 d'une manière très pénible prend le compteur de programme partout où ce code arrive se termine et basé sur des instructions soigneusement placées, calcule l'adresse de retour, si vous modifiez cette séquence d'instructions entre mov r1, pc et bx r2, vous devez réajuster l'add. Maintenant, pourquoi ne pourrions-nous pas faire quelque chose comme ceci:

add r1,pc,#1
bx r2

avec les instructions du pouce que vous ne pouvez pas, avec thumb2 vous pourriez probablement le faire. Et il semble y avoir des processeurs (armv7) qui prennent en charge à la fois les instructions de bras et le pouce / pouce2, donc vous pourriez être dans une situation où vous voudriez le faire, mais vous n'ajouteriez pas # 1 parce qu'une instruction d'ajout de thumb2, s'il y en a une qui autorise les registres supérieurs et a trois opérandes serait une instruction de 4 octets pouce 2. (vous devrez ajouter # 3).

Donc testthumb5 vient directement du code que je vous ai montré qui a conduit à une partie de cette question, et il plante. ce n'est pas comme ça que cela fonctionne, désolé j'ai induit les gens en erreur, je vais essayer de revenir en arrière et de corriger les questions SO avec lesquelles j'ai utilisé cela.

testthumb6 est une expérience pour s'assurer que nous ne sommes pas tous fous. Tout va bien, le registre de lien obtient en effet le lsbit défini de sorte que lorsque vous bx lr plus tard, il connaisse le mode à partir de ce bit.

testthumb7, ceci est dérivé du trampoline latéral ARM que vous voyez le linker faire en passant du mode bras au mode pouce, dans ce cas, bien que je passe du mode pouce au mode bras. pourquoi l'éditeur de liens ne peut-il pas le faire de cette façon? car en mode pouce au moins, vous devez utiliser un registre bas et à ce stade du jeu, une fois le code compilé, l'éditeur de liens n'a aucun moyen de savoir quel registre il peut détruire. En mode arm, bien que le registre ip, je ne sais pas ce que c'est peut-être r12, peut être détruit, je suppose qu'il est réservé au compilateur. Je sais dans ce cas que r1 peut être détruit et utilisé, et cela fonctionne comme vous le souhaitez. le code armbounce est appelé qui saisit le registre de lien si où retourner, qui est une instruction de pouce (lsbit set) après le bl armbounce_thumb dans la fonction testthumb7, exactement là où nous voulions qu'il soit.

testthumb8 c'est ainsi que l'éditeur de liens gnu le fait lorsqu'il a besoin de passer du mode pouce au mode armement. l'instruction bl est définie pour aller à un trampoline. puis ils font quelque chose de très très délicat et de fou.

d6008140 <armbounce_thumb_two>:
d6008140:   4778        bx  pc
d6008142:   46c0        nop         ; (mov r8, r8)
d6008144:   eaffffdc    b   d60080bc <armbounce>

Un pc bx. Nous savons par les expériences ci-dessus que le pc a quatre octets d'avance, nous savons également que le lsbit n'est PAS SET. Donc, ce que cela signifie est une branche vers le CODE ARM qui est quatre octets après celui-ci. Le nop est un espaceur de deux octets, alors nous devons générer une instruction ARM quatre octets à l'avance ET ALIGNÉ SUR UNE LIMITE DE QUATRE octets, et nous en faisons une branche inconditionnelle à n'importe quel endroit où nous allions, cela pourrait être un quelque chose ou un pc ldr ,= quelque chose en fonction de la distance à parcourir. Très délicat.

L'original bl arm_bounce_thumb_two configure le registre de liens pour revenir à l'instruction après ce bl. Le trampoline ne modifie pas le registre des liens, il effectue simplement des branches.

si vous voulez accéder au mode pouce depuis arm, faites ce que fait l'éditeur de liens:

...
bl myfun_from_arm
...


myfun_from_arm:
  ldr ip,[pc]
  bx ip
.word myfun

qui ressemble à ceci quand ils le font (récupéré à partir d'un binaire différent non pas à 0xD6008xxx mais à 0x0001xxxx).

   101f8:   eb00003a    bl  102e8 <__testthumb1_from_arm>


000102e8 <__testthumb1_from_arm>:
   102e8:   e59fc000    ldr ip, [pc]    ; 102f0 <__testthumb1_from_arm+0x8>
   102ec:   e12fff1c    bx  ip
   102f0:   00010147    andeq   r0, r1, r7, asr #2

donc quel que soit ce registre IP (r12?), cela ne les dérange pas de le détruire et je suppose que vous êtes invités à le supprimer vous-même.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top