重定位的实现

概念

dbug版本 & release

        软件编译过程为 预编译、编译、链接,链接后的文件已经是二进制文件,cup已经可以运行,此时的版本被称为dbug版本,但是dbug版本的二进制信息中有很多的符号信息,如函数名,目的是用于调试,反汇编时就可以显示出符号,实际符号cpu是不需要的,所以可以使用strin工具将其符号信息去掉,就变成了release发布版,程序中通常有三分之一的空间是符号,所以release可以节约空间。

程序段

        编译器把一个程序分成多个段(代码段(.tex段)、数据段、bbs段、自定义段),在链接时按指定的顺序存放,链接器则根据段名来识别其段的类型。

代码段

        又被称为.text段,就是函数编译后的段。

数据段

        就是初始值非零全局变量和非零静态变量。

bbs

        又叫ZI段(zero initial),初始值是零的全局和静态变量,初始值是0

位置无关码

        相对地址访存,举例“同学B的前面是同学A,前后关系,与位置无关”,如我们使指针基于当前进行偏移,就是位置无关码。例如bl指令。

位置有关码

        绝对地址访存,举例“B同学在第二,第一的是A同学,则与位置有关”,如我们调用函数,函数存放在内存的指定位置,要调用该函数就要pc指针跳转到该函数的起始地址,是基于地址进行跳转的所以是位置有关码,我们写的代码90%都是位置有关码。例如pc长跳转。

链接地址

        链接地址是程序员给链接器指定的地址(makefile、-Ttext),告诉链接器将来程序的运行地址,链接器就会从给定的地址开始使用,来分配变量,划分各个数据区。

运行地址

        则是代码实际在运行时的地址,取决于代码被下载到内存的什么位置,210的硬件是从d0020010开始引导,所以必须下载到该地址。要程序运行必须运行地址与链接地址相同。然而很多时候我们无法做到相同。

两地址为什么不相同

统一编址

        cpu的所有外设都是统一编址,例如iram、irom的起始地址是0x0000000;DRAM(ddr)的起始地址是0x20000000;

iRAM的大小限制

        cpu内的iRAM大小仅为96K,uboot的大小超出了该大小。

DRAM需要初始化

        SRAM不需要初始化,但是的价格高,容量小,所以选择使用DRAM,DRAM的特点是价格低、容量大,但是需要初始化后才能使用。

结论

        结合以上特点,程序运行时要求运行地址与链接地址相同,且BL0会从0X2000010地址(IRAM)引导,我们视乎可以把我们代码的链接地址设置为0X2000010,但是遗憾的是iRAM装不下我们所有的代码。最终还是得到SRAM中去运行,可是SRAM的起始地址是0x20000000,没办法我们只能把链接地址设置成SRAM的地址,所以是注定了前部分的代码的运行地址与链接地址不同。需要进行重定位。

重定位

        有时候运行地址与链接地址不能相同,直接运行就会出错,所以就要重定位,重定位的原理就是先用位置无关码去初始化SRAM,再将整个程序拷贝到SRAM中的链接地址处,再用长跳转,跳转段SRAM中的指定位置(不是起始地址),继续运行(不是重头运行)。实现步骤如下:

  • 脚本链接地址改成想要的地址x,makefile中的链接地址改脚本名称。
  • 把程序下载到d0020010地址(必须是该地址,硬件决定的)。
  • 判断运行地址是否等于链接地址(adr获取运行地址;ldr 获取链接地址)。
  • 使用少量的位置无关码将代码拷贝到链接地址X处。
  • 长跳转到链接地址处继续执行,给pc赋值实现。

链接脚本示例

SECTIONS          // SECTIONS { } 表示这是个整体
{
    . = 0x2000;       //  点 . = 设置当前地址(开始地址)0x2000
    .text : {         //.text 表示代码段
         start.o      //该程序放第一个地址0x2000
         * (.text)    // *通配符,其它所有程序都放在这里,先后顺序自由分配。
             }  
    .data : {         //数据段
         * (.data)    //所有初始值不为0的数据(变量),存放在这。
         }
    bss_start = . ;    // bss_start 是一个符号,等于当前地址(0x2000+代码段长
                        //度+数据段长度),在其他文件中会引用该符号来判断运行地
                        //址是否等于链接地址,将当前地址定义为bss段
    .bss : {            // bss_start 是一个符号,等于当前地址(0x2000+代码段长
         * (.bss)       //   度+数据段长度),
            }
    bss_end  = .;    // bss_end当前地址(0x2000+代码段长度+数据段长度+bss段)
}

重定位示例代码

.global _start
_start:
    /*重定位,加载并判断运行与连接地址*/
    adr r0, _start          //加载_start的运行地址 ,重定位判断的关键
    ldr r1,  =_start        //加载_start的链接地址,重定位判断的关键      
    cmp r0, r1              //比较两地址是否相同,
    beq adr_jump            //相同则不执行重定位;不同则向下执行。
    /*拷贝代码到链接地址去*/
    ldr r2, = bss_start     //加载bbs_start的链接地址,即代码段的结束地址。
copy_loop:
    ldr r3,[r0],#4          //向运行地址获取代码后R0的值加4,长加载;
    str r3,[r1],#4          //写入到链接地址后R1的值加4;短加载
    cmp r1,r2               //判断代码是否搬运完成
    bne copy_loop           //没有完成就继续跳回去搬运,直到完成,bss段是0,不搬运;
    /*清除bss,运行地址的bbs段在执行main函数前已被清零,重定位的bss没被清零*/
    ldr r3, =bss_end        //加载bbs_end的链接地址,即bbs段的结束地址。
cmp r3,r2                   //起始地址与结速地址相同          
beq leng_jump               //相同则,不执行清bss,不同则继续向下
mov r4, #0                  //将R4写0
cleanloop:
    str r4,[r2],#4          //将0写入到链接地址的bbs段后,地址加4
    cmp r2,r3               //判断是否清理完成
    bne cleanloop           //不等就继续循环清理bss段。
leng_jump:
    ldr pc, = led_blink     //长跳转到链接地址的c函数。
adr_jump:      
    bl  led_blink           //短跳转到运行地址的c函数,用于没有执行重定位。
b .                         //汇编的结束标志,必须要。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值