Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> u-boot-2012.04.1 第一階段分析 內存劃分 代碼重定位

u-boot-2012.04.1 第一階段分析 內存劃分 代碼重定位

編輯:關於Android編程

今天學習了一下 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 ,但是我在反匯編文件中沒有發現這種情況,後邊遇到在分析吧~
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved