一乐电子

 找回密码
 请使用微信账号登录和注册会员

QQ登录

只需一步,快速开始

微信扫码登录

搜索
查看: 4371|回复: 4

u-boot链接分析

[复制链接]
发表于 2017-6-12 17:05 | 显示全部楼层 |阅读模式
1 A& k; _0 r0 |8 A, p
一个典型的嵌入式系统中,bootloader代码放在NOR Flash或NAND Flash里面,系统加电或复位后,首先运行这段代码。通常把bootloader代码放在NOR Flash里面,NAND Flash由于硬件原因不能随机访问,需要特殊的硬件支持机制。
. a+ q: O9 ^. N7 I0 u
4 p# c5 u  W$ h* c! |bootloader代码除了初始化以外就是搬运程序,即地址重定位(relocate)。我们为什么需要relocate?主要是经济方面和速度方面的原因。经济方面,NOR Flash和NAND Flash每兆价格相差悬殊,bootloader代码一般在几十到几百K大小,而应用程序通常都很大,几M到几十M的大小,所以用价格低廉的NAND Flash存储。速度方面,程序在NOR Flash里执行的速度远远小于在SDRAM中执行的速度,为了追求更高的速度,也需要relocate,让程序在SDRAM里面执行。5 D6 U  a! w# Q; x3 `# w4 g: O7 z
1 |3 y2 m2 |7 j" ]" Y
relocate涉及到加载域(VMA)和运行域(LMA)两个概念。加载域是程序代码在ROM、FLASH中的排列次序及地址安排,运行域是程序运行时代码在SRAM、SDRAM中地址安排。存储代码时按照加载域存放在FLASH中,运行时再从FLASH中取出代码到RAM运行域运行,一段代码的加载域和存储域可以不同。(可以参考杜春雷的《ARM体系结构与编程》一书的有关章节)。( l8 [) p/ ~' T; W, n" U

9 o. A$ b6 o/ N* \9 P7 H以smdk2410为例,密切相关的就两个文件夹/board/smdk2410和/cpu/arm920t,里面核心文件就u-boot.lds 、config.mk 、start.S。
# k6 l5 o5 T. L5 I! \& w1 Z( U
3 F3 u. a  [- P  z! G: r9 q- B  G! C/cpu/arm920t/u-boot.lds
# }+ ]: c( g7 ~6 a/ h        OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
8 o; _  Y/ i3 q+ s! K  K$ _        OUTPUT_ARCH(arm)# `. X5 Q+ T- y2 p
        ENTRY(_start): j! Y3 k$ r9 q, K2 T
        SECTIONS
0 w3 m, s3 e' m3 j        {# x) W3 }, z" [! [8 v, n1 W) h
                . = 0x00000000; // 从0地址起始0 m0 X, j- a' S/ R4 j
; E0 |- H$ h( o5 o
        . = ALIGN(4);0 C. N( N- C( @( K4 w# K& U
                .text :+ y3 r; e/ s; J! \. H
                {# W5 e) p- B3 Y; P' ~7 _
                        cpu/arm920t/start.o (.text)
8 t3 F8 `: d/ M5 P% Q                        *(.text)$ Q( r9 F1 e1 {; B" U  j
                }0 w5 q, B4 G/ i, J' V9 n4 w' h

7 l/ ?' d8 j, ~1 ~2 l& Z' g        . = ALIGN(4);2 X2 Q7 O) C$ l: l; M
                .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
1 e8 x9 U- s  ~  i5 O
% R# }3 o- g" _; E# e- R4 A5 b, t        . = ALIGN(4);& z3 H/ H  v5 A
                .data : { *(.data) }
# E5 W1 i( Q2 Z* N* t! p' v' t. F1 I1 z! z/ t3 K; E- u, i
        . = ALIGN(4);5 T) [$ |; q* r1 b7 U6 A) F
                .got : { *(.got) }4 h+ H7 `# ?2 o7 l9 R
4 Y! w# b$ c6 K% X9 Q* g. ]" S
        . = .;
2 Q; w0 O1 D7 T; f6 I- n) p+ E                __u_boot_cmd_start = .;
1 q+ s% t( d; ]$ S                .u_boot_cmd : { *(.u_boot_cmd) }
3 r; V4 H8 `2 {0 c6 X% f  I; W$ d                __u_boot_cmd_end = .;1 L4 q3 a$ O. l, P2 G$ f

. _/ v6 g: }/ `9 [7 A9 Q        . = ALIGN(4);
) m2 U( Z% M# c6 P                __bss_start = .;
2 ~( _# k  }* z8 A3 R                .bss (NOLOAD) : { *(.bss) . = ALIGN(4); }
# Y8 c* w+ m" `5 U5 Q1 i* D+ `; u; J                _end = .;: b1 K9 h1 |3 U% t. _* m9 F
        }
/ y. R3 j& W( [$ `+ T
+ P8 o* ~7 K9 D$ L连接脚本文件lds中没有设置LMA,只是设置了VMA。VMA的设置是通过顶层目录下的config.mk文件中的LDFLAGS实现的,TEXT_BASE在/board/smdk2410/config.mk中定义为0x33F80000(SDRAM地址)。
- }2 R, |. c3 H9 J, J
8 k/ @9 T  R! OLDFLAGS += -Bstatic -T $(obj)u-boot.lds $(PLATFORM_LDFLAGS)
# h; d; e: T! O' Q4 [4 P- z) }1 \0 P! i        ifneq ($(TEXT_BASE),)+ ?; O0 [8 H0 Z  o& d3 b) w, Q
        LDFLAGS += -Ttext $(TEXT_BASE)
  A/ L. |5 K" C! ^0 h        endif
9 O0 B8 P- u2 e8 A9 t% Y1 K, k: o. Q  q
查看u-boot.map文件,代码的连接地址是从0x33F80000开始的。6 N. k# w6 W8 h9 j. w2 [
+ y  U( O' v8 S" k: V- x% v, A
167 .text         0x33f80000        0x232c8* T  {2 S0 Y2 }4 c2 y$ Z' h
        168        cpu/arm920t/start.o(.text), Z; l! M1 a$ z
        169        .text                0x33f80000                0x4a0 cpu/arm920t/start.o
6 {( }9 z6 _8 E5 C1 Z- i        170                                0x33f80048                _bss_start8 a) l( r% v, F3 h& U* z( t+ j
        171                                0x33f8004c                _bss_end4 q8 @" G2 B) L% H) i8 j7 r
        172                                0x33f80044                _armboot_start
6 V- C1 \+ a. o6 n        173                                0x33f80000                _start- t8 \$ }) ]1 n) ~3 t, O. x
        174        board/samsung/fs2410/lowlevel_init.o(.text)8 a' I" p2 o) L9 B7 L) y1 M
        175        .text                0x33f804a0         0x64 board/samsung/fs2410/lowlevel_init.o- w7 n7 e8 E+ b# z" S4 o; ~3 I+ h, d
        176                                0x33f804a4                lowlevel_init$ ^7 @) o$ \% d1 m, {  `
        177        board/samsung/fs2410/nand_read.o(.text)
0 o8 W; D& J8 D( R        178        .text                0x33f80504        0xe8 board/samsung/fs2410/nand_read.o$ A" l, y) h6 e  P2 Y3 ^& w
        179                                0x33f80504                wait_idle
7 ~8 t3 f2 S/ |* T  B, O; W5 F        180                                0x33f80518                nand_read_ll$ f8 ?( T1 I/ w4 k) N' n: f
* j4 j. @: ?) W! R( Q
bootloader代码上电之后之所以能够正确执行,有个很重要的原因,就是最初执行的bootloader代码是地址无关的,即这个映象文件可以被放在内存中的任何一个地址上运行。
" \4 C/ ~( f9 i/ v& N
( W7 V% {0 s8 H& j4 i对于地址无关的代码, 寻址是基于pc值的, 在pc值上+/-一个偏移值得到运行地址,如跳转指令B。当执行完代码搬运,就需要跳到和地址相关的地方去执行,即RAM中。一般是跳转到一个标号,这时地址相关代码就开始运行了,如:ldr pc,_start_armboot。
# ^3 ~7 ]$ \- c$ z7 I6 _" {: U) r! m# j7 x/ X; t( |9 x% N
因为在bin映象生成的时候,就已经把_start_armboot这个符号和实际地址绑定在一起,当执行ldr pc,_start_armboot 语句时,程序就从在ROM中执行跳入到RAM中了,前提是进行了代码搬移。如果没有代码搬运就执行ldr pc,_start_armboot,因为RAM中没有正确的可执行代码,程序就马上飞掉了,所有在搬运之前不能寻址绝对地址有关代码,必须执行代码地址无关.# I3 N! l3 R! @$ ~, D
" {. Z. g2 ~! U# a1 F+ }
下面的代码是从NOR Flash向SDRAM搬运的代码:/ j6 q. t: P# U4 l- s8 k

+ v7 r6 @* M% y9 R% G8 B) z+ ~4 C0 Xrelocate:
/ r  @+ X' U7 {; F# U                adr r0, _start2 ~2 V3 ]8 x! f9 N, ^, K
                ldr r1, _TEXT_BASE
0 y" A! O$ c4 p( f  S4 v+ R                cmp r0, r1
; n: p# A+ Q3 c9 u# C0 b                beq stack_setup, u, e# _% l7 w. Y! l6 w. J
                ldr r2, _armboot_start
- O; @! {7 m3 U                ldr r3, _bss_start
6 b' ~( R1 P5 @1 y                sub r2, r3, r2- k0 x5 ^" H( W/ ]
                add r2, r0, r2
% c$ p1 J, A7 W% F5 q        copy_loop:
1 h  X5 o- L! m' B8 o; h  Q7 \9 m                ldmia r0!, {r3-r10}
* E' X% l4 N& \& S& m5 W  l                stmia r1!, {r3-r10}0 q7 S2 |  S. V$ ^, f+ G) F
                cmp r0, r29 U) p3 m1 `/ \/ b4 s
                ble copy_loop
4 N: {* a% g* G; L6 c
1 Y- G+ d/ D0 k4 x6 c注意其中的 adr r0, _start,这是一条伪指令,一般被编译器替换为sub r0, pc,#offset ,不要理解为读取符合表中_start符号的地址(0x33F80000)。上电开始执行时,pc从0开始,所以现在r0值为0+offset,不等于_TEXT_BASE(0x33F80000)。接下来要用到链接时确定的符号地址_armboot_start(0x33F80044)了,把_start:0x0 (NOR Flash)里的.text、.data的代码往SDRAM里_TEXT_BASE确定的地址: 0x33f80000搬运。s3c2410的SDRAM基地址是0x3000_0000,由于uboot支持的这个board SDRAM64M(0x3000_0000-0x3400_0000),所以把u-boot.bin搬运到内存的高端地址.然后跳到内存中执行,提高速度。
 楼主| 发表于 2017-6-12 17:05 | 显示全部楼层
2012030114582457.jpg
2.u-boot映像的地址0并非指物理地址0,由不同的启动方式映射到不同的地址。例如v210是映射到0xD0000000处的irom。
3.TEXT_BASE等指向SDRAM的地址均为虚拟地址。
4.TEXT_BASE为顶层Makefile中定义的,例如三星官方BSP中定义的是0xC3E00000,它是程序实际的链接首地址。
5.SDRAM_BASE被MMU映射在0xC0000000。
6._end和__bss_start为链接脚本文件中最后定义的bss段,在链接时确定,并与u-boot映像编译在一起。
7.在bl1段运行时,u-boot映像被复制到TEXT_BASE开始的地址处。
8. u-boot分配用户栈顶的代码为:4 ^1 K5 N. L$ ^  T
ldr r0, _TEXT_BASE  /* upper 128 KiB: relocated uboot   */      将0xc3e00000加载到r0" T; L' ]. O3 v
sub r0, r0, #CFG_MALLOC_LEN /* malloc area                      */      r0减去0x4000的malloc域
: v. [5 S0 p- P% t4 [; w sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo                        */      r0减去128字节的全局结构体, S& y& M+ O2 h- F
#if defined(CONFIG_USE_IRQ)
9 c( H. Q2 o) s+ Z sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)   如果用户有使用IRQ,再减去2*4*1024的中断栈空间5 P$ d! ^& x+ w0 E9 m2 _7 o
#endif* q7 ?* ?" n  L% r: b/ N( t
sub sp, r0, #12  /* leave 3 words for abort-stack    */      为取址终止异常预留3个字空间后设置好用户sp
+ C$ P1 |) l! @. V) j# P9 O
回复

使用道具 举报

 楼主| 发表于 2017-6-12 17:10 | 显示全部楼层
转自于http://hi.baidu.com/willowduan/item/911a7ad2e0f343312b35c733
+ p* V4 o; |: ]; ]

花了两天时间来专门研究u-boot的内存分布,这个图网上已经有了,但只是大致图形,没有详细、深入解析。所以自己就专门画了图,添加一些东西。

此外,还专门测试了一下u-boot下全局变量、未初始化变量等等的地址分布,也画了一张图。不过好像跟linux下进程的内存分布不太一致,估计是u-boot自己管理内存的——很明显,此时linux还不知道在哪里呢。但是,这些都不妨碍我们学习一些底层的东西。

: D& }6 I4 d+ V: S, S1 ^2 ^6 [

这个测试就是自己编写一个自定义的命令,添加自定义命令其实很简单的,在中已经简单写了一下了。本着“够用即可”的原则,还没有深入追踪u-boot到底如何实现shell命令的——有些时候难得糊涂是很有必要的,凡事看开些总归有好处。


, Q6 B$ p# x$ q/ k5 ~* `

先上第一张图:

1007961891618773150.jpg

再上第二张图:

1007961891618773151.jpg

测试代码如下:

#include ( \( [3 k! |9 b: r- x% `* |
#include
% C8 I2 H$ R( n: x  Z4 n#include 0 B' b1 K2 {7 ^/ c* V1 \
DECLARE_GLOBAL_DATA_PTR;1 k) b) [' T- n$ M
int g_foo = 100;
; m* s; Q6 B7 O3 t" |7 Xint g_foo_bss;
9 ?6 O; N2 W1 I' j$ \& A; dstatic int g_foo_static;6 D/ @* Q) e1 s) f0 w
int do_test(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]): d: x+ D3 O% s, x
{0 v. @- ^/ Y/ `' I  b
int l_foo = 100;
; F1 G7 G& E" F6 F int l_foo_bss;
( u0 a$ F6 j: r$ q/ c( A1 m static int l_foo_static;
- u! [# x% t! b8 [6 E: }2 A9 Q char *bar;5 Z4 z: E8 {2 [) K2 p
char *hello="hello world";4 H# H( X) A. s8 k+ r

% j$ y0 s/ g3 C# q: G( b bar = (char*)malloc(strlen(hello) + 1);. F5 h/ J! P$ o6 a( H& @! T. U4 v. C0 w
if (bar == NULL)( O  c/ V8 r- q: Z4 F1 |
  return 1;
, l3 m6 P7 W; m strcpy(bar, hello);
9 X$ ]8 `- o& T/ K 4 ]$ e8 ?2 C  _7 o
printf("sizeof gd:%d sizeof bd:%d", sizeof(gd_t), sizeof(bd_t));
9 ]5 C4 p' J* D( L$ m) Y5 |! W printf("gd:%08lx bd:%08lx bd->flags:%d",gd,gd->bd,gd->flags);- B5 s) D# s% F9 n
9 ~7 T( e0 w$ y8 p3 X+ w
printf("do_test:%p &do_test:%p", do_test, &do_test);
7 X3 M4 O- y; O* T" Q8 Z5 W0 j* t8 i printf("g_foo:%p g_foo_bss:%p g_foo_static:%p",&g_foo,&g_foo_bss,&g_foo_static);
  h) R1 k2 Z! l* m printf("l_foo:%p l_foo_bss:%p l_foo_static:%p",&l_foo,&l_foo_bss,&l_foo_static);, p# P0 u1 ~* b$ e; Q1 E: f  B
printf("hello:%p bar:%s bar:%p", hello, bar, &bar);
+ I2 m2 v8 ^4 l" C2 m free(bar);
4 ]+ E  Y' I+ f; L return 0;
7 _: B1 c5 I+ Y. q: H0 J# E}
' q% x2 A* U) n6 sU_BOOT_CMD(
1 Y% E9 D9 j) M3 c, r3 [ gotohell, 2, 1,do_test,
8 Q2 s( j& h4 i "just a test of my own",+ W% M2 F7 f9 E
"nothing") Q( S" u* h6 t: |. F
);

% Q( u' M; S8 {! ]  w

启动信息如下(有删改)

U-Boot 2010.09-svn9 (Nov 30 2010 - 09:36:08)


* B& @( Y8 Z2 P6 I/ z. ^1 bU-Boot code: 33F80000 -> 33F9CC64  BSS: -> 33FA1EC0
/ H& ]4 {2 z4 n$ TI2C:   ready
, P1 j0 h, G: p  ^% H; m) U" X+ cRAM Configuration:# a+ b6 f% f7 J* H. x
Bank #0: 30000000 64 MiB  W) B. E% {4 v& P$ G6 H
Flash: 8 MiB9 t2 H* Z5 D' i! u4 [( c
*** Warning - bad CRC, using default environment

In:    serial
$ E! ~  o+ y' b7 o, sOut:   serial
( l) x  b0 ?3 i5 WErr:   serial; F7 z6 @4 B7 K! x* h4 N# o
Net:   dm9000
: U8 R9 H  L( u! DHit any key to stop autoboot:  00 V: S+ f$ _9 q7 g
LATE2440> gotohell
+ J2 b4 X9 |) h( M8 U* Fsizeof gd:32 sizeof bd:289 H& h1 v0 h6 Q9 {! S5 w
gd:33f4ffe0 bd:33f4ffc4 bd->flags:3
6 `/ n3 w6 X  N& a; |- ~do_test:33f90740 &do_test:33f90740
3 o8 E# n0 Q& p7 A, q  G& a5 B5 tg_foo:33f9c59c g_foo_bss:33fa1dbc g_foo_static:33fa1dc0# D6 ?9 ^; h+ W# Q8 t4 F5 N  `$ \2 b
l_foo:33f4fbc4 l_foo_bss:33f4fbc0 l_foo_static:33fa1dc4
# N5 s' o& l; p# Jhello:33f9b5e4 bar:hello world bar:33f4fbbc
9 `( L/ S7 X& h+ {LATE2440>

2 W0 \$ t3 x" N  r

其中的“U-Boot 2010.09-svn9 ”表示在svn控制下的第9个版本(看来提交服务器不勤快啊!)

( X9 V0 K1 }* Z8 \4 D7 Z( N9 G

注:本文出现的地址肯定会根据实际情况而改变(因为u-boot映像文件大小会改变的)!但也肯定不会影响其本质!这一点,山人可以作保证。如果有心情,可以使用md来查看你想查看的内存地址的内容,对比代码,这样可以认识更深入一些。

比如,某一些查看内存是这样的:

LATE2440> md.b 33f9b62c (这个地址是hello那个地址,注意,这个地址改变了)* J1 d; O) S5 z2 T3 d
33f9b62c: 68 65 6c 6c 6f 20 77 6f 72 6c 64 00 73 69 7a 65    hello world.size9 h& M/ n0 U- {) x) {0 r
33f9b63c: 6f 66 20 67 64 3a 25 64 20 73 69 7a 65 6f 66 20    of gd:%d sizeof
- K5 D5 E- N4 G/ ~0 K" `# ?33f9b64c: 62 64 3a 25 64 0a 00 67 64 3a 25 30 38 6c 78 20    bd:%d..gd:%08lx
% t2 e1 R" p) d0 u8 |# d8 u, h33f9b65c: 62 64 3a 25 30 38 6c 78 20 62 64 2d 3e 66 6c 61    bd:%08lx bd->fla
4 y, ]) Q; r8 Q2 V2 HLATE2440>5 M7 F& O5 ]$ }2 l1 A6 f& ^. D
33f9b66c: 67 73 3a 25 64 0a 00 64 6f 5f 74 65 73 74 3a 25    gs:%d..do_test:%
) t. E% S/ j- a6 Z7 v3 @33f9b67c: 70 20 26 64 6f 5f 74 65 73 74 3a 25 70 0a 00 67    p &do_test:%p..g1 a1 R4 e) k& k: T' W) e
33f9b68c: 5f 66 6f 6f 3a 25 70 20 67 5f 66 6f 6f 5f 62 73    _foo:%p g_foo_bs
/ k) [/ Y% X* o2 b2 N33f9b69c: 73 3a 25 70 20 67 5f 66 6f 6f 5f 73 74 61 74 69    s:%p g_foo_stati
- `1 `# l5 J, G7 b: ?LATE2440>' K8 C% L, r0 P* U/ F( d
33f9b6ac: 63 3a 25 70 0a 00 6c 5f 66 6f 6f 3a 25 70 20 6c    c:%p..l_foo:%p l0 L4 Y; J9 i4 w: y) h. q9 W
33f9b6bc: 5f 66 6f 6f 5f 62 73 73 3a 25 70 20 6c 5f 66 6f    _foo_bss:%p l_fo
0 D7 ^9 L) r% r$ m8 C  W33f9b6cc: 6f 5f 73 74 61 74 69 63 3a 25 70 0a 00 68 65 6c    o_static:%p..hel- ^- i+ c" \) e2 u
33f9b6dc: 6c 6f 3a 25 70 20 62 61 72 3a 25 73 20 62 61 72    lo:%p bar:%s bar

+ Z" ?5 l' Z8 c& c7 G

本文有图有真相,不作太多解释,以免显露自己的无知及不足。

: Y8 v7 s. L+ z. h
回复

使用道具 举报

发表于 2017-6-12 21:26 | 显示全部楼层
谢谢楼主解惑,写的这么详细。
回复

使用道具 举报

 楼主| 发表于 2017-6-12 21:31 | 显示全部楼层
hotdll 发表于 2017-6-12 21:26
9 ?, Z7 H5 h3 O6 [谢谢楼主解惑,写的这么详细。

4 H8 Y8 w, O& @很高兴对你有帮助
回复

使用道具 举报

本版积分规则

QQ|一淘宝店|手机版|商店|一乐电子 ( 粤ICP备09076165号 ) 公安备案粤公网安备 44522102000183号

GMT+8, 2025-8-20 08:59 , Processed in 0.045459 second(s), 26 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表