代码重定位 - SOC裸机

1. 重定位

关于重定位的必要性,核心是硬件资源大小的有限性和硬件资源的属性导致,最终为了节省成本,引入的重定位的概念。

1.1. 重定位引入

根据之前的S3C2440程序启动流程,对于Norflash启动的状态,初始地址0为nor起始地址,而Nandflash启动时起始0地址是4kRAM空间。对于这两种状态情况下,进行地址0x0写操作效果完全不同。对于norflash其没有写的功能,只能读数据,写不起任何作用;对于Nandflash的0x0地址其写的是RAM空间,这个地址写可以正常生效。因此,对于norflash启动方法,如果程序放在这个位置,在进行局部变量或者全局变量的读写时,写操作不生效。对于Nandflash,起始ram空间4K有限。基于如上两种原因,需要将代码重新定位到内存SDRAM中进行代码执行。

1.2. lds链接脚本

从定位是非常必要的,通过什么方式实现从定位呢?通过连接脚本配合实际的代码实现代码重定位。一个代码实际生成文件包含多种不同类型的结构内容,详细见一个C文件的文件结构和运行情况的文章程序文件分布_生活需要深度-CSDN博客。重定位就是讲这些文件中一定的字段进行数据搬移,放在能够规避硬件弊端的SDRAM中进行执行。

对于Nandflash类型的启动,在启动过程开始就会将4K内存空间直接硬件拷贝到内存SDRAM中执行,但是Norflash没有这一项功能,如果在编译脚本中    arm-linux-ld -Ttext 0 -Tdata 0x800  start.o led.o uart.o init.o main.o -o sdram.elf指定代码段从0开始,数据段从0x800开始,这种情况下全局初值非零的变量是没有办法进行修改操作,导致Norflash启动以后的代码没有办法正常运行。这个时候如果我们将数据段的内容地址指定到SDRAM空间位置是否可以?可以,但是这个时候可执行文件就会一下子变得很大。有其他方式吗?重定位。重定位两种方法解决,一种方式重定位数据段到内存所在空间,另外一种方式就是将整体代码重定位到内存所在空间。对于第一种方式的从定位需要配合脚本文件解决。

2. 定位方式

 2.1. 数据段重定位

如果数据断已经知道,重定位很简单直接复制即可。但实际代码编写过程内容都是变化的,如何解决这种问题?通过连接脚本类似宏的指定语言配合汇编代码实现数据段的重定位。在连接脚本中通过如下方式制定数据段的起始变量和结束变量的位置。实际汇编代码中使用连接脚本动态制定的当前代码数据段和代码段位置进行数据的搬移。

SECTIONS {
   .text   0  : { *(.text) }
   .rodata  : { *(.rodata) }
   .data 0x30000000 : AT(0x800) 
   { 
      data_load_addr = LOADADDR(.data);
      data_start = . ;
      *(.data) 
      data_end = . ;
   }
   .bss  : { *(.bss) *(.COMMON) }
}
	/* 重定位data段 */
	ldr r1, =data_load_addr  /* data段在bin文件中的地址, 加载地址 */
	ldr r2, =data_start 	 /* data段在重定位地址, 运行时的地址 */
	ldr r3, =data_end 	     /* data段结束地址 */

cpy:
	ldrb r4, [r1]
	strb r4, [r2]
	add r1, r1, #1
	add r2, r2, #1
	cmp r2, r3
	bne cpy

对应的链接命令使用连接脚本sdram.lds,命令:arm-linux-ld -T sdram.lds start.o led.o uart.o init.o main.o -o sdram.elf

在连接脚本中我们可以对代码的段位置进行相应的定位,在实际代码编写过程中进行使用,进而实现代码大小的搬移,便实现了代码复制搬运的工作。

2.2 链接脚本

Using LD, the GNU linker官方链接文档说明:
http://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_mono/ld.html

2.3. 清除BSS段

代码bin文件长度是不包含bss段,这些未初始化的变量实际加载进来也是没有意义的,但是需要在代码加载的过程中将这部分字段直接初始化为0,这样防止代码打印出莫名其妙的乱码,这个工作叫做BSS段清除

3. 位置无关码

实际代码开发过程中,编写的代码最好是位置无关,通过相对偏移地址来实现代码的执行,这种方式硬件适配性很高。使用ldr指令将需要长跳转的地址位置加载到PC寄存器中,对应的位置就进行了跳转,将对应执行结果跳转到对应的其他位置进行执行。

  ldr和bl在启动程序中,都是可以负责pc跳转的指令。

  bl是地址无关指令,即和当前的运行地址无关。链接器脚本中标明了一个运行地址,但是arm中的代码实际是从地址0开始运行的。这个时候,实际的地址和运行地址是不符的

  如果想让程序正常的运行,就得使用地址无关指令。比如在完成将程序复制到内存之前想要跳转到一个函数里,就得使用bl。因为bl跳转依靠的是相对地址和运行地址无关,所以能完成跳转。

  ldr是地址有关指令。如果这个时候使用“ldr pc,=函数名”来跳转,实际上是跳转到这个函数在链接器脚本中标明的地址上了。所以使用地址相关指令之前,要把代码复制到链接器脚本中指明的那个地址上,否则的话程序就跑飞了。复制完成之后再使用ldr跳转到内存中,使程序继续运行。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值