一、
代码为什么需要重定向?
代码测试NOR 和NAND 区别:
#include "s3c2440_soc.h"
#include "uart.h"
#include "init.h"
char g_Char = '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启动时, 此代码无效 ,nor不能直接的写*/
delay(1000000);
}
return 0;
}
nor启动全局变量不可更改,解决办法:
①修改Makefile
arm-linux-ld -Ttext 0 -Tdata 0x30000000 start.o led.o uart.o init.o main.o -o sdram.elf
让全局变量放在基址0x30000000处(SDRAM基址),但是编译出来的bin文件非常大,八百多兆!代码段和数据段之间有巨大间隔
②代码段和数据段不应该有那么大的黑洞,应该是运行的时候数据段拷贝到0x3000,0000处,而不是链接的时候把数据段单独链接到0x3000,0000.
参考资料:
Using LD, the GNU linker
http://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_mono/ld.html
两种解决办法:
1. 只把全局变量重定位到SDRAM
修改Makefile,写一个链接脚本sdram.lds
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
#elf文件含有地址信息,bin文件没有了地址信息
arm-linux-objcopy -O binary -S sdram.elf sdram.bin
arm-linux-objdump -D sdram.elf > sdram.dis
clean:
rm *.bin *.o *.elf *.dis
链接脚本如下:
SECTIONS {
.text 0 : { *(.text) }
.rodata : { *(.rodata) }
.data 0x30000000 : AT(0x800) { *(.data) }
.bss : { *(.bss) *(.COMMON) }
}
对于.lds文件,它定义了整个程序编译之后的连接过程,决定了一个可执行程序的各个段的存储位置,格式如下:
SECTIONS {
...
secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
{ contents } >region :phdr =fill
...
}
参考文档:https://blog.csdn.net/guoke312/article/details/55504341
但是只写了一个链接脚本是不行的,上面只是实现了链接的过程。运行的时候需要把全局变量拷贝到运行地址。
bl sdram_init
/* 重定位data段 */
mov r1, #0x800
ldr r0, [r1]
mov r1, #0x30000000
str r0, [r1]
简单粗暴,直接把0x800上的数据拷贝至0x30000000,虽然能实现效果,但是显然不能适应所有的情况。(NOR上运行没有NAND快)
在链接脚本上可以添加一些变量,在start.S中使用
SECTIONS {
.text 0 : { *(.text) }
.rodata : { *(.rodata) }
.data 0x30000000 : AT(0x800)
{
data_load_addr = LOADADDR(.data);
. = ALIGN(4);//向4取整
data_start = . ;
*(.data)
data_end = . ;
}
. = ALIGN(4);
bss_start = .;
.bss : { *(.bss) *(.COMMON) }
bss_end = .;
}
start.S更改如下:
/* 重定位data段 */
ldr r1, =data_load_addr /* data段在bin文件中的地址, 加载地址 */
ldr r2, =data_start /* data段在重定位地址, 运行时的地址 */
ldr r3, =data_end /* data段结束地址 */
cpy:
ldr r4, [r1]
str r4, [r2]
add r1, r1, #4
add r2, r2, #4
cmp r2, r3
ble cpy
清除bss:
/* 清除BSS段 */
ldr r1, =bss_start
ldr r2, =bss_end
mov r3, #0
clean:
str r3, [r1]//从flash中写入内存
add r1, r1, #4
cmp r1, r2
ble clean//小于或者等于
2. 把全部代码重定位到SDRAM
lds文件
SECTIONS
{
. = 0x30000000;
. = ALIGN(4);
.text :
{
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) *(.COMMON) }
_end = .;
}
start.S文件
/* 重定位text, rodata, data段整个程序 */
mov r1, #0
ldr r2, =_start /* 第1条指令运行时的地址 */
ldr r3, =__bss_start /* bss段的起始地址 */
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, =_end
mov r3, #0
clean:
str r3, [r1]
add r1, r1, #4
cmp r1, r2
ble clean
//bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */
ldr pc, =main /* 绝对跳转, 跳到SDRAM */
重定位_清除BSS段的C函数实现
C代码中如何使用链接脚本中定义的变量
参考文章:https://sourceware.org/ml/binutils/2007-07/msg00154.html
C函数怎么使用lds文件中的变量abc?
a. 在C函数中声明改变量为extern类型, 比如:
extern int abc;
b. 使用时, 要取址, 比如:
int *p = &abc; // p的值即为lds文件中abc的值