关于拇指 16/32 位混合指令流中的 arm pc 值

Bil*_*son 1 arm instruction-set thumb

我阅读了几篇文章,其中包括 SO为什么 ARM PC 寄存器指向下一个要执行的指令之后的指令?,该 pc 寄存器值实际上是当前执行指令地址加上前面的 2 条指令,因此在 ARM 状态下它是 +8 字节(2*32 位)。

我的问题是,对于拇指状态,可能有 16 位或 32 位指令,这是否意味着对于 16/32 位指令,获取 pc 地址可能是 +4 字节或 +8 字节的偏移量?

例如:

279ae6: f8df 9338   ldr.w   r9, [pc, #824] --> pc value= 279aea or 279aee
279aea: f44f 7380   mov.w   r3, #256
279aee: 48cd        ldr r0, [pc, #820]
Run Code Online (Sandbox Code Playgroud)

我使用以下代码进行了更多测试:

1598:   467b        mov r3, pc
159a:   f8bf 4000   ldrh.w  r4, [pc]    ; 159c
159e:   46f9        mov r9, pc
15a0:   f83f 5001   ldrh.w  r5, [pc, #-1]   ; 15a3
15a4:   f83f 6002   ldrh.w  r6, [pc, #-2]   ; 15a6
15a8:   f83f 7003   ldrh.w  r7, [pc, #-3]   ; 15a9
15ac:   f83f 8004   ldrh.w  r8, [pc, #-4]   ; 15ac
15b0:   f04f 0908   mov.w   r9, #8
15b4:   f8d9 0008   ldr.w   r0, [r9, #8]    ; Trigger crash to check registers
Run Code Online (Sandbox Code Playgroud)

崩溃时,寄存器为:

I/DEBUG   ( 2632): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x10
I/DEBUG   ( 2632):     r0 b8ef4fc0  r1 aca6bb6c  r2 00000000  r3 aca7c59c
I/DEBUG   ( 2632):     r4 00004000  r5 00003f50  r6 00006002  r7 000003f8
I/DEBUG   ( 2632):     r8 0000f83f  r9 00000008  sl 00000000  fp aca6bbc0
I/DEBUG   ( 2632):     ip aca7c591  sp aca6bb40  lr acab722d  pc aca7c5b4  cpsr 60070030
Run Code Online (Sandbox Code Playgroud)

上面代码注释中显示的地址(159c/15a3/15a6/15a9/15ac)是由objdump生成的,我用寄存器检查了这些位置的内容,看起来没问题。

对于 16 位指令:

1598:   467b        mov r3, pc  ;    // r3 = 1598 + 4 = 159c, correct for +4 theory
Run Code Online (Sandbox Code Playgroud)

而对于 32 位拇指指令:

159a:   f8bf 4000   ldrh.w  r4, [pc]    ; // ld addr = 159a + 2 = 159c, where the content is 4000(hw), exactly r4 shows
                                        ; // Inconsistent with +4 theory
Run Code Online (Sandbox Code Playgroud)

这样,对于 32 位指令,pc 读取 = pc 执行 +2。我错过了什么吗??现在我真的对 pc 偏移感到困惑。

顺便说一句,这是使用thumb2 的armv7a 平台。

谢谢你们。

Not*_*hat 5

在 Thumb 状态下,PC 偏移量始终为 4 个字节。原因是,这两个指令进取,Thumb指令获取(概念)总是一个半字-因此为什么32位编码还有两个小端的半字,而不是一个小端字的搞笑字节顺序。

原始 Thumb 指令集中的一个“32 位”编码,将bl每个半字的操作分别定义为“前缀”和“后缀”指令,巧妙的技巧是在执行第一部分时,采用返回地址直接来自 PC,因为在那个阶段它指向第二部分之后的指令。到 Thumb-2 技术出现并使 32 位编码成为正式事物(包括 bl追溯)时,PC 偏移量已经与实际微体系结构几代*无关,因此将其定义的行为更改为取决于指令流实际上没有任何好处,并引入了大量的兼容性问题。

更复杂的是,当 PC 用作寻址操作(即adr/ ldr/ str/ 等)的基址寄存器时,它始终使用字对齐值,即使在 Thumb 状态下也是如此。因此,在 0x159a 处执行加载指令时,PC 寄存器将读取为 0x159e,但其基址ldr...[pc]Align(0x159e, 4),即 0x159c。由于 PC 相对寻址通常是通过指定标签而不是手动计算偏移量来写入的,因此这个细节很容易被遗漏。

* 就 ARM 自己的设计而言,ARM7 是最后一个基于原始 3 级流水线的微架构。