重定位(第二次学印象更深刻)

说在前面的牢骚话

刚开始第一次学重定位的时候懵懵懂懂,现在回想起来那时候也不清楚为什么要学重定位,不过第二次的学的时候就觉得其实还是很重要的,这是个基础,后面学uboot的时候是需要用到的。

为什么要重定位?

重定位是把烧写到运行地址的程序重新定位到链接地址

有些位置有关码的代码是必须运行地址和链接地址一致才能执行的。

运行地址:

举个例子,用dnw烧写到0xd0020010的位置,这个地址就是运行的起始地址。
在这里插入图片描述

链接地址:

在链接脚本或者Makefile里面
在这里插入图片描述
划线的0x0就是链接的地址。

或者是在链接脚本里:
在这里插入图片描述

那为什么会要这样?

直接在烧录的时候把运行地址写在链接地址的位置不就可以了吗?
在代码量非常少的时候确实可以,但是如果超过了96KB的时候就不行了。

为什么是96KB?
因为S5PV210的iROM application Note讲的,如果其他芯片的话可能SRAM大小不一样。
在这里插入图片描述

为什么写在SRAM里面?
因为SDRAM、DDR这些都没初始化,SRAM是不用初始化就能用的,虽然比较小,所以只能写在SRAM里面。

那控制在96KB不行?
你丫的96KB能跑操作系统?
在这里插入图片描述
这个uboot都384KB了,装不下,所以要用到重定位。

小总结

按照我的理解,程序的链接地址可以写得跟你的DDR或者SDRAM相匹配,然后在烧录的时候,iROM会先从外部启动设备里面执行BL0,初始化各种巴拉巴拉(上两节博客有写),然后把BL1放在SRAM里面,SRAM里面运行的BL1的代码(并且是位置无关码)负责把DDR/SDRAM初始化,然后再从你的外部启动设备里面把代码复制到对应的你写的链接地址上面(这就是重定位)使得运行地址和链接地址相匹配。
外部启动设备:
USB启动:那就是你的电脑
SD卡启动:那就是你的SD卡

代码,重点看后面的重定位

前面的已经之前有做过记录了,重定位部分是自己写了一些注释。
补充:

  • adr是短跳转,加载的是运行地址
  • ldr是长跳转,加载的是链接地址
  • STR Store 字数据存储指令 STR R0,[R1],#8;将R0中的字数据写入R1为地址的存储器中,并将新地址R1+8写入R1
/*
 * 文件名:	led.s	
 * 作者:	朱老师
 * 描述:	演示重定位(在SRAM内部重定位)
 */

#define WTCON		0xE2700000

#define SVC_STACK	0xd0037d80

.global _start					// 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
	// 第1步:关看门狗(向WTCON的bit5写入0即可)
	ldr r0, =WTCON
	ldr r1, =0x0
	str r1, [r0]
	
	// 第2步:设置SVC栈
	ldr sp, =SVC_STACK
	
	// 第3步:开/关icache
	mrc p15,0,r0,c1,c0,0;			// 读出cp15的c1到r0中
	//bic r0, r0, #(1<<12)			// bit12 置0  关icache
	orr r0, r0, #(1<<12)			// bit12 置1  开icache
	mcr p15,0,r0,c1,c0,0;
	
	// 第4步:重定位
	// adr指令用于加载_start当前运行地址
	adr r0, _start  		// adr加载时就叫短加载		
	// ldr指令用于加载_start的链接地址:0xd0024000
	ldr r1, =_start // ldr加载时如果目标寄存器是pc就叫长跳转,如果目标寄存器是r1等就叫长加载	
	// bss段的起始地址
	ldr r2, =bss_start	// 就是我们重定位代码的结束地址,重定位只需重定位代码段和数据段即可
	cmp r0, r1			// 比较_start的运行时地址和链接地址是否相等
	beq clean_bss		// 如果相等说明不需要重定位,所以跳过copy_loop,直接到clean_bss
						// 如果不相等说明需要重定位,那么直接执行下面的copy_loop进行重定位
						// 重定位完成后继续执行clean_bss。

// 用汇编来实现的一个while循环
copy_loop:
	//r0里面的值是运行地址,整个copy_loop的目的是把运行地址的代码复制到链接地址里面
	//把r0寄存器里面的值load到r3里面,并且将新地址r0+4写到r0里面
	//r3就是一个中转作用
	ldr r3, [r0], #4    // 源
	
	r1里面的值是链接地址
	//把r3寄存器里面的值放到r1里面,并且将新地址r1+4写到r0里面
	str r3, [r1], #4	// 目的   这两句代码就完成了4个字节内容的拷贝
	//当连接地址r1一直+4总有一天会加到bss段的起始地址r2的里面的值,这时候就证明复制完了
	cmp r1, r2			// r1和r2都是用ldr加载的,都是链接地址,所以r1不断+4总能等于r2
	bne copy_loop

	// 清bss段,其实就是在链接地址处把bss段全部清零
clean_bss:
	ldr r0, =bss_start					
	ldr r1, =bss_end
	cmp r0, r1				// 如果r0等于r1,说明bss段为空,直接下去
	beq run_on_dram			// 清除bss完之后的地址
	mov r2, #0       		// r2也是一个中转作用,放0用的
clear_loop:
	str r2, [r0], #4		// 先将r2中的值放入r0所指向的内存地址(r0中的值作为内存地址),
	cmp r0, r1				// 然后r0 = r0 + 4
	bne clear_loop

run_on_dram:	
	// 长跳转到led_blink开始第二阶段
	ldr pc, =led_blink				// ldr指令实现长跳转
	
	// 从这里之后就可以开始调用C程序了
	//bl led_blink					// bl指令实现短跳转
	
// 汇编最后的这个死循环不能丢
	b .
	
发布了38 篇原创文章 · 获赞 1 · 访问量 1030

猜你喜欢

转载自blog.csdn.net/qq_40897531/article/details/104327992
今日推荐