ARM新一期第四天

一、
代码为什么需要重定向?
在这里插入图片描述

代码测试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的值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值