重定位

前言:

一个程序分成代码段,只读数据段,可读可写数据段,BSS段。

  1. 代码段:就是程序本身,不会被修改

  2. 只读数据段:放在ROM上,不需要复制到RAM

  3. 可读可写数据段:有初始值的全局变量,静态变量,需要从ROM复制到RAM

  4. BSS或者ZI段:未初始化的全局变量或静态变量在使用前清零即可

    Tips:局部变量保存在栈中,堆用malloc分配

重定位

保存在ROM上的全局变量的值,如果想使用,需要先复制到RAM,这个过程就是重定位

1. 重定位怎么做

  • 谁来做重定位

既然想使用变量和函数,需要将其复制到RAM,那怎么去做?程序本身可以把自己复制到RAM上,也就是复制到所谓的链接地址。我们可以使用位置无关码(放在RAM或者ROM上都可以执行的代码)来实现。另外BSS段不需要重定位,因为程序里根本不保存BSS段,使用前把BSS段对应的空间清零。

  • 如何写出位置无关码:

使用跳转指令实现,并且使用相对跳转指定,而不是绝对跳转指令。用bl main,而不是 ldr pc, = main。相对跳转指令会让指令有一个offset偏移到RAM上,而绝对跳转指令,直接指向该地址,但是该地址还是空的,所以程序必然跑飞。

  • 如何重定位

需要知道三要素:源、目的、长度

  • 源:代码段/数据段保存地方(加载地址)
  • 目的:代码段/数据段要被复制地方(链接地址)
  • 长度:代码段/数据段的长度

通过链接脚本可以知道上述的三要素

  • 清除BSS段

需要知道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++;
    }
}

最后祝大家新年快乐。

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值