16

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

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

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

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

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

我认为这是正确的?

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

假设我要调用的函数位于 0x24000建议我使用:

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。

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

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

4

1 回答 1

22

哇,谢谢你打电话给我。我知道我在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 是一条拇指指令,您bx的寄存器包含地址加一,那么这就是您从 arm 或 thumb 分支到拇指代码的方式。

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

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 上完成的,但我也在testthumbARM7TDMI 和 qemu 上尝试了函数 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 Architectural Reference Manual 中 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

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

testthumb3是一个特别的希望mov lr,pc,但不是。

如果您还没有看到该模式,lsbit则程序计数器的 没有设置,我想这对于例如分支表是有意义的。因此mov lr,pc,在拇指模式下,不会为返回设置链接寄存器。

testthumb4以一种非常痛苦的方式确实会在这段代码碰巧结束的地方使用程序计数器,并根据精心放置的指令计算返回地址,如果您在之间更改指令序列mov r1,pc并且bx r2您必须返回加法。现在为什么我们不能做这样的事情:

add r1,pc,#1
bx r2

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

因此testthumb5,直接来自我向您展示的导致此问题的一部分的代码,并且它崩溃了。这不是它的工作原理,对不起,我误导了人们,我将尝试回去修补我使用的 SO 问题。

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

testthumb7,这是从 ARM 侧蹦床派生的,当您从 arm 模式转到 thumb 模式时,您会看到链接器正在执行此操作,在这种情况下,虽然我正在从 thumb 模式转到 arm 模式。为什么链接器不能这样做?因为在拇指模式下,至少你必须使用一个低位寄存器,并且在游戏中的这一点上,在编译代码之后,链接器无法知道它可以丢弃哪个寄存器。在 arm 模式下,虽然 ip 寄存器不确定可能是什么 r12,但可能会被丢弃,我猜它是保留给编译器使用的。我知道在这种情况下r1可能会被丢弃并使用它,这可以按需要工作。armbounce 代码被调用,如果返回到哪里,它会获取链接寄存器,这是函数中的拇指指令( lsbit set),正是我们想要的位置。bl armbounce_thumbtestthumb7

testthumb8这就是 gnu 链接器在需要从 thumb 模式到 arm 模式时执行它的方式。该bl指令设置为去蹦床。然后他们做了一些非常非常棘手,看起来很疯狂的事情:

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

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

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

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

...
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?),他们都不介意丢弃它,我认为欢迎您自己丢弃它。

于 2012-02-21T00:21:33.457 回答