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>
。