概述
在一篇文章(实现简易安卓inlinehook)中,Thumb状态下,相对寄存器pc寻址,结果不符合预期。谷歌后找到原因。
问题描述
问题指令:
1 | B3AFB212 ldr r2,[pc,#2] #预期R2=[pc+4+2=B3AFB218],结果变为[B3AFB216] |
对应arm指令:
1 | DF F8 02 20 |
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