中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

ARMv8匯編指令adrp和adr怎么使用

發布時間:2021-12-15 15:22:16 來源:億速云 閱讀:614 作者:iii 欄目:開發技術

這篇文章主要介紹“ARMv8匯編指令adrp和adr怎么使用”,在日常操作中,相信很多人在ARMv8匯編指令adrp和adr怎么使用問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”ARMv8匯編指令adrp和adr怎么使用”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

    1.概述

    在閱讀Linux內核代碼時,經常能碰到匯編代碼,網上能查的資料千篇一律,大多都描述的很模糊。俗話說,實踐是檢驗真理的唯一標準,我們就參考官方文檔,自己寫匯編代碼并反匯編,探尋其中的奧妙。

    2.adrp

    在Linux內核啟動代碼primary_entry中,使用adrp指令獲取Linux內核在內存中的起始頁地址,頁大小為4KB,由于內核啟動的時候MMU還未打開,此時獲取的Linux內核在內存中的起始頁地址為物理地址。adrp通過當前PC地址的偏移地址計算目標地址,和實際的物理無關,因此屬于位置無關碼。對于具體的計算過程,下面慢慢分析。

    [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  // 內核的物理地址
    [arch/arm64/include/asm/memory.h]
    // 內核的起始地址和結束地址在vmlinux.lds鏈接腳本中定義
    #define KERNEL_START    _text         // 內核代碼段的起始地址,也即內核的起始地址
    #define KERNEL_END		_end          // 內核的結束地址

    2.1.定義

    adrp指令根據PC的偏移地址計算目標頁地址。首先adrp將一個21位有符號立即數左移12位,得到一個33位的有符號數(最高位為符號位),接著將PC地址的低12位清零,這樣就得到了當前PC地址所在頁的地址,然后將當前PC地址所在頁的地址加上33位的有符號數,就得到了目標頁地址,最后將目標頁地址寫入通用寄存器。此處頁大小為4KB,只是為了得到更大的地址范圍,和虛擬內存的頁大小沒有關系。通過adrp指令,可以獲取當前PC地址±4GB范圍內的地址。通常的使用場景是先通過adrp獲取一個基地址,然后再通過基地址的偏移地址獲取具體變量的地址。
    下面是adrp指令的編碼格式。立即數占用21位,在運行的時候,會將21位立即數擴展為33位有符號數。最高位為1,表示這是一個aarch74指令。

    ARMv8匯編指令adrp和adr怎么使用

    2.2.測試

    Linux內核啟動代碼不好測試,需要寫一個簡單的測試代碼。下面是本次adrp的測試代碼,使用adrp指令獲取g_val1g_val2數組所在頁的基地址,同時會打印數組的地址和調用函數的地址,由于是應用層的程序,這些地址都是虛擬地址,但是計算過程都是一樣的。

    #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);
    }

    上面程序運行的輸出結果如下,g_val1g_val2的地址分別為0x5583e250280x5583e26028g_val1的頁基地址為0x5583e25000g_val2頁的基地址為0x5583e26000adrp_test函數的地址為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頁基地址的計算過程,包括編譯時和運行時,g_val2頁基地址的計算過程類似,這里不再贅述。

    • g_val1址低低12位清零,得到0x1100,將當前adrp指令所在地址的低12清零,得到0x0(編譯時完成)

    • 0x1100減去0x0得到偏移地址0x11000,偏移地址右移12位得到偏移頁數量0x11,將立即數0x11保存到指令編碼中(編譯時完成)

    • 取出立即數0x11,左移12位轉換成偏移的字節數,即0x11000(運行時完成)

    • 將PC地址的低12位清零得到0x5583e14000(運行時完成)

    • 將0x5583e14000加上0x1100得到g_val1運行時頁基地址0x5583e25000(運行時完成)

    000000000000079c <adrp_test>:  // 運行時的地址為0x5583e1479c
    ......
     7b0:	b0000080 	adrp	x0, 11000 <__data_start>    // 獲取g_val1頁基地址
    ......
     7e0:	d0000080 	adrp	x0, 12000 <g_val1+0xfd8>    // 獲取g_val2頁基地址
    
    Disassembly of section .data:       // 數據段定義
    0000000000011000 <__data_start>:    // 運行時的地址為0x5583e25000
    	...
    ......
    Disassembly of section .bss:        // bss段定義
    0000000000011028 <g_val1>:    // 運行時地址為0x5583e25028
    	...
    0000000000012028 <g_val2>:    // 運行時地址為0x5583e26028
    	...

    從上面可以看出,編譯時和運行時的地址不一樣,但通過adrp指令都能正確獲取g_val1頁基地址和g_val2頁基地址。說明adrp獲取的地址是位置無關的,不管運行時的地址怎么變,都可以正確獲取對應變量頁基地址。當然我們也可以使用專業的反匯編工具,直接將機器碼轉換為匯編代碼。上面兩條adrp指令轉換的匯編代碼如下,和上面一樣,這里的偏移地址都已經做了左移12位的處理。

    ARMv8匯編指令adrp和adr怎么使用

    3.adr

    3.1.定義

    adr指令根據PC的偏移地址計算目標地址。偏移地址是一個21位的有符號數,加上當前的PC地址得到目標地址。adr可以獲取當前PC地址±1MB范圍內的地址。下面是adr指令的編碼格式。立即數占用21位。

    ARMv8匯編指令adrp和adr怎么使用

    3.2.測試

    下面是測試代碼,使用adr指令獲取變量g_val3g_val4的地址,并與通過&獲取的地址進行對比。

    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);
    }

    下面是測試結果,使用&獲取的地址和通過adr獲取的地址相同。

    g_val3 addr 0x5583e25018, adr_val1 0x5583e25018, adr_test addr 0x5583e14810
    g_val4 addr 0x5583e25020, adr_val2 0x5583e25020, adr_test addr 0x5583e14810

    下面是反匯編的代碼。可以看出,adr匯編代碼中的偏移地址被objdump使用符號地址代替了,沒有使用真正的偏移地址。g_val3真正的偏移地址為0x107f4,g_val4真正的偏移地址為0x107cc。執行第一條adr指令的PC地址為0x5583e14824,則0x5583e14824+0x107f4=0x5583e25018為g_val3的地址。g_val4的計算過程類似,不再贅述。

    0000000000000810 <adr_test>:    // 運行地址為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>:      // 運行地址為0x5583e25018
    	...
    
    0000000000011020 <g_val4>:      // 運行地址為0x5583e25020
        ...

    ARMv8匯編指令adrp和adr怎么使用

    到此,關于“ARMv8匯編指令adrp和adr怎么使用”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    承德市| 汽车| 仙居县| 台北县| 闵行区| 望奎县| 乌拉特中旗| 黄浦区| 丘北县| 孟津县| 且末县| 西安市| 萍乡市| 吉林省| 安顺市| 崇明县| 湘阴县| 广西| 松原市| 东安县| 敖汉旗| 始兴县| 安泽县| 海阳市| 辰溪县| 绥宁县| 石门县| 长沙市| 信丰县| 海淀区| 札达县| 皮山县| 阳春市| 启东市| 万宁市| 巨野县| 行唐县| 会理县| 阿城市| 贵南县| 江都市|