关于拇指 16/32 位混合指令流中的 arm pc 值
About arm pc value in thumb 16/32bits mixed instructions stream
我读了几篇文章,包括 SO Why does the ARM PC register point to the instruction after the next one to be executed? 中的问题,pc 寄存器值实际上是当前正在执行的指令地址加上前面的 2 条指令,所以在 ARM 状态下它是 +8 字节(2*32 位) .
我的问题是,对于 thumb 状态,可能有 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]
我用以下代码做了更多测试:
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
崩溃时,寄存器为:
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
上面代码注释中显示的地址(159c/15a3/15a6/15a9/15ac)是objdump生成的,我用寄存器检查了这些位置的内容,好像没问题。
对于 16 位指令:
1598: 467b mov r3, pc ; // r3 = 1598 + 4 = 159c, correct for +4 theory
而对于 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
据此,对于32位指令,pc read = pc executing +2。我错过了什么吗??现在我对 pc offset 真的很困惑。
顺便说一句,这是使用 thumb2 的 armv7a 平台。
谢谢大家
在 Thumb 状态下,PC 偏移量始终为 4 个字节。原因是它前面有两条指令 fetches,而 Thumb 指令取指(概念上)总是一个半字 - 因此 32 位编码仍然具有两个小端字节序的有趣字节顺序半字,而不是一个小端字。
原始 Thumb 指令集中的一个“32 位”编码,bl
,将每个半字的操作分别定义为 "prefix" 和 "suffix" 指令,整齐技巧在于,在执行第一部分时,return 地址直接从 PC 获取,因为在那个阶段它指向第二部分之后的指令。到 Thumb-2 技术出现并使 32 位编码成为正式事物时(包括 bl
追溯),PC 偏移量已经承担了 no relation to the actual microarchitecture 几个generations*,因此将其定义的行为更改为依赖于指令流的变量实际上没有任何好处,并且会引入大量兼容性问题。
更复杂的是,当 PC 用作寻址操作的基址寄存器时(即 adr
/ldr
/str
/等),它始终是 字对齐 使用的值,即使在 Thumb 状态下也是如此。因此,当在 0x159a 处执行加载指令时,PC 寄存器将 读取 为 0x159e,但 ldr...[pc]
的基地址为 Align(0x159e, 4)
,即 0x159c。由于 PC 相对寻址通常是通过指定标签而不是手动计算偏移量来编写的,因此这个细节很容易被遗漏。
* 就ARM自己的设计而言,ARM7是最后一个基于原始三级流水线的微架构。
我读了几篇文章,包括 SO Why does the ARM PC register point to the instruction after the next one to be executed? 中的问题,pc 寄存器值实际上是当前正在执行的指令地址加上前面的 2 条指令,所以在 ARM 状态下它是 +8 字节(2*32 位) .
我的问题是,对于 thumb 状态,可能有 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]
我用以下代码做了更多测试:
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
崩溃时,寄存器为:
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
上面代码注释中显示的地址(159c/15a3/15a6/15a9/15ac)是objdump生成的,我用寄存器检查了这些位置的内容,好像没问题。
对于 16 位指令:
1598: 467b mov r3, pc ; // r3 = 1598 + 4 = 159c, correct for +4 theory
而对于 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
据此,对于32位指令,pc read = pc executing +2。我错过了什么吗??现在我对 pc offset 真的很困惑。
顺便说一句,这是使用 thumb2 的 armv7a 平台。
谢谢大家
在 Thumb 状态下,PC 偏移量始终为 4 个字节。原因是它前面有两条指令 fetches,而 Thumb 指令取指(概念上)总是一个半字 - 因此 32 位编码仍然具有两个小端字节序的有趣字节顺序半字,而不是一个小端字。
原始 Thumb 指令集中的一个“32 位”编码,bl
,将每个半字的操作分别定义为 "prefix" 和 "suffix" 指令,整齐技巧在于,在执行第一部分时,return 地址直接从 PC 获取,因为在那个阶段它指向第二部分之后的指令。到 Thumb-2 技术出现并使 32 位编码成为正式事物时(包括 bl
追溯),PC 偏移量已经承担了 no relation to the actual microarchitecture 几个generations*,因此将其定义的行为更改为依赖于指令流的变量实际上没有任何好处,并且会引入大量兼容性问题。
更复杂的是,当 PC 用作寻址操作的基址寄存器时(即 adr
/ldr
/str
/等),它始终是 字对齐 使用的值,即使在 Thumb 状态下也是如此。因此,当在 0x159a 处执行加载指令时,PC 寄存器将 读取 为 0x159e,但 ldr...[pc]
的基地址为 Align(0x159e, 4)
,即 0x159c。由于 PC 相对寻址通常是通过指定标签而不是手动计算偏移量来编写的,因此这个细节很容易被遗漏。
* 就ARM自己的设计而言,ARM7是最后一个基于原始三级流水线的微架构。