您的位置:首页 > 编程语言

u-boot-2012.04.1 第一阶段分析 内存划分 代码重定位

2016-08-01 22:24 405 查看
    今天学习了一下 u-boot-2012.04.01 的第一阶段,发现与 u-boot1.1.6 的差异还是很大的,尤其是在代码重定位方面。在 u-boot1.1.6 中链接地址为 0x33f80000 ,重定位时,uboot也被拷贝到这个固定的地址。然而,u-boot-2012 的链接地址为 0 ,在重定位时,uboot 在 sdram 中的地址也不是固定的,这样就带来了一个问题 uboot 中使用了大量位置无关码如果不作处理,那么就无法正确执行,如何处理的,原理很简单,后面分析。

还是先来看下链接脚本:

SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
__image_copy_start = .;
arch/arm/cpu/arm920t/start.o (.text)
*(.text)
}
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
. = ALIGN(4);
.data : {
*(.data)
}
. = ALIGN(4);
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
__image_copy_end = .;
.rel.dyn : {
__rel_dyn_start = .;
*(.rel*)
__rel_dyn_end = .;
}
.dynsym : {
__dynsym_start = .;
*(.dynsym)
}
_end = .;
. = ALIGN(4096);
.mmutable : {
*(.mmutable)
}
.bss __rel_dyn_start (OVERLAY) : {
__bss_start = .;
*(.bss)
. = ALIGN(4);
__bss_end__ = .;
}
链接:arm-linux-ld  -pie -Tu-boot.lds -Bstatic -Ttext
0x0 ...


    之前从未接触过 -pie 参数,它的作用就是填充 .rel 段 和 .dynsym 段,至于这两个段放的什么东西,后边再说吧。先来按照以前的流程分析一下。

start_code:

/* set the cpu to SVC32 mode */
mrs	r0, cpsr
bic	r0, r0, #0x1f
orr	r0, r0, #0xd3
msr	cpsr, r0

/* turn off the watchdog */
ldr	r0, =pWTCON
mov	r1, #0x0
str	r1, [r0]

/* mask all IRQs by setting all bits in the INTMR - default */
mov	r1, #0xffffffff
ldr	r0, =INTMSK
str	r1, [r0]
# if defined(CONFIG_S3C2410)
ldr	r1, =0x3ff
ldr	r0, =INTSUBMSK
str	r1, [r0]

/* FCLK:HCLK:PCLK = 1:2:4 default FCLK is 120 MHz ! */
ldr	r0, =CLKDIVN
mov	r1, #3
str	r1, [r0]

/* Set stackpointer in internal RAM to call board_init_f */
* #define PHYS_SDRAM_1		0x30000000 /* SDRAM Bank #1 */
* #define CONFIG_SYS_SDRAM_BASE	PHYS_SDRAM_1
* #define CONFIG_SYS_INIT_SP_ADDR	(CONFIG_SYS_SDRAM_BASE + 0x1000 - GENERATED_GBL_DATA_SIZE)   ==  30000f80
*/
call_board_init_f:
ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR)
bic	sp, sp, #7 /* 8-byte alignment for ABI compliance */
ldr	r0,=0x00000000
bl	board_init_f
    上边都是老掉牙的东西,没啥好分析的了。
void board_init_f(ulong bootflag)
{
bd_t *bd;
init_fnc_t **init_fnc_ptr;
gd_t *id;
ulong addr, addr_sp;

/* Pointer is writable since we allocated a register for it */
gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07); // 30000f80
memset((void *)gd, 0, sizeof(gd_t));

/* .word __bss_end__ - _start == u-boot 的长度 */
gd->mon_len = _bss_end_ofs;
/*
init_fnc_t *init_sequence[] = {
board_early_init_f,
timer_init,			/* initialize timer */
env_init,			/* initialize environment */
init_baudrate,		/* initialze baudrate settings */
serial_init,		/* serial communications setup */
console_init_f,		/* stage 1 init of console */
display_banner,		/* say that we are here */
print_cpuinfo,		/* display cpu info (and speed) */
dram_init,			/* gd->ram_size = PHYS_SDRAM_1_SIZE = 0x04000000  64 MB */
NULL,
}
*/
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
/* 0x30000000 +  0x04000000 = 0x3400 0000 */
addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;

/* reserve kernel log buffer
* #define LOGBUFF_OVERHEAD (4096)
* #define LOGBUFF_LEN	(16384)
* #define LOGBUFF_RESERVE (LOGBUFF_LEN+LOGBUFF_OVERHEAD) = 0x5000
*/
addr -= (LOGBUFF_RESERVE); //addr = 0x3400 0000 - 0x5000 = 33ff b000

/* reserve TLB table */
addr -= (4096 * 4);	// addr = 33ff b000 - 4096*4 = 33ff 7000

/* round down to next 64 kB limit */
addr &= ~(0x10000 - 1);	// addr = 33ff 0000
gd->tlb_addr = addr;	// addr = 33ff 0000
addr &= ~(4096 - 1);	// addr = 33ff 0000
/*
* reserve memory for U-Boot code, data & bss
* round down to next 4 kB limit
*/
addr -= gd->mon_len;	// addr = 33ff 0000 - u-boot的长度 = 33F4101C
addr &= ~(4096 - 1);	// addr = 33ff 0000 - u-boot的长度 = 33F41000

/* reserve memory for malloc() arena */
* #define CONFIG_SYS_MALLOC_LEN	(4 * 1024 * 1024)
* #define TOTAL_MALLOC_LEN	CONFIG_SYS_MALLOC_LEN
*/
addr_sp = addr - TOTAL_MALLOC_LEN; // 33B41000

/* gd 结构体 */
addr_sp -= sizeof (bd_t);	// 	33B41000 - sizeof (bd_t)
bd = (bd_t *) addr_sp;
gd->bd = bd;

addr_sp -= sizeof (gd_t);   // 33B41000 - sizeof (bd_t) - sizeof (gd_t)
id = (gd_t *) addr_sp;

/* setup stackpointer for exeptions */
gd->irq_sp = addr_sp;

/* leave 3 words for abort-stack    */
addr_sp -= 12;	// 	 33B41000 - sizeof (bd_t) - sizeof (gd_t) - 12
/* 8-byte alignment for ABI compliance */
addr_sp &= ~0x07;

gd->bd->bi_baudrate = gd->baudrate;
/* Ram ist board specific, so move it to board code ... */
dram_init_banksize();
display_dram_config();	/* and display it */

gd->relocaddr = addr;	// addr = 33ff 0000 - u-boot的长度 = 33F41000
gd->start_addr_sp = addr_sp;
gd->reloc_off = addr - _TEXT_BASE;	// 33F41000 - 0 = 33F41000

memcpy(id, (void *)gd, sizeof(gd_t));

relocate_code(addr_sp, id, addr);
}
    上边的重点工作是进行了一些初始化,最重要的是进行了内存的划分,大致如下图所示:



    需要说明的是,u-boot 的起始地址并不是固定的,会根据 uboot 的大小而改变,也就是说重定位的目的地址不固定,当然对于一个编译好的 uboot 来说是固定的。划分完空间就开始重定位了。
.globl	relocate_code
relocate_code:
mov	r4, r0	/* addr_sp 栈  */
mov	r5, r1	/* id gd结构体 */
mov	r6, r2	/* sdram 中 addr u-boot 起始地址 */

/* Set up the stack	*/
stack_setup:
mov	sp, r4	/* 设置栈 */

adr	r0, _start	/* 判断当前代码位置 */
cmp	r0, r6
beq	clear_bss		/* skip relocation */

/* 位于norflash中 */
mov	r1, r6				/* sdram 中 addr u-boot 起始地址 */
ldr	r3, _bss_start_ofs	/* .word __bss_start - _start 代码段长度 */
add	r2, r0, r3			/* 当前代码段结束地址 */

copy_loop:
/* 将 uboot 代码段 拷贝到 sdram */
ldmia	r0!, {r9-r10}		/* copy from source address [r0]    */
stmia	r1!, {r9-r10}		/* copy to   target address [r1]    */
cmp	r0, r2			/* until source end address [r2]    */
blo	copy_loop

#ifndef CONFIG_SPL_BUILD
/*
* 修正位置有关码的数据
* 举个简单的例子:ldr pc,=_start 假设链接地址为 0 ,_start 的地址为 0
* ldr pc, =_start 将被翻译成 ldr pc, [pc,#x]
* pc + #x 存放的是个定值 _start 的地址 0 这个值是根据链接地址来确定的
* 代码位于链接地址处运行,pc 能够跳转到正确的地方 0 处
* 如果代码被重定位到了0x3000 0000 ,此时 ldr pc, =_start
* 依旧会去 pc + #x 处取值,注意此时的 pc + #x 已经是个 > 0x3000 0000 的值了,相比之前的 pc + #x 差了 0x3000 0000
* 但是里面存的值却是相同的 都是 0 ,因此它跳转不到 我们想跳的 0x3000 0000 处去,
* 为了能够正确跳转,我们要修改 pc + #x 处的值,给它加上一个 0x3000 0000
* 而,这些要修改的值的地址 pc + #x 都存放在  __rel_dyn_start 开始的地方
*/
ldr	r0, _TEXT_BASE		/* 0 */
sub	r9, r6, r0			/* sdram 中 addr u-boot 地址 与 norflash 中 u-boot 地址的偏差 */

ldr	r10, _dynsym_start_ofs	/* r10 <- sym table ofs */
add	r10, r10, r0			/* r10 <- sym table in FLASH */
/*
* __rel_dyn_start 起始的地址放了些啥?
* 比如 ldr r0, =main
* 首先,ldr r0, [pc,#xxx]
* __rel_dyn_start 存放的就是 pc + #xxx ,要去取数的地方
*/
ldr	r2, _rel_dyn_start_ofs	/* __rel_dyn_start - _start  ,__rel_dyn_start 相对起始地址的偏移量 */
add	r2, r2, r0				/*  __rel_dyn_start 地址 	*/
ldr	r3, _rel_dyn_end_ofs	/* _rel_dyn_end_ofs - _start , _rel_dyn_end_ofs 相对起始地址的偏移量 */
add	r3, r3, r0				/* _rel_dyn_end_ofs 的地址 	*/
fixloop:
ldr	r0, [r2]				/* 从 __rel_dyn_start 处取一个值 */
add	r0, r0, r9				/* 加上 sdram 中 addr u-boot 地址 与 norflash 中 u-boot 地址的偏差 */
ldr	r1, [r2, #4]			/* 判断 下一个值 是不是 0x17 ,我看了一下反汇编,都是0x17的情况 */
and	r7, r1, #0xff
cmp	r7, #23			/* relative fixup? */
beq	fixrel
cmp	r7, #2			/* absolute fixup? */
beq	fixabs
/* ignore unknown type of fixup */
b	fixnext
fixabs:
/* 不会执行 */
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:
/* 取出数据 */
ldr	r1, [r0]
add	r1, r1, r9	/* 修正数据 */
fixnext:
str	r1, [r0]
add	r2, r2, #8		/* each rel.dyn entry is 8 bytes */
cmp	r2, r3
blo	fixloop
#endif

clear_bss:
#ifndef CONFIG_SPL_BUILD
ldr	r0, _bss_start_ofs
ldr	r1, _bss_end_ofs
mov	r4, r6			/* reloc addr */
add	r0, r0, r4
add	r1, r1, r4
mov	r2, #0x00000000		/* clear			    */

clbss_l:str	r2, [r0]		/* clear loop...		    */
add	r0, r0, #4
cmp	r0, r1
bne	clbss_l

bl coloured_LED_init
bl red_led_on
#endif

/*
* We are done. Do not return, instead branch to second part of board
* initialization, now running from RAM.
*/
#ifdef CONFIG_NAND_SPL
ldr     r0, _nand_boot_ofs
mov	pc, r0

_nand_boot_ofs:
.word nand_boot
#else
ldr	r0, _board_init_r_ofs
adr	r1, _start
add	lr, r0, r1
add	lr, lr, r9
/* setup parameters for board_init_r */
mov	r0, r5		/* gd_t */
mov	r1, r6		/* dest_addr */
/* jump to it ... */
mov	pc, lr

_board_init_r_ofs:
.word board_init_r - _start
#endif
    以上将全部的位置有关码的数据进行了偏差修正。新版的 u-boot 都采用这种方法,导致代码复杂了不说,还让uboot 大了不少,不知道是利大于弊,还是弊大于利呀?
    韦东山老师说,在引用动态链接库函数的时候,运行时才能确定函数地址,这个动态地址也是需要修正的,对应于上边代码的另一个分支 fixabs ,但是我在反汇编文件中没有发现这种情况,后边遇到在分析吧~
    关于位置无关码、位置有关码、链接地址
    请参考我的另一篇文章:http://blog.csdn.net/lizuobin2/article/details/52049892
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息