移植u-boot学习笔记3-----分析启动过程之重定位

1、重定位代码步骤

1.1 从NOR FLASH把代码复制到SDRAM
1.2 程序的链接地址是0,访问全局变量、静态变量、调用函数时是使"基于0地址编译得到的地址"
      现在把程序复制到了SDRAM
     需要修改代码,把"基于0地址编译得到的地址"改为新地址
1.3 程序里有些地址在链接时不能确定,要到运行前才能确定:fixabs

1、重定位的实现

(1)代码原型(C语言调用汇编代码)

参数addr_sp存在r0里

参数id存在r1里

参数addr存在r2里



(2)里面的addr参数

a、addr的地址是0x33ff0000减去整个u-boot的大小。

b、看看u-boot大小


gd结构体的mon_len成员


_bss_end_ofs的定义,里面的_start是代码段的开始


_bss_end_在反汇编文件u-boot.lds中搜索,由于此值不容易搜,因而在反汇编文件中搜索bss_end_ofs,也就是说是0xae4e0,换算为697k,而我们的代码段从中看只有400多k,加上bss段就差不多700多k.0x33ff0000减去0xae4e0就等于addr(0x33f41B20),然后把低12位清零(因为4096-1=4095,换算成16进制是0xfff).也就是addr=0x33f40000.



(3)函数分析

先把栈的地址赋给r4.

把gd结构体的地址赋给r5.

吧u-boot从flash拷贝到SDRAM 上的目的地址赋给r6.


重新设置栈,r4是栈的地址。把栈地址赋给sp就是设置栈。一开始调用board_init_f函数前已经设置栈指向SDRAM 的0x30000f80,现在调用重定位函数时重新修改了这个栈。

    adr r0,_start里面_start是第一条语句的当前地址,当前地址和r6目的地址比较,如果相等,就没必要拷贝代码(重定位),直接跳过清除bss段。如果不相等,就把代码从flash拷贝到内存里。从r0这个地方拷贝到r6这个地方。add r2,r0,r3的意思是r2=r0+r3,r2是源代码的结束地址,而r0是源代码的开始地址,而r3是代码段的长度(因为,在二进制文件里面是不包含bss段的,)。


从源地址吧数据存到寄存器r9和r10上来(每次复制8字节),每复制一次改变一次地址,再把这两个寄存器的值复制到目的地址r1里面去,再比较r0和r2,这里r2是结束地址,r0是源地址(单每次复制数据后地址值都会递增),当两值不相等时,继续循环操作,相等时跳出。从这里看出,u-boot只支持nor flash启动,因为这种读写代码只适合于nor flash.


4、拷贝后修改代码(把"基于0地址编译得到的地址"改为新地址)

(1)假若访问nor flash 的0x100地址的变量,复制到SDRAM的0x33f41000 后使用新地址0x33f4,1100访问变量,怎么知道去修改哪一些代码?

(2)分析"重定位之修改代码为新地址":
#ifndef CONFIG_SPL_BUILD
/*
* fix .rel.dyn relocations(动态重定位)
*/
ldr r0, _TEXT_BASE/* r0<- Text base */
// r0=0, 代码基地址m,

sub r9, r6, r0/* r9 <- relocation offset */重定位偏移值
// r9 = r6-r0 = 0x33f41000 - 0 = 0x33f41000  ,r0是源地址,r6是目标地址

ldr r10, _dynsym_start_ofs/* r10 <- sym table ofs */动态符号表的偏移值
// r10 = 00073608

其中_dynsym_start在链接脚本中定义了,编译的时候会把它确定下来。就是这一段的起始地址,减去start就是偏移地址。在反汇编里确定值 是00073608

add r10, r10, r0/* r10 <- sym table in FLASH */flash上动态符号表的地址
// r10 = 00073608 + 0 = 00073608

在反汇编中动态表如下



ldr r2, _rel_dyn_start_ofs/* r2 <- rel dyn start ofs */
// r2=0006b568,在反汇编中搜索
链接文件有定义是相对信息段,是可重定位的意思,把要修改地址的变量的信息都放在此段。
add r2, r2, r0/* r2 <- rel dyn start in FLASH */这些信息在flash上的起始地址
// r2=r2+r0=0006b568

ldr r3, _rel_dyn_end_ofs/* r3 <- rel dyn end ofs */这些信息在flash上的结束地址
// r3=00073608


add r3, r3, r0/* r3 <- rel dyn end in FLASH */这是偏移值
// r3=r3+r0=00073608


修改循环,把旧地址改成新地址

fixloop:
ldr r0, [r2]/* r0<- location to fix up, IN FLASH! */搜索反汇编文件第一次在flash读取r2地址的值
1. r0=[0006b568]=00000020

add r0, r0, r9/* r0<- location to fix up in RAM */r0是刚读出的值,r9是在内存里面的地址
1. r0=r0+r9=00000020 + 0x33f41000 = 0x33f41020

ldr r1, [r2, #4]   r2等于0006b568
1. r1=[0006b568+4]=00000017,也就是相对动态地址的第二个值



and r7, r1, #0xff
1. r7=r1&0xff=00000017 ,也就是保留低八位。
下面有两路标志:一路是0x17,另外一路是2
cmpr7, #23/* relative fixup? */比较r7是否等于23(16进制是0x17)
1. r7 == 23(0x17)
如果相等,跳到fixrel函数执行,(修改相对位移)
beqfixrel
cmpr7, #2/* absolute fixup? */如果为2跳转到fixabs

beqfixabs
/* ignore unknown type of fixup */
b fixnext
fixabs:
/* absolute fix: set location to (offset) symbol value */
mov r1, r1, LSR #4/* r1 <- symbol index in .dynsym */

add r1, r10, r1/* r1 <- address of symbol in table */

ldr r1, [r1, #4]/* r1 <- symbol value */

add r1, r1, r9/* r1 <- relocated sym addr */

b fixnext
fixrel:
/* relative fix: increase location by offset */
ldr r1, [r0]
1. r1=[00000020]=000001e0,也就是从20的地址读值,也就是说下面的相对位移表里存的是地址

add r1, r1, r9 ,这里r9对应的是SDRAM上的目标地址
1. r1=r1+r9=000001e0 + 0x33f41000 = 33F411E0

fixnext:
str r1, [r0]  r0是原flash地址20的SDRAM上的目标地址,地址0x33f41020的值是33F411E0
1. [0x33f41020] = 33F411E0

add r2, r2, #8/* each rel.dyn entry is 8 bytes */
1. r2=r2+8=0006b568+8=6B570
r3是相对信息在flash的结束地址,如果相等表示全部处理完
cmp r2, r3
1. 
当比较中r2<r3时跳转到fixloop,再次循环执行
blo fixloop
#endif

5、图例分析重定位过程(修改代码,改变里面的变量,函数使用新地址)

在nor flash上存储的程序有如图右所示的rel_dyn_start这一段,里面的6b568地址存储的值是0x20,在nor flash上的0x20地址里存放的是某个地址值rel_dyn_start这一段表明你想让程序能够运行,需要修改像0x20地址里面的地址值。nor flash上0地址的代码要复制到SDRAM的33f4,1000地址来,SDRAM地址33f4,1000偏移值为0x20的地方存储的值跟nor flash上0x20地址的值完全一样,我们把代码复制到SDRAM后,要去修改SDRAM上的代码,把里面用到的地址全部改为新地址,rel_dyn_start这一段表明用到哪些地址,表明要修改地址0x20这个地址里的值( 地址值),设这个值在nor flash的0x20地址上是V,在SDRAM 的33f4,1000地址的偏移值0x20的地址值改为v+0x33f4,1000,下面的0x00000017是标志位,表示是直接把0x20里面的值取出来,加上一个偏移,得到一个新的值后,写到SDRAM的33f4,1000地址的偏移值0x20的地址里面去。


这样修改的作用

假设程序从0x08地址(第二条指令)开始执行,也就是执行到ldr pc,...这条指令。就去_undefined_instruction这个地方取一个值undefined_instruction,并把这个值赋给pc,相当于跳转到undefined_instruction



看反汇编代码的实现

从[pc,#20]这个地方


也就是0x20这个地址取出值0x000001e0,把这个值赋给pc,也就调到地址0x000001e0这个地方,也就是undefined_instruction这个函数。在SDRAM 里面,假设执行到地址33f4,1000偏移值为0x08的地方,就去地址33f4,1000偏移值为0x20的地方取值,然后调到33f4,1000+0x000001e0这个地方,也就是跳到undefined_instruction这个函数



6、举例(另外一路标志是2的原理

写mian函数时用到printf,假若程序是动态链接,printf是在库函数里面实现的,运行时才知道printf函数的地址,汇编上会写成ldr pc ,[addr],去某个地方addr读值,这个地方本来的值应该是printf的地址,但是一开始不知道,就写为0,在运行前确定printf地址(是操作系统帮我们做的),把地址写入addr,再赋值给pc,从而执行程序。


7、重定位后程序分析

2.8 重定位代码:
2.8.1 从NOR FLASH把代码复制到SDRAM
2.8.2 程序的链接地址是0,访问全局变量、静态变量、调用函数时是使"基于0地址编译得到的地址"
      现在把程序复制到了SDRAM
      需要修改代码,把"基于0地址编译得到的地址"改为新地址
2.8.3 程序里有些地址在链接时不能确定,要到运行前才能确定:fixabs

另外一路标志是2的原理
2.9 clear_bss
2.10 调用C函数board_init_r:第2阶段的代码

8、链接脚本u-boot.lds分析

链接时加上pie选项(arm-linux-ld -pie),链接脚本u-boot.lds里面就会生成地址信息。

相对动态信息,根据这些信息就可以知道要修改哪一些地址



  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值