您好,登錄后才能下訂單哦!
這篇文章主要介紹“ARMv8匯編指令adrp和adr怎么使用”,在日常操作中,相信很多人在A(yíng)RMv8匯編指令adrp和adr怎么使用問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”ARMv8匯編指令adrp和adr怎么使用”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
在閱讀Linux內(nèi)核代碼時(shí),經(jīng)常能碰到匯編代碼,網(wǎng)上能查的資料千篇一律,大多都描述的很模糊。俗話(huà)說(shuō),實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn),我們就參考官方文檔,自己寫(xiě)匯編代碼并反匯編,探尋其中的奧妙。
在Linux內(nèi)核啟動(dòng)代碼primary_entry
中,使用adrp
指令獲取Linux內(nèi)核在內(nèi)存中的起始頁(yè)地址,頁(yè)大小為4KB,由于內(nèi)核啟動(dòng)的時(shí)候MMU還未打開(kāi),此時(shí)獲取的Linux內(nèi)核在內(nèi)存中的起始頁(yè)地址為物理地址。adrp
通過(guò)當(dāng)前PC地址的偏移地址計(jì)算目標(biāo)地址,和實(shí)際的物理無(wú)關(guān),因此屬于位置無(wú)關(guān)碼。對(duì)于具體的計(jì)算過(guò)程,下面慢慢分析。
[arch/arm64/kernel/head.S] SYM_CODE_START(primary_entry) ...... adrp x23, __PHYS_OFFSET and x23, x23, MIN_KIMG_ALIGN - 1 // KASLR offset, defaults to 0 ...... SYM_CODE_END(primary_entry) [arch/arm64/kernel/head.S] #define __PHYS_OFFSET KERNEL_START // 內(nèi)核的物理地址 [arch/arm64/include/asm/memory.h] // 內(nèi)核的起始地址和結(jié)束地址在vmlinux.lds鏈接腳本中定義 #define KERNEL_START _text // 內(nèi)核代碼段的起始地址,也即內(nèi)核的起始地址 #define KERNEL_END _end // 內(nèi)核的結(jié)束地址
adrp
指令根據(jù)PC的偏移地址計(jì)算目標(biāo)頁(yè)地址。首先adrp
將一個(gè)21位有符號(hào)立即數(shù)左移12位,得到一個(gè)33位的有符號(hào)數(shù)(最高位為符號(hào)位),接著將PC地址的低12位清零,這樣就得到了當(dāng)前PC地址所在頁(yè)的地址,然后將當(dāng)前PC地址所在頁(yè)的地址加上33位的有符號(hào)數(shù),就得到了目標(biāo)頁(yè)地址,最后將目標(biāo)頁(yè)地址寫(xiě)入通用寄存器。此處頁(yè)大小為4KB,只是為了得到更大的地址范圍,和虛擬內(nèi)存的頁(yè)大小沒(méi)有關(guān)系。通過(guò)adrp
指令,可以獲取當(dāng)前PC地址±4GB范圍內(nèi)的地址。通常的使用場(chǎng)景是先通過(guò)adrp
獲取一個(gè)基地址,然后再通過(guò)基地址的偏移地址獲取具體變量的地址。
下面是adrp
指令的編碼格式。立即數(shù)占用21位,在運(yùn)行的時(shí)候,會(huì)將21位立即數(shù)擴(kuò)展為33位有符號(hào)數(shù)。最高位為1,表示這是一個(gè)aarch74指令。
Linux內(nèi)核啟動(dòng)代碼不好測(cè)試,需要寫(xiě)一個(gè)簡(jiǎn)單的測(cè)試代碼。下面是本次adrp
的測(cè)試代碼,使用adrp
指令獲取g_val1
和g_val2
數(shù)組所在頁(yè)的基地址,同時(shí)會(huì)打印數(shù)組的地址和調(diào)用函數(shù)的地址,由于是應(yīng)用層的程序,這些地址都是虛擬地址,但是計(jì)算過(guò)程都是一樣的。
#define PAGE_4KB (4096) #define __stringify_1(x...) #x #define __stringify(x...) __stringify_1(x) uint64_t g_val1[PAGE_4KB / sizeof(uint64_t)]; uint64_t g_val2[PAGE_4KB / sizeof(uint64_t)]; #define ADRP(label) ({ \ uint64_t __adrp_val__ = 0; \ asm volatile("adrp %0," __stringify(label) :"=r"(__adrp_val__)); \ __adrp_val__; \ }) static void adrp_test() { printf("g_val1 addr 0x%lx, adrp_val1 0x%lx, adrp_test addr 0x%lx\n", (uint64_t)g_val1, ADRP(g_val1), (uint64_t)adrp_test); printf("g_val2 addr 0x%lx, adrp_val2 0x%lx, adrp_test addr 0x%lx\n", (uint64_t)g_val2, ADRP(g_val2), (uint64_t)adrp_test); }
上面程序運(yùn)行的輸出結(jié)果如下,g_val1
和g_val2
的地址分別為0x5583e25028
和0x5583e26028
,g_val1
的頁(yè)基地址為0x5583e25000
,g_val2
頁(yè)的基地址為0x5583e26000
,adrp_test
函數(shù)的地址為0x5583e1479c
。
g_val1 addr 0x5583e25028, adrp_val1 0x5583e25000, adrp_test addr 0x5583e1479c g_val2 addr 0x5583e26028, adrp_val2 0x5583e26000, adrp_test addr 0x5583e1479c
反匯編代碼如下所示。下面分析一下g_val1
頁(yè)基地址的計(jì)算過(guò)程,包括編譯時(shí)和運(yùn)行時(shí),g_val2
頁(yè)基地址的計(jì)算過(guò)程類(lèi)似,這里不再贅述。
將g_val1
址低低12位清零,得到0x1100,將當(dāng)前adrp
指令所在地址的低12清零,得到0x0(編譯時(shí)完成)
0x1100減去0x0得到偏移地址0x11000,偏移地址右移12位得到偏移頁(yè)數(shù)量0x11,將立即數(shù)0x11保存到指令編碼中(編譯時(shí)完成)
取出立即數(shù)0x11,左移12位轉(zhuǎn)換成偏移的字節(jié)數(shù),即0x11000(運(yùn)行時(shí)完成)
將PC地址的低12位清零得到0x5583e14000(運(yùn)行時(shí)完成)
將0x5583e14000加上0x1100得到g_val1
運(yùn)行時(shí)頁(yè)基地址0x5583e25000(運(yùn)行時(shí)完成)
000000000000079c <adrp_test>: // 運(yùn)行時(shí)的地址為0x5583e1479c ...... 7b0: b0000080 adrp x0, 11000 <__data_start> // 獲取g_val1頁(yè)基地址 ...... 7e0: d0000080 adrp x0, 12000 <g_val1+0xfd8> // 獲取g_val2頁(yè)基地址 Disassembly of section .data: // 數(shù)據(jù)段定義 0000000000011000 <__data_start>: // 運(yùn)行時(shí)的地址為0x5583e25000 ... ...... Disassembly of section .bss: // bss段定義 0000000000011028 <g_val1>: // 運(yùn)行時(shí)地址為0x5583e25028 ... 0000000000012028 <g_val2>: // 運(yùn)行時(shí)地址為0x5583e26028 ...
從上面可以看出,編譯時(shí)和運(yùn)行時(shí)的地址不一樣,但通過(guò)adrp
指令都能正確獲取g_val1
頁(yè)基地址和g_val2
頁(yè)基地址。說(shuō)明adrp
獲取的地址是位置無(wú)關(guān)的,不管運(yùn)行時(shí)的地址怎么變,都可以正確獲取對(duì)應(yīng)變量頁(yè)基地址。當(dāng)然我們也可以使用專(zhuān)業(yè)的反匯編工具,直接將機(jī)器碼轉(zhuǎn)換為匯編代碼。上面兩條adrp
指令轉(zhuǎn)換的匯編代碼如下,和上面一樣,這里的偏移地址都已經(jīng)做了左移12位的處理。
adr
指令根據(jù)PC的偏移地址計(jì)算目標(biāo)地址。偏移地址是一個(gè)21位的有符號(hào)數(shù),加上當(dāng)前的PC地址得到目標(biāo)地址。adr
可以獲取當(dāng)前PC地址±1MB范圍內(nèi)的地址。下面是adr
指令的編碼格式。立即數(shù)占用21位。
下面是測(cè)試代碼,使用adr
指令獲取變量g_val3
和g_val4
的地址,并與通過(guò)&
獲取的地址進(jìn)行對(duì)比。
uint64_t g_val3 = 0; uint64_t g_val4 = 0; #define ADR(label) ({ \ uint64_t __adr_val__ = 0; \ asm volatile("adr %0," __stringify(label) :"=r"(__adr_val__)); \ __adr_val__; \ }) static void adr_test() { printf("g_val3 addr 0x%lx, adr_val1 0x%lx, adr_test addr 0x%lx\n", (uint64_t)&g_val3, ADR(g_val3), (uint64_t)adr_test); printf("g_val4 addr 0x%lx, adr_val2 0x%lx, adr_test addr 0x%lx\n", (uint64_t)&g_val4, ADR(g_val4), (uint64_t)adr_test); }
下面是測(cè)試結(jié)果,使用&
獲取的地址和通過(guò)adr
獲取的地址相同。
g_val3 addr 0x5583e25018, adr_val1 0x5583e25018, adr_test addr 0x5583e14810 g_val4 addr 0x5583e25020, adr_val2 0x5583e25020, adr_test addr 0x5583e14810
下面是反匯編的代碼??梢钥闯觯?code>adr匯編代碼中的偏移地址被objdump使用符號(hào)地址代替了,沒(méi)有使用真正的偏移地址。g_val3
真正的偏移地址為0x107f4,g_val4
真正的偏移地址為0x107cc。執(zhí)行第一條adr
指令的PC地址為0x5583e14824,則0x5583e14824+0x107f4=0x5583e25018為g_val3的地址。g_val4
的計(jì)算過(guò)程類(lèi)似,不再贅述。
0000000000000810 <adr_test>: // 運(yùn)行地址為0x5583e14810 ...... 824: 10083fa0 adr x0, 11018 <g_val3> // 偏移地址為0x11018-0x824=0x107f4 ...... 854: 10083e60 adr x0, 11020 <g_val4> // 偏移地址為0x11020-0x854=0x107cc ...... isassembly of section .data: 0000000000011000 <__data_start>: ... ...... Disassembly of section .bss: ...... 0000000000011018 <g_val3>: // 運(yùn)行地址為0x5583e25018 ... 0000000000011020 <g_val4>: // 運(yùn)行地址為0x5583e25020 ...
到此,關(guān)于“ARMv8匯編指令adrp和adr怎么使用”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。