zoukankan      html  css  js  c++  java
  • uboot移植之重定位

    参考文章:https://blog.csdn.net/skyflying2012/article/details/37660265

      代码的重定位有很多种,之前认识到的重定位是把链接地址设在程序员认为程序应该运行在的地址上,比如对于s3c2440来说,SDRAM的起始位置是0x30000000,就可以把链接地址设为0x31000000,当编译器编译时,会根据程序员设定的0x31000000这个地址,计算各个全局变量的存储地址(在data段或rodata段)。

    如果一个函数要读写一个全局变量,要怎么寻址呢?

      可以用arm-linux-objdump -D uboot > uboot.dis命令生成反汇编文件查看board_init_f的汇编实现,会发现函数末尾存储着一段内容,这些是所谓“LABEL”;

    000020d0 <board_init_f>:
        20d0:    e59f8104     ldr    r8, [pc, #260]    ; 
        20d4:    e3a01000     mov    r1, #0    ; 0x0
        20d8:    e3a02078     mov    r2, #120    ; 0x78
        ...
        ...
        ...
        21a4:    e2455084     sub    r5, r5, #132    ; 0x84
        21a8:    e3c55007     bic    r5, r5, #7    ; 0x7
        ...
        ...
        21d0:    e1a01007     mov    r1, r7
        21d4:    e1a02004     mov    r2, r4
        21d8:    ebfff7bb     bl    cc <relocate_code>
        21dc:    30000f80     .word    0x30000f80
        21e0:    00000048     .word    0x00000048
        21e4:    0005a6c2     .word    0x0005a6c2
        21e8:    00068e94     .word    0x00068e94
        21ec:    0005a6d1     .word    0x0005a6d1
        21f0:    0006104f     .word    0x0006104f
        21f4:    00000040     .word    0x00000040

      当board_init_f函数想调用某个全局变量时,会从“PC+某个偏移量”的地址上定位到LABEL中的某一个存储单元,里面存着的就是存储全局变量的地址。如果链接文件中设的链接地址是0x31000000,那么LABEL中存储的全局变量地址都应该大于这个链接地址,而对于smdk2410这款开发板的uboot来说,它的链接地址设为了0,可以看出LABEL里面存的地址值都比较小,是基于0地址得到的。那么当进行代码重定位以后,这里存储的变量地址也应该进行修改,因为如果不修改的话,如果函数要读写一个全局变量,它读写的地址仍然是重定位之前的地址。

    所以重定位之后,需要把所有函数之后的LABEL区域里面的内容,增加一个重定位的偏移量。为此编译器提供了一个编译选项“-pie”,在arch/arm/config.mk文件中添加这个编译选项:

      添加了这个编译选项之后,编译器会把所有函数的LABEL集中起来放在.rel.dyn段,需要注意的是,.rel.dyn段存储的内容是“存储着全局变量地址的LABEL的地址”,简单来说就是.rel.dyn段存储着LABEL的地址,而不是全局变量的地址,所以重定位代码并不直接修改.rel.dyn段里面的内容,而是通过对.rel.dyn段里存储的地址进行寻址,来找到各个函数LABEL所处的位置,对于LABEL里的内容进行修改。

    注:上面代码中的     21dc: 30000f80 .word 0x30000f80    存储的不是全局变量的地址,而是之前代码中设置的栈指针指向的位置,因此不需要重定位,.rel.dyn段里面也找不到21dc这个地址

    重定位的代码量不多,如下:

    .globl    relocate_code
    relocate_code:
        mov    r4, r0    /* save addr_sp */  
        mov    r5, r1    /* save addr of gd */ 
        mov    r6, r2    /* save addr of destination */  //r6->重定位目标地址
    
        /* Set up the stack                            */
    stack_setup:
        mov    sp, r4  //设置栈,与重定位无关
    
        adr    r0, _start  //根据 当前pc的值 和 _start与当前PC的偏移量 得到_start的绝对地址,存入r0.由于uboot的链接地址是0,这里的_start为0.r0->重定位源位置的首地址
        cmp    r0, r6    //比较 当前代码段的起始地址 和 重定位目标地址是否一样,如果一样就只清除bss段(这是重定位后每次上电都要进行的),不一样就进行重定位
        beq    clear_bss        /* skip relocation */
        mov    r1, r6            /* r1 <- scratch for copy_loop */  //r1->重定位目标地址
        ldr    r3, _bss_start_ofs                      //r3->需要拷贝的代码段、数据段、rel.dyn段的总大小,bss段不需要拷贝,只要把对应的地址段清0即可
        add    r2, r0, r3        /* r2 <- source end address        */  //由于r0==0,这里r2==r3,表示重定位源位置的结束地址(如果链接脚本里定义的链接地址不为0,r2就会加上一个r0的偏移量)。
                                              //r2->重定位源位置的结束地址
    copy_loop: ldmia r0!, {r9-r10} /* copy from source address [r0] */  //从重定位源地址读两个32位的数据,保存在r9、r10,之后r0自增8字节 stmia r1!, {r9-r10} /* copy to target address [r1] */  //把r9、r10中的数据存到重定位目标地址,之后r1自增8字节 cmp r0, r2 /* until source end address [r2] */     //比较重定位源地址是否等于结束地址 blo copy_loop /***************************到此各段拷贝完成,接下来需要修改全局变量的调用地址*****************************/ #ifndef CONFIG_SPL_BUILD /* * fix .rel.dyn relocations */ ldr r0, _TEXT_BASE /* r0 <- Text base */ sub r9, r6, r0 /* r9 <- relocation offset */ ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */  //目前不知道dynsym段时干啥的,先不关心 add r10, r10, r0 /* r10 <- sym table in FLASH */ ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs */ add r2, r2, r0 /* r2 <- rel dyn start in FLASH */  //得到重定位前的.rel.dyn段的起始地址r2 ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs */ add r3, r3, r0 /* r3 <- rel dyn end in FLASH */   //得到重定位前的.rel.dyn段的结束地址r3 fixloop:  //修改全局变量地址的循环 ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! */  //得到r2地址的内容,也就是重定位前某个LABEL的地址 add r0, r0, r9 /* r0 <- location to fix up in RAM */ //加上一个重定位偏移量,得到重定位之后的某个LABEL地址,也是实际使用的LABEL地址 ldr r1, [r2, #4] and r7, r1, #0xff                            //获取这个LABEL的重定位类型,如果是23(0x17),就是上文讲的那种重定位方式,跳转到fixrel cmp r7, #23 /* relative fixup? */ beq fixrel cmp r7, #2 /* absolute fixup? */            //如果重定位类型是0x02,跳转到fixabs,先不关心 beq fixabs /* 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]              //读取这个LABEL中的内容,加上一个重定位的偏移量 add r1, r1, r9 fixnext: str r1, [r0]              //再存回LABEL里面 add r2, r2, #8 /* each rel.dyn entry is 8 bytes */  在.rel.dyn段里找到下一个LABEL地址,继续修改。 cmp r2, r3 blo fixloop #endif
  • 相关阅读:
    python subprocess.Popen 非阻塞
    linux错误码
    python中logging
    python多线程和多进程对比
    python多进程提高cpu利用率
    django orm 操作
    linux故障判断
    linux中软链接打包、计算以及同步
    小程序收集formid跳转后收集不到
    Git Base 操作(二)
  • 原文地址:https://www.cnblogs.com/physworld/p/15029543.html
Copyright © 2011-2022 走看看