13.2代码重定位_链接脚本的引入与简单测试
前面程序运行,发现从Nand Flash启动和从Nor Flash启动表现是不一样的。
设置成Nand Flash启动没有问题 显示ABCDE...
设置成NOor Flash启动则显示AAA...
这是什么原因呢?
假如现在是Nor启动:

Nor Flash就被认为是0地址,g_Char被放在0x700后面。CPU上电后从0地址开始执行,它能读取Nor Flash上的代码,打印出A,当进行g_Char++的时候,写操作操作无效,下次读取的数据仍然是A。
假如现在是Nand启动:

上电后,Nand Flash前4K代码就被自动的复制到SRAM里面,SRAM是CPU认为的0地址。CPU上电后从0地址开始执行,它读取SRAM上的代码,并g_Char++修改变量,下次读取的数据就依次增加了。
为了解决Nor Flash里面的变量不能写的问题,我们把变量所在的数据段放在SDRAM里面,看行不行。
修改Makefile 指定数据段为0x30000000 -Tdata 0x30000000:
arm-linux-ld -Ttext 0 -Tdata 0x30000000 start.o led.o uart.o init.o main.o -o sdram.elf
这样的话编译出来的bin文件 从0地址 到 0x30000000地址 文件大小有700多MB,代码段和数据段直接有间隔,称之为黑洞。

解决黑洞有两个办法:
第一个方法
把数据段的g_Char和代码段靠在一起;
烧写在Nor Flash上面;
运行时把g_char(全局变量)复制到SDRAM,即0x3000000位置(重定位);
第二个方法
让文件直接从0x30000000开始,全局变量在0x3......;
烧写Nor Flash上 0地址处;
运行会把整个代码段数据段(整个程序)从0地址复制到SDRAM的0x30000000(重定位);
这两个方法的区别是前者只重定位了数据段,后者重定位了数据段和代码段。
第一种办法如何实现 修改Makefile的代码段地址,使用链接脚本sdram.lds指定。
#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
链接脚本的语法:

我们需要依次排列 代码段、只读数据段、数据段、.bss段、.common。
其中数据段放在0x700,但运行时在0x3000000:

重新编译后烧写bin文件,发现启动后显示乱码。原因是我们从0x30000000处获取g_Char,但在这之前,并没有在0x30000000处准备好数据。因此需要重定位数据段,将0x700的数据移动到0x30000000处,在start.S加入:

上面的这种方法,只能复制0x700处的一位数据,不太通用,下面写一个更加通用的复制方法:
链接脚本修改如下:

修改start.S


