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

vma*_*nta 15 gcc arm thumb

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

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

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

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

LDR R6, =0x24000
ADD R6, #1       @ (set lsb to 1)
BX R6
Run Code Online (Sandbox Code Playgroud)

我觉得这是对的吗?

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

让我说我要调用的函数是0x24000有人建议我使用:

ldr r2, =0x24000
mov lr, pc
bx r2
Run Code Online (Sandbox Code Playgroud)

这是我不明白的地方:

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

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

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

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

old*_*mer 21

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

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

LDR R6, =0x24000
ADD R6, #1       @ (set lsb to 1)
BX R6
Run Code Online (Sandbox Code Playgroud)

因为你碰巧知道0x24000是拇指代码,只需这样做:

LDR R6, =0x24001
BX R6
Run Code Online (Sandbox Code Playgroud)

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

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

ldr r6,=something
bx r6
Run Code Online (Sandbox Code Playgroud)

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

.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
Run Code Online (Sandbox Code Playgroud)

没有工作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
Run Code Online (Sandbox Code Playgroud)

现在我们得到了我们想要的东西

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

变成了

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)
Run Code Online (Sandbox Code Playgroud)

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

D60080BC testarm
D60080C8 testthumb1
D60080D6 testthumb2
D60080E6 testthumb3
D60080FB testthumb4
         testthumb5 crashes
D6008111 testthumb6
D600811D testthumb7
D6008127 testthumb8
Run Code Online (Sandbox Code Playgroud)

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

我已经在这个级别编程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...
Run Code Online (Sandbox Code Playgroud)

那么让我们检查一下,看看它的真正含义是什么,这意味着在arm模式下两个指令,前面8个字节?并在拇指模式下,前面两个指令,或前面4个字节?

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

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

testthumb2

d60080d2:   4678        mov r0, pc
d60080d4:   f7ff fffb   bl  d60080ce <bounce>
d60080d8:   4710        bx  r2
Run Code Online (Sandbox Code Playgroud)

如果程序计数器是两个"指令"头,我们将得到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
Run Code Online (Sandbox Code Playgroud)

用拇指指示你不能用thumb2你可能.并且似乎有一些处理器(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>
Run Code Online (Sandbox Code Playgroud)

一台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
Run Code Online (Sandbox Code Playgroud)

当他们这样做时看起来像这样(从不同的二进制文件中获取,而不是在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
Run Code Online (Sandbox Code Playgroud)

所以无论这个ip注册是什么(r12?),他们都不介意把它丢弃,我想你可以自己把它丢弃.