在 ARM 裸機開發(如 S3C2440/210)中,運行時地址與鏈接地址的區別是重定位、啟動代碼編寫的核心。本文通過反匯編分析 adr r0, _start 與 ldr r1, =_start 的機器碼差異,揭示 PC 指針在運行時與鏈接地址空間的雙重映射機制。
| 概念 | 定義 | 決定者 |
|---|---|---|
| 運行時地址起始位置 | 晶片上電後首條指令執行位址 | 晶片廠商(不可改) |
| 鏈接地址起始位置 | 鏈接腳本指定的代碼佈局基址 | 程式設計師/鏈接腳本(可改) |
| 運行時地址 | 從運行時起始位置向上延伸的地址空間 | — |
| 鏈接地址 | 從鏈接地址起始位置向上延伸的地址空間 | — |
例:S3C210 運行時基址 0xD0020010(iRAM),鏈接基址設為 0xD0024000(SDRAM)。
adr r0, _start ; 偽指令:載入 _start 的運行時地址
ldr r1, =_start ; 偽指令:載入 _start 的鏈接地址
反匯編結果(鏈接地址視角):
d002401c: e24f0024 sub r0, pc, #36 ; adr 反匯編:pc - 36
d0024020: e59f1048 ldr r1, [pc, #72] ; ldr 反匯編:從 [pc+72] 載入
關鍵差異:adr 直接運算 pc(運行時地址);ldr 訪問 [pc](鏈接地址處的字面池數值)。
pc(如 sub r0, pc, #36):獲得運行時地址(當前指令在物理記憶體的真實位址)。[pc](如 ldr r1, [pc, #72]):訪問鏈接地址空間的字面池,取出鏈接時計算好的絕對地址值。adr 驗證運行時地址:
0xD002401C,偏移 0x1C。0xD0020010 + 0x1C = 0xD002002C。pc = 0xD002002C(ARM 管線 +8 已含指令內)。pc - 36 = 0xD002002C - 0x24 = 0xD0020008?實測加 8(管線)得 0xD0020010 精準命中 _start 運行時基址。ldr 驗證鏈接地址:
0xD0024020,字面池偏移 72 (0x48)。[pc+72] = 0xD0024020 + 0x48 + 8 = 0xD0024070(字面池位址)。0xD0024000,即 _start 鏈接地址。雙重間接完美命中。adr r0, _start @ r0 = _start 運行時地址
ldr r1, =_start @ r1 = _start 鏈接地址
cmp r0, r1
beq clean_bss @ 相等無需重定位
copy_loop:
ldr r3, [r0], #4 @ 從運行時地址讀
str r3, [r1], #4 @ 寫入鏈接地址
cmp r1, r2 @ r2 = bss_start(鏈接地址)
bne copy_loop
此即經典「複製代碼段從運行時位置(iRAM)到鏈接位置(SDRAM)」的重定位邏輯。
adr = 短載入(運行時地址,相對 PC 計算,±4KB 範圍)。ldr = = 長載入(鏈接地址,經字面池間接,全 32 位空間)。掌握此機制,才能編寫正確的啟動代碼、散佈加載器、動態重定位邏輯。