0%

ARM Thumb状态下 相对pc寄存器寻址问题

概述

在一篇文章(实现简易安卓inlinehook)中,Thumb状态下,相对寄存器pc寻址,结果不符合预期。谷歌后找到原因。

问题描述


问题指令:

1
2
3
B3AFB212 ldr r2,[pc,#2]            #预期R2=[pc+4+2=B3AFB218],结果变为[B3AFB216]
B3AFB216 mov r0, r0 #将pc赋值,替换为 mov r0, r0 方便测试
B3AFB218 xx xx xx xx #跳回地址,以78563412为例(小端模式)

对应arm指令:

1
2
3
DF F8 02 20
00 46
12 34 56 78

ldr指令位置为B3AFB212,注意到并未4字节对齐

将指令位置对齐到4字节后,再次反编译,发现跳转地址正确

原因探究

当pc寄存器被用于相对寄存器寻址操作时(如此处的ldr),会先被对齐到word(在32位CPU中即为4字节)(即使是在Thumb状态下)
由于相对PC寻址通常是指定标签而非手动计算偏移,所以这个细节很容易被忽略。

例子:

1
0x159a ldr...[pc]

0x159a+4 = 0x159e
再将其对齐到word,得到0x159c

About arm pc value in thumb 16/32bits mixed instructions stream - Stack Overflow

To further complicate matters, when the PC is used as a base register for addressing operations (i.e. adr/ldr/str/etc.) it is always the word-aligned value that is used, even in Thumb state. So, whilst executing a load instruction at 0x159a, the PC register will read as 0x159e, but the base address of ldr…[pc] is Align(0x159e, 4), i.e. 0x159c. Since PC-relative addressing is normally written by specifying a label rather than calculating offsets manually, this detail can be easy to miss.

指令解析

再来看问题指令,就可以理解了

1
B3AFB212 ldr r2,[pc,#2]            #令R2=[Align(pc+4, word)+2]

pc=B3AFB212,pc+4=B3AFB216,再对齐到word,变为B3AFB214
B3AFB214+2 = B3AFB216