ARM—代码重定位

参考文章
一、为什么需要重定位

  1. NandFlash启动时:CPU无法直接读写NandFlash中的数据,因此当设置为NandFlash启动时,硬件会自动将NandFlash的前4k的数据复制到SRAM中,CPU会从SRAM中执行烧录到NandFlash的程序,此时SRAM的基地址是0地址。但如果启动时的程序超过了4k,则前4k的内容需将整个程序复制到SDRAM中,然后程序在SDRAM中执行。
  2. NorFlash启动时:NorFlash的特性是只能像内存一样的读,但不能像内存一样的写,因此当设置为NorFlash启动时,启动的程序无法修改和写入程序中的全局变量和静态变量,导致无法正常启动。因此需要将整个程序复制到SDRAM中,然后程序在SDRAM中执行。

二、如何重定位?

  1. 段的概念
    所有的程序在都包括:

.text 代码段 .data 数据段
.rodata 只读数据段(const全局变量)
.bss段 (初始值为0,无初始值的全局变量)
.commen 注释段

其中.bss段和.commen段不保存在bin文件汇总,但保存在elf文件中。

  1. 链接脚本:用来指导程序的链接及运行地址
    链接脚本的语法:

SECTIONS {

secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
{ contents } >region :phdr =fill

}

语法的解释

secname :段名
start :起始地址:运行时的地址(runtime addr);重定位地址(relocate addr)
AT ( ldadr ) :可有可无(load addr:加载地址) 不写时LoadAddr = runtime addr
{ contents } 的内容:
start.o //内容为start.o文件

*(.text)所有的代码段文件
start.o *(.text)文件

对于不同的段,可以设置不同的链接地址和加载地址。

  1. 重定位方法示例
    1)
arm-linux-ld -Ttest 0 -Tdata 0x30000000 start.o main.o -o sdram.elf

这种方式本意是将.data段放到0x30000000(SDRAM)中运行,从而避免NorFlash无法写数据的问题,但同时也将数据段的加载地址放到0x30000000,而从.text段后到.data段中间会产生黑洞也包括在文件中,从而导致bin文件过大。

2)

SECTIONS{
.test 0 : {*(.text)}
.rodata : {*(.radata)}
.data 0x30000000 : AT(0x700)
{
	data_loadaddr = LOADADDR(.data);
	data_start = .;
	*.data
	data_end = .;
}
.bss : {*(.bss) *(.COMMOM)}
ldr r0, =data_loadaddr
ldr r1, =data_start
ldr r2, =data_end

cpy:
	ldrb r4, [r0]
	strb r4, [r1]
	add r0, r0, #1
	add r1, r1, #1
	cmp r1, r2
	ble cpy

这种方法是将.rodata放到.text段后,将.data段的加载地址放到0x700,运行地址放到0x30000000,由于加载地址和运行地址不一致,因此将.data段的加载地址的数据复制到了0x30000000,当程序在0x30000000读写数据时,就可以正常读写.data段的数据。由于.rodata段本身就是只读不能更改,所以无需重定位。

3)

SECTIONS{
.test 0 : {*(.text)}
.rodata : {*(.radata)}
.data 0x30000000 : AT(0x700)
{
	data_loadaddr = LOADADDR(.data);
	data_start = .;
	*.data
	data_end = .;
}
	bss_start = .;
	.bss : {*(.bss)  *(.COMMOM)}
	bss_end = .;
/**********复制.data段*************/
ldr r0, =data_loadaddr
ldr r1, =data_start
ldr r2, =data_end

cpy:
	ldrb r4, [r0]
	strb r4, [r1]
	add r0, r0, #1
	add r1, r1, #1
	cmp r1, r2
	ble cpy
	
/**********清除.bss段*************/
ldr r0, =bss_start
ldr r1, =bss_end
ldr r2, =0

clean:
	str r2, [r0]
	add r0, r0, #1
	cmp r0, r1
	ble clean

此部分是清除bss段,由于.bss段是所有初始值为0的数据,如果放在bin文件会占用大量的空间导致文件过大,可以通过清零arm中相关的内存空间达到相同的效果,因此在.bin文件中不会含有bss段,需要烧录的bin文件中的程序根据.bss段的地址和大小将.bss段在内存中占用的空间清零。

  1. 重定位程序完整版
SECTIONS{
	. = 0x30000000;
	
	. = ALIGN(4);
	.text : {*(.text)}
	. = ALIGN(4);
	.rodata : {*(.rodata)}
	. = ALIGN(4);
	.data : {*(.data)}
	. = ALIGN(4);
	bss_start = .;
	.bss : {*(.bss) *(.COMMON)}
	bss_end = .;
}
/*******************整体重定位******************/
ldr r0, =0
ldr r1, =_start
ldr r2, =bss_start

cpy:
	ldr r3, [r0]
	str r3, [r1]	//第一条指令运行的地址,即0x30000000
	add r0, r0, #1
	add r1, r1, #1
	cmp r1, r2
	ble cpy

/*******************整体重定位******************/
ldr r0, =0
ldr r1, =bss_start
ldr r2, =bss_end

clean:
	str r0, [r1]
	add r1, r1, #1
	cmp r1, r2
	ble clean
  1. 位置无关码:加载地址与运行地址可以不一致,因为位置无关码使用的是相对地址来进行跳转和执行,即只要在原来的pc指向的地址上加减,不需要绝对地址进行跳转执行。
    包括:1、使用相对跳转指令b、bl,而不是用绝对跳转指令ldr pc, =xxx;
    2、全局变量/静态变量、初始值不为0的数组(在.rodata段中)的访问都是需要绝对地址访问,因此不是位置无关码
    3、重定位后需要绝对跳转指令将当前运行地址跳转到重定位地址。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值