首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

ARM 匯編 LDR 指令與 LDR 偽指令深度辨析:加載立即數、地址、標號的區別與編碼實現

     編輯:Java 綜合教學

核心結論先行

特性LDR 指令LDR 偽指令
語法標識LDR Rd, [Rn, #offset] 或 LDR Rd, =label (無等號)LDR Rd, =value (有等號)
功能從內存地址讀取數據到寄存器將 32 位立即數/地址值直接載入寄存器
數據源內存字面量池 或 mov/mvn 指令
立即數限制無(訪問內存)無(通過字面量池)
編譯器處理直接編碼為機器指令根據值大小優化為 mov/mvn 或 LDR+字面量池

一、ARM 載存架構背景

ARM 是 RISC (Load/Store 架構):

  • 數據處理指令(ADD、AND、MOV 等)只能操作寄存器。
  • 內存與寄存器交互只能通過 LDR/STR。
  • 這與 x86 CISC 的 MOV EAX, [0x1234] 直接內存操作截然不同。

二、LDR 指令:真正的內存讀取

LDR R0, [R1, #0x10]   ; R0 = *(R1 + 0x10)  基址+偏移
LDR R0, [R1, R2, LSL #2] ; R0 = *(R1 + R2*4) 比例索引
LDR R0, =0x12345678   ; 這是偽指令!見下文

LDR 指令不直接支持 32 位立即數作為地址,地址必須在寄存器中(PC 相對、基址寄存器、或通過偽指令預載入)。

三、LDR 偽指令:裝載 32 位常數的神器

3.1 語法:LDR Rd, =expr

expr 可以是:

  • 32 位立即數:LDR R0, =0x12345678
  • 標號地址:LDR R0, =_start
  • 表達式:LDR R0, =buffer+4

3.2 編譯器優化策略

  1. 值可用 MOV/MVN 編碼(8 位循環右移):直接生成 MOV Rd, #imm 或 MVN Rd, #imm。
    例:LDR R0, =0x10 → MOV R0, #0x10
  2. 值不可用 MOV 編碼:將常數放入字面量池,生成 PC 相對寻址的 LDR 指令 從池中讀取。
    例:LDR R0, =0x12345678 → 字面量池存 0x12345678 → LDR R0, [PC, #offset_to_pool]

3.3 字面量池

  • 編譯器在代碼段插入數據區,存放偽指令需載入的常數。
  • 通常放在函數末尾、區塊結束處,由 LTORG 指令顯式控制位置。
  • PC 相對偏移範圍:±4KB (ARM) / ±1MB (Thumb-2)。超出需手動 LTORG 分池。

四、ADR/ADRL/LDR/MOV 地址加載對比

指令類型范圍地址類型指令數用途
ADR偽指令±256B (ARM) / ±1KB (Thumb)相對地址 (PC+offset)1 (ADD/SUB)同段近距離標號
ADRL偽指令±4KB (ARM) / ±1MB (Thumb-2)相對地址2 (ADD+ADD)同段中距離標號
LDR =label偽指令全地址空間 (配合字面量池)絕對地址 (鏈接時確定)1 LDR + 1 池條目跨段、遠距離、需絕對地址
MOV指令8 位循環立即數立即數值1小常數、掩碼

關鍵差異:ADR vs LDR =label

ADR R0, _start     ; R0 = PC + offset_to_start (相對地址,運行時 PC 相關)
LDR R0, =_start    ; R0 = _start 的鏈接時絕對地址 (需字面量池存儲 32 位地址)

若代碼運行地址 = 鏈接地址(非 PIC),兩者結果相同。若為位置無關代碼 (PIC) 或動態加載,ADR 給出正確相對地址,LDR= 給出鏈接時固定地址(可能錯誤)。

五、實戰案例

5.1 加載外設基址

; GPIO 基址 0x20200000,超出 MOV 范圍
LDR R0, =0x20200000  ; 偽指令 → 字面量池 + PC 相對 LDR
STR R1, [R0, #0x14]  ; 設置 GPIO 輸出

5.2 獲取函數地址調用

LDR R0, =my_function  ; 獲取絕對地址
BLX R0                ; 跳轉調用(支持 Thumb/ARM 切換)

5.3 字面量池溢出處理

; 大量 LDR= 導致池超出 ±4KB
LDR R0, =const1
...
LDR R1, =const100
LTORG                 ; 強制在這裡生成字面量池,防止後續引用超範圍
LDR R2, =const101

六、常見誤區

  1. 以為 LDR 指令能直接寫 32 位立即數:LDR R0, 0x12345678 是語法錯誤,必須 LDR R0, =0x12345678 (偽指令) 或先載入寄存器 LDR R0, [PC, #offset]。
  2. 忽略字面量池位置:導致「offset out of range」編譯錯誤,需插入 LTORG。
  3. 混淆 ADR 和 LDR=:ADR 適合同段近跳,LDR= 適合跨段、需絕對地址、或作為函數指針。
  4. 以為 MOV 能載入任意 32 位值:MOV 只能編碼 8 位循環右移常數(如 0xFF、0xFF00、0xABCDEF00 循環右移後可編碼)。

七、總結

  • LDR 指令 = 內存讀取,地址來自寄存器(含 PC 相對)。
  • LDR 偽指令 = 載入 32 位常數/地址到寄存器,編譯器自動選擇 MOV/MVN 或 字面量池+LDR。
  • ADR/ADRL = 計算 PC 相對偏移,生成 ADD/SUB 指令序列,適用同代碼段標號。
  • 選擇原則:小常數用 MOV,同段標號用 ADR/ADRL,跨段/絕對地址/大常數用 LDR=。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved