天天看點

從0寫bootloader —— APP自重定位APP重定位資料段、清除BSS段APP自我複制所有段實作重定位

APP重定位資料段、清除BSS段

使用的IDE為MDK。

散列檔案:

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x800B000 0x75000 {    ; load region size_region   75000
  ER_IROM1 0x800B000 0x75000  {  ; load address = execution address  75000
   *.o (RESET, +First)
;   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
   ;.ANY (+RW +ZI)
  }
  RW_IRAM1 0x20000000 0x00010000  {  ; RW data
   .ANY (+RW +ZI)
  }
}
           
  • RW 段

    ZI段

    的加載位址和連結位址不一緻

Bootloader程式:

PRESERVE8    ; instruct is aligned by 8 bytes 指令集8位元組對齊
                THUMB        ; use Thumb instruction set    使用thumb指令集
					
				AREA    RESET, DATA, READONLY  ;DATA定義資料段,READONLY隻讀
				EXPORT  __Vectors

__Vectors       DCD     0                          ; CPU自動将該處的值設定給sp,Top of Stack
                DCD     Reset_Handler              ; Reset Handler 指令位址,CPU首先執行此句
				
				AREA    |.text|, CODE, READONLY    ; CODE表示定義代碼段,READONLY隻讀
; Reset handler
Reset_Handler    PROC                 ; 子程式開始标志
				 EXPORT  Reset_Handler             [WEAK]   ; 導出Reset_Handler全局可見以及弱定義
				 IMPORT  main               ; 導入main,類似C語言的extern main,聲明main由外部定義
					
                 LDR     sp, = (0x20000000+0x10000)  ; 手動設定棧,隻有設定了棧才能跳到C語言的世界執行
			
                 BL     main                         ; 跳到C語言世界執行
         
                 ENDP				; 子程式結束标志
					 
boot_app    	PROC       ; 彙編标号即函數名或者說函數位址
				 EXPORT  boot_app    
					
				 STR R0, [R1]  ; 向中斷向量寄存器寫入程式連結位址0x800B000
				 
				 LDR sp, [R0]  ; 取0x800B000位址處的值寫入sp即設定棧
				 
				 LDR R3, [R1]
				 
				 LDR R2, [R0, #4]  ;  0x800B000 + 4 = 0x800B004,取出0x800B004位址處的值指派給R2
				 
                 BX     R2    ; 跳去執行APP
         
                 ENDP
				
				 END     ;彙編檔案結
					 
           
#include "usart.h"
//#include "stdio.h"

#define BOOTLOADER_VERSION    "1.3" 

#define APP_ADDR   0x800B000   /* APP起始位址 */
#define VECTOR_REG_ADDR    0xE000ED08

void boot_app(unsigned int start_addr, unsigned int vector_reg);

int main(void)
{
    unsigned int s_addr = APP_ADDR;
    unsigned int vector_addr = VECTOR_REG_ADDR;
    uart_init();
    
    myputstr("\r\nBootloader: ");  
    myputstr(BOOTLOADER_VERSION);
    //printf("\r\n");
    myputstr(__DATE__);
    //printf(" %s\r\n", __TIME__);

    boot_app(s_addr, vector_addr);
	
	return 0;
}
           

APP程式:

#include "usart.h"

void uart_init(void);

void delay(int times)
{
   while (--times);
}

char buf[100] = {"Hello, My App!!!"};
int xmain(void)
{  
	static unsigned int global;
    uart_init();

    myputstr("\r\nApp Start\r\n");
	myputstr(buf);	
    myputstr("\r\n");
    
    while (1)
    {
        myputstr("app runing\r\n");
        //myputstr("App runing\r\n");
        delay(1000000);
    }
}
           
  • myputstr(buf)

    ,列印資料為亂碼,因為buf是全局變量,屬于資料段,它的加載位址則是FLASH上,散列檔案定義的資料段連結位址是

    0x20000000

    ,資料段的加載位址和連結不一緻,是以需要重定位,而bootloader和APP都沒有做重定位,是以列印出來是亂碼。

修改APP實作重定位資料段和清除BSS段:

/* 重定位資料段 */
void c_relocate_data(char *from, char *to, unsigned int len)
{
    while (len--)
    {
        *to = *from;
		to++;
		from++;
    }
}

/* 清除bss段 */
void c_clear_bss(char *dest, unsigned int len, char val)
{
    while (len--)
    {
        *dest++ = val;
    }
}
           
PRESERVE8    ; instruct is aligned by 8 bytes 指令集8位元組對齊
                THUMB        ; use Thumb instruction set    使用thumb指令集
					
				AREA    RESET, DATA, READONLY  ;DATA定義資料段,READONLY隻讀
				EXPORT  __Vectors  

CODE_START_ADDR   EQU     0x800B000

__Vectors       DCD     0x20000000+0x10000   ; CPU自動取改處的值設定為棧頂 Top of Stack
                DCD     Reset_Handler           ; Reset Handler
				
				AREA    |.text|, CODE, READONLY    ; CODE表示定義代碼段,READONLY隻讀
; Reset handler
Reset_Handler    PROC                 ; 子程式開始标志
				 EXPORT  Reset_Handler             [WEAK]   ; 導出Reset_Handler全局可見以及弱定義
				 IMPORT  xmain         ; 導入main,類似C語言的extern main,聲明main由外部定義
				 IMPORT |Load$$RW_IRAM1$$RW$$Base|     ; 加載于RW段的起始位址
				 IMPORT |Image$$RW_IRAM1$$RW$$Base|     ; 資料段的加載起始位址(源)
                 IMPORT |Image$$RW_IRAM1$$RW$$Length|  ; 資料段的長度(長度)	
              	 IMPORT |Image$$RW_IRAM1$$ZI$$Base|    ; ZI段的連結起始位址
                 IMPORT |Image$$RW_IRAM1$$ZI$$Length|  ; ZI段的長度
                 
                 LDR     sp, = (0x20000000+0x10000)  ; 設定棧
				 
				 LDR R0, = |Load$$RW_IRAM1$$RW$$Base| 
				 LDR R1, = |Image$$RW_IRAM1$$RW$$Base|
				 LDR R2, = |Image$$RW_IRAM1$$RW$$Length|
				 BL c_relocate_data
				 
				 LDR R0, = |Image$$RW_IRAM1$$ZI$$Base| 
				 LDR R1, = |Image$$RW_IRAM1$$ZI$$Length| 
				 MOV R2 , #0
				 BL     c_clear_bss
				 
				 BL  xmain
                      
                 ENDP				; 子程式結束标志
					 	 
				END     ;彙編檔案結束				
           
  • BL c_relocate_data

    在調用xmain之前重定位資料段
  • BL c_clear_bss

    在調用xmain之前清除BSS段

APP自我複制所有段實作重定位

修改散列檔案如下:

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x800B000 0x75000 {    ; load region size_region   75000
  ER_IROM1 0x20000000 0x00010000  {  ; load address = execution address  75000
   *.o (RESET, +First)
;   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
   .ANY (+RW +ZI)
  }
;  RW_IRAM1 0x20000000 0x00010000  {  ; RW data
;   .ANY (+RW +ZI)
  ;}
}
           
  • 加載位址是0x800B000
  • 連結位址是0x20000000
  • 加載位址和連結位址不一緻,需要對APP程式進行重定位

針對加載位址和連結位址不一緻的情況,有一種特殊情況不需要進行重定位代碼,隻要跳到第一條指令位址的正确位置

這種情況就是APP程式是位置無關碼。

位置無關碼無論放在記憶體的哪個位址,都能正确運作,能正确運作的原因是代碼沒有用到絕對位址進行跳轉, 對于代碼指令的執行都是相對PC指針的偏移,根據偏移就能準确下一條指令或資料。

從0寫bootloader —— APP自重定位APP重定位資料段、清除BSS段APP自我複制所有段實作重定位

當程式連結完成,各個指令之間偏移位址都是固定的,即使燒錄在flash上,也是根據偏移找到對應的位址。

從0寫bootloader —— APP自重定位APP重定位資料段、清除BSS段APP自我複制所有段實作重定位

跟位置無關碼相反的就是位置有關碼,位置有關碼的程式指令使用了絕對尋址指令,它的位址與代碼所處的位置有關,也就是跟我們自己編譯連結程式時指定的連結位址有關。

一些常見絕對跳轉,彙編如

LDR pc , = mymain

絕對跳轉執行函數,

C語言如通過函數指針調用一個函數,此時函數指針指向的就是函數的絕對位址,絕對位址由于跟連結位址有關,但程式不進行重定位則程式無法執行,因為絕對位址處無指令。

APP程式,位置有關碼:通過函數指針調用函數

void (*fputstr)(char *);
char buf[100] = {"Hello, My App!!!"};

int xmain(void)
{  
	static unsigned int global;
    uart_init();
	
	fputstr = myputstr;
    fputstr ("\r\nApp Start\r\n");
	fputstr (buf);	
    fputstr ("\r\n");
    
    while (1)
    {
        myputstr("app runing\r\n");
        //myputstr("App runing\r\n");
        delay(1000000);
    }
}
           

對于應用程式,不應該限制代碼的編寫,如果寫成位置無關碼,代碼的編寫就會受限,是以一般連結位址和加載位址不一緻,就要實作代碼重定位。

代碼重定位實作:

void relocate_app(char *from, char *to, unsigned int len)
{
	while (len--)
	{
		*to++ = *from++;
	}
}
           
PRESERVE8    ; instruct is aligned by 8 bytes 指令集8位元組對齊
                THUMB        ; use Thumb instruction set    使用thumb指令集
					
				AREA    RESET, DATA, READONLY  ;DATA定義資料段,READONLY隻讀
				EXPORT  __Vectors  

CODE_START_ADDR   EQU     0x800B000

__Vectors       DCD     0x20000000+0x10000   ; CPU自動取改處的值設定為棧頂 Top of Stack
                DCD     Reset_Handler           ; Reset Handler
				
				AREA    |.text|, CODE, READONLY    ; CODE表示定義代碼段,READONLY隻讀
; Reset handler
Reset_Handler    PROC                 ; 子程式開始标志
				 EXPORT  Reset_Handler             [WEAK]   ; 導出Reset_Handler全局可見以及弱定義
				 IMPORT  xmain         ; 導入xmain,類似C語言的extern xmain,聲明xmain由外部定義
                 IMPORT relocate_app
				 IMPORT  |Load$$ER_IROM1$$Base| ; 加載域的代碼起始位址
				 IMPORT  |Image$$ER_IROM1$$Base|
				 IMPORT	 |Image$$ER_IROM1$$Length|
					 
				 LDR     sp, = (0x20000000+0x10000)  ; 設定棧
				 	 
				 LDR R0, = |Load$$ER_IROM1$$Base| 
				 LDR R1, = |Image$$ER_IROM1$$Base|
				 LDR R2, = |Image$$ER_IROM1$$Length|
				 
				 BL relocate_app
				  
				 LDR pc, = xmain    ; 代碼已經重定位,絕對跳轉到RAM中的xmain,用BL xmain也可以,不過跳轉執行的是APP中的代碼
                              
                 ENDP				; 子程式結束标志					 
					
				 END     ;彙編檔案結束				
           

參考文章:

[012] [STM32] 代碼重定位與清除BSS段深入分析_柯西的彷徨的部落格-CSDN部落格_stm32向量表重定位

https://blog.csdn.net/kouxi1/article/details/123492797

(轉)位置無關碼、位置有關碼 位置無關代碼,即該段代碼無論放在記憶體的哪個位址,都能正确運作

繼續閱讀