链接地址--位置无关码

1、一个程序可以分成哪些部分?

   代码段、数据段,BSS段。
代码段:指令。 
数据段:有初始值,并初值不等于0的全局变量或静态变量。
BSS段:初始化为0或没初始化的全局变量或静态变量。

2、链接脚本

以前的makefile 中指定的数据都是顺序存放的,现在我们要弄复杂一点,用一个链接脚本来指定。

led.bin: start.o led.o
	arm-linux-ld -T leds.lds -o led.elf start.o led.o
	arm-linux-objcopy -O binary led.elf led.bin
	arm-linux-objdump -D led.elf > led.dis

start.o : start.S
	arm-linux-gcc -o start.o start.S -c
led.o : led.c
	arm-linux-gcc -o led.o led.c -c
clean:
	rm *.o led.elf led.bin led.dis -f
arm-linux-ld    链接命令          -T leds.lds   指定链接脚本                  -o led.elf start.o led.o     编译输出              arm-linux-gcc   -nostdlib 是不导入标准库
所谓链接脚本就是定义了整个程序编译之后的链接过程,决定了一个可执行程序的各个段的存储位置。来看看这个leds.lds怎么写?
SECTIONS                   
{
	. = 0x50000000;       /* ‘.’当前地址,也就是程序的链接地址从0x50000000开始 */
	.text : {             /* .text 段的名称,这个无所谓,叫什么都可以,{ }中的是内容*/ 
		start.o       /* start.o 整个文件都放这里*/
		* (.text)     /* *表示所有文件,整行就是所有程序的代码段都放这里  */
	}
    		
	.data : {
		* (.data)    /*所有文件的数据段*/
	}
	
	bss_start = .;       /*定义一个变量bss_start,把当前地址赋给它*/
	.bss : {
		* (.bss)
	}
	bss_end  = .;	    /*定义一个变量bss_end,把当前地址赋给它*/
}
一般型式是这样的:
SECTIONS
{...
  
secname start BLOCK(align) (NOLOAD) : AT (ldadr) {contents} >region:phdr=fill
...}

secname和contents是必须的,其他的都是可选的。
secname:段名
contents:决定哪些内容放在本段,可以是整个目标文件,也可以是目标文件中的某段(代码段、数据段等)
start:本段连接(运行)的地址,如果没有使用AT(ldadr),本段存储的地址也就是start地址。start可以用任意一种描述地址的符号来描述。
AT(ldadr):定义本段存储(加载)的地址。

1>上面定义的bss_start 与 bss_end有什么用?

一个程序编译出来的.bin文件是不包含bss段的(避免存太多了,以节省空间)。在程序启动之前,这个bss段应该自己清0,这两个变量就是来记录清哪些内存。

/* 清BSS段 */
clean_bss:
	ldr r0, =bss_start
	ldr r1, =bss_end
	mov r2, #0
clean_loop:
	str r2, [r0], #4
	cmp r0, r1
	bne clean_loop	

2> . = 0x50000000;   链接地址不同会有什么不一样呢?
链接地址为0 时  全局变量 i 的地址为:0x100.
链接地址为0x50000000时  全局变量 i 的地址为:0x50000100.
main程序执行时,是从i的地址去把值读出来,链接地址不一样,存放i的地址也不一样。所以 一个程序运行的时候应该位于它的链接地址,否则去访问全局变量时就会出错。

3、重定位

首先面临一个问题: 为什么重定位代码呢?

系统一启动,就会把nand flash前8k的内容拷到6410内存(硬件实现刚好复制到内存0地址开始)上去,然后从内存0地址开始运行。即使这个程序的链接地址是0x50000000,我们知道一个程序应该位于它的链接地址去执行,否则访问全局变量的地址就会出错,得到的值不对。所以要使这个程序正确运行,在被拷贝到内存的8k代码中要做一件事情:就是把自己拷贝到链接地址去运行,这个就叫做重定位。  重定位:就是把程序拷贝到它的链接地址去。

/* 重定位 */
	adr r0, _start           /* _start当前指令地址 */
	ldr r1, =_start          /* _start的编译地址 */
	ldr r2, =bss_start     
	cmp r0, r1               /*如果当前地址不是链接地址,执行copy_loop*/
	beq clean_bss
copy_loop:
	ldr r3, [r0], #4
	str r3, [r1], #4
	cmp r1, r2
	bne copy_loop
copy_loop:把程序拷贝到链接地址去。内存拷贝3要素:源,目的,大小。 
src:从0地址开始拷,即_start开始。    des:拷到链接地址去,即=_start       大小:从链接地址到bss_start的内容,bss段不拷贝
      那为什么重定位之前的代码 没在链接地址,也能够正确运行呢?
这是另外一个问题了,因为它是用位置无关码写的。

4、位置无关码

 位置无关码就是不管在什么位置,在不在它的链接地址,都可以跑。
   它要符合什么条件呢?
 条件:1、跳转时用相对跳转 b,bl。        2、不能用全局变量和静态变量。

bl test        /* 位置无关 */
ldr pc, =test  /* 位置相关 */

5、重定位之后,怎么跳到链接地址去执行呢?
很简单,给pc 直接赋值就可以了。
ldr pc, =main         /* 重定位完后, 使用位置相关的指令跳转 */
这样就跑到链接地址去执行了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值