代码重定位
Posted shwzh1990
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代码重定位相关的知识,希望对你有一定的参考价值。
S2C2440当上电的时候会自动的把nor 或者nandflash的前4k字节拷贝到自己的ram中,可是如果当整个程序大于4k的时候怎么办?
代码重定义的方针就是把nor 或者是nandflash里面的代码通通的拷贝到SDRAM中去,然后所有的程序会从SDRAM里面去运行。
首先是写出脚本文件其格式如下:
SECTIONS { . = 0x30000000; //表示重定位的地址是在SDRAM的最初地址 __code_start = .; //把当前的地址给到__code_start 变量 . = ALIGN(4); //四字节对齐 .text : //之后放置。text段 { *(.text) } . = ALIGN(4); //四字节对齐 .rodata : { *(.rodata) } //放置只读数据段 . = ALIGN(4); .data : { *(.data) } //放置数据段 . = ALIGN(4); __bss_start = .; //放置。bss数据段和common段 .bss : { *(.bss) *(.COMMON) } _end = .; }
这个脚本文件会指定分配和指定数据段的位置。然后这个脚本文件会在make里面被调用 其格式如下:
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-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
arm-linux-ld -T sdram.lds start.o led.o uart.o init.o main.o -o sdram.elf
其中这一行就是 arm 编译器会连接sdram.lds 分配各种数据段的位置。后面的文件顺序是有规定的,越靠近前面的越先被编译,有更大的几率会编译进前4k。
在start.S 中,我们需要用C语言把数据写入SDRAM 而且需要把.BSS 段清零。
1 ldr r0, [r1] /* 读出原来的值备份 */ 2 str r1, [r1] /* 0->[0] */ 3 ldr r2, [r1] /* r2=[0] */ 4 cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */ 5 ldr sp, =0x40000000+4096 /* 先假设是nor启动 */ 6 moveq sp, #4096 /* nand启动 */ 7 streq r0, [r1] /* 恢复原来的值 */ 8 9 bl sdram_init 10 //bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */ 11 12 /* 重定位text, rodata, data段整个程序 */ 13 bl copy2sdram 14 15 /* 清除BSS段 */ 16 bl clean_bss 17 18 //bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */ 19 ldr pc, =main /* 绝对跳转, 跳到SDRAM */
其中第十三行和第十6行分别是调用了两个函数用来重定位
text, rodata, data段整个程序 和 清除.bss 段。
这两个函数的代码如下:
1 void copy2sdram(void) 2 { 3 /* 要从lds文件中获得 __code_start, __bss_start 4 * 然后从0地址把数据复制到__code_start 5 */ 6 7 extern int __code_start, __bss_start; 8 9 volatile unsigned int *dest = (volatile unsigned int *)&__code_start; 10 volatile unsigned int *end = (volatile unsigned int *)&__bss_start; 11 volatile unsigned int *src = (volatile unsigned int *)0; 12 13 while (dest < end) 14 { 15 *dest++ = *src++; 16 } 17 } 18 19 20 void clean_bss(void) 21 { 22 /* 要从lds文件中获得 __bss_start, _end 23 */ 24 extern int _end, __bss_start; 25 26 volatile unsigned int *start = (volatile unsigned int *)&__bss_start; 27 volatile unsigned int *end = (volatile unsigned int *)&_end; 28 29 30 while (start <= end) 31 { 32 *start++ = 0; 33 } 34 }
第九道十一行
9 volatile unsigned int *dest = (volatile unsigned int *)&__code_start; 10 volatile unsigned int *end = (volatile unsigned int *)&__bss_start; 11 volatile unsigned int *src = (volatile unsigned int *)0;
后面的那几个变量都是从脚本文件获得而来,其中的思路就是我们确定了代码的初始地址和代码段的结束的地址,然后我们从0地址(就是norflash基地址 或者是nandflash自动拷贝4k到内存的基地址)拷贝所有的内容进入指定的内存的地址上面(就是SDRAM的基地址)
后面的26 到 33行决定了我们应该清楚.bss 段 把所有的变量都清零。 我们要注意只有elf文件是带有没有初始化的全局变量,而bin文件是不带这些变量的。
以上是关于代码重定位的主要内容,如果未能解决你的问题,请参考以下文章