在 Thumb 代码中使用 BX 调用 Thumb 函数,或跳转到另一个函数中的 Thumb 指令 [英] Using BX in Thumb code to call a Thumb function, or to jump to a Thumb instruction in another function

查看:33
本文介绍了在 Thumb 代码中使用 BX 调用 Thumb 函数,或跳转到另一个函数中的 Thumb 指令的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试学习固件修改中有用的技能(我没有源代码)这些问题涉及使用 BX 从拇指代码跳转或调用其他现有拇指代码.

I'm trying to learn skills useful in firmware modding (for which i don't have source code) These questions concern use of BX from thumb code to jump or call other existing thumb code.

  1. 如何使用 BX 从我的 THUMB 代码跳转到现有固件 THUMB 代码.
  2. 如何使用 BX 从我的 THUMB 代码中调用现有的 THUMB 函数(必须先设置 LR).

我的理解是 cpu 查看 lsb 位(位 0),我必须确保将其设置为 1 以将 cpu 状态保持为拇指状态".所以我想我必须加 1,才能将 lsb 位设置为 1.

My understanding is that cpu looks at lsb bit (bit 0) and i have to make sure this is set to 1 in order to keep cpu state to "thumb state". So I guess i have to ADD 1, to set lsb bit to 1.

所以...说我只想跳转到 0x24000(在一些现有的 THUMB 代码中间)

So ...say i want to just JUMP to 0x24000 ( in the middle of some existing THUMB code)

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

我认为这是正确的吗?

现在说我想使用 BX 调用现有的拇指函数,并且我希望它返回给我,所以我需要将 LR 设置为我希望它返回的位置.

Now say i want to CALL an existing thumb function, using BX, and i want it to return to me, so i need to set LR to where i want it to return.

假设我想调用的函数在 0x24000它是 建议我使用:

Lets say the function i want to call is at 0x24000 It was suggested to me to use:

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

这里是我不明白的地方:

Here comes what i don't understand:

  1. R2 中的地址没有设置 lsb 位...所以不会 bx r2 将模式切换到 ARM 模式??

  1. the address in R2 doesn't have lsb bit set... so won't bx r2 switch mode to ARM mode??

LR..有人告诉我,PC 的地址为(当前指令的开始,+ 4).在 Thumb 和 Arm 中,任何指令地址都必须对齐(16 位或 32 位),因此不会将 LSB 位设置为 1.只有奇数的 LSB 位设置为 1.

The LR.. The PC has the address of (begining of current instruction, + 4), i was told. In both Thumb and Arm, any instruction address has to be aligned (16 bit or 32 bit), so it won't have the LSB bit set to 1. Only odd numbers have lsb bit set to 1.

因此,在上面的代码中,我将 LR 设置为 (PC),该地址也没有设置 lsb 位 1.因此,当我调用的函数进入尾声时,BX LR,... ummm .. 如何返回到我的 THUMB 代码?我一定是遗漏了什么……

So in the code above, i'm setting LR to (PC), an address that DOESN'T have lsb bit 1 set either. So when the function i called comes to it's epilogue, and does BX LR, ... uhmmm.. how can that work to return to my THUMB code ? I must be missing something...

通常 BL 用于调用函数.手册说 BL 指令将 LR 设置为下一行代码...那么这是否意味着(通常使用的)BL THUMB 指令会自动将 LR 设置为 return addr + 1 ?

Normally BL is used to call functions. The manual says BL instruction sets the LR to the next line of code... So does this mean that a (normally used) BL THUMB instruction, sets the LR to return addr + 1 automatically?

推荐答案

哇,谢谢你叫我解决这个问题.我知道我在 http://github.com/dwelch67/yagbat 中尝试了 qemu 代码并认为 XPUT32它以您描述的方式调用 PUT32,并且它起作用了.但它似乎不起作用.我创建了许多实验并且感到非常惊讶,这不是我所期望的.现在我明白了为什么 gnu 链接器会做它所做的.抱歉,这是一个很长的回复,但我认为它非常有价值.这是一个令人困惑的话题,我知道多年来我一直认为电脑会拖拽模式位,但事实并非如此.

Wow, thanks for calling me out on this one. I know I tried the qemu code in http://github.com/dwelch67/yagbat and thought XPUT32 which calls PUT32 in the way you describe, and it worked. But it DOES NOT appear to work. I created a number of experiments and am quite surprised, this is not what I was expecting. Now I see why the gnu linker does what it does. Sorry this is a long response but I think it very valuable. It is a confusing topic, I know I have had it wrong for years thinking the pc drags the mode bit around, but it doesn't.

在我开始下面的实验之前,如果你打算这样做:

Before I start with the experiments below, if you are going to do this:

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

因为您碰巧知道 0x24000 是拇指代码,所以改为这样做:

because you happen to know that 0x24000 is thumb code, just do this instead:

LDR R6, =0x24001
BX R6

是的,如果您碰巧知道硬编码地址 0x24000 是您 bx 带有包含地址加 1 的寄存器的拇指指令,那么这就是您从 arm 或 thumb 分支到拇指代码的方式.

And yes, that is how you branch to thumb code from arm or thumb if you happen to know that that hardcoded address 0x24000 is a thumb instruction you bx with a register containing the address plus one.

如果您不知道地址但知道地址名称:

If you don't know the address but know the name of the address:

ldr r6,=something
bx r6

这样做的好处是某些东西可以是手臂或拇指地址,并且上面的代码可以正常工作.好吧,如果链接器正确地知道手臂或拇指是什么类型的标签,它就可以工作,如果搞砸了,它将无法正常工作,正如您在此处看到的:

The nice thing about that is that something can be an arm or thumb address and the above code just works. Well it works if the linker properly knows what type of label that is arm or thumb, if that gets messed up it won't work right as you can see here:

.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

那没有用 pong 想从 0xD600815C 中提取一个拇指地址,但得到了一个手臂地址.

That didn't work pong wanted to pull a thumb address from 0xD600815C but got an arm address.

顺便说一句,这都是 gnu 汇编程序的东西,对于其他工具,您可能需要做其他事情.对于气体,您需要将 .thumb_func 放在要声明为拇指标签的标签之前(术语 func 暗示函数具有误导性,不要担心 .thumb_func意味着它只是一个汇编器/链接器游戏).

This is all gnu assembler stuff btw, for other tools you may have to do something else. For gas you need to put .thumb_func before a label that you want declared as a thumb label (the term func implying function is misleading, don't worry about what .thumb_func means it just is an assembler/linker game).

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

现在我们得到了我们想要的:

and now we get what we wanted:

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 设置了 lsbit,这样您就不必做任何工作.例如,当您调用 C 函数时,编译器会处理所有这些.对于汇编程序,尽管您必须使用该 .thumb_func(或其他一些指令,如果有的话)来让 gas 知道这是一个拇指标签并为您设置 lsbit.

0xD600815C has that lsbit set so that you don't have to do any work. The compiler takes care of all of this when you are doing calls to C functions for example. For assembler though you have to use that .thumb_func (or some other directive if there is one) to get gas to know this is a thumb label and set the lsbit for you.

所以下面的实验是在 mpcore 上完成的,它是 ARM11,但我也在 ARM7TDMI 和 qemu 上尝试了 testthumb 函数 1 到 4,结果相同.

So the experiment below was done on an mpcore which is an ARM11 but I also tried testthumb functions 1 through 4 on an ARM7TDMI and qemu with the same results.

.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

变成:

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)

以及调用和打印所有这些函数的结果:

And the results of calling and printing all of these functions:

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

那么所有这些是做什么的,它与您的问题有什么关系.这与从拇指模式(也从更简单的手臂)调用混合模式有关

So what is all of this doing and what does it have to do with your question. This has to do with mixed mode calling from thumb mode (and also from arm which is simpler)

多年来,我一直在这个级别对 ARM 和拇指模式进行编程,但不知何故一直都犯了这个错误.我认为程序计数器总是保持该 lsbit 的模式,我知道你知道在执行 bx 指令时要设置或不设置它.

I have been programming ARM and thumb mode at this level for many years, and somehow have had this wrong all along. I thought the program counter always held the mode in that lsbit, I know as you know that you want to have it set or not set when you do a bx instruction.

ARM 架构参考手册中 ARM 处理器的 CPU 描述的很早部分(如果您正在编写汇编程序,您应该已经有了这个,如果没有,也许您的大部分问题都会得到解答).

Very early in the CPU description of the ARM processor in the ARM Architectural Reference Manual (if you are writing assembler you should already have this, if not maybe most of your questions will be answered).

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

那么让我们检查一下这到底意味着什么,这是否意味着在 arm 模式下两条指令,前面 8 个字节?而在拇指模式下,是提前两条指令,还是提前 4 个字节?

So let's check and see what that really means, does that mean in arm mode two instructions, 8 bytes ahead? And in thumb mode, two instructions ahead, or 4 bytes ahead?

所以 testarm 验证程序计数器是否提前 8 个字节.这也是两条指令.

So testarm verifies that the program counter is 8 bytes ahead. Which is also two instructions.

testthumb1 验证程序前面 4 个字节,在这种情况下也是两条指令.

testthumb1 verifies that the program is 4 bytes ahead, which in this case is also two instructions.

testthumb2:

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

如果程序计数器是前面两个指令",我们将得到 0xD60080D8,但我们得到的是 0xD60080D6,它是前面四个字节,这更有意义.Arm 模式提前 8 个字节,thumb 模式提前 4 个字节,不会干扰正在执行的代码之前的解码指令(或数据),只需添加 4 或 8.

If the program counter was two "instructions" ahead we would get 0xD60080D8 but we instead get 0xD60080D6 which is four bytes ahead, and that makes a lot more sense. Arm mode 8 bytes ahead, thumb mode 4 bytes ahead, no messing with decoding instructions (or data) that are ahead of the code being executed, just add 4 or 8.

testthumb3 希望 mov lr,pc 很特别,但事实并非如此.

testthumb3 was a hope that mov lr,pc was special, it isn't.

如果你还没有看到模式,程序计数器的 lsbit 没有设置,我想这对于分支表来说是有意义的,例如.所以 mov lr,pc 在拇指模式下不会设置返回的链接寄存器.

If you don't see the pattern yet, the lsbit of the program counter is NOT set, and I guess this makes sense for branch tables for example. So mov lr,pc in thumb mode does NOT set up the link register right for a return.

testthumb4 以一种非常痛苦的方式在这段代码碰巧结束的地方获取程序计数器,并根据精心放置的指令计算返回地址,如果您在 之间更改该指令序列mov r1,pcbx r2 你必须返回添加.现在为什么我们不能做这样的事情:

testthumb4 in a very painful way does take the program counter wherever this code happens to end up and based on carefully placed instructions, computes the return address, if you change that instruction sequence between mov r1,pc and bx r2 you have to return the add. Now why couldn't we just do something like this:

add r1,pc,#1
bx r2

用拇指指令你不能,用拇指2你可能可以.并且似乎有一些处理器 (armv7) 支持 arm 指令和 thumb/thumb2,所以你可能会想要这样做.但是您不会添加#1,因为thumb2 添加指令,如果有一个允许高位寄存器并具有三个操作数的指令将是一个4 字节的thumb 2 指令.(您需要添加 #3).

With thumb instructions you can't, with thumb2 you probably could. And there appear to be some processors (armv7) that support both arm instructions and thumb/thumb2 so you might be in a situation where you would want to do that. But you wouldn't add #1 because a thumb2 add instruction, if there is one that allows upper registers and has three operands would be a 4 byte thumb 2 instruction. (you would need to add #3).

所以 testthumb5 直接来自我向您展示的导致此问题的一部分的代码,并且它崩溃了.这不是它的工作方式,对不起,我误导了人们,我会尝试回去修补我用过的 SO 问题.

So testthumb5 is directly from the code I showed you that lead to part of this question, and it crashes. This is not how it works, sorry I mislead folks I will try to go back and patch up the SO questions I used this with.

testthumb6 是一个确保我们都没有疯的实验.一切都很好,链接寄存器确实设置了 lsbit,这样当你 bx lr 以后它知道那个位的模式.

testthumb6 is an experiment to make sure we are all not crazy. All is well the link register does indeed get the lsbit set so that when you bx lr later it knows the mode from that bit.

testthumb7,这是从 ARM 侧蹦床派生而来的,您可以看到链接器在从手臂模式切换到拇指模式时所做的动作,在这种情况下,尽管我将从拇指模式切换到手臂模式.为什么链接器不能这样做?因为在拇指模式下,至少你必须使用一个低寄存器,而在游戏的这一点上,在编译代码之后,链接器无法知道它可以丢弃哪个寄存器.在 arm 模式下,虽然 ip 寄存器(不确定可能是 r12 是什么)可能会被丢弃,但我猜它是保留供编译器使用的.我知道在这种情况下 r1 可能会被丢弃并使用它,这可以按需要工作.armbounce 代码被调用,它在返回到哪里时获取链接寄存器,这是 testthumb7bl armbounce_thumb 之后的拇指指令(lsbit set)/code> 函数,正是我们想要的位置.

testthumb7, this is derived from the ARM side trampoline that you see the linker doing when going from arm mode to thumb mode, in this case though I am going from thumb mode to arm mode. Why can't the linker do it this way? Because in thumb mode at least you have to use a low register and at this point in the game, after the code is compiled the linker has no way of knowing what register it can trash. In arm mode though the ip register, not sure what that is maybe r12, can get trashed, I guess it is reserved for the compiler to use. I know in this case that r1 can get trashed and used it, and this works as desired. The armbounce code gets called which grabs the link register if where to return to, which is a thumb instruction (lsbit set) after the bl armbounce_thumb in the testthumb7 function, exactly where we wanted it to be.

testthumb8 这是 gnu 链接器在需要从拇指模式到手臂模式时的工作方式.bl 指令设置为去蹦床.然后他们做了一些非常非常棘手的事情,看起来很疯狂:

testthumb8 this is how the gnu linker does it when it needs to get from thumb mode to arm mode. The bl instruction is set to go to a trampoline. Then they do something very very tricky, and crazy looking:

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

A bx pc.我们从上面的实验中知道 pc 前面四个字节,我们也知道 lsbit 没有设置.因此,这意味着跳转到 ARM CODE 之后的四个字节.nop 是一个两字节的分隔符,然后我们必须在四个字节之前生成一条 ARM 指令并在四字节边界上对齐,然后我们将其无条件分支到我们要去的任何地方,这可以是某物还是 ldr pc,=某物取决于你需要走多远.非常棘手.

A bx pc. We know from the experiments above that the pc is four bytes ahead, we also know that the lsbit is NOT SET. So what this is saying is branch to the ARM CODE that is four bytes after this one. The nop is a two byte spacer, then we have to generate an ARM instruction four bytes ahead AND ALIGNED ON A FOUR BYTE BOUNDARY, and we make that an unconditional branch to whatever place we were going, this could be a b something or a ldr pc,=something depending on how far you need to go. Very tricky.

原来的bl arm_bounce_thumb_two设置了链接寄存器返回到bl之后的指令.蹦床不修改链接寄存器,它只是执行分支.

The original bl arm_bounce_thumb_two sets up the link register to return to the instruction after that bl. The trampoline does not modify the link register it simply performs branches.

如果您想从 arm 进入拇指模式,请执行链接器所做的操作:

If you want to get to thumb mode from arm then do what the linker does:

...
bl myfun_from_arm
...


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

他们这样做时看起来像这样(从不同的二进制文件中抓取,不是在 0xD6008xxx 而是在 0x0001xxxx).

Which looks like this when they do it (grabbed from a different binary not at 0xD6008xxx but at 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

所以无论这个 ip 寄存器是什么 (r12?),他们都不介意把它扔掉,我想你可以自己扔掉它.

So whatever this ip register is (r12?) they don't mind trashing it and I assume you are welcome to trash it yourself.

这篇关于在 Thumb 代码中使用 BX 调用 Thumb 函数,或跳转到另一个函数中的 Thumb 指令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆