IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 从0写bootloader —— APP自重定位 -> 正文阅读

[嵌入式]从0写bootloader —— 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指针的偏移,根据偏移就能准确下一条指令或数据。
在这里插入图片描述
当程序链接完成,各个指令之间偏移地址都是固定的,即使烧录在flash上,也是根据偏移找到对应的地址。

在这里插入图片描述

跟位置无关码相反的就是位置有关码,位置有关码的程序指令使用了绝对寻址指令,它的地址与代码所处的位置有关,也就是跟我们自己编译链接程序时指定的链接地址有关。
一些常见绝对跳转,汇编如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

(转)位置无关码、位置有关码 位置无关代码,即该段代码无论放在内存的哪个地址,都能正确运行

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-07-20 19:03:54  更:2022-07-20 19:04:34 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 16:23:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码