uboot开启地址无关后的重定位地址操作

描述

开启地址无关后的重定位地址操作

在由save_boot_params跳转回save_boot_params_ret后启动流程继续往下执行来到下面代码段:

save_boot_params_ret:

#if CONFIG_POSITION_INDEPENDENT
    /* Verify that we're 4K aligned.  */
    adr x0, _start
    ands x0, x0, #0xfff --------------------------------------------------------- (1)
    b.eq 1f
0:
    /*
     * FATAL, can't continue.
     * U-Boot needs to be loaded at a 4K aligned address.
     *
     * We use ADRP and ADD to load some symbol addresses during startup.
     * The ADD uses an absolute (non pc-relative) lo12 relocation
     * thus requiring 4K alignment.
     */
    wfi
    b 0b
1:

    /*
     * Fix .rela.dyn relocations. This allows U-Boot to be loaded to and
     * executed at a different address than it was linked at.
     */
pie_fixup:
    adr x0, _start  /* x0 < - Runtime value of _start */
    ldr x1, _TEXT_BASE  /* x1 < - Linked value of _start */
    subs x9, x0, x1  /* x9 < - Run-vs-link offset */
    beq pie_fixup_done ------------------------------------------------------------- (2)
    adrp    x2, __rel_dyn_start     /* x2 < - Runtime &__rel_dyn_start */ ----------- (3)
    add     x2, x2, #:lo12:__rel_dyn_start
    adrp    x3, __rel_dyn_end       /* x3 < - Runtime &__rel_dyn_end */
    add     x3, x3, #:lo12:__rel_dyn_end
pie_fix_loop: ---------------------------------------------------------------------- (4)
    ldp x0, x1, [x2], #16 /* (x0, x1) < - (Link location, fixup) */
    ldr x4, [x2], #8  /* x4 < - addend */
    cmp w1, #1027  /* relative fixup? */
    bne pie_skip_reloc
    /* relative fix: store addend plus offset at dest location */
    add x0, x0, x9
    add x4, x4, x9
    str x4, [x0]
pie_skip_reloc:
    cmp x2, x3
    b.lo pie_fix_loop
pie_fixup_done:
#endif

此部分的功能主要是在定义POSITION_INDEPENDENT后,进行地址无关的相对地址修复,以此保证后续在跳入c语言部分时可正常执行,一般不定义此配置则是继续往下执行boot流程。

  • • (1)正如在链接脚本中说的,地址无关功能最基本需要保证加载地址是4K对齐的,经过一些测试发现某些情况需要64K对齐,这里不展开说明。此处使用adr指令获取_start的运行时地址并检验是否是4K对齐,如果是则继续往下执行,如果不是则调用wfi指令挂死在此处。wfi为等待中断指令,只有在接收到中断事件是才会唤醒cpu继续往下执行。(补充一个知识点:wfi指令只能被中断唤醒,wfe指令可以被sev指令唤醒也可以被中断唤醒)
  • • (2)通过对运行时地址和加载地址相减得到一个地址偏移值,如果偏移值等于0,说明加载地址和运行时地址是一致的不需要进行地址修复,则直接跳pie_fixup_done继续下面流程初始化,否则就进入地址修复逻辑。
  • • (3)首先需要说明为什么不使用adr指令而是adrp指令,adr指令是一个小范围读相对pc指针地址内存的指令,可以使用adr说明读取的地址一定是离pc指针很近的位置,而当读取__rel_dyn_start这种并不能确定实际地址在哪里的地址时则只能使用大范围读地址指令的adrp指令,不过adrp指令是以页为单位进行读取的,所以add x2, x2, #:lo12:__rel_dyn_start的作用就是将__rel_dyn_start地址的页内偏移读取出来并与页对齐的运行地址相加得到实际的运行地址。(此操作指令在Linux上被封装为adr_l指令)
  • • (4)在对地址修复分析时,首先需要了解一个elf动态库重定位的知识点,先来看一个结构体定义:
typedef struct {
        Elf64_Addr      r_offset;
        Elf64_Xword     r_info;
        Elf64_Sxword    r_addend;
} Elf64_Rela;

当对动态库进行地址重定向时首先会去查找rela.dyn段中的信息,此段中每一组信息就是上面结构体定义的信息,对于64为系统的elf则是24字节为一个表,r_offset保存需要重定位作用的地址位置,r_info描述此表重定位类型此类型特定于处理器,如arm64位则是:

/* AArch64 relocs */
#define R_AARCH64_NONE  0 /* No relocation */
#define R_AARCH64_RELATIVE 1027 /* Adjust by program base */

r_addend是一个常量加数,用于计算存储在可重定位字段中的值。 对表进行重定位有以下公式:

重定位为值 = *(r_offset + 实际偏移值) = 实际偏移值 + r_addend。

根据此公式则可以重定向每一个需要重定向的地址值。

因此在此处同样是如此,x0保存r_offset值,x1保存r_info值,x4保存r_addend,并通过x1与R_AARCH64_RELATIVE比较看是否属于aarch64相对地址类型,如果不是则不是需要重定向的一组表则跳到pie_skip_reloc判断是否完成所有rela.dyn段重定向,如果是则继续往下执行启动流程初始化,如果不是则跳回pie_fix_loop继续下一组表的重定向。

当判断属于aarch64相对地址类型时进入重定向逻辑,首先需要知道x9是运行地址减去链接地址得到的偏移地址,那么实际运行地址也等于链接地址加上x9,所以add x0, x0, x9,add x4, x4, x9,x0是需要修复的重定位运行地址,x4是实际运行时需要附加的常量值,根据上面的公式,将x4这个由附加常量值加偏移值得到的运行时附加常量值写入到x0这个运行时重定向地址即可完成整个重定向修复功能。

此处没有实际代码或者流程图展示,逻辑比较绕,大致逻辑就是根据重定向段中每24个字节组成的一个表读取出实际需要进行重定向的地址,将这个地址的运行地址找出来并往这个地址写入附加常量值加偏移值即可完成一次重定向。在完成段中所有重定向后此时地址已经被修复好了,后续调用任何绝对地址引用的指令也不会出问题了。当然这个操作是耗时的,一般也不会有board会开启此功能。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分