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 中使用了大量位置无关码如果不作处理,那么就无法正确执行,如何处理的,原理很简单,后面分析。
还是先来看下链接脚本:
0x0 ...
之前从未接触过 -pie 参数,它的作用就是填充 .rel 段 和 .dynsym 段,至于这两个段放的什么东西,后边再说吧。先来按照以前的流程分析一下。
需要说明的是,u-boot 的起始地址并不是固定的,会根据 uboot 的大小而改变,也就是说重定位的目的地址不固定,当然对于一个编译好的 uboot 来说是固定的。划分完空间就开始重定位了。
韦东山老师说,在引用动态链接库函数的时候,运行时才能确定函数地址,这个动态地址也是需要修正的,对应于上边代码的另一个分支 fixabs ,但是我在反汇编文件中没有发现这种情况,后边遇到在分析吧~
关于位置无关码、位置有关码、链接地址
请参考我的另一篇文章:http://blog.csdn.net/lizuobin2/article/details/52049892
还是先来看下链接脚本:
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
相关文章推荐
- Uboot优美代码赏析2:第一阶段_硬件平台启动start.s分析
- 分析 u-boot 的第一阶段代码(cpu/arm920t/start.S)
- U-Boot启动第一阶段代码分析
- U-Boot启动第一阶段代码分析
- U-Boot启动第一阶段代码分析
- U-Boot启动第一阶段代码分析
- uboot第一阶段代码概要分析
- linux内核学习初笔记(5)uboot第一阶段我的分析
- U-Boot第一阶段关键代码理解(绿色加粗为自己添加)
- U-Boot第一阶段关键代码理解(绿色加粗为自己添加)
- Uboot优美代码赏析2:第一阶段_硬件平台启动start.s分析
- 图解U-Boot:第一阶段源码分析
- U-Boot重定位代码分析
- U-Boot启动第二阶段代码分析
- u-boot源码分析 --- 启动第二阶段 ,基于2410 启动代码 分析
- U-Boot重定位代码分析 (学习u-boot时网上搜到的一篇文章,感觉挺给力的)
- 对U-BOOT的第1阶段代码的分析
- u-boot源码分析 --- 启动第二阶段 ,基于2410 启动代码 分析
- u-boot-1.1.6 源码分析(2)--第一阶段
- U-boot启动第一阶段详细分析