u-boot重定位过程代码分析

u-boot重定位过程代码分析

本文是在ubuntu下使用qemu+gdb调试u-boot下进行。

u-boot 启动过程

程序的入口参数有五种:

  • 命令行-e 指定
  • 链接器脚本ENTRY(symbol)指定
  • 如果定义了start, 那么start标号是程序入口
  • .text段的第一个字节
  • 地址0

这里是u-boot.lds链接器脚本指定了ENTRY(_start),所以程序入口地址就是_start
_start入口之后,跳到reset,对协处理器进行初始化后,跳转到_mainarm64跳转到crt0_64.S里面的_main
接下来是进行一些内存预留,然后跳转到board_init_f进行第一步的板级初始,执行init_sequence_f数组里面的函数;u-boot-2016版本的结构与u-boot-2010的这个初始化流程进行了优化。
其中比较重要的几个就是在RAM顶部预留空间的部分函数,比如reserve_uboot为后续搬移u-boot预留空间,reserve_boardbd_t结构体预留空间,reserve_global_datagd_t预留空间。
setup_reloc拷贝gd_t到预留的RAM顶部空间之后,开始跳转到relocate_code,开始代码重定位处理。
relocate_code之前的流程大概如下:

 -> _start      [arch/arm/lib/vectors.S]
 -> reset       [arch/arm/cpu/armv7/start.S]
 ...
 -> _main   [arch/arm/lib/crt0.S]
 -> board_init_f_alloc_reserve  ->> [_main]  [common/init/board_init.c] 
 -> board_init_f_init_reserve   ->> [_main] 
 -> board_init_f    [common/board_f.c]
 -> initcall_run_list   [lib/initcall.c]
 -> setup_mon_len   ->> [initcall_run_list] [common/board_f.c]
 -> initf_malloc    ->> [initcall_run_list] [common/dlmalloc.c]
 ... 
 -> init_baud_rate  ->> [initcall_run_list] [common/board_f.c]
 -> serial_init ->> [initcall_run_list] [drivers/serial/serial.c]
 -> console_init_f  ->> [initcall_run_list] [common/console.c]
 ...
 -> reserve_round_4k    ->> [initcall_run_list]
 -> reserve_mmu     ->> [initcall_run_list]
 -> reserve_trace   ->> [initcall_run_list]
 -> reserve_uboot   ->> [initcall_run_list]
 -> reserve_malloc  ->> [initcall_run_list]
 -> reserve_board   ->> [initcall_run_list]
 -> setup_machine   ->> [initcall_run_list]
 -> reserve_global_data ->> [initcall_run_list]
 -> reserve_fdt ->> [initcall_run_list]
 -> reserve_arch    ->> [initcall_run_list]
 -> reserve_stacks  ->> [initcall_run_list]
 -> setup_dram_config   ->>   [initcall_run_list]
 -> show_dram_config    ->>   [initcall_run_list]
 -> display_new_sp  ->>   [initcall_run_list]
 -> reloc_fdt   ->>   [initcall_run_list]
 -> setup_reloc ->>   [initcall_run_list]   ->> [_main]
 -> relocate_code   [arch/arm/lib/relocate.S] ->> [_main]

u-boot 对函数及全局变量的寻址过程

参考这个博客:
[https://blog.csdn.net/skyflying2012/article/details/37660265]

在u-boot代码[common/main.c]里面修改一下, 只要保证代码被这个平台编译, 且不被优化掉就可以了。如果代码或者变量没有被引用, 可能会被编译器优化掉, 取决于优化等级。

static int xxx_var = 100;
void xxx_func_1(void)
{
	xxx_var +=1;
}

void xxx_func_2(void)
{
	xxx_var = 200;
}

typedef void (*xxx_func_t)(void);
static xxx_func_t xxx_func_var = xxx_func_1;

void xxx_rel_dyn(void)
{
	xxx_var = 55;
			
	(*xxx_func_var)();
				
	xxx_func_var = xxx_func_2;
					
	(*xxx_func_var)();
						
}

编译完后的代码的段情况如下:

There are 30 section headers, starting at offset 0x190e24:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        60800000 008000 02d978 00  AX  0   0 32
  [ 2] .rodata           PROGBITS        6082d978 035978 00bfd1 00   A  0   0  8
  [ 3] .hash             HASH            6083994c 04194c 00002c 04   A 13   0  4
  [ 4] .data             PROGBITS        60839978 041978 001b38 00  WA  0   0  8
  [ 5] .got.plt          PROGBITS        6083b4b0 0434b0 00000c 04  WA  0   0  4
  [ 6] .u_boot_list      PROGBITS        6083b4bc 0434bc 000774 00  WA  0   0  4
  [ 7] .efi_runtime      PROGBITS        6083bc30 043c30 000100 00 WAX  0   0  8
  [ 8] .efi_runtime_rel  REL             6083bd30 043d30 000090 08   A 13   0  4
  [ 9] .rel.dyn          REL             6083bdc0 043dc0 007240 08   A 13   0  4
  [10] .bss_start        PROGBITS        6083bdc0 04b11d 000000 00   W  0   0  1
  [11] .bss              NOBITS          6083bdc0 000000 037764 00  WA  0   0 64
  [12] .bss_end          PROGBITS        60873524 04b11d 000000 00   W  0   0  1
  [13] .dynsym           DYNSYM          60843000 04b000 000060 10   A 14   3  4
  [14] .dynstr           STRTAB          60843060 04b060 00002a 00   A  0   0  1
  [15] .dynamic          DYNAMIC         6084308c 04b08c 000080 08  WA 14   0  4
  [16] .interp           PROGBITS        6084310c 04b10c 000011 00   A  0   0  1
  [17] .ARM.attributes   ARM_ATTRIBUTES  00000000 04b11d 000029 00      0   0  1
  [18] .comment          PROGBITS        00000000 04b146 00002b 01  MS  0   0  1
  [19] .debug_line       PROGBITS        00000000 04b171 0195d6 00      0   0  1
  [20] .debug_info       PROGBITS        00000000 064747 0a0a7a 00      0   0  1
  [21] .debug_abbrev     PROGBITS        00000000 1051c1 01bbe0 00      0   0  1
  [22] .debug_aranges    PROGBITS        00000000 120da8 0037d8 00      0   0  8
  [23] .debug_frame      PROGBITS        00000000 124580 008fe4 00      0   0  4
  [24] .debug_str        PROGBITS        00000000 12d564 0109ac 01  MS  0   0  1
  [25] .debug_loc        PROGBITS        00000000 13df10 04aa07 00      0   0  1
  [26] .debug_ranges     PROGBITS        00000000 188918 0083e0 00      0   0  8
  [27] .shstrtab         STRTAB          00000000 190cf8 00012b 00      0   0  1
  [28] .symtab           SYMTAB          00000000 1912d4 010000 10     29 3251  4
  [29] .strtab           STRTAB          00000000 1a12d4 0065f8 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

反汇编后的三个函数的代码如下:

60809708 <xxx_func_1>:
60809708:	e59f300c 	ldr	r3, [pc, #12]	; 6080971c <xxx_func_1+0x14>
                                            @ r3 = 6083ad50, 6083ad50 = <xxx_var>, [6083ad50] = xxx_var
6080970c:	e5932000 	ldr	r2, [r3]        @ r2 = [r3], r2 = xxx_var, 取变量的值
60809710:	e2822001 	add	r2, r2, #1      @ +1 操作
60809714:	e5832000 	str	r2, [r3]        @ 存回 xxx_var
60809718:	e12fff1e 	bx	lr              @ 返回
@ 后面这个是 xxx_func_1 函数寻址用的
6080971c:	6083ad50 	addvs	sl, r3, r0, asr sp  @ 后面这段汇编指令应该是反汇编有问题, 应该是类似 
                                                    @ 这样的: 608010b4:	6083997c .word 0x6083997c
                                                    @ 指定某一个立即数的

60809720 <xxx_func_2>:
60809720:	e59f3008 	ldr	r3, [pc, #8]	; 60809730 <xxx_func_2+0x10>
                                            @ r3 = 6083ad50, 6083ad50 = <xxx_var>, [6083ad50] = xxx_var
60809724:	e3a020c8 	mov	r2, #200	; 0xc8
60809728:	e5832000 	str	r2, [r3]        @ xxx_var = #200
6080972c:	e12fff1e 	bx	lr              @ 返回
@ 后面这个是 xxx_func_2 函数寻址用的
60809730:	6083ad50 	addvs	sl, r3, r0, asr sp  @ 后面这段汇编指令应该是反汇编有问题, 应该是类似 
                                                    @ 这样的: 608010c8:	6083997c .word 0x6083997c
                                                    @ 指定某一个立即数的

60809734 <xxx_rel_dyn>:
@ 入栈
60809734:	e92d4038 	push	{r3, r4, r5, lr}

@ 准备工作
60809738:	e3a03037 	mov	r3, #55	; 0x37  @ r3 存入临时变量, r3 = #55
6080973c:	e59f4020 	ldr	r4, [pc, #32]	; 60809764 <xxx_rel_dyn+0x30>
                                            @ r4 = 6083ad50, 6083ad50 = <xxx_var>, [6083ad50] = xxx_var
60809740:	e59f5020 	ldr	r5, [pc, #32]	; 60809768 <xxx_rel_dyn+0x34>
                                            @ r5 = 6083ad4c, 6083ad4c = <xxx_func_var>, [6083ad4c] = xxx_func_var
                                            @ 此时 [6083ad4c] = 60809708 = <xxx_func_1>

@ C代码 : xxx_var = 55;
60809744:	e5843000 	str	r3, [r4]        @ [r4] = xxx_var , r3 = #55, [r4] = r3 => xxx_var = #55 
                                            @ 将 r3 的值存到 xxx_var 变量

@ C代码 : (*xxx_func_var)(); (第一个)
60809748:	e5953000 	ldr	r3, [r5]        @ [r5] = xxx_func_var, r3 = [r5] => r3 = xxx_func_var => r3 = <xxx_func_1>
6080974c:	e12fff33 	blx	r3              @ 跳转 r3 所在的地址, 此时是 <xxx_func_1>

@ C代码 : xxx_func_var = xxx_func_2;
60809750:	e59f3014 	ldr	r3, [pc, #20]	; 6080976c <xxx_rel_dyn+0x38>
                                            @ r3 = 60809720, 60809720 = <xxx_func_2>
60809754:	e5853000 	str	r3, [r5]        @ [r5] = xxx_func_var, r3 = <xxx_func_2>, [r5] = r3 => xxx_func_var = <xxx_func_2>

@ 这里应该是函数 xxx_func_2() 被内联了? 
60809758:	e3a030c8 	mov	r3, #200	; 0xc8
6080975c:	e5843000 	str	r3, [r4]        @ r3 = #200, [r4] = xxx_var, [r4] = r3 => xxx_var = #200

@ 出栈
60809760:	e8bd8038 	pop	{r3, r4, r5, pc}

@ 后面这几个是 xxx_rel_dyn 函数寻址用的
60809764:	6083ad50 	addvs	sl, r3, r0, asr sp  @ 后面这段汇编指令应该是反汇编有问题, 应该是类似 
                                                    @ 这样的: 60809764:	6083ad50 .word 0x6083ad50
                                                    @ 指定某一个立即数的
60809768:	6083ad4c 	addvs	sl, r3, ip, asr #26
6080976c:	60809720 	addvs	r9, r0, r0, lsr #14

.data段中可以发现: (6083ad4c地址的值就是60809708, 6083ad50的值就是00000064)

Disassembly of section .data:

...

6083ad4c <xxx_func_var>:
6083ad4c:	60809708 	addvs	r9, r0, r8, lsl #14

6083ad50 <xxx_var>:
6083ad50:	00000064 	andeq	r0, r0, r4, rrx

addvsumullvs后面的vs代表操作条件, Overflow时会对置位寄存器的V=1。但后面这几行的应该是代表代码段或数据段的位置, 而不是汇编指令。

经过上面的代码分析, 可以看到:

  • 对函数的寻址是通过 bl 或者 b 指令实现的, 是相对地址的跳转
  • 对全局变量的寻址是通过函数代码段后的地址实现的

全局变量经过重定向后的地址会改变, 导致后面寻址有问题, 所以这里要对这些全局变量的地址进行重新修改。

查看拷贝后__image_copy_start__image_copy_end的内容

变量xxx_func_varxxx_var: 重定向前地址分别为0x6083ad4c0x6083ad50, 查看gd->reloc_off或者当前relocate_coder4, 可以得知重定向的偏移为: 0x1f77c000, 这两个地址的连续的, 直接查看0x6083ad4c

反汇编代码:

6083ad4c <xxx_func_var>:
6083ad4c:	60809708 	addvs	r9, r0, r8, lsl #14

6083ad50 <xxx_var>:
6083ad50:	00000064 	andeq	r0, r0, r4, rrx

重定向后的内存地址的值:

(gdb) x/2x 0x7ffb6d4c
0x7ffb6d4c:     0x60809708      0x00000064

反汇编的相关代码段地址为0x60809708~0x6080976c, 查看0x7ff85708 : (当前都是机器码了)

(gdb) x/26x 0x7ff85708
0x7ff85708:     0xe59f300c      0xe5932000      0xe2822001      0xe5832000
0x7ff85718:     0xe12fff1e      0x6083ad50      0xe59f3008      0xe3a020c8
0x7ff85728:     0xe5832000      0xe12fff1e      0x6083ad50      0xe92d4038
0x7ff85738:     0xe3a03037      0xe59f4020      0xe59f5020      0xe5843000
0x7ff85748:     0xe5953000      0xe12fff33      0xe59f3014      0xe5853000
0x7ff85758:     0xe3a030c8      0xe5843000      0xe8bd8038      0x6083ad50
0x7ff85768:     0x6083ad4c      0x60809720

对比反汇编代码:

60809734 <xxx_rel_dyn>:
60809734:	e92d4038 	push	{r3, r4, r5, lr}
60809738:	e3a03037 	mov	r3, #55	; 0x37
6080973c:	e59f4020 	ldr	r4, [pc, #32]	; 60809764 <xxx_rel_dyn+0x30>
60809740:	e59f5020 	ldr	r5, [pc, #32]	; 60809768 <xxx_rel_dyn+0x34>
60809744:	e5843000 	str	r3, [r4]
60809748:	e5953000 	ldr	r3, [r5]
6080974c:	e12fff33 	blx	r3
60809750:	e59f3014 	ldr	r3, [pc, #20]	; 6080976c <xxx_rel_dyn+0x38>
60809754:	e5853000 	str	r3, [r5]
60809758:	e3a030c8 	mov	r3, #200	; 0xc8
6080975c:	e5843000 	str	r3, [r4]
60809760:	e8bd8038 	pop	{r3, r4, r5, pc}
60809764:	6083ad50 	addvs	sl, r3, r0, asr sp
60809768:	6083ad4c 	addvs	sl, r3, ip, asr #26
6080976c:	60809720 	addvs	r9, r0, r0, lsr #14

可以看到里面的寻址标签还是0x6083ad50, 0x6083ad4c, 0x60809720。另外两个函数同样。

在这里插入图片描述

.rel.dyn段中, 可以找到下面的内容: 其中的60809764 60809768 6080976c这几个值分别是 xxx_rel_dyn 函数用来寻址变量的值。

...
6083d380:	60809764 	addvs	r9, r0, r4, ror #14
6083d384:	00000017 	andeq	r0, r0, r7, lsl r0
6083d388:	60809768 	addvs	r9, r0, r8, ror #14
6083d38c:	00000017 	andeq	r0, r0, r7, lsl r0
6083d390:	6080976c 	addvs	r9, r0, ip, ror #14
6083d394:	00000017 	andeq	r0, r0, r7, lsl r0
...

relocate_code关于.rel.dyn段重定向的打上断点:

(gdb) b 98 if $r0=0x60809764
Breakpoint 3 at 0x608006e0: file arch/arm/lib/relocate.S, line 98.
(gdb) c
Continuing.

Breakpoint 3, fixloop () at arch/arm/lib/relocate.S:98
(gdb) i r r0 r1
r0             0x60809764       1619040100
r1             0x17     23

(gdb) i r r0 r4
r0             0x60809764       1619040100
r4             0x1f77c000       527941632

(gdb) i r r0
r0             0x7ff85764       2146981732
r1             0x17     23

(gdb) i r r0 r1
r0             0x7ff85764       2146981732
r1             0x6083ad50       1619242320

(gdb) i r r1
r1             0x7ffb6d50       2147183952

(gdb) x /x 0x7ff85764
0x7ff85764:     0x6083ad50

(gdb) x /x 0x7ff85764
0x7ff85764:     0x7ffb6d50

在代码上补上上面的调试信息:

relocate_code重定向

u-boot relocate_code 第一阶段

第一阶段比较简单,就是将Flash或者RAM中的u-boot重新拷贝到RAM顶端的过程,直接看汇编代码(relocate_codecopy_loop)。

u-boot relocate_code 第二阶段

第一阶段的就是纯粹的一个拷贝过程,基于前面的变量及函数的寻址过程,接下来查看relocate_code的第二阶段:对全局变量的寻址进行修正(剩余部分代码)。

汇编注释

/*
 * void relocate_code(addr_moni)
 *
 * This function relocates the monitor code.
 *
 * NOTE:
 * To prevent the code below from containing references with an R_ARM_ABS32
 * relocation record type, we never refer to linker-defined symbols directly.
 * Instead, we declare literals which contain their relative location with
 * respect to relocate_code, and at run time, add relocate_code back to them.
 */

ENTRY(relocate_code)
	ldr	r1, =__image_copy_start	/* r1 <- SRC &__image_copy_start */
                                    @ __image_copy_start = 0x60800000, 可以用 `nm` 工具看到
	subs	r4, r0, r1		/* r4 <- relocation offset */
                                    @ r0 = 0x7ff7c000, r1 = 0x60800000, r4 = 0x1f77c000
	beq	relocate_done		/* skip relocation */
	ldr	r2, =__image_copy_end	/* r2 <- SRC &__image_copy_end */
                                    @ __image_copy_end = 0x6083bdc0, 可以用 `nm` 工具看到

    @ 从上面的准备可以看到, r0 指向的是目标地址, r1 执行源地址, 
    @ r0 = 0x7ff7c000, r1 = 0x60800000, r2 = 0x6083bdc0, r4 = 0x1f77c000
    @ 接下来就是拷贝过程了, 将 r1 指向的地址的内容 load 到 r10-r11 寄存器,
    @ 然后再将 r10-r11 的内容 stor 到 r0 指向的地址, r0 和 r1 的地址都递增, 
    @ 在 r1 小于 r2 时一直执行 copy_loop 循环.
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
	 */
    @ 对 .rel.dyn 段进行重定向 
    @ r2 = 0x6083bdc0, r3 = 0x60843000  可以用 `nm` 工具看到 __rel_dyn_start 和 __rel_dyn_end
	ldr	r2, =__rel_dyn_start	/* r2 <- SRC &__rel_dyn_start */
	ldr	r3, =__rel_dyn_end	/* r3 <- SRC &__rel_dyn_end */
	cmp r2, r3
	beq relocate_done
    @ 
fixloop:
	ldmia	r2!, {r0-r1}		/* (r0,r1) <- (SRC location,fixup) */
                            @ r0 = 0x60809764, r1 = 0x17, r2= 0x6083bdc8
	and	r1, r1, #0xff       @ 取低8位
	cmp	r1, #23			/* relative fixup? */
                            @ 与 23 比较, 如果不等则表明已经重定向过了
	bne	fixnext

	/* relative fix: increase location by offset */
	add	r0, r0, r4          @ r4 = 0x1f77c000, r0 = r0 + r4 = 0x7ff85764
	ldr	r1, [r0]            @ [0x7ff85764] = 0x6083ad50(与搬移前的反汇编一致), r1 = 0x6083ad50
	add	r1, r1, r4          @ r4 = 0x1f77c000, r1 = r1 + r4 = 0x7ffb6d50, [0x7ffb6d50] = 0x00000064
	str	r1, [r0]            @ r0 = 0x7ff85764, r1 = 0x7ffb6d50, [r0] = r1 => [0x7ff85764] = 0x7ffb6d50 => 指向新的地址
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)

过程大概是这样的图:
重定向前:

 ----------------------------   0x80000000
|
|           Other
|
 ----------------------------
|       xxx_var(100)
 ----------------------------   0x6083ad50
|   xxx_func_var(60809708)
 ----------------------------   0x6083ad4c
|
|           Other
|
 ----------------------------
|           60809720
 ----------------------------   0x6080976C
|           6083ad4c
 ----------------------------   0x60809768
|           6083ad50
 ----------------------------   0x60809764
|
|           Other
|
 ----------------------------   0x60809730
|        xxx_func_2
 ----------------------------   0x60809720
|        xxx_func_1
 ----------------------------   0x60809708
|
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值