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