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)
}
}
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"
#define BOOTLOADER_VERSION "1.3"
#define APP_ADDR 0x800B000
#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);
myputstr(__DATE__);
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");
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++;
}
}
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");
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
(转)位置无关码、位置有关码 位置无关代码,即该段代码无论放在内存的哪个地址,都能正确运行
|