.data
varA: .quad 25
.text
.global main
//main function
main:
adr x0,varA
ldr x1, [x0]
ldr x2,=varA
ldr x3,[x2]
x0 和 x2 會一樣嗎?據我所知,這里只是記憶體訪問差異。
但是為什么我們需要再次使用 [] 將其分配給相同或不同的暫存器來分配值?不是 adr 做了它的第一行主要代碼嗎?
uj5u.com熱心網友回復:
adr reg, label原則上,ldr reg, =label兩者都會將 的地址label放在暫存器中reg。因此,您問題中的代碼將導致x0, x2既包含 的地址varA,又x1, x3包含值 25。
但是,它們在底層的作業方式不同,這很容易理解,因為它會導致每種方法的某些限制和權衡。
adr reg, label將指令自身地址與label. 在運行時,它將這個位移添加到 的值中pc,并將結果放在 中reg,就像add reg, pc, #(label - .)存在這樣的指令一樣。這具有與位置無關的優點;如果整個二進制檔案加載到記憶體中的任意地址,那么相同的機器代碼仍然可以作業,只要.text和.data部分保持相同的相對位置。限制是指令只有空間來編碼一個 19 位位移,所以如果你的程式太大以至于標簽和指令在任一方向上都超過 1 MB,程式將不會鏈接。
adrp您可以使用and的兩個指令序列將其增加到 4 GBadd,請參閱了解 ARM 重定位(示例:str x0, [tmp, #:lo12:zbi_paddr])。ldr reg, =label會將 的絕對地址組裝label到記憶體中的某個附近位置(稱為文字池),并發出加載指令以從那里獲取地址。相當于:ldr reg, literal_pool ... literal_pool: .quad label其中
ldr reg, literal_pool是“文字加載”:它自己的地址與 的地址之間的位移literal_pool被編碼在指令中,并在運行時將位移添加到pc,然后從結果地址加載。位移必須小于 1 MB,但匯編器通常可以安排在該范圍內放置一個文字池。好處是,原則上,它不受 的相對位置限制
label,可以在 64 位地址空間中的任何位置。缺點是需要額外的記憶體:除了指令本身的 4 個位元組之外,文字池中ldr reg, literal_pool的絕對地址還需要額外的 8 個位元組。label此外,從上面可以看出,
ldr reg, =label它不是位置獨立的,因為它需要絕對地址label才能進入文字池。如果代碼最終被加載到與聯結器假定的地址不同的地址,例如地址空間布局隨機化label,則必須在加載程式時更新存盤的地址,然后再執行。在某些情況下,作業系統會自動為您執行此操作(例如在 Linux 上),但它確實會使程式的加載花費一些額外的周期,并且必要的元資料會使您的二進制檔案更大一些。其他作業系統不支持這一點,例如 MacOS(為什么我不能在 ARM64 MacOS 的 .text 部分中組裝絕對地址?),在這種情況下ldr reg, =label根本不可用。而且,如果您在裸機上而不是在現有作業系統下運行,則必須在加載程式中撰寫額外的代碼來執行此重定位。因此,雖然
ldr reg, =label看起來是最簡單的方法,但它并不是最有效或最便攜的。編譯器通常使用adrp/add上面提到的組合,它提供了一種與位置無關的解決方案,也適用于相當大的程式。
uj5u.com熱心網友回復:
我終于明白了這個概念。
x0,x2 只保存地址,然后我們將值分配給地址中的 x1,x3。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/503876.html
下一篇:了解不同位ISA的跳轉地址計算
