天天看點

1.1 關于ARM中重定位:位置有關碼和位置無關碼及運作位址和連結位址

關于ARM中的重定位引入:

要想弄明白重定義的問題,首先我們需要引入4個概念: 連結位址 / 運作位址 / 位置無關碼 / 位置有關碼

這裡我們先簡單回顧一下三星S5PV210晶片的啟動過程(如果想詳細了解,請翻我之前的文章):

由于三星晶片設計時IROM為64Kb(存放BL0的位置),ISRAM為96Kb(也就是加載BL1和BL2的地方,并且前16KB預留給了BL1),是以按照三星推薦的啟動方式,bootloader必須大于16KB小于96KB,我們先假設bootloader為80Kb。給Soc上電後,BL0先運作,然後BL0會加載外部啟動裝置中bootloader的16KB(BL1)到晶片内部的SRAM中執行,BL1再繼續加載BL2(bootloader中剩餘的80kb-16kb=64kb)到内部SRAM(前16KB存放BL1,BL2内容内容接着16Kb的結束位址存放)執行,然後初始化外部的記憶體(DDR)并将OS從外部啟動裝置中搬運到DDR并将運作位址跳轉到DDR上繼續執行,整個啟動過程完成。

現在來說一下關于連結位址和運作位址:

運作位址: 程式實際運作時位址,也就是目前執行程式中運作指令所對應的位址。

連結位址: 程式在編譯器編譯成可執行檔案時(四個過程:我之前的文章也說過,預處理、編譯、彙編、連結),在連結過程中我們指定的位址,如果不顯式指定,編譯器會預設為0x00位址。

連結位址就是我們寫Makefile時用-Ttext指定的位址。

運作位址就是我們用dnw下載下傳程式到晶片上指定的位址。

關于位置無關碼和位置有關碼:

位置無關碼: PIC(Position independent code),在程式被編譯成二進制可執行檔案後,其指令的運作與記憶體位址無關。

位置有關碼: 和位置無關碼相反,也就是編譯後指令的運作和記憶體位址有關。

像我之前,如果隻看這個解釋肯定會懵逼,舉個例子應該就好了解了,順便還能拓展一下知識:

adr和ldr僞指令: 這裡我說的ldr是僞指令而不是普通的加載指令(詳細了解,可以翻我之前的文章)ldr是長加載,adr是短加載。這裡我們通過反彙編代碼來說這倆個指令的運作過程。

(寫到這裡我發現今天這個電腦沒有條件用Linux反彙編,悲劇,我就直接口述算了,雖然我很想把代碼什麼的貼上來,那樣會很容易就懂了)

我們假設_start是下載下傳程式到s5pv210上的起始運作位址0xd0020000。而我們編寫程式時的指定的連結位址是0xd0024000。

1.adr r0, _start  

2.ldr r1, =_start 

經過反彙編後這倆個代碼就會變成:

1.sub r0,pc,#xx

2.ldr r1,[pc,#xx]

那麼問題來了,

r0=pc+xx: pc就是目前程式運作時指向的位址,r0存放的就是pc指針向前或者向後偏移xx的位址。是以adr加載的位址隻跟運作位址有關,和連結位址沒有半毛關系,它是位置無關碼。

r1=[pc+xx]: r1存放的是pc+xx這個記憶體位址所存放的内容,由于這個記憶體位址經過編譯器連結後生成的,和連結時指定的位址有關,根據反彙編可以看到其實這個記憶體位址存放的是0xd0024000,它是位置有關碼。

綜上:加載同樣一個标号,但是加載的内容卻不相同,這是因為連結位址和運作位址不相同的緣故,我們可以看到實質adr加載的是運作位址(0xd0020000),ldr加載的是連結位址(0xd0024000)。

是以我們可以了解到,如果程式的運作位址和連結位址相同時,那麼所謂的位置有關碼和位置無關碼執行的結果都是一樣的。如果程式的運作位址和連結位址不同時,問題就來了,比如說,由于程式員寫程式時的代碼編譯出來的可執行檔案中指令相應的記憶體位址是和連結位址有關的,比如我們遇到了上面的ldr僞指令,需要在編譯後的程式的某個記憶體位址讀取内容,如果下載下傳時程式的運作位址和連結位址不同時,理論上這個記憶體位址讀出來的内容是錯誤的,是以就會導緻程式出錯甚至不能繼續運作。這個時候我們就需要重定位,也就是說重定位可以解決我們運作位址和連結位址不用時引起的位置有關碼先關的問題。

是以什麼時候我們需要重定位呢?

那就是當我們寫程式時連結位址和運作位址有時候必須不相同,而且還不能全部用位置無關碼來寫程式時,我們可以用重定位來解決問題。

比如說上面舉的這個例子用重定位解決:

我們可以在程式開始的幾句代碼,用位置無關碼來編寫,來實作将0xd0020000開始的程式内容複制一份到0xd0024000處,然後程式可以跳到0xd0024000合适的地方執行,這樣就可以解決位置有關碼出現的問題了。

繼續閱讀