链接脚本(编译链接使用,改变数据运行地址)
1、nor flash为自读存储器,虽然可以在上面直接运行代码,但是变量由于自读,其值不可改变,需要将其搬运到SDRAM上运行。
2、在-Tdata段,0x30000000数据段变量的存储地址,但是这样做会使得生成的bin文件十分庞大,因为在代码段和数据段之间有很多空白区域。(data包含静态初始化的数据,所以有初值的全局变量和static变量在data区)
all: arm-linux-gcc -c -o led.o led.c arm-linux-gcc -c -o uart.o uart.c arm-linux-gcc -c -o init.o init.c arm-linux-gcc -c -o main.o main.c arm-linux-gcc -c -o start.o start.S arm-linux-ld -Ttext 0 -Tdata 0x30000000 start.o led.o uart.o init.o main.o -o sdram.elf arm-linux-objcopy -O binary -S sdram.elf sdram.bin arm-linux-objdump -D sdram.elf > sdram.dis clean: rm *.bin *.o *.elf *.dis
3、所以需要引入链接脚本 sdram.lds,将数据地址重新定位,复制到SDRAM中,在SDRAM中才可进行数据修改。
all: arm-linux-gcc -c -o led.o led.c arm-linux-gcc -c -o uart.o uart.c arm-linux-gcc -c -o init.o init.c arm-linux-gcc -c -o main.o main.c arm-linux-gcc -c -o start.o start.S arm-linux-ld -T sdram.lds start.o led.o uart.o init.o main.o -o sdram.elf arm-linux-objcopy -O binary -S sdram.elf sdram.bin arm-linux-objdump -D sdram.elf > sdram.dis clean: rm *.bin *.o *.elf *.dis
4、引入的 sdram.lds 即为链接脚本,其格式如下:
SECTIONS { .text 0 : { *(.text) } ;代码段地址0 .rodata : { *(.rodata) } ;自读数据段,接着.text存放 .data 0x30000000 : AT(0x800) ;运行地址0x30000000, 数据加载地址0x800(在bin文件中的地址) { data_load_addr = LOADADDR(.data); ;赋值数据加载地址 data_start = . ; *(.data) data_end = . ; } bss_start = .; .bss : { *(.bss) *(.COMMON) } ;bss段,接着.data 0x3xxxxxxx存放 bss_end = .; }
未加AT()时,运行地址与数据加载地址相同;
5、 在start.S文件中写上重定位data段的代码
ldr r1, =data_load_addr /* data段在bin文件中的地址, 加载地址 */ ldr r2, =data_start /* data段在重定位地址, 运行时的地址 */ ldr r3, =data_end /* data段结束地址 */ cpy: ldrb r4, [r1] /* 把bin文件中的数据放入r4 */ strb r4, [r2] /* 把r4的值放入data_start(sdram中运行地址)地址 */ add r1, r1, #1 /* 以下为循环执行拷贝,直到r2(data_start)=r3(data_end),停止拷贝 */ add r2, r2, #1 cmp r2, r3 bne cpy /* 不相等则循环cpy */
6、bss段存放程序中未初始化的或者初始化为0的全局变量和静态变量的一块内存区域;在start.S文件中写上清除bss段数据的代码,不然该空间可能会有其他值;
/* 清除BSS段 */ ldr r1, =bss_start ldr r2, =bss_end mov r3, #0 /* 对应地址清零 */ clean: strb r3, [r1] add r1, r1, #1 cmp r1, r2 bne clean