嵌入式系統中,動態載入模組要求代碼可在任意記憶體位址執行,即位置無關代碼 (Position Independent Code, PIC)。ARM 編譯器需指定 --apcs=/ropi/rwpi 選項啟用 ROPI (Read-Only Position Independence) 與 RWPI (Read-Write Position Independence)。
// main.c
void fun_for_sub(void) { int j; for (j=10; j>=0; j--); }
int main(void) { SystemInit(); fun_for_sub(); }
armcc -c --cpu Cortex-M3 -O0 --apcs=interwork --apcs=/ropi/rwpi -o main.o main.c
fromelf.exe -s -c main.o # 反組譯查看
反組譯輸出關鍵行:
0x00000006: F7FF FFFE BL fun_for_sub ; 0x2 Section
機器碼 F7FF FFFE 為 Thumb-2 BL (Branch with Link) 指令,編碼格式:
S | imm10 | J1 | J2 | imm11
15 14 4 3 2 1 0
F7FF = 1111 0111 1111 1111
FFFE = 1111 1111 1111 1110
S=1, J1=1, J2=1, imm10=0x3FF, imm11=0x7FE
I1 = NOT(J1 XOR S) = NOT(1 XOR 1) = 1
I2 = NOT(J2 XOR S) = NOT(1 XOR 1) = 1
imm32 = SignExtend(S:I1:I2:imm10:imm11:'0', 32)
= SignExtend(1:1:1:11_1111_1111:111_1111_1110:0, 32)
= 0xFFFFFFFC (= -4 有號數)
PC = 0x00000006 (BL 指令地址 + 4, Thumb 模式 PC 對齊)
目標地址 = PC + imm32 = 0x6 + (-4) = 0x2 → 正是 fun_for_sub 入口
PC + offset 定址PC + offset 計算基址 (GOT 表或直接 PC 相對載入)x86 同樣使用 CALL rel32 (E8 cd) 相對呼叫指令實現 PIC。《程式設計師的自我修養》詳述鏈接器如何處理重定位項生成位置無關可執行檔 (PIE)。
BL/B 相對跳轉無需修正--apcs=/ropi 強制編譯器生成 PC 相對存取常數池、全域變數Cortex-M3 Thumb-2 指令集的 BL 採用 23 位元有號 PC 相對偏移 (±16MB 範圍),配合編譯器生成的相對偏移,使代碼段內部控制流轉移天然具備位置無關特性。這是動態載入、模組化韌體、Bootloader 設計的指令級基石。