U-Boot relocate_code功能

 所谓的relocation,就是重定位,uboot运行后会将自身代码拷贝到sdram的另一个位置继续运行。新版uboot跟老版uboot不太一样的地方在于新版uboot不管uboot的load addr(entry pointer)在哪里,启动后会计算出一个靠近sdram顶端的地址,将自身代码拷贝到该地址,继续运行。

uboot的编译选项发现,在arch/arm/config.mk,如下: 

# needed for relocation

LDFLAGS_u-boot += -pie

uboot只指定了-pie给ld,而没有指定-fPIC或-fPIE给gcc。 

指定-pie后编译生成的uboot中就会有一个rel.dyn段,uboot就是靠rel.dyn段实现了完美的relocation!

    ldr    r0, [r9, #GD_START_ADDR_SP]    /* sp = gd->start_addr_sp */
    bic    r0, r0, #7    /* 8-byte alignment for ABI compliance */
    mov    sp, r0
    ldr    r9, [r9, #GD_NEW_GD]        /* r9 <- gd->new_gd */

    adr    lr, here
    ldr    r0, [r9, #GD_RELOC_OFF]        /* r0 = gd->reloc_off */
    add    lr, lr, r0
#if defined(CONFIG_CPU_V7M)
    orr    lr, #1                /* As required by Thumb-only */
#endif
    ldr    r0, [r9, #GD_RELOCADDR]        /* r0 = gd->relocaddr */
    b    relocate_code
here:
/*
 * now relocate vectors
 */

    bl    relocate_vectors

 

NTRY(relocate_code)
    ldr    r1, =__image_copy_start    /* r1 <- SRC &__image_copy_start */
    subs    r4, r0, r1        /* r4 <- relocation offset */
    beq    relocate_done        /* skip relocation */
    ldr    r2, =__image_copy_end    /* r2 <- SRC &__image_copy_end */

copy_loop:
    ldmia    r1!, {r10-r11}        /* copy from source address [r1]    */
    stmia    r0!, {r10-r11}        /* copy to   target address [r0]    */
    cmp    r1, r2            /* until source end address [r2]    */
    blo    copy_loop

r1=__image_copy_start,也就是 r1 寄存器保存源地址
r0= 
 gd->relocaddr 这个地址就是 uboot 拷贝的目标首地址, 此处的gd为放置到sdram高端的gd,是在setup_reloc函数中对原来gd的复制。

    /*主要目的是修改label中的地址。
   *对于一些绝对地址符号(例如已经初始化的全局变量),会将其以label的形式放在每个函数的代码实现的末端。
*同时,在链接的过程中,会把这些label的地址统一维护在.rel.dyn段中,当relocation的时候,方便对这些地址的fix。
* fix .rel.dyn relocations */ ldr r2, =__rel_dyn_start /* r2 <- SRC &__rel_dyn_start */ ldr r3, =__rel_dyn_end /* r3 <- SRC &__rel_dyn_end */ fixloop: ldmia r2!, {r0-r1} /* (r0,r1) <- (SRC location,fixup) */ and r1, r1, #0xff cmp r1, #R_ARM_RELATIVE bne fixnext /* relative fix: increase location by offset */
// label在relocate uboot的时候也已经复制到了新的uboot地址空间了!!!
// 这里要注意,是对新的uboot地址空间label进行修改!!!

add r0, r0, r4
// 根据前面的描述,我们的目的就是要fix label中绝对地址符号的地址,也就是将其修改为新地址空间的地址

// 所以为r1加上偏移之后,重新存储到label中。

// 后面CPU就可以根据LABEL在新uboot的地址空间中寻址到正确的符号。
//对于一些绝对地址符号(例如已经初始化的全局变量),会将其以label的形式放在每个函数的代码实现的末端。
//同时,在链接的过程中,会把这些label的地址统一维护在.rel.dyn段中,当relocation的时候,方便对这些地址的fix
ldr r1, [r0]
// 从label中获取绝对地址符号的地址,存放在r1中 add r1, r1, r4 str r1, [r0]
// 根据前面的描述,我们的目的就是要fix label中绝对地址符号的地址,也就是将其修改为新地址空间的地址

// 所以为r1加上偏移之后,重新存储到label中。

// 后面CPU就可以根据LABEL在新uboot的地址空间中寻址到正确的符号。
fixnext: cmp r2, r3 blo fixloop

 

可以看出__image_copy_start---end之间包括了text data rodata段,但是没有包括rel_dyn。
继续看relocate_code函数,拷贝__image_copy_start----end之间的数据,但没有拷贝rel.dyn段。

首先获取__rel_dyn_start地址到r2,将start地址上连续2个4字节地址的值存在r0 r1中

判断r1中的值低8位,如果为0x17,则将r0中的值加relocation offset。

获取以此r0中值为地址上的值,存到r1中

将r1中值加relocation offset,再存回以r0中值为地址上。

以此循环,直到__rel_dyn_end。

* 绝对地址符号的地址会放在label中提供位置无关代码使用
* label的地址会放在.rel.dyn段中

当uboot对自身进行relocate之后,此时全局变量的绝对地址已经发生变化,如果函数按照原来的label去获取全局变量的地址的时候,这个地址其实是relocate之前的地址。因此,在relocate的过程中需要对全局变量的label中的地址值进行修改,所以uboot将这些label的地址全部维护在.rel.dyn段中,然后再统一对.rel.dyn段指向的label进行修改。后续代码可以看出来。可以看出.rel.dyn段用了8个字节来描述一个label,其中,高4字节是label地址标识0x17,低4字节就是label的地址。
所以需要先判断label地址标识是否正确,然后再根据第四字节获取label,对label中的符号地址进行修改。

  ARM9中断向量重定位   
* Copy the relocated exception vectors to the * correct address * CP15 c1 V bit gives us the location of the vectors: * 0x00000000 or 0xFFFF0000. */ ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */ mrc p15, 0, r2, c1, c0, 0 /* V bit (bit[13]) in CP15 c1 */ ands r2, r2, #(1 << 13) ldreq r1, =0x00000000 /* If V=0 */ ldrne r1, =0xFFFF0000 /* If V=1 */ ldmia r0!, {r2-r8,r10} stmia r1!, {r2-r8,r10} ldmia r0!, {r2-r8,r10} stmia r1!, {r2-r8,r10}

arm9手册中:

 [13] V bit Location of exception vectors:
0 = Normal exception vectors selected, address range = 0x0000 0000 to
0x0000 001C
1 = High exception vectors selected, address range = 0xFFFF 0000 to
0xFFFF 001C. Set to the value of VINITHI on reset.

/*
* Copy the relocated exception vectors to the
* correct address
* CP15 c1 V bit gives us the location of the vectors:
* 0x00000000 or 0xFFFF0000.
*/
@@ 注意看注释,通过cp15协处理器的c1寄存器的V标志来判断cpu从什么位置获取中断向量表,
@@ 换句话说,就是中断向量表应该被复制到什么地方!!!

ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
@@ 获取uboot新地址空间的起始地址,存放到r0寄存器中

mrc p15, 0, r2, c1, c0, 0 /* V bit (bit[13]) in CP15 c1 */
ands r2, r2, #(1 << 13)
ldreq r1, =0x00000000 /* If V=0 */
ldrne r1, =0xFFFF0000 /* If V=1 */
@@ 获取cp15协处理器的c1寄存器的V标志,当V=0时,cpu从0x00000000获取中断向量表,当V=1时,cpu从0xFFFF0000获取中断向量表
@@ 将该地址存在r1中

ldmia r0!, {r2-r8,r10}
stmia r1!, {r2-r8,r10}
@@ 前面说了异常中断向量表就是从偏移0x20开始的32个字节。
@@ 所以这里是过滤掉前面的0x20个字节(32个字节,8*4)
@@ 但是不明白为什么还要stmia r1!, {r2-r8,r10},理论上只需要让r0的值产生0x20的偏移就可以了才对???不明白。

//一下的数据是中断矢量的全局符号,这些符号在rel段中已经进行了位置重新计算。

@@ 经过上述两行代码之后,此时r0的值已经偏移了0x20了
ldmia r0!, {r2-r8,r10}
stmia r1!, {r2-r8,r10}
@@ 继续从0x20开始,获取32个字节,存储到r1指向的地址,也就是cpu获取中断向量表的地址
@@ r2-r8,r10表示从r2到r8寄存器和r10寄存器,一个8个寄存器,每个寄存器有4个字节,所以就从r0指向的地址处获取到了32个字节
@@ 再把 {r2-r8,r10}的值存放到r1指向的地址,也就是cpu获取中断向量表的地址



posted @ 2022-02-06 23:09  liujunhuasd  阅读(855)  评论(0编辑  收藏  举报