位置無關碼
即該段代碼無論放在內存的哪個地址,都能正確運行。究其原因,是因為代碼里沒有使用絕對地址,都是相對地址。
位置相關碼
即它的地址與代碼處於的位置相關,是絕對地址
BL :帶鏈接分支跳轉指令,也是位置無關碼(相對位置),用於調用函數用的。
B:分支跳轉指令,指目標不能太遠,一般用於同一個文件下的目標地址跳轉。
LDR:通常都是作加載指令的,但是它也可以作偽指令,通常有兩種不同的表示:
1)LDR pc, =MyHandleIRQ 表示將MyHandleIRQ地址放入pc寄存器中,相當於PC=MyHandleIRQ 。
1. LDR r0,=label //用於加載立即數或一個地址值到指定寄存器中
//如果label是立即數: LDR r0,=0X123 ;將0X123存入r0中
//如果name是個標識符: LDR r0,=label_1 ;將label_1所指向的地址值存入r0中
例如:
- LDR r0,[r1] //將R1中的值存到r0中
- LDR r1,[r2,#16] //將(r2+16)地址中的內容存到r1中
- LDR r1,[r2],#4 //將r2地址中的內容存到r1中,同時r2=r2+4
1 Reset: 2 ldr sp, =4096 @ 設置棧指針,以下都是C函數,調用前需要設好棧 3 bl disable_watch_dog @ 關閉WATCHDOG,否則CPU會不斷重啟 4 // bl是位置無關碼,相當於:PCnew = PC + 偏移 5 // PCnew = (4+8) + 0x28 = 0x34 6 7 //ldr pc, =disable_watch_dog /* 這樣寫將出錯 */ 8 9 bl clock_init @ 設置MPLL,改變FCLK、HCLK、PCLK 10 bl memsetup @ 設置存儲控制器以使用SDRAM 11 bl copy_steppingstone_to_sdram @ 復制代碼到SDRAM中 12 ldr pc, =on_sdram @ 跳到SDRAM中繼續執行 13 on_sdram: 14 ldr sp, =0x34000000 @ 設置棧指針 15 ldr lr, =halt_loop @ 設置返回地址 16 ldr pc, =main @ 調用main函數 17 halt_loop: 18 b halt_loop
鏈接腳本如下,鏈接地址在0X30000000:
1 SECTIONS { 2 . = 0x30000000; 3 .text : { *(.text) } 4 .rodata ALIGN(4) : {*(.rodata)} 5 .data ALIGN(4) : { *(.data) } 6 .bss ALIGN(4) : { *(.bss) *(COMMON) } 7 }
反匯編如下:
1 30000000 <_start>: 2 30000000: e3a0da01 mov sp, #4096 ; 0x1000 3 30000004: eb00000a bl 30000034 <disable_watch_dog> 4 30000008: eb00000d bl 30000044 <clock_init> 5 3000000c: eb000026 bl 300000ac <memsetup> 6 30000010: eb000040 bl 30000118 <copy_steppingstone_to_sdram> 7 30000014: e59ff00c ldr pc, [pc, #12] ; 30000028 <.text+0x28> 8 9 30000018 <on_sdram>: 10 30000018: e3a0d30d mov sp, #872415232 ; 0x34000000 11 3000001c: e59fe008 ldr lr, [pc, #8] ; 3000002c <.text+0x2c> 12 30000020: e59ff008 ldr pc, [pc, #8] ; 30000030 <.text+0x30> 13 14 30000024 <halt_loop>: 15 30000024: eafffffe b 30000024 <halt_loop> 16 30000028: 30000018 andcc r0, r0, r8, lsl r0 17 3000002c: 30000024 andcc r0, r0, r4, lsr #32 18 30000030: 30000200 andcc r0, r0, r0, lsl #4 19 20 30000034 <disable_watch_dog>:
... ...
從反匯編中可以看出當執行ldr pc, =on_sdram 時的反匯編是 ldr pc, [pc, #12] ; 相當於pc=*(pc+12)=30000018,此時的*(pc+12)是指的pc+12地址所指的地址,所以無論pc怎么變都是指的30000018這個常量來執行on_sdram,屬於絕對轉移。
執行 bl disable_watch_dog 時,地址0X30000004跳轉到0X30000034.這里的0X30000034是通過機器碼算出來的,機器碼格式如下圖所示:
其中[31:28]位是條件碼;[27:24]位為“1010”(0xeaffffff)時,表示B跳轉指令,為“1011”時,表示BL跳轉指令;[23:0]表示偏移地址。
從反匯編中可以看到 bl disable_watch_dog 的機器碼是eb00000a ,二進制為1110 1011 000000000000000000001010。
其中1110表示無條件執行,接下的1011就是BL指令,如L==0則就表示B指令,剩下的Offset就是鏈接位。
BL指令的跳轉地址計算:
1.如上圖所示,先將24位Offset補碼左移兩位,得到000000000000000000001010 00 =0X28
2.由於ARM流水線,當前PC永遠等於PC+8,所以PC=PC+0X28+8=0X30000004+0X28+8=0X30000034。
若這里的PC值為其它值,算出來的轉移地址也會隨之改變,所以BL指令為地址無關碼,跳轉地址與位置無關。
注:ARM9是3級流水線,也就是PC處理時正在執行第1條指令的同時對第2條指令進行譯碼,並將第3條指令從存儲器中取出,如下圖所示,PC總是指向第3條指令取值的地方。