2012-02-20 6 views
13

Sto cercando di apprendere le competenze utili nel modding del firmware (per il quale non ho codice sorgente) Queste domande riguardano l'uso di BX dal codice del pollice per saltare o chiamare un altro codice di pollice esistente.Braccio/Pollice: utilizzando BX in Thumb code, per chiamare una funzione Thumb, o per passare a un'istruzione Thumb in un'altra funzione

  1. Come utilizzare BX su JUMP per il codice THUMB del firmware esistente, dal mio codice THUMB.
  2. Come uso BX per CHIAMARE una funzione THUMB esistente (deve prima impostare LR), dal mio codice THUMB.

mia comprensione è che cpu guarda LSB bit (bit 0) e devo per assicurarsi che questo è impostato su 1 al fine di mantenere lo stato cpu a "pollice Stato". Quindi credo che devo aggiungere 1, per impostare LSB bit a 1.

Quindi ... dire che voglio saltare solo a 0x24000 (nel bel mezzo di un codice esistente POLLICE)

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

Penso che sia corretto?

Ora dire che voglio CHIAMARE una funzione per il pollice esistente, usando BX, e voglio che ritorni da me, quindi ho bisogno di impostare LR su dove voglio che ritorni.

Diciamo che la funzione che voglio chiamare è a 0x24000 Era suggested to me da usare:

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

Arriva quello che non capisco:

  1. l'indirizzo in R2 doesn 't avere bit lsb impostato ... quindi non sarà bx r2 passare alla modalità ARM ??

  2. Il LR .. Il PC ha l'indirizzo di (inizio dell'istruzione corrente, + 4), mi è stato detto. In entrambi Pollice e del braccio, qualsiasi indirizzo di istruzione deve essere allineata (16 bit o 32 bit), in modo da non avere la LSB bit impostato a 1. I numeri dispari sono solo LSB bit impostato a 1.

Quindi nel codice sopra, sto impostando LR su (PC), un indirizzo che NON ha bit 1 lsb impostato. Quindi, quando la funzione che ho chiamato arriva al suo epilogo, e fa BX LR, ... uhmmm .. come può funzionare per tornare al mio codice THUMB? Mi manca qualcosa ...

Normalmente BL è utilizzato per chiamare le funzioni. Il manuale dice che l'istruzione BL imposta l'LR sulla riga successiva del codice ... Questo significa che un'istruzione (normalmente utilizzata) BL THUMB imposta automaticamente la LR su return addr + 1?

risposta

19

Wow, grazie per avermi chiamato su questo. So di aver provato il codice qemu in http://github.com/dwelch67/yagbat e ho pensato a XPUT32 che chiama PUT32 nel modo in cui descrivi, e ha funzionato. Ma NON sembra funzionare. Ho creato una serie di esperimenti e sono abbastanza sorpreso, questo non è quello che mi aspettavo. Ora capisco perché il linker di Gnu fa quello che fa. Spiacente, questa è una risposta lunga, ma penso che sia molto preziosa. È un argomento confuso, so di aver sbagliato per anni pensando che il pc trascini il bit della modalità, ma non lo è.

Prima di cominciare con gli esperimenti di seguito, se avete intenzione di fare questo:

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

perché vi capita di sapere che 0x24000 è il codice del pollice, basta fare questo, invece:

LDR R6, =0x24001 
BX R6 

E sì, è così che si passa al codice del pollice da braccio o pollice se si sa che quell'indirizzo codificato 0x24000 è un'istruzione di pollice che si bx con un registro contenente l'indirizzo più uno.

se non conoscere l'indirizzo, ma si conosce il nome dell'indirizzo

ldr r6,=something 
bx r6 

La cosa bella di questo è che qualcosa può essere un indirizzo braccio o il pollice e il codice di cui sopra semplicemente funziona. Beh, funziona se il linker sa correttamente che tipo di etichetta che è il braccio o il pollice, se quello ottiene incasinato non funzionerà proprio come si può vedere qui

.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 

che ha funzionato pong voluto tirare un indirizzo di pollice da 0xD600815C ma ha ottenuto un indirizzo di braccio.

questo è tutto roba di assembler gnu btw, per altri strumenti potresti dover fare qualcos'altro. Per il gas è necessario inserire .thumb_func prima di un'etichetta che si desidera dichiarare come etichetta del pollice (il termine func che implica la funzione è fuorviante, non preoccuparsi di cosa .thumb_func significa che è solo un gioco assemblatore/linker).

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

e ora otteniamo quello che volevamo

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 ha stabilito che LSBit in modo che non dovete fare qualsiasi lavoro. Il compilatore si occupa di tutto ciò quando si eseguono chiamate a funzioni C, ad esempio. Per l'assemblatore, però, devi usare quello .thumb_func (o qualche altra direttiva se ce n'è uno) per far capire che questa è un'etichetta con il pollice e impostare l'lsbit per te.

Quindi l'esperimento di seguito è stato fatto su un mpcore che è un ARM11 ma ho anche provato le funzioni testthumb da 1 a 4 su un ARM7TDMI e qemu con gli stessi risultati.

.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 

che diventa

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) 

E i risultati della chiamata e la stampa di tutte queste funzioni

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

Così che cosa è tutto questo fare e che cosa hanno a che fare con il vostro domanda. Questo ha a che fare con la chiamata in modalità mista dalla modalità pollice (e anche da braccio che è più semplice)

Ho programmato ARM e la modalità pollice a questo livello per molti anni, e in qualche modo ho sbagliato tutto questo. Pensavo che il contatore del programma mantenesse sempre la modalità in quel lsbit, lo so come tu sai che vuoi averlo impostato o non impostato quando fai un'istruzione bx.

Molto presto nella descrizione della CPU del processore ARM nel Manuale di riferimento architettonico ARM (se si sta scrivendo assemblatore si dovrebbe già avere questo, se non forse la maggior parte delle vostre domande sarà risposta).

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... 

Quindi, consente di controllare e vedere che cosa significa realmente, significa che in modalità braccio due istruzioni, 8 byte avanti? e in modalità pollice, due istruzioni in anticipo o 4 byte avanti?

Così testarm verifica che il contatore del programma sia avanti di 8 byte. che è anche due istruzioni.

testthumb1 verifica che il programma sia 4 byte avanti, che in questo caso sono anche due istruzioni.

testthumb2

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

se il contatore di programma è stato due testa "istruzioni" otterremmo 0xD60080D8 ma noi invece otteniamo 0xD60080D6 che è quattro byte avanti, e che ci fa molto più senso. Arm mode 8 byte avanti, thumb mode 4 byte avanti, niente scherzi con le istruzioni di decodifica (o dati) che precedono il codice in esecuzione, basta aggiungere 4 o 8.

testthumb3 era una speranza che mov lr, pc era speciale, non lo è.

se non si vede ancora il modello, l'lsbit del contatore del programma NON è impostato, e immagino che questo abbia senso per le tabelle di diramazione, ad esempio. Così mov lr, la modalità pc in thumb NON imposta il registro dei collegamenti per il ritorno.

testthumb4 in modo molto doloroso esegue il contatore del programma ovunque questo codice capiti a e si basa su istruzioni attentamente posizionate, calcola l'indirizzo di ritorno, se si modifica quella sequenza di istruzioni tra mov r1, pc e bx r2 si è necessario risintonizzare l'add. Ora, perché non potremmo fare qualcosa del genere:

add r1,pc,#1 
bx r2 

con le istruzioni del pollice non puoi, con thumb2 probabilmente potresti. E sembrano esserci alcuni processori (armv7) che supportano sia le istruzioni del braccio che il pollice/pollice2 in modo che tu possa trovarti in una situazione in cui vorresti farlo, ma non aggiungerei # 1 perché un'istruzione thumb2 add, se ce n'è una che consente registri superiori e ha tre operandi sarebbe un'istruzione pollice 2 a 4 byte. (Dovresti aggiungere # 3).

Quindi testthumb5 è direttamente dal codice che ho mostrato che porta a una parte di questa domanda e si blocca. questo non è il modo in cui funziona, mi dispiace di indurre in errore la gente, cercherò di tornare indietro e correggere le domande SO con cui l'ho usato.

testthumb6 è un esperimento per assicurarsi che non siamo tutti pazzi. Tutto bene, il registro dei collegamenti in effetti ottiene il set lsbit in modo tale che quando lo bx lr più tardi conosce la modalità da quel bit.

testthumb7, questo è derivato dal trampolino sul lato ARM che si vede fare dal linker quando si passa dalla modalità braccio alla modalità pollice, in questo caso però sto passando dalla modalità pollice alla modalità braccio. perché non può farlo il linker in questo modo? perché in modalità pollice almeno devi usare un registro basso ea questo punto del gioco, dopo che il codice è stato compilato, il linker non ha modo di sapere quale registro può cancellare. In modalità arm sebbene il registro ip, non sicuro di quello che è forse r12, può essere spazzato via, suppongo che sia riservato per il compilatore da usare. So che in questo caso r1 può essere cestinato e utilizzato, e funziona come desiderato. Viene chiamato il codice armbounce che cattura il registro dei collegamenti se dove tornare, che è un'istruzione thumb (set lsbit) dopo il bl armbounce_thumb nella funzione testthumb7, esattamente dove volevamo che fosse.

testthumb8 questo è il modo in cui il linker di Gnu lo fa quando deve passare dalla modalità pollice alla modalità braccio. l'istruzione bl è impostata per andare su un trampolino.poi fanno qualcosa di molto difficile, e dall'aspetto folle.

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

A bx pc. Sappiamo dagli esperimenti precedenti che il pc è a quattro byte di distanza, sappiamo anche che l'lsbit NON è SET. Quindi quello che sta dicendo è una diramazione verso il CODICE ARM che è quattro byte dopo questo. Il nop è un distanziatore di due byte, quindi dobbiamo generare un'istruzione ARM di quattro byte avanti E ALLINEATO SU UN QUARTO PERNO DI CONTESTO, e facciamo di questo un ramo incondizionato a qualunque posto stessimo andando, questo potrebbe essere un qualcosa o un pc ldr , = qualcosa dipende da quanto lontano devi andare. Molto difficile.

L'originale bl arm_bounce_thumb_two imposta il registro di collegamento per tornare all'istruzione dopo quella bl. Il trampolino non modifica il registro di collegamento ma esegue semplicemente rami.

se si vuole arrivare a modalità pollice dal braccio poi fare quello che il linker fa:

... 
bl myfun_from_arm 
... 


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

che assomiglia a questo quando lo fanno (afferrato da un binario diverso non a 0xD6008xxx ma a 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 

così qualunque questo registro IP (r12?) Essi non mente cestinare e presumo siete i benvenuti nel cestino da soli.

+0

ama assolutamente la tua risposta e lo sforzo. È anche quello che ho notato in questo firmware dissasemblato su cui sto lavorando. E spiega i crash nei miei test (non ho usato l'emulatore .. ho appena installato il firmware modificato e caricato il chip flash ... lol) Il firmware ha il codice della modalità ARM, nella parte inferiore, e il codice del pollice solo per la parte dell'app principale . Ma sì, è così che fa Thumb to Arm e Arm to Thumb. Questo firmware è stato compilato con ADS v 1.2 btw. – vmanta

+0

Anche per aggiungere, per quanto posso dire e per quanto ho letto, in modalità thumb (pollice 1 .. se c'è una cosa del genere) puoi cestino R0 - R3, ma devi Push/Pop R4 - R7 . Il firmware che sto guardando lo fa e ho notato che anche il codice di gnu gcc (arm-elf-gcc) fa lo stesso. Beh ... è stato GRANDE hai fatto tutti questi test, perché ho fatto lo stesso sul mio dispositivo con i miei firmware, ma a volte mi sento così confuso (specialmente quando si verificano degli arresti dovuti ad altri errori) che ricorro a BL (dolore nel sedere .... con il calcolo delle compensazioni, come ho spiegato) – vmanta

+0

Sto per scrivere un programma da utilizzare come preprocessore per "come" e impostare i miei offset per me ... forse usare qualche notazione speciale. .. come BL ADR: 0x24000, e il mio prog per calcolare questo offset per me ... ahhh .. sarebbe meraviglioso ... perché nessuno ha pensato a quel lol ... forse c'è un modo però, proprio non lo so ancora .. e se c'è un modo suppongo che sarebbe una sorta di direttiva nel linker..don so ma ho letto e letto .. speso troppo tempo. Ehi, grazie mille amico ... e felice che tu abbia trovato la VERITÀ ... hehe – vmanta

Problemi correlati