Utilizzo di BX nel codice del pollice per chiamare una funzione del pollice o per passare a un'istruzione del pollice in un'altra funzione

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

  •  28-10-2019
  •  | 
  •  

Domanda

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 altro codice di pollice esistente.

  1. Come posso usare BX per saltare sul codice pollice del firmware esistente, dal mio codice del pollice.
  2. Come posso usare BX per chiamare una funzione del pollice esistente (deve prima impostare LR), dal mio codice del pollice.

La mia comprensione è che la CPU guarda al bit LSB (bit 0) e devo assicurarmi che questo sia impostato su 1 Al fine di mantenere lo stato della CPU a "Stato del pollice". Quindi immagino di dover aggiungere 1, per impostare il bit LSB su 1.

Quindi ... dì che voglio solo saltare a 0x24000 (nel mezzo di un codice del pollice esistente)

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

Penso che sia corretto?

Ora dì che voglio chiamare una funzione di pollice esistente, usando BX, e voglio che ritorni da me, quindi devo impostare LR dove voglio che ritorni.

Diciamo che la funzione che voglio chiamare è allo 0x24000 era mi ha suggerito usare:

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

Ecco quello che non capisco:

  1. L'indirizzo in R2 non ha un bit LSB impostato ... quindi non lo farò bx r2 Modalità Switch in modalità ARM ??

  2. L'LR .. Il PC ha l'indirizzo di (inizio delle istruzioni attuali, + 4), mi è stato detto. Sia in pollice che in braccio, qualsiasi indirizzo di istruzione deve essere allineato (16 bit o 32 bit), quindi non avrà il bit LSB impostato su 1. Solo i numeri dispari hanno impostato Bit LSB su 1.

Quindi, nel codice sopra, sto impostando LR su (PC), un indirizzo che non ha un set di bit 1 LSB. Quindi quando la funzione che ho chiamato arriva al suo epilogo e lo fa BX LR, ... uhmmm .. Come può funzionare per tornare al mio codice pollice? Devo mancare qualcosa ...

Normalmente BL viene utilizzato per chiamare le funzioni. Il manuale dice che le istruzioni BL impostano la LR alla riga di codice successiva ... così significa che A (normalmente utilizzato) BL Istruzioni per il pollice, imposta la LR su return addr + 1 automaticamente?

È stato utile?

Soluzione

Wow, grazie per avermi chiamato su questo. So di aver provato il codice QEMU in http://github.com/dwelch67/yagbat E ha pensato Xput32 che chiama Put32 nel modo in cui descrivi e ha funzionato. Ma non sembra funzionare. Ho creato una serie di esperimenti e sono piuttosto sorpreso, questo non è quello che mi aspettavo. Ora vedo perché il linker GNU fa quello che fa. Mi dispiace che questa sia una lunga risposta, ma penso che sia molto preziosa. È un argomento confuso, so che ho sbagliato per anni pensando che il PC trascini la modalità bit, ma non lo fa.

Prima di iniziare con gli esperimenti di seguito, se hai intenzione di farlo:

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

Perché ti capita di sapere che 0x24000 è un codice del pollice, fallo invece:

LDR R6, =0x24001
BX R6

E sì, è così che ramifica il codice del pollice dal braccio o dal pollice se ti capita di sapere che quell'indirizzo hardcoded 0x24000 è un'istruzione del pollice che BX con un registro contenente l'indirizzo più uno.

Se non conosci l'indirizzo ma conosci il nome dell'indirizzo

ldr r6,=something
bx r6

La cosa bella è che qualcosa può essere un indirizzo a braccio o pollice e il codice sopra funziona. Bene, funziona se il linker sa correttamente quale tipo di etichetta è braccio o pollice, se questo viene incasinato, non funzionerà bene come puoi 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

Non ha funzionato Pong voleva tirare un indirizzo di pollice da 0xd600815c ma ha ottenuto un indirizzo a braccio.

Questa è tutta la roba dell'assemblatore GNU btw, per altri strumenti potresti dover fare qualcos'altro. Per il gas devi mettere .thumb_func prima di un'etichetta che si desidera dichiarare come etichetta del pollice (il termine funzione che implica func è fuorviante, non preoccuparti 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 quel set di LSBIT in modo da non dover fare alcun lavoro. Il compilatore si occupa di tutto questo quando si effettuano chiamate alle funzioni C, ad esempio. Per l'assemblatore, anche se devi usarlo .thumb_func (o qualche altra direttiva se ce n'è una) per convincere il gas a sapere che si tratta di un'etichetta del pollice e impostare l'LSBIT per te.

Quindi l'esperimento seguente è stato condotto su un MPCore che è un ARM11 ma ho anche provato le funzioni di 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 della stampa di tutte queste funzioni

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

Allora cosa sta facendo tutto questo e cosa c'entra con la tua domanda. Questo ha a che fare con la modalità mista che chiama dalla modalità pollice (e anche dal braccio che è più semplice)

Ho programmato la modalità ARM e Thumb a questo livello per molti anni e in qualche modo ho avuto questo male. Ho pensato che il contatore del programma avesse sempre tenuto la modalità in quella LSBIT, so come sai che vuoi impostare o non impostare quando fai un'istruzione BX.

Molto presto nella descrizione della CPU del processore ARM nel manuale di riferimento architettonico ARM (se stai scrivendo assemblatore dovresti già avere questo, se non forse la maggior parte delle tue domande verrà data 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 controlliamo e vediamo cosa significa veramente, ciò significa in modalità ARM due istruzioni, 8 byte in vista? E in modalità pollice, due istruzioni davanti o 4 byte in vista?

Quindi Testarm verifica che il contatore del programma sia in anticipo di 8 byte. che sono anche due istruzioni.

Testthumb1 verifica che il programma sia a 4 byte, 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 del programma fosse due "istruzioni", otterremmo 0xd60080d8 ma invece otteniamo 0xd60080d6 che è quattro byte in vantaggio e ciò ha molto più senso. Modalità ARM 8 byte in anticipo, modalità pollice 4 byte in anticipo, nessun disastro con istruzioni di decodifica (o dati) che sono in anticipo rispetto al codice eseguito, basta aggiungere 4 o 8.

Testthumb3 era una speranza che Mov LR, PC fosse speciale, non lo è.

Se non vedi ancora il modello, l'LSBIT del contatore del programma non è impostato e immagino che questo abbia senso per le tabelle delle filiali, ad esempio. Quindi mov LR, PC in modalità pollice non imposta il registro del collegamento a destra per un ritorno.

Testthumb4 in modo molto doloroso prende il contatore del programma ovunque questo codice finisca e basate su istruzioni accuratamente posizionate, calcola l'indirizzo di ritorno, se si modificano quella sequenza di istruzioni tra Mov R1, PC e BX R2 . Ora perché non potremmo fare qualcosa del genere:

add r1,pc,#1
bx r2

Con le istruzioni per il pollice non puoi, con il pollice2 probabilmente potresti. E sembrano esserci alcuni processori (ARMV7) che supportano sia le istruzioni del braccio che il pollice/pollice2, quindi potresti trovarti in una situazione in cui vorresti farlo, ma non aggiungeresti #1 perché un pollice aggiungi istruzioni, se ce n'è una Ciò consente ai registri superiori e ha tre operandi sarebbero un'istruzione 2 a 4 byte. (Dovresti aggiungere #3).

Quindi Testthumb5 è direttamente dal codice che ti ho mostrato che porta a una parte di questa domanda e si blocca. Non è così che funziona, scusa se fuormo gente che cercherò di tornare indietro e rattoppare le domande così che ho usato.

Testthumb6 è un esperimento per assicurarsi che non siamo tutti pazzi. Tutto va bene il registro di collegamento effettivamente impostato l'LSBIT in modo che quando si è in seguito conosce la modalità da quel bit.

Testthumb7, questo deriva dal trampolino laterale del braccio che si vede il linker quando si passa dalla modalità ARM alla modalità Thumb, in questo caso, anche se vado dalla modalità pollice alla modalità ARM. Perché il linker non può farlo in questo modo? Perché in modalità pollice almeno devi usare un registro basso e a questo punto del gioco, dopo che il codice è stato compilato, il linker non ha modo di sapere quale registro può cestino. In modalità ARM per quanto il registro IP, non sono sicuro di cosa sia forse R12, possa essere distrutto, immagino che sia riservato al compilatore da utilizzare. So in questo caso che R1 può essere spazzato via e usarlo, e questo funziona come desiderato. Il codice Armerbounce viene chiamato che afferra il registro dei collegamenti se dove tornare, che è un'istruzione per il pollice (set LSBIT) dopo la BL Armbuunce_thumb nella funzione Testthumb7, esattamente dove volevamo che fosse.

Testthumb8 Questo è il modo in cui il linker GNU lo fa quando deve ottenere dalla modalità pollice alla modalità ARM. L'istruzione BL è impostata per andare su un trampolino. Quindi fanno qualcosa di molto molto complicato e pazzo.

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

Un PC BX. Sappiamo dagli esperimenti sopra che il PC è a quattro byte, sappiamo anche che l'LSBIT non è impostato. Quindi ciò che sta dicendo è di ramo sul codice ARM che è quattro byte dopo questo. Il NOP è un distanziatore a due byte, quindi dobbiamo generare un'istruzione ARM quattro byte davanti e allinearci su un confine a quattro byte, e facciamo che un ramo incondizionato in qualunque posto stesse andando, questo potrebbe essere qualcosa o un PC LDR , = qualcosa a seconda di quanto è necessario andare. Molto complicato.

L'originale bl arm_bounce_thumb_two imposta il registro di collegamento per tornare all'istruzione dopo quel bl. Il trampolino non modifica il registro dei collegamenti che esegue semplicemente i rami.

Se vuoi arrivare alla modalità pollice da ARM, fai quello che fa il linker:

...
bl myfun_from_arm
...


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

Che sembra così 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

Quindi, qualunque sia questo registro IP (R12?) Non si preoccupano di distruggerlo e presumo che tu sia il benvenuto per distruggerlo da solo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top