1.重定位的引入
NOR FLASH 可以像内存一样的读,但不能像内存一样的写。无法直接去修改全局变量和静态变量 NAND FLASH 把前面4k的代码放入SRAM。如果程序大于4k时,前面4k的代码需要把整个程序读出来 针对以上情况,引入重定位
程序结构
代码段:text 数据段(全局变量):data 只读全局变量(const):rodata 初始值为0,或者无初始值的全局变量:bss 注释段:common
注:
bss和common段不保存在bin中
2.连接脚本的改进
SECTIONS { ... secname start BLOCK(align) (NOLOAD) : AT ( ldadr ) { contents } >region :phdr =fill ... } secname和contents是必须的,其他可选 secname:段名,用来命名此段 contents:决定哪些内容放在本段,可以是整个目标文件(.o)也可以是目标文件中的某段(代码段,数据段等)。start.o或者这样start.o *(.text) start:是段的重定位地址,即本段运行的地址。如果代码中有位置无关指令,程序运行时这个段必须放在这个地址上。start可以用在任意一种描述地址的符号来描述 BLOCK(align)指定快对齐。比如,前面一个段从0x30000000到0x300003F1此处标记ALIGN(4),表示此处最小占用4Bytes,即使下一个段是紧挨这个段,那么下一个段的起始位置(也就是运行地址)为0x0x300003F4。 NOLOAD:告诉加载器程序运行时不加载该段到内存 AT(ldadr):定义本段存储的地址,如果不使用这个选项,则加载地址等于运行地址,通过这个选项可以控制隔断分别保存于输出文件中不同的位置。
3.简单链接脚本讲解
Makefile
arm-linux-ld -T sdram.lds start.o led.o uart.o init.o main.o -o sdram.elf 1.使用链接脚本时,它将所以的.o文件生成了一个.elf格式的文件。含有地址信息(load addr) 2.使用加载器。把elf文件读入内存(读到load addr) 对裸板,加载器是JTAG调试工具 对APP,加载器也是APP 3.运行 4.如果load addr != runtime addr程序本身要重定位
4.通过汇编来实现重定位
重定位的流程
1.代码被编译成一个bin文件 2.bin文件烧写到flash中 3.flash中的代码被重定位(拷贝)到sdram中 启动开发板时,代码先从flash上运行
重定位的两种方式
A.只重定位数据段(适用于单片机)
说明:
代码段和数据段运行时的地址是分开的 代码段运行时的地址是 0 数据段运行时的地址是 0x30000000
Makefile
arm-linux-ld -T sdram.lds start.o led.o uart.o init.o main.o -o sdram.elf
sdram.lds
SECTIONS{ .text 0 : {*(.text)} .rodata : {*(.rodata)} .data 0x30000000 : AT(0x800) { data_load_addr = LOADADDR(.data); . = ALIGN(4); data_start = . ; *(.data) data_end = . ; } . = ALIGN(4); bss_start = .; .bss : {*.(bss) *(.COMMON)} bss_end = .; }
start.S
bl sdram_init /* 初始化sdram */ /* 重定位data段 */ ldr r1, =data_load_addr ldr r2, =data_start ldr r3, =data_end cpy: ldr r4, [r1] str r4, [r2] add r1, r1, #4 add r2, r2, #4 cmp r2, r3 ble cpy /* 小于或等于跳转 */ /* 清楚bss段 */ ldr r1, =bss_start ldr r2, =bss_end mov r3, #0 clean: str r3, [r1] add r1, r1, #4 cmp r1, r2 ble clean
B.重定位整个程序(常用)
说明:
代码段和数据段是一体的
注意事项
1.链接脚本中指定runtime addr为SDRAM 2.重定位之前的代码与位置无关(此时代码在flash上运行),用位置无关码写成 注:重定位拷贝代码后应该有两份代码,一份在flash中,一份在SDRAM中
sdram.lds
SECTIONS{ . = 0x30000000; __code_start = .; . = ALIGN(4); .text : {*(.text)} . = ALIGN(4); .rodata : {*(.rodata)} . = ALIGN(4); .data : {*(.data)} . = ALIGN(4); __bss_start = .; .bss : { *(.bss) *(.COMMON) } _end = .; }
start.S
bl sdram_init /* 重定位text, rodata, data段 */ mov r1, #0 ldr r2, =_start ldr r3, =_bss_start cpy: ldr r4, [r1] str r4, [r2] add r1, r1, #4 add r2, r2, #4 cmp r1, r2 ble cpy /* 清楚bss段 */ ldr r1, =_bss_start ldr r2, = _end mov r3, #0 clean: str r3, [r1] add r1, r1, #4 cmp r1, r2 ble clean
5.相对跳转,绝对跳转
相对跳转:b/bl 说明:相对跳转,它是相对于当前运行时所处的环境而言的。比如说现在在NorFlash运行代码,则会跳到NorFlash上去执行代码。而不会去SDRAM上去执行代码 eg: bl main 绝对跳转:ldr pc, =??? 当NorFlash上完成了SDRAM的初始化和重定位后,就可以使用绝对跳转。使程序从NorFlash上跳转到SDRAM上。 eg: ldr pc, =main
图解
注释: 黑色为相对跳转 红色为绝对跳转
注意事项
重定位之前,不可使用绝对地址,即不可访问全局变量,静态变量,不可访问有初始值的数组。因为这些放在data段。
6.c语言来实现SDRAM重定位
知识补充
1.C程序中不保存lds文件中的变量 2.借助symbol table保存lds文件中的变量,使用时加上"&"得到它的地址从而得到它的值
start.S
/* 重定位text, rodata, data段整个程序 */ bl copy2sdram /* 清除BSS段 */ bl clean_bss
init.c
void copy2sdram(void) { extern int __code_start, __bss_start; volatile unsigned int *dest = (volatile unsigned int *)&__code_start; volatile unsigned int *end = (volatile unsigned int *)&__bss_start; volatile unsigned int *src = 0; while(dest < end) { *dest++ = *src++; } } void clean_bss(void) { extern int _end, __bss_start; volatile unsigned int *start = (volatile unsigned int *)&__bss_start; volatile unsigned int *dest = (volatile unsigned int *)&_end; while(start < dest) { *start++ = 0; } }
来源:https://www.cnblogs.com/huangdengtao/p/12165037.html