前言:
一个程序分成代码段,只读数据段,可读可写数据段,BSS段。
代码段:就是程序本身,不会被修改
只读数据段:放在ROM上,不需要复制到RAM
可读可写数据段:有初始值的全局变量,静态变量,需要从ROM复制到RAM
BSS或者ZI段:未初始化的全局变量或静态变量在使用前清零即可
Tips:局部变量保存在栈中,堆用malloc分配
重定位
保存在ROM上的全局变量的值,如果想使用,需要先复制到RAM,这个过程就是重定位
1. 重定位怎么做
既然想使用变量和函数,需要将其复制到RAM,那怎么去做?程序本身可以把自己复制到RAM上,也就是复制到所谓的链接地址。我们可以使用位置无关码(放在RAM或者ROM上都可以执行的代码)来实现。另外BSS段不需要重定位,因为程序里根本不保存BSS段,使用前把BSS段对应的空间清零。
使用跳转指令实现,并且使用相对跳转指定,而不是绝对跳转指令。用bl main,而不是 ldr pc, = main。相对跳转指令会让指令有一个offset偏移到RAM上,而绝对跳转指令,直接指向该地址,但是该地址还是空的,所以程序必然跑飞。
需要知道三要素:源、目的、长度
- 源:代码段/数据段保存地方(加载地址)
- 目的:代码段/数据段要被复制地方(链接地址)
- 长度:代码段/数据段的长度
通过链接脚本可以知道上述的三要素
需要知道BSS段的地址范围:起始地址、长度
通过链接脚本可以知道上述的二要素
2. 重定位如何获取各段信息
2.1 确定源
可以通过adr伪指令获取当前代码的地址,如下:
.text
.global _curry_start
_curry_start:
......
adr r0, _curry_start
adr是伪指令,最终转换成真正指令。adr r0, _curry_start
本质是r0 = pc - offset
,偏移在链接确定。
2.2 确定目的
可以通过ldr伪指令获取当前代码的地址,如下:
.text
.global _curry_start
_curry_start:
......
ldr r0, =_curry_start
2.3 确定长度
可以通过修改链接脚本的方式确定长度,如下:
SECTIONS{
**********
. = ALIGN(4);
__rwdata_start = .;
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) *(.COMMON) }
__bss_end = .;
}
3.编写程序验证
3.1 将数据段重定位
修改start.S
ldr r0, =__rwdata_start
ldr r2, =_start /* 链接地址 */
adr r3, _start /* 加载地址 */
sub r2, r2, r3 /* 计算得到offset */
sub r1, r0, r2 /* 计算得到源 */
ldr r3, =__bss_start
sub r2, r3, r0 /* BSS段起始地址减去数据段的地址 */
bl memcpy /* r0: 目的, r1: 源, r2:长度 */
3.2 将BSS段清零
Tips:程序全局变量,如果初始值为0,或没有初始值,这些变量被放在BSS段。BSS段并不会放入hex中,太浪费空间。在使用BSS段变量前,需将BSS段所占据的内存清零。
ldr r0, =__bss_start /* 起始地址 */
mov r1, #0 /* 清零 */
ldr r2, =__bss_end
sub r2, r2, r1 /* end减去start,就是长度 */
bl memset /* r0: 起始地址, r1: 值, r2: 长度 */
3.3 代码段重定位
ldr pc, =main /* 这样调用函数的前提条件是代码段需要重定位 */
如果代码段没有重定位,就调用上面代码,就跑飞。之前代码段没有位于它的链接地址上,并没有重定位,可以执行是因为是使用位置无关码写的。代码重定位的代码和上述的数据段重定位的方法一样。下面介绍下位置无关码:
bl main /* bl相对跳转,程序仍在原来的区域运行 */
ldr pc, =main /* 绝对跳转,跳到链接地址去运行 */
ldr r0, =main /* 更规范的写法,支持指令集切换 */
blx r0
3.4 重定位C编写
- 方式1
声明为外部变量,使用时需要使用取址符:
extern unsigned int __bss_start;
extern unsigned int __bss_end;
unsigned int len;
len = (unsigned int)&__bss_end - (unsigned int)&__bss_start;
memset(&__bss_start, 0, len);
-
方式2
声明为外部数组,使用时不需要使用取址符:extern char __bss_start[]; extern char __bss_end[]; unsigned int len; len = __bss_end - __bss_start; memset(__bss_start, 0, len);
4.对比S32K144的启动代码
下面是ld
MEMORY
{
/* Flash */
m_app_flag (RX) : ORIGIN = 0x0000E000, LENGTH = 0x00000004
m_interrupts (RX) : ORIGIN = 0x00010000, LENGTH = 0x00000400
m_flash_config (RX) : ORIGIN = 0x00010400, LENGTH = 0x00000010
m_text (RX) : ORIGIN = 0x00010410, LENGTH = 0x0007F9F0
m_data (RW) : ORIGIN = 0x1FFF8000, LENGTH = 0x00008000
m_data_2 (RW) : ORIGIN = 0x20000000, LENGTH = 0x00006E00
m_cal (RW) : ORIGIN = 0x20006000, LENGTH = 0x00001000
}
SECTIONS
{
/* App flag*/
.app_flag :
{
. = ALIGN(4);
KEEP(*(.AppFlag))
. = ALIGN(4);
} > m_app_flag
.interrupts :
{
__VECTOR_TABLE = .;
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} > m_interrupts
.flash_config :
{
. = ALIGN(4);
KEEP(*(.FlashConfig)) /* Flash Configuration Field (FCF) */
. = ALIGN(4);
} > m_flash_config
*****以下省略*****
}
下面是startup.S
前面中断向量
.size __isr_vector, . - __isr_vector /* 得到大小 */
.section .FlashConfig, "a"/* 根据ld文件设置4字节的FLash配置 */
.long 0xFFFFFFFF /* 8 bytes backdoor comparison key */
.long 0xFFFFFFFF /* */
.long 0xFFFFFFFF /* 4 bytes program flash protection bytes */
.long 0xFFFF7FFE /* FDPROT:FEPROT:FOPT:FSEC(0xFE = unsecured) */
/* Init the rest of the registers */
ldr r1,=0
ldr r2,=0
ldr r3,=0
ldr r4,=0
ldr r5,=0
ldr r6,=0
ldr r7,=0
mov r8,r7
mov r9,r7
mov r10,r7
mov r11,r7
mov r12,r7
/* Initialize the stack pointer */
ldr r0,=__StackTop
mov r13,r0
ldr r0,=SystemInit /* 用位置无关码跳转 */
blx r0
ldr r0,=init_data_bss /* 用位置无关码跳转 */
blx r0
cpsie i /* Unmask interrupts */
bl main
下面是startup.c
,主要实现了init_data_bss
void init_data_bss(void)
{
uint32_t n;
uint8_t * data_ram;
uint8_t * code_ram;
uint8_t * bss_start;
const uint8_t * data_rom, * data_rom_end;
const uint8_t * code_rom, * code_rom_end;
const uint8_t * bss_end;
/* Addresses for VECTOR_TABLE and VECTOR_RAM come from the linker file */
extern uint32_t __RAM_VECTOR_TABLE_SIZE[];
extern uint32_t __VECTOR_TABLE[];
extern uint32_t __VECTOR_RAM[];
/* Get section information from linker files */
extern uint32_t __DATA_ROM[];
extern uint32_t __DATA_RAM[];
extern uint32_t __DATA_END[];
extern uint32_t __CODE_RAM[];
extern uint32_t __CODE_ROM[];
extern uint32_t __CODE_END[];
extern uint32_t __BSS_START[];
extern uint32_t __BSS_END[];
/* Data */
data_ram = (uint8_t *)__DATA_RAM;
data_rom = (uint8_t *)__DATA_ROM;
data_rom_end = (uint8_t *)__DATA_END;
/* CODE RAM */
code_ram = (uint8_t *)__CODE_RAM;
code_rom = (uint8_t *)__CODE_ROM;
code_rom_end = (uint8_t *)__CODE_END;
/* BSS */
bss_start = (uint8_t *)__BSS_START;
bss_end = (uint8_t *)__BSS_END;
/* Check if VECTOR_TABLE copy is needed */
if (__VECTOR_RAM != __VECTOR_TABLE)
{
/* Copy the vector table from ROM to RAM */
for (n = 0; n < (((uint32_t)__RAM_VECTOR_TABLE_SIZE)/sizeof(uint32_t)); n++)
{
__VECTOR_RAM[n] = __VECTOR_TABLE[n];
}
/* Point the VTOR to the position of vector table */
INT_VECTOR_Reg = (uint32_t)__VECTOR_RAM;
}
else
{
/* Point the VTOR to the position of vector table */
INT_VECTOR_Reg = (uint32_t)__VECTOR_TABLE;
}
/* Copy initialized data from ROM to RAM */
while (data_rom_end != data_rom)
{
*data_ram = *data_rom;
data_ram++;
data_rom++;
}
/* Copy functions from ROM to RAM */
while (code_rom_end != code_rom)
{
*code_ram = *code_rom;
code_ram++;
code_rom++;
}
/* Clear the zero-initialized data section */
while(bss_end != bss_start)
{
*bss_start = 0;
bss_start++;
}
}
最后祝大家新年快乐。