在Thumb代码中使用BX来调用Thumb函数,或者跳转到另一个函数中的Thumb指令

时间:2012-02-20 21:01:49

标签: gcc arm thumb

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

  1. 如何从我的THUMB代码中将BX用于JUMP到现有固件THUMB代码。
  2. 如何使用BX从我的THUMB代码中调用现有的THUMB函数(必须先设置LR)。
  3. 我的理解是cpu查看lsb位(位0)并且我必须确保将其设置为1以便将cpu状态保持为“拇指状态”。 所以我想我必须添加1,将lsb位设置为1。

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

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

    我认为这是正确的吗?

    现在说我想要使用BX来调用现有的拇指功能,我想让它返回给我,所以我需要将LR设置为我希望它返回的位置。

    假设我要调用的函数是0x24000 使用suggested to me

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

    这是我不明白的地方:

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

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

    3. 所以在上面的代码中,我将LR设置为(PC),一个没有lsb位1设置的地址。因此,当我调用的函数来到它的结尾时,并BX LR,...嗯......如何才能返回到我的THUMB代码?我一定错过了什么......

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

1 个答案:

答案 0 :(得分:20)

哇,谢谢你叫我这个。我知道我在http://github.com/dwelch67/yagbat中尝试了qemu代码,并认为XPUT32以你描述的方式调用PUT32,并且它有效。但它似乎不起作用。我创造了许多实验,我很惊讶,这不是我所期待的。现在我明白为什么gnu链接器会做它的功能。对不起,这是一个很长的回应,但我认为这很有价值。这是一个令人困惑的话题,我知道我多年来一直认为电脑拖动模式位错了,但它没有。

在开始下面的实验之前,如果您要这样做:

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

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

LDR R6, =0x24001
BX R6

是的,如果你碰巧知道那个硬编码的地址0x24000是一个拇指指令你用一个包含地址加1的寄存器来分支你手臂或拇指的拇指代码。

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

ldr r6,=something
bx r6

关于这一点的好处是,某些东西可以是手臂或拇指地址,上面的代码就可以了。好吧,如果链接器正确地知道什么类型的标签是手臂或拇指,如果它搞砸了它就不会正常工作,就像你在这里看到的那样它是有效的

.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拉一个拇指地址但是得到了一个手臂地址。

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

.thumb
.thumb_func
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:   d6008149    strle   r8, [r0], -r9, asr #2

0xD600815C设置了lsbit,这样你就不用做任何工作了。例如,当您调用C函数时,编译器会处理所有这些。对于汇编程序虽然你必须使用.thumb_func(或其他一些指令,如果有的话)得到气体知道这是一个拇指标签并为你设置lsbit。

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

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

调用和打印所有这些功能的结果

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

那么这一切是做什么的,以及它与你的问题有什么关系。这与从拇指模式(以及更简单的手臂)的混合模式调用有关

我已经在这个级别编程ARM和拇指模式多年了,不知何故一直都有这个错误。我认为程序计数器总是在lsbit中保持模式,我知道你知道你想要在执行bx指令时设置或不设置它。

ARM建筑参考手册中ARM处理器的CPU描述很早(如果您正在编写汇编程序,那么您应该已经有了这个,如果没有,可能会回答大部分问题)。

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个字节?

因此,testarm验证程序计数器是否提前8个字节。这也是两个指示。

testthumb1验证程序是否提前4个字节,在这种情况下也是两个指令。

testthumb2

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

如果程序计数器是两个&#34;指令&#34;我们会得到0xD60080D8,但我们得到的是前面四个字节的0xD60080D6,这更有意义。 Arm模式提前8个字节,拇指模式提前4个字节,没有弄乱正在执行的代码之前的解码指令(或数据),只需添加4或8个。

testthumb3是一个希望mov lr,pc很特别,它不是。

如果你还没有看到模式,程序计数器的lsbit没有设置,我想这对于分支表是有意义的。所以mov lr,pc in thumb mode不会为返回设置正确的链接寄存器。

testthumb4以非常痛苦的方式在这个代码发生的地方占用程序计数器 最终并根据精心放置的指令计算返回地址,如果在mov r1,pc和bx r2之间更改指令序列,则必须重新调整添加。现在为什么我们不能做这样的事情:

add r1,pc,#1
bx r2

用拇指指示你不能用拇指2你可能。并且似乎有一些处理器(armv7)支持arm指令和thumb / thumb2,因此您可能处于想要这样做的情况,但是你不会添加#1因为thumb2 add指令,如果有的话允许上部寄存器并具有三个操作数的是4字节的拇指2指令。 (你需要添加#3)。

所以testthumb5直接来自我向你展示的代码导致了这个问题的一部分,并且它崩溃了。这不是它的工作原理,对不起,我误导人们,我会回去修补我用过的SO问题。

testthumb6是一个确保我们都不疯狂的实验。一切都很好,链接寄存器确实得到了lsbit设置,所以当你bx lr以后它知道该位的模式。

testthumb7,这是从ARM侧蹦床获得的,你看到链接器在从手臂模式转到拇指模式时所做的,在这种情况下虽然我从拇指模式进入手臂模式。为什么连接器不能这样做?因为在拇指模式下,至少你必须使用低位寄存器,并且在游戏中的这一点上,在编译代码之后,链接器无法知道它可以丢弃哪个寄存器。在arm模式下虽然ip寄存器,不知道是什么,也许r12,可以得到删除,我想它是保留给编译器使用的。我知道在这种情况下r1可以被删除并使用它,这可以按照需要工作。 armbounce代码被调用,它抓取链接寄存器,如果返回到哪里,这是在testthumb7函数中的bl armbounce_thumb之后的拇指指令(lsbit set),正是我们想要它的位置。

testthumb8这是gnu链接器在需要从拇指模式进入手臂模式时的工作方式。 bl指令设置为蹦床。然后他们做了一件非常棘手的事情,并且疯狂地看着。

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

一台bx电脑。我们从上面的实验中知道pc前面是四个字节,我们也知道lsbit是未设置的。所以这就是ARM CODE的分支,在此之后是四个字节。 nop是一个双字节的间隔符,然后我们必须在前面四个字节生成一个ARM指令并在四个字节边界上对齐,然后我们将它作为无条件分支到我们要去的任何地方,这可能是ab或某个ldr pc ,=取决于你需要走多远的东西。非常棘手。

原始bl arm_bounce_thumb_two设置链接寄存器以返回到该bl之后的指令。蹦床不会修改它只是执行分支的链接寄存器。

如果你想从arm获得拇指模式,那么做链接器做的事情:

...
bl myfun_from_arm
...


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

当他们这样做时看起来像这样(从不同的二进制文件中获取,而不是在0xD6008xxx但是在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?),他们都不介意把它丢弃,我想你可以自己把它丢弃。