任务:

在SRAM中将代码从0xd0020010重定位到0xd0024000
任务解释:本来代码是运行在0xd0020010的,但是因为一些原因我们又希望代码实际是在0xd0024000位置运行的。这时候就需要重定位了

思路:

第一点:通过链接脚本将代码链接到0xd0024000
第二点:dnw下载时将bin文件下载到0xd0020010
第一点加上第二点,就保证了:代码实际下载运行在0xd0020010,但是却被链接在0xd0024000。从而为重定位奠定了基础。
当我们把代码链接地址设置为0xd0024000时,实际隐含意思就是我这个代码将来必须放在0xd0024000位置才能正确执行。如果实际运行地址不是这个地址就要出事(除非代码是PIC位置无关码),当以上都明白了后,就知道重定位代码的作用就是:在PIC执行完之前(在代码中第一句位置有关码执行之前)必须将整个代码搬移到0xd0024000位置去执行,这就是重定位。
第三点:代码执行时通过代码前段的少量位置无关码将整个代码搬移到0xd0024000
第四点:使用一个长跳转跳转到0xd0024000处的代码继续执行,重定位完成

总结:重定位实际就是在运行地址处执行一段位置无关码PIC,让这段PIC(也就是重定位代码)从运行地址处把整个程序镜像拷贝一份到链接地址处,完了之后使用一句长跳转指令从运行地址处直接跳转到链接地址处去执行同一个函数(led_blink),这样就实现了重定位之后的无缝连接。

代码修改

link.lds:
. = 0xd0024000
makefile: 修改链接脚本文件名:

led.bin: start.o led.o
    arm-linux-ld -Tlink.lds -o led.elf $^
    arm-linux-objcopy -O binary led.elf led.bin
    arm-linux-objdump -D led.elf > led_elf.dis
    gcc mkv210_image.c -o mkx210
    ./mkx210 led.bin 210.bin
    
%.o : %.S
    arm-linux-gcc -o $@ $< -c -nostdlib

%.o : %.c
    arm-linux-gcc -o $@ $< -c -nostdlib

clean:
    rm *.o *.elf *.bin *.dis mkx210 -f

    
    

start.S:重定义代码

#define WTCON       0xE2700000

#define SVC_STACK   0xd0037d80

.global _start               
_start:
    // 
    ldr r0, =WTCON
    ldr r1, =0x0
    str r1, [r0]
    
    // 
    ldr sp, =SVC_STACK
    
    // 
    mrc p15,0,r0,c1,c0,0;         
    //bic r0, r0, #(1<<12)         
    orr r0, r0, #(1<<12)           
    mcr p15,0,r0,c1,c0,0;
    
   // 第4步:重定位

//ldr和adr都是伪指令,区别是ldr是长加载、adr是短加载。
//重点:adr指令加载符号地址,加载的是运行时地址;
//ldr指令在加载符号地址时,加载的是链接地址。

    adr r0, _start //adr指令用于加载 start当前运行地址        
    

    ldr r1, =_start // // ldr指令用于加载 start的链接地址:0xd0024000
    //
    ldr r2, =bss_start // bss段的起始地址 重定位代码的结束地址 只需要重定位代码段和数据段
    cmp r0, r1          // 比较_start运行地址和链接地址是否相等
    beq clean_bss       //相等执行这里 跳过copy_loop,直接到clean_bss
                        // 不相等 需要重定位 执行copy_loop后执行clean_bss
                        // 

//深入分析:只要知道adr和ldr分别用于加载运行地址和链接地址,
//从而可以判断是否需要重定位即可;
//根本不需知道为什么adr和ldr是这样子,
//但是我们还是给大家扩展讲下为什么adr和ldr可以加载不同的地址。


//重定位:代码的作用是使用循环结构来逐句复制代码到链接地址
//复制的源地址是SRAM的0xd0020010,
//复制目标地址是SRAM的0xd0024000,
//复制长度是bss_start减去_start
//所以复制的长度就是整个重定位需要重定位的长度,也就是整个程序中代码段+数据段的长度。
//bss段(bss段中就是0初始化的全局变量)不需要重定位。

copy_loop:
    ldr r3, [r0], #4    //源
    str r3, [r1], #4    // 目的 完成四字节内容拷贝
    cmp r1, r2          // r1, r2都是链接地址
    bne copy_loop

// 清bss段
clean_bss:
    ldr r0, =bss_start                  
    ldr r1, =bss_end
    cmp r0, r1              // 相等 bss段为空 
    beq run_on_dram         // 为空执行这里
    mov r2, #0
clear_loop: //清空操作
    str r2, [r0], #4        // 先将r2中的值放入r0所指向的内存地址(R0中的内存地址内容)
    cmp r0, r1              // r0 = r0 + 4
    bne clear_loop

run_on_dram:    
    // 
    ldr pc, =led_blink  // 位置有关码 实现长跳转
    
    // 
    //bl led_blink                  // 短跳转
    
// 死循环
    b .
    

link.lds

SECTIONS
{
    . = 0xd0024000;
    
    .text : {
        start.o
        * (.text)
    }
            
    .data : {
        * (.data)
    }
    
    bss_start = .; 
    .bss : {
        * (.bss)
    }
    
    bss_end  = .;    
}

总结

重定位可以理解为需要将代码先重新写入到指定地址,然后长跳转到重写后代码正式执行的地方
SRAM不需要进行初始化可以直接使用

学习记录,侵权联系删除。
来源:朱老师物联网大课堂

07-25 02:42