Uso de BX en código de pulgar para llamar a una función de pulgar, o para saltar a una instrucción de pulgar en otra función

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

  •  28-10-2019
  •  | 
  •  

Pregunta

Estoy tratando de aprender habilidades útiles en modificación de firmware (para el cual no tengo código fuente) Estas preguntas se refieren al uso de BX del código de pulgar para saltar o llamar a otro código de pulgar existente.

  1. ¿Cómo uso BX para saltar al código de pulgar de firmware existente, desde mi código de pulgar?
  2. ¿Cómo uso BX para llamar a una función de pulgar existente (debe establecer LR primero), desde mi código de pulgar?

Entiendo que CPU mira LSB Bit (bit 0) y tengo que asegurarme de que esto esté configurado para 1 Para mantener el estado de la CPU al "estado del pulgar". Así que supongo que tengo que agregar 1, para establecer el bit LSB en 1.

Entonces ... digamos que solo quiero saltar a 0x24000 (en el medio de algún código de pulgar existente)

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

Creo que esto es correcto?

Ahora diga que quiero llamar a una función de pulgar existente, usando BX, y quiero que vuelva a mí, por lo que necesito configurar LR en donde quiero que regrese.

Digamos que la función que quiero llamar es a 0x24000 fue me sugirió usar:

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

Aquí viene lo que no entiendo:

  1. La dirección en R2 no tiene un bit LSB establecido ... así que no bx r2 Modo de cambio al modo ARM ??

  2. La LR ... la PC tiene la dirección de (comienza la instrucción actual, + 4), me dijeron. Tanto en el pulgar como en el brazo, cualquier dirección de instrucción debe estar alineada (16 bits o 32 bits), por lo que no tendrá el bit LSB establecido en 1. Solo los números impares tienen bit LSB establecido en 1.

Entonces, en el código anterior, estoy configurando LR en (PC), una dirección que tampoco tiene LSB Bit 1 Set. Entonces, cuando la función que llamé llega a su epílogo, y lo hace BX LR, ... uhmmm .. ¿Cómo puede funcionar eso para volver a mi código de pulgar? Debo estar perdiendo algo...

Normalmente se usa BL para llamar a las funciones. El manual dice que la instrucción BL establece el LR en la siguiente línea de código ... también significa que A (normalmente usado) BL Instrucción del pulgar, establece el LR a return addr + 1 ¿automáticamente?

¿Fue útil?

Solución

Wow, gracias por llamarme por este caso. Sé que probé el código QEMU en http://github.com/dwelch67/yagbat y pensó xput32 que llama a Put32 en la forma en que describe, y funcionó. Pero no parece funcionar. Creé varios experimentos y estoy bastante sorprendido, esto no es lo que esperaba. Ahora veo por qué el enlazador GNU hace lo que hace. Lo siento, esta es una respuesta larga, pero creo que es muy valioso. Es un tema confuso, sé que me he equivocado durante años pensando que la PC arrastra el modo mordido, pero no lo hace.

Antes de comenzar con los experimentos a continuación, si va a hacer esto:

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

Debido a que sabes que 0x24000 es código de pulgar, solo haz esto en su lugar:

LDR R6, =0x24001
BX R6

Y sí, así es como se ramifica para pulgar el código del brazo o el pulgar si sabe que esa dirección codificada 0x24000 es una instrucción de pulgar con un registro que contiene la dirección más una.

Si no sabe la dirección pero conoce el nombre de la dirección

ldr r6,=something
bx r6

Lo bueno de eso es que algo puede ser una dirección de brazo o pulgar y el código anterior simplemente funciona. Bueno, funciona si el enlazador sabe correctamente qué tipo de etiqueta es brazo o pulgar, si eso se equivoca, no funcionará bien como puede ver aquí

.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

Eso no funcionó, Pong quería sacar una dirección de pulgar de 0xd600815c, pero obtuvo una dirección de brazo.

Todo esto es material de ensamblador de GNU por cierto, para otras herramientas que puede tener que hacer otra cosa. Para el gas, debe poner .thumb_func ante una etiqueta que desea declarar como una etiqueta de pulgar (el término functing que implica la función es engañoso, no se preocupe por lo que .thumb_func significa que es un juego de ensamblador/enlazador).

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

Y ahora obtenemos lo que queríamos

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 tiene ese conjunto de LSBIT para que no tenga que hacer ningún trabajo. El compilador se encarga de todo esto cuando realiza llamadas a las funciones de C, por ejemplo. Para el ensamblador, aunque debe usar eso .thumb_func (o alguna otra directiva si hay una) para que el gas sepa que esta es una etiqueta de pulgar y establece el LSBIT para usted.

Por lo tanto, el siguiente experimento se realizó en un MPCore, que es un ARM11, pero también probé las funciones de Testthumb 1 a 4 en un ARM7TDMI y QEMU con los mismos resultados.

.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

que se convierte en

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)

Y los resultados de llamar e imprimir todas estas funciones

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

Entonces, ¿qué está haciendo todo esto y qué tiene que ver con su pregunta? Esto tiene que ver con las llamadas de modo mixto desde el modo pulgar (y también desde el brazo que es más simple)

He estado programando el modo de brazo y pulgar en este nivel durante muchos años, y de alguna manera he tenido esto mal todo el tiempo. Pensé que el mostrador del programa siempre contenía el modo en ese LSBIT, lo sé, ya que sabes que quieres tenerlo establecido o no configurado cuando haces una instrucción BX.

Muy temprano en la descripción de la CPU del procesador ARM en el Manual de referencia arquitectónica del brazo (si está escribiendo ensamblador, ya debería tener esto, si no tal vez la mayoría de sus preguntas se respondan).

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

Entonces, verifiquemos y veamos qué significa eso realmente, ¿eso significa en el modo ARM dos instrucciones, 8 bytes por delante? y en modo pulgar, ¿dos instrucciones por delante, o 4 bytes por delante?

Por lo tanto, Testarm verifica que el contador del programa esté a 8 bytes por delante. que también son dos instrucciones.

TestThumb1 verifica que el programa está a 4 bytes por delante, lo que en este caso también es dos instrucciones.

testthumb2

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

Si el contador del programa fuera dos directores de "instrucciones", obtendríamos 0xd60080d8, pero en su lugar obtendremos 0xd60080d6, que es cuatro bytes por delante, y eso tiene mucho más sentido. Modo de brazo 8 bytes por delante, el modo de pulgar 4 bytes por delante, sin meterse con las instrucciones (o datos) de decodificación que están por delante del código que se ejecuta, solo agregue 4 u 8.

TestThumb3 era una esperanza de que MOV LR, PC fuera especial, no lo es.

Si aún no ve el patrón, el LSBIT del mostrador del programa no está establecido, y supongo que esto tiene sentido para las tablas de rama, por ejemplo. Por lo tanto, MoV LR, PC en modo Thumb no configura el registro de enlace correctamente para una devolución.

TestThumb4 de una manera muy dolorosa lleva el contador del programa donde sea que termine este código y, según las instrucciones cuidadosamente colocadas, calcula la dirección de retorno, si cambia esa secuencia de instrucciones entre Mov R1, PC y BX R2, debe retener el ADD. . Ahora, ¿por qué no podríamos hacer algo como esto?

add r1,pc,#1
bx r2

Con las instrucciones del pulgar no puedes, con pulgar2 probablemente podrías. Y parece haber algunos procesadores (ARMV7) que respaldan tanto las instrucciones del brazo como el pulgar/pulgar2 para que pueda estar en una situación en la que desee hacerlo, pero no agregaría #1 porque un pulgar2 agregue instrucción, si hay una Eso permite que los registros superiores y tengan tres operandos serían una instrucción de 4 bytes pulgar 2. (necesitaría agregar #3).

Entonces, TestThumb5 es directamente del código que le mostré que conduce a parte de esta pregunta, y se bloquea. No es así como funciona, lo siento, engañé a la gente. Intentaré regresar y reparar las preguntas con las que usé esto.

TestThumb6 es un experimento para asegurarnos de que todos no estamos locos. Todo está bien, el registro de enlaces realmente obtiene el LSBIT establecido para que cuando BX LR más tarde conozca el modo desde ese bit.

TestThumb7, esto se deriva del trampolín lateral del brazo que ve que el enlazador hace cuando va del modo ARM al modo pulgar, en este caso, aunque voy del modo pulgar al modo del brazo. ¿Por qué el enlazador no puede hacerlo de esta manera? Porque en modo pulgar al menos tienes que usar un registro bajo y en este punto del juego, después de que se compila el código, el enlazador no tiene forma de saber qué registro puede basura. En modo ARM, aunque el registro de IP, no estoy seguro de lo que es tal vez R12, puede ser basado en la basura, supongo que está reservado para que el compilador use. Sé en este caso que R1 puede ser destrozado y usado, y esto funciona como se desea. Se llama al código de ARMBOUNTE que toma el registro de enlaces si está a dónde regresar, que es una instrucción de pulgar (conjunto LSBIT) después del BL ArmBounce_Thumb en la función TestThumb7, exactamente donde queríamos que fuera.

TestThumb8 Así es como lo hace el enlazador GNU cuando necesita obtener del modo pulgar al modo ARM. La instrucción BL está configurada para ir a un trampolín. Luego hacen algo muy muy complicado y loco.

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

Una PC BX. Sabemos por los experimentos anteriores que la PC está a cuatro bytes por delante, también sabemos que el LSBIT no está establecido. Entonces, lo que esto dice es Branch al código del brazo que es cuatro bytes después de este. El NOP es un espaciador de dos bytes, luego tenemos que generar una instrucción en el brazo cuatro bytes por delante y alineados en un límite de cuatro bytes, y hacemos que sea una rama incondicional para cualquier lugar al que vayamos, esto podría ser AB algo o una PC LDR , = algo dependiendo de qué tan lejos debes llegar. Muy engañoso.

El BL Arm_Bounce_Thumb_TWO original establece el registro de enlaces para volver a la instrucción después de esa BL. El trampolín no modifica el registro de enlaces que simplemente realiza ramas.

Si desea llegar al modo de pulgar desde el brazo, haga lo que hace el enlazador:

...
bl myfun_from_arm
...


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

Lo que se ve así cuando lo hacen (agarrado de un binario diferente no en 0xd6008xxx pero en 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

Entonces, sea cual sea este registro de IP (¿R12?) No les importa destrozarlo y supongo que eres bienvenido a destrozarlo tú mismo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top