关于ARM中的adrp与adr指令
adrp指令
ADRP <Xd>, <label>
adrp指令将label的值传递给xd寄存器。label的值为pc所在页基值 + (立即数 * 4096) ,立即数就相当于是页索引。
此指令一般用于将变量的页基地址存入寄存器中,下面的代码是ndk实现的一个JNI接口函数,定义一个全局变量num,JNI接口函数会访问此全局变量。
jint num = 1;
extern "C" JNIEXPORT jstring JNICALL
stringFromJNI(JNIEnv* env, jobject /* this */) {
num = 2;
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
将编译生成的so文件拖进IDA里查看,#0x34000就是对应变量所在页面基地址,而#0xFD8就是变量对应的页内偏移。
变量页面基地址为0x34000, 页内偏移为0xFD8,所以对应的变量地址为 0x34FD8.
IDA中查看Section发现其在got表中,而got表此0x34FD8存放的是真实num全局变量的地址,会在链接器加载程序时重定位修复。
adr和adrl指令
adr是小范围的地址读取指令,adrl是中等范围的地址读取指令(伪指令)。ADR<c> <Rd>, <label>
都是将<label>
标签的值存放到目标寄存器rd中,<label>
的值为立即数 * 4 + pc。
对于thumb指令模式而言,adr指令长度为2,低8位为立即数等于0x44,pc值等于0xd64,所以计算结果为0x44 * 4 + 0xd64 = 0xE74
,对应的反汇编结果为adr r1, #0xE74
需要注意的是在使用内联汇编时adr指令给出的是最后一个操作数不是<label>
的值,也不是立即数的值,其实际是立即数 * 4的值(相当于是相对于pc的偏移)。例如下面的内联汇编中 adr r0,0x100
指令中的0x100实际是立即数*4的值。
__asm__ volatile(
"nop \n\t"
"nop \n\t"
"nop \n\t"
"adr r0,0x100 \n\t"
);
查看反汇编即可看到立即数等于0x40,0x40 * 4等于0x100,adr r0,0x100
指令处对应的PC值为0x12A68,0x100 + 0x12A68 = 0x12B68 = <label>
。