重定位的介绍
以下內(nèi)容源于朱有鵬嵌入式課程的學(xué)習(xí),如有侵權(quán),請(qǐng)告知?jiǎng)h除。
1、鏈接地址和運(yùn)行地址
(1)鏈接地址,是程序員通過(guò)Makefile中-Ttext ?xxx,或者在鏈接腳本中指定的地址。
- 程序員預(yù)知自己的程序的執(zhí)行要求,并且有一個(gè)期望的執(zhí)行地址,并且會(huì)用這個(gè)地址來(lái)做鏈接地址。
- 換言之,鏈接地址由程序員指定,程序員知道該程序應(yīng)該放在哪里才能順利運(yùn)行,否則此段代碼運(yùn)行不起來(lái)(假設(shè)里面有位置相關(guān)碼。?
(2)運(yùn)行地址,代碼實(shí)際運(yùn)行的地址,編譯鏈接時(shí),無(wú)法絕對(duì)確定運(yùn)行時(shí)地址。
- 鏈接地址和運(yùn)行地址能不同。比如我們指定鏈接地址為0xd0024000,但實(shí)際DNW下載到0xd002 0010。此時(shí)運(yùn)行地址是0xd002 0010,如果代碼里面有位置相關(guān)碼,那么位置相關(guān)碼就運(yùn)行不起來(lái),因?yàn)槲恢冒l(fā)生改變了。
- 在位置相關(guān)碼運(yùn)行前,需要把代碼拷貝到以鏈接地址為起始地址的空間里,然后通過(guò)跳轉(zhuǎn)語(yǔ)句,跳轉(zhuǎn)到以鏈接地址為起始地址的代碼的相應(yīng)的位置繼續(xù)運(yùn)行。
(3)舉例
- linux中的應(yīng)用程序。比如gcc hello.c -o hello,使用默認(rèn)的鏈接地址就是0x0,所以應(yīng)用程序都是鏈接在0地址。因?yàn)閼?yīng)用程序運(yùn)行在操作系統(tǒng)的一個(gè)進(jìn)程中,在這個(gè)進(jìn)程中這個(gè)應(yīng)用程序獨(dú)享4G的虛擬地址空間。所以應(yīng)用程序都可以鏈接到0地址,因?yàn)槊總€(gè)進(jìn)程都是從0地址開(kāi)始的。(編譯時(shí)可以不給定鏈接地址而都使用0)。
- 210中的裸機(jī)程序。運(yùn)行地址由我們下載時(shí)確定,下載時(shí)下載到0xd0020010,所以就從這里開(kāi)始運(yùn)行。這個(gè)下載地址不是隨意定的,是iROM中的BL0加載BL1時(shí)事先指定好的地址,這是由CPU的設(shè)計(jì)決定的。所以理論上我們編譯鏈接時(shí)應(yīng)該將地址指定到0xd0020010。
2、重定位
(1)在運(yùn)行地址處執(zhí)行一段位置無(wú)關(guān)碼,把整個(gè)程序鏡像拷貝一份到鏈接地址處,然后使用長(zhǎng)跳轉(zhuǎn)指令從運(yùn)行地址處直接跳轉(zhuǎn)到鏈接地址處去執(zhí)行同一個(gè)函數(shù)。
- 實(shí)質(zhì)的區(qū)別是操作使用的是絕對(duì)地址,還是PC + offset的相對(duì)地址,主要集中在取址、跳轉(zhuǎn)這兩個(gè)操作上;
- b、BL都是用的相對(duì)地址,所以是位置無(wú)關(guān)碼;
- ldr pc,=main;因?yàn)閙ain標(biāo)志在編譯的時(shí)候,會(huì)受到鏈接腳本的鏈接位置的影響,因此main是鏈接后的絕對(duì)地址,所以是位置有關(guān)碼。
- ldr r0, =bss_start,這句代碼的作用是把bss_start(在鏈接腳本中)的地址放入r0中,因?yàn)閎ss_start的值會(huì)受到鏈接腳本中鏈接位置的影響,所以是位置有關(guān)碼。
- ldr r0, 0xe0002700;這個(gè)操作取0xe0002700內(nèi)存地址中的值賦值給r0,因?yàn)檫@個(gè)內(nèi)存地址不會(huì)受到鏈接腳本中鏈接位置的影響,所以是位置無(wú)關(guān)碼。
- 下面這幾句代碼不會(huì)受到鏈接腳本中鏈接位置的影響(在宏定義中),所以也是位置無(wú)關(guān)碼:
- 見(jiàn)博客http://www.cnblogs.com/biaohc/p/6344562.html
(2)當(dāng)執(zhí)行完代碼重定位后,在SRAM中有2份代碼的鏡像。
- 一份是下載到0xd0020010處開(kāi)頭的,另一份是重定位代碼復(fù)制到0xd0024000處開(kāi)頭的。
- 重定位后使用ldr pc, =led_blink這句長(zhǎng)跳轉(zhuǎn),直接從0xd0020010開(kāi)頭的代碼跳轉(zhuǎn)到0xd0024000開(kāi)頭的那一份代碼的led_blink函數(shù)處去執(zhí)行。
- 如果短跳轉(zhuǎn)bl led_blink,則執(zhí)行的是運(yùn)行地址0xd0020010開(kāi)頭的這一份;
- 如果長(zhǎng)跳轉(zhuǎn)ldr pc, =led_blink,則執(zhí)行的是連接地址0xd0024000開(kāi)頭處的這一份;
- 當(dāng)鏈接地址和運(yùn)行地址相同時(shí),短跳轉(zhuǎn)和長(zhǎng)跳轉(zhuǎn)效果一樣。
3、例子說(shuō)明
(1)在SRAM內(nèi)部重定位,不涉及內(nèi)存的初始化
/** 文件名: led.s * 描述: 演示重定位(在SRAM內(nèi)部重定位)*/#define WTCON 0xE2700000 #define SVC_STACK 0xd0037d80.global _start // 把_start鏈接屬性改為外部,這樣其他文件就可以看見(jiàn)_start了 _start:// 第1步:關(guān)看門(mén)狗(向WTCON的bit5寫(xiě)入0即可)ldr r0, =WTCONldr r1, =0x0str r1, [r0]// 第2步:設(shè)置SVC棧ldr sp, =SVC_STACK// 第3步:開(kāi)/關(guān)icachemrc p15,0,r0,c1,c0,0; // 讀出cp15的c1到r0中//bic r0, r0, #(1<<12) // bit12 置0 關(guān)icacheorr r0, r0, #(1<<12) // bit12 置1 開(kāi)icachemcr p15,0,r0,c1,c0,0;// 第4步:重定位// adr指令用于加載_start當(dāng)前運(yùn)行地址adr r0, _start // adr加載時(shí)就叫短加載 // ldr指令用于加載_start的鏈接地址:0xd0024000ldr r1, =_start // ldr加載時(shí)如果目標(biāo)寄存器是pc就叫長(zhǎng)跳轉(zhuǎn),如果目標(biāo)寄存器是r1等就叫長(zhǎng)加載 // bss段的起始地址ldr r2, =bss_start // 就是我們重定位代碼的結(jié)束地址,重定位只需重定位代碼段和數(shù)據(jù)段即可cmp r0, r1 // 比較_start的運(yùn)行時(shí)地址和鏈接地址是否相等beq clean_bss // 如果相等說(shuō)明不需要重定位,所以跳過(guò)copy_loop,直接到clean_bss// 如果不相等說(shuō)明需要重定位,那么直接執(zhí)行下面的copy_loop進(jìn)行重定位// 重定位完成后繼續(xù)執(zhí)行clean_bss。// 用匯編來(lái)實(shí)現(xiàn)的一個(gè)while循環(huán) copy_loop:ldr r3, [r0], #4 // 源str r3, [r1], #4 // 目的 這兩句代碼就完成了4個(gè)字節(jié)內(nèi)容的拷貝cmp r1, r2 // r1和r2都是用ldr加載的,都是鏈接地址,所以r1不斷+4總能等于r2bne copy_loop// 清bss段,其實(shí)就是在鏈接地址處把bss段全部清零 clean_bss:ldr r0, =bss_start ldr r1, =bss_endcmp r0, r1 // 如果r0等于r1,說(shuō)明bss段為空,直接下去beq run_on_dram // 清除bss完之后的地址mov r2, #0 clear_loop:str r2, [r0], #4 // 先將r2中的值放入r0所指向的內(nèi)存地址(r0中的值作為內(nèi)存地址),cmp r0, r1 // 然后r0 = r0 + 4bne clear_looprun_on_dram: // 長(zhǎng)跳轉(zhuǎn)到led_blink開(kāi)始第二階段ldr pc, =led_blink // ldr指令實(shí)現(xiàn)長(zhǎng)跳轉(zhuǎn)// 從這里之后就可以開(kāi)始調(diào)用C程序了//bl led_blink // bl指令實(shí)現(xiàn)短跳轉(zhuǎn)// 匯編最后的這個(gè)死循環(huán)不能丟b .(2)重定位到內(nèi)存中,需要先初始化內(nèi)存
/** 文件名: led.s * 作者: 朱老師* 描述: 演示重定位*/#define WTCON 0xE2700000#define SVC_STACK 0xd0037d80.global _start // 把_start鏈接屬性改為外部,這樣其他文件就可以看見(jiàn)_start了 _start:// 第1步:關(guān)看門(mén)狗(向WTCON的bit5寫(xiě)入0即可)ldr r0, =WTCONldr r1, =0x0str r1, [r0]// 第2步:設(shè)置SVC棧ldr sp, =SVC_STACK// 第3步:開(kāi)/關(guān)icachemrc p15,0,r0,c1,c0,0; // 讀出cp15的c1到r0中//bic r0, r0, #(1<<12) // bit12 置0 關(guān)icacheorr r0, r0, #(1<<12) // bit12 置1 開(kāi)icachemcr p15,0,r0,c1,c0,0;// 第4步:初始化ddrbl sdram_asm_init// 第5步:重定位// adr指令用于加載_start當(dāng)前運(yùn)行地址adr r0, _start // adr加載時(shí)就叫短加載 // ldr指令用于加載_start的鏈接地址:0xd0024000ldr r1, =_start // ldr加載時(shí)如果目標(biāo)寄存器是pc就叫長(zhǎng)跳轉(zhuǎn),如果目標(biāo)寄存器是r1等就叫長(zhǎng)加載 // bss段的起始地址ldr r2, =bss_start // 就是我們重定位代碼的結(jié)束地址,重定位只需重定位代碼段和數(shù)據(jù)段即可cmp r0, r1 // 比較_start的運(yùn)行時(shí)地址和鏈接地址是否相等beq clean_bss // 如果相等說(shuō)明不需要重定位,所以跳過(guò)copy_loop,直接到clean_bss// 如果不相等說(shuō)明需要重定位,那么直接執(zhí)行下面的copy_loop進(jìn)行重定位// 重定位完成后繼續(xù)執(zhí)行clean_bss。// 用匯編來(lái)實(shí)現(xiàn)的一個(gè)while循環(huán) copy_loop:ldr r3, [r0], #4 // 源str r3, [r1], #4 // 目的 這兩句代碼就完成了4個(gè)字節(jié)內(nèi)容的拷貝cmp r1, r2 // r1和r2都是用ldr加載的,都是鏈接地址,所以r1不斷+4總能等于r2bne copy_loop// 清bss段,其實(shí)就是在鏈接地址處把bss段全部清零 clean_bss:ldr r0, =bss_start ldr r1, =bss_endcmp r0, r1 // 如果r0等于r1,說(shuō)明bss段為空,直接下去beq run_on_dram // 清除bss完之后的地址mov r2, #0 clear_loop:str r2, [r0], #4 // 先將r2中的值放入r0所指向的內(nèi)存地址(r0中的值作為內(nèi)存地址),cmp r0, r1 // 然后r0 = r0 + 4bne clear_looprun_on_dram: // 長(zhǎng)跳轉(zhuǎn)到led_blink開(kāi)始第二階段ldr pc, =led_blink // ldr指令實(shí)現(xiàn)長(zhǎng)跳轉(zhuǎn)// 從這里之后就可以開(kāi)始調(diào)用C程序了//bl led_blink // bl指令實(shí)現(xiàn)短跳轉(zhuǎn)// 匯編最后的這個(gè)死循環(huán)不能丟b .總結(jié)
- 上一篇: 测试部门工作周报模板
- 下一篇: 文章发送到多平台软件:融媒宝