u-boot是如何重定位代的(如何拷贝自己的)

u-boot在启动之后会在_main函数中将自己重定位到靠近DDR内存尾部的地方,避免和linux内核代码冲突,_main函数在arch/arm/lib/crt0.S文件中,部分代码如下:

ENTRY(_main)

/*
 * Set up initial C runtime environment and call board_init_f(0).
 */

#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
	ldr	sp, =(CONFIG_SPL_STACK)
#else
	ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR)
#endif
#if defined(CONFIG_CPU_V7M)	/* v7M forbids using SP as BIC destination */
	mov	r3, sp
	bic	r3, r3, #7
	mov	sp, r3
#else
	bic	sp, sp, #7	/* 8-byte alignment for ABI compliance */
#endif
	mov	r0, sp
	bl	board_init_f_alloc_reserve
	mov	sp, r0
	/* set up gd here, outside any C code */
	mov	r9, r0
	bl	board_init_f_init_reserve

	mov	r0, #0
	bl	board_init_f

#if ! defined(CONFIG_SPL_BUILD)

/*
 * Set up intermediate environment (new sp and gd) and call
 * relocate_code(addr_moni). Trick here is that we'll return
 * 'here' but relocated.
 */

	ldr	sp, [r9, #GD_START_ADDR_SP]	/* sp = gd->start_addr_sp */
#if defined(CONFIG_CPU_V7M)	/* v7M forbids using SP as BIC destination */
	mov	r3, sp
	bic	r3, r3, #7
	mov	sp, r3
#else
	bic	sp, sp, #7	/* 8-byte alignment for ABI compliance */
#endif
	ldr	r9, [r9, #GD_BD]		/* r9 = gd->bd */
	sub	r9, r9, #GD_SIZE		/* new GD is below bd */

	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

上述代码中所有关于SPL的宏我们都没有定义,V7M我们也没有定义,所以条件编译就一目了然了。

27行,bl board_init_f,其中board_init_f的作用是运行一系列函数,如串口,定时器,以及打印一些信息到终端,最重要的是初始化全局结构体gd,gd的地址是保存到r9寄存器中的,gd结构体的作用主要是代码重定位的目的地址处内存如何分配的问题。gd结构体中的成员初始化之后,board_init_f函数会在最后调用一个名为setup_reloc的函数将gd结构体(此时保存在芯片内部ram中)拷贝到外部DDR中,也就是重定位后的地址处。

45行

    ldr    r9, [r9, #GD_BD]        /* r9 = gd->bd */
    sub    r9, r9, #GD_SIZE        /* new GD is below bd */

作用是:此时r9寄存器保存的是老的gd结构体的地址,现在将DDR中新的结构体的地址赋值给r9,此时新的gd结构体也是初始化好的,它是老的结构体的拷贝,在上述setup_reloc中完成的。

48行

    adr    lr, here
    ldr    r0, [r9, #GD_RELOC_OFF]        /* r0 = gd->reloc_off */
    add    lr, lr, r0

作用是:将56行的here处的地址赋值给lr寄存器,当55行调用完relocate_code完成代码拷贝后,跳转到here处继续执行,但是这里需要跳转到重定位后的代码中的here处执行,所以here的地址需要加上老代码和新代码中间的地址偏移,记录在gd->reloc_off中。这样就保证执行完55行relocate_code函数,就直接跳转到重定位后的代码中去执行here处的代码。

54行

    ldr    r0, [r9, #GD_RELOCADDR]        /* r0 = gd->relocaddr */

将重定位的代码目的地址写入r0寄存器,传递给55行的relocate_code函数。

55行

  调用relocate_code函数,relocate_code代码如下(arch/arm/lib/relocate.S文件):

ENTRY(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

	/*
	 * 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, #23			/* relative fixup? */
	bne	fixnext

	/* relative fix: increase location by offset */
	add	r0, r0, r4
	ldr	r1, [r0]
	add	r1, r1, r4
	str	r1, [r0]
fixnext:
	cmp	r2, r3
	blo	fixloop

relocate_done:

#ifdef __XSCALE__
	/*
	 * On xscale, icache must be invalidated and write buffers drained,
	 * even with cache disabled - 4.2.7 of xscale core developer's manual
	 */
	mcr	p15, 0, r0, c7, c7, 0	/* invalidate icache */
	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */
#endif

	/* ARMv4- don't know bx lr but the assembler fails to see that */

#ifdef __ARM_ARCH_4__
	mov	pc, lr
#else
	bx	lr
#endif

ENDPROC(relocate_code)

第2行

    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 */

调用此函数之前r0保存的是重定位的目的地址,第2行,将源地址处代码拷贝的开始地址保存到r1寄存器中,第3行计算地址偏移保存到r4寄存器中,第5行,如果地址偏移为0,直接跳出拷贝,第6行将源地址拷贝代码的结束地址放入r2寄存器。

第7行

开始拷贝代码,每次拷贝8字节,知道完成。

第16行

.rel.dyn段中存放着全局非指针变量的Label的地址和全局指针变量的地址。

.rel.dyn段是在链接器ld链接的命令中加入-pie选项生成的,表示生成与位置无关的代码,.rel.dyn存储的格式是2个4字节空间为一组,高4字节存放固定的值0x17(可以用于校验),低4字节存放Label的地址或者全局指针变量的地址。Label是指,ld加入了-pie选项后,会在调用全局变量的函数末尾生成一个空间,存放全局变量的绝对地址。函数访问全局变量的时候是先读取Label处存放的绝对地址,然后根据绝对地址找到全局变量。

根据.rel.dyn段的记录,对目标地址中的Label进行操作,.rel.dyn段记录的是代码中所有的Label的地址(源地址),我们要修改目的地址处的所有的Label值,所以读取.rel.dyn中的数据,加上偏移(r4寄存器保存的偏移)得到的就是目的地址处的Label的地址,此时目的地址处的Label处保存的值,其实是源地址中全局变量的地址,将目的地址处的Label中保存的值加上偏移,这样,目的地址处的Label中保存的值就是目的地址中全区变量的地址了。

另外关于函数调用,实际使用的是bl或者b指令,本来就是根据PC寄存器的相对偏移来实现的,而不是绝对地址(ldr PC, = xxx)这种格式,所以函数调用不用我们手动进行额外操作。

详细可参考正点原子教程,或者https://blog.csdn.net/skyflying2012/article/details/37660265

第49行

拷贝完成后,以及目的地址的所有Label处存储的地址都加上了偏移之后,就可以跳转到目的地址运行了。

接着执行_main函数的here函数,arch/arm/lib/crt0.S文件61行,进行中断向量表的重定位。这里虽然是同一个函数,同一份代码,但是已经是目的地址处拷贝完成的代码了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值