韦东山嵌入式Linux学习笔记之——代码重定位002_链接脚本的引入

2019-07-12 18:13发布

① NOR启动:
② nand 启动:
上次讲到的g_char不能按照程序正确输出的原因是nor启动的时候g_char在nor flash中,其不可写的特性决定了输出结果。现在能否修改Makefile让nor启动时讲代码拷贝到SDRAM中,这样就可以实现全局变量的可读可写了。
现在想在nor flash启动的时候将其中的代码段拷贝至SDRAM中0地址起始的空间,将全局变量g_char拷贝至0x30000000起始的空间。
编译之后发现.bin文件变成了800M,805306369-->0x30000001,符合程序中的g_char所占1字节的设置。但是nor flash一共才2M,不可实现。解决方法有两种:第1种: 只是重定位了全局变量① 在,bin文件中让g_char和代码段靠在一起② 烧写至nor flash中③ 运行时让前面的代码段将g_char复制到SDRAM起始的0x30000000地址空间中,即重定位。第2种:重定位了整个代码+全局变量让代码段和g_char之间没有那么大的空洞。
我们怎么将位于0x30000000的数据data段跟位于0地址的代码段拼在一起呢?因为这些复杂的操作通过简单的参数设置已经无法实现,因此需要引入链接脚本来完成相应的操作。链接脚本:
通过链接脚本的方式实现:
SECTIONS { .test 0 : { *(.test) } .rodata : { *(.rodata) } .data 0x30000000 :  AT(0X800) { *(.data) } .bss : { *(.bss) *(.COMMON)} }编译

烧写至开发板的nor flash之后发现输出是乱码,烧写至nand flash之后依然是乱码。
在链接脚本中,我们将data段定位到了0x800(2048)的位置上,但是在main函数中访问g_char时的地址是0x30000000。
下面查看main函数的反汇编码:
从反汇编码可以看出,寄存器r3确实在0x30000000地址中得到了其中的值,但是我们在程序中并没有将g_char的值放到0x3000000的地址空间去,因此缺少了重定位的一个步骤。修改代码:现在需要将g_char的值从0x800的地方复制到0x30000000的地址空间去。在start.S文件中加入重定位的代码即可: /* 重定位data段 */ mov r1, #0x800 mov r0, [r1] mov r1, #0x30000000 str r0, [r1]之后编译代码下载至开发板,发现还是乱码。。。“RP值低预警”。。。查找问题后发现,原因竟然是。。。在start.S中没有初始化SDRAM!!
之后再次编译代码下载至开发板(注意设置开发板启动模式为nor启动):

成功!!接下来的问题是,刚刚的重定位代码并不通用。在重定位的时候我们是在知道原地址和重定位地址的情况下从0x800仅仅复制了一个字节到0x30000000地址中去的,如果我们要复制多个字节又该怎么做呢?首先修改链接脚本:SECTIONS { .text 0 : { *(.text) } .rodata : { *(.rodata) } .data 0x30000000 : AT(0X800) { data_load_addr = LOADADDR(.data); data_start = . ; *(.data) data_end = . ; } .bss : { *(.bss) *(.COMMON)} }注意:链接脚本中的“.”代表当前地址,这里的当前地址指的是前面各种段地址的顺延。
然后修改start.S文件: /* 重定位data段 */ ldr r1, =data_load_addr /* data段在bin文件中的地址,加载地址 */、 ldr r2, =data_start /* data段的重定位(从nor或者nand flash中定位至SDRAM中)开始地址,运行时的地址 */ ldr r3, =data_end /* data段重定位的结束地址 */ /* 接下来将r1所指地址中的值拷贝至r2所指的地址空间 */ cpy: ldrb r4, [r1] //从r1地址处拷贝一个字节到r4 strb r4, [r2] add r1, r1, #1 add r2, r2, #1 cmp r2, r3 bne cpy在main函数中加入一个全局变量:char g_char = 'A'; char g_char3 = 'a'; const char g_char2 = 'B'; int g_A = 0; int g_B; int main(void) { uart0_init(); while(1) { putchar(g_char); g_char++; /* nor启动时,此代码无效 */ putchar(g_char3); g_char3++; /* nor启动时,此代码无效 */ delay(100000); } 编译烧写:
成功。