哇,谢谢你打电话给我。我知道我在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 上完成的,但我也在testthumb
ARM7TDMI 和 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_thumb
testthumb7
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
?),他们都不介意丢弃它,我认为欢迎您自己丢弃它。