一乐电子

一乐电子百科

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

QQ登录

只需一步,快速开始

快捷登录

手机号码,快捷登录

搜索
查看: 2368|回复: 0
收起左侧

uboot中的TEXT_BASE

[复制链接]
发表于 2017-6-10 16:48 | 显示全部楼层 |阅读模式
, [: `$ g! q; U/ Z
uboot中的TEXT_BASE3 U' F/ a: J" ~6 L! @( `/ z2 Q
& _3 i  @' G2 y7 |. X* j0 e+ n& c
都知道U-BOOT分为两个阶段,第一阶段是(~/cpu/ARM920t/start.S中)在FLASH上运行(一般情况下),完成对硬件的初始化,包括看门狗,中断缓存等,并且负责把代码搬移到SDRAM中(在搬移的时候检查自身代码是否在SDRAM中),然后完成C程序运行所需要环境的建立,包括堆栈的初始化等,最后执行一句跳转指令:& W- O' Y. L1 T7 g! b0 o
$ y1 f7 G: V& k- y* h% m. J
        ldr pc, _start_armboot4 x( Q) T2 A7 q
# X* d; K" I" N: P/ n$ g6 C
        _start_armboot: .word start_armboot,
* ]+ L. z$ q! u" j8 F  N2 @5 r, @
进入到/lib_arm/board.c中的函数void start_armboot (void),从此就进入了第二阶段。这是在很多资料上都有讲述的,所以勿需多言了。
. D/ H& y; s8 }+ P, b1 b- N' ?- L' F' ]4 m3 z! a; r
    现在对于第一阶段有几个问题,以前我一直是没有搞明白的,既然在FLASH中的代码是把自己拷贝到SDRAM中,那么在S3C2410的内存地址空间,就有两份的启动代码,第一份就是在FLASH中,第二份就是在SDRAM中。根据链接脚本文件(~/board/smdk2410/u-boot.lds)9 k. R1 P( `. V5 b5 b2 _! k4 R

6 w$ f  {* n7 J& M" P4 mOUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")9 A9 M0 P- C& I" P  U; r
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
0 Z8 m/ H9 R3 e7 X, iOUTPUT_ARCH(arm)
, k) d3 g2 }$ s* p4 y, sENTRY(_start)
9 |; S& a' `( d! }7 T, @  O. zSECTIONS3 a9 t4 l$ `0 X; Q
{( u9 _) u# g6 d" a# n5 U4 c& Q
. = 0x00000000;    /* 后记:这个链接起始地址实际上被-Ttest $(TEST_BASE)更新了*/, l- j' Q/ \. e6 T3 P6 t
* G  L  i" F( f. g4 A/ |! `
. = ALIGN(4);
  ?4 q# k% J, }, n& r& P. m .text      :+ q; d8 \* N9 t  c, o1 L- G- i
{# W8 P( g% f8 f* l- t: ~/ D
   cpu/arm920t/start.o (.text)
7 n# P0 r; Z9 @4 D: H" I# D0 u   *(.text)
/ }' g7 r0 @. Q4 g7 ^ }
$ _  ]- t8 r* q1 ?" S8 Q5 I, R0 K2 h5 x! k
. = ALIGN(4);& I% y" E  o: g( j8 ?9 P
.rodata : { *(.rodata) }
* R* e' x, l( \) ?, X9 Y) U/ t1 ~- A/ J& X
. = ALIGN(4);6 N1 ?/ t6 ]. v9 E
.data : { *(.data) }
- C6 h- g" L+ Q' [- c* Q+ e2 X
1 B: U2 x# P$ R . = ALIGN(4);$ t  d1 c8 [  |. E# E2 ~
.got : { *(.got) }
* @9 z! D8 b' [+ M: R( I
+ w( v0 f0 U1 m5 f7 \ . = .;: K8 }- T) {) A( @* [5 E  B
__u_boot_cmd_start = .;. Y7 d7 m3 B- V+ O1 A9 }0 t
.u_boot_cmd : { *(.u_boot_cmd) }' A, {# f9 U3 k" _4 Z# l0 F1 _4 g
__u_boot_cmd_end = .;
+ W! Y& _. @+ p3 u$ V6 d; ]$ k; L& V# d6 j. q
. = ALIGN(4);# f& d; C9 x' T* Z+ y0 o* J. Q
__bss_start = .;) c+ o# n; I6 F4 W, @  A) n  U$ F
.bss : { *(.bss) }
5 j# l, \2 |1 t$ j1 `* `. M) m _end = .;
4 Y1 e- y$ S. ~
& L$ S5 U4 [& x0 d% A}
" H; e) x5 ~! ^- Y! z0 f  ]! q! Y    其中的链接命令 . = 0x00000000;表示地址计数器从0地址开始计数,而且_start 是程序代码段的入口,那么*.text中的所有地址标号(cpu/arm920t/start.S中定义的)就应该从0地址开始计数,那么标号 start_armboot(就是void start_armboot (void)函数的入口地址)应该在FLASH中才对啊,所以按照上边的分析,
' x# t, F7 T+ b) X
/ H- w0 y  H9 M2 ~. z  D        ldr pc, _start_armboot% P$ t  a* ~5 f' K

. |  D0 |) S% ?$ T0 A# M        _start_armboot: .word start_armboot' C7 X* Y( ?3 k7 X2 `- x& w
7 j6 |; g) m' _+ `# z2 ]) a2 b
此条语句后,并没有跳转到SDRAM中的void start_armboot (void),而是跳转到了FLASH中的void start_armboot (void)中。; F; Q" n- @7 @/ ~, ?2 g

6 S$ a- X, g1 t6 h所以就出现了这样的矛盾,在FLASH中有一段代码把自己拷贝到SDRAM中,产生了两份UBOOT可执行的指令流,但是最后却没有跳转到SDRAM中去运行以提高指令执行的速度。0 b9 l/ N" r' Q* u- T

# i- H6 t, g' f) H7 Y7 b) e" K! v" v产生以上的认识是基于以下几个认识(肯定是错误的):
, R% d- M# h2 w0 T/ f- M! b' K1 @) h& w! t1 F( @% v
1.*.text中的所有地址标号(在链接时确定)是从0地址开始生成的。) I% s7 U! r% R0 \" ^8 c
+ Z0 M9 G4 G' R) ~7 L  P3 T
      实际上在arm-linux-ld 执行时,原来定义的0x0地址被更新为TEXT_BASE定义的地址。7 C5 A- G% x0 o0 P+ n' Z/ I- B  `
( X. b% m& B! y8 K
2.relocate:    /* relocate U-Boot to RAM     */
# v$ g; V8 T$ j  J) B; m" o   adr r0, _start  /* r0 <- current position of code   */5 D8 C4 _. x& \6 m, ?: n
   ldr r1, _TEXT_BASE  /* test if we run from flash or RAM */# ~* t2 E( k3 o& D
   cmp     r0, r1                  /* don't reloc during debug         */* d3 a$ M$ Q1 J( E2 ?9 j  Z
   beq     stack_setup
* ]+ s$ E4 s) a/ t, E5 ^6 L% P$ @: V  s' x
   ldr r2, _armboot_start
" @% \/ ?3 Y2 L+ d# z+ t   ldr r3, _bss_start6 t' x' [) S! j3 B) s6 M) K$ |
   sub r2, r3, r2  /* r2 <- size of armboot            */, o! N9 A4 {& R8 S% c7 e9 u0 d3 X
   add r2, r0, r2  /* r2 <- source end address         */0 {' z* O; s6 ^7 I7 ?9 m7 E# S

9 S6 B3 o  \! {) k1 E如果不是出于调试阶段,这段搬移代码中的r0和r1肯定不相等的,r0=#0,r1=#TEXT_BASE: 0x33F80000(在./board/smdk2410/config.mk中),所以执行代码的自身拷贝与搬移。2 J' [6 K4 ]' p* k/ T6 H6 Z, Z' s

2 u( K6 P  M3 n注意:在GNU中:adr r0, _start 作用是获得 _start 的实际运行所在的地址值,而ldr r1, _TEXT_BASE 为获得地址_TEXT_BASE中所存放的数据,其中adr r0, _start翻译成 add r0,(PC+#offset),offset 就是 adr r0, _start 指令到_start 的偏移量,在链接时确定,这个偏移量是地址无关的。而 ldr r1, _TEXT_BASE 指令表示以程序相对偏移的方式加载数据,是索引偏移加载的另外一种形式,等同于ldr r1,[PC+#offset],offset 是 ldr r1, _TEXT_BASE 到 _TEXT_BASE 的偏移量。注意这种用法并不是伪指令,伪指令的特征是 ldr r1, =expr/lable_expr。对于LDR伪指令,ADS的情况有些不一样(细微差别),在ADS中的情况可以参考杜春雷<ARM体系结构与编程>144页。8 _  ~& s8 V; C
) B6 c" _) G7 Z$ N6 w- W7 @0 @
# y1 W) E- [" D0 {" I% `
比较一下:
' H- T- g: D9 B5 |# E- A* H* j7 N: _5 B: n+ q
add r0,(PC+#offset):(PC+#offset)是相对地址,表示把本指令上溯或下溯offset处的地址加载到 r0;) F0 ?' S, w+ ]

" v/ a: I5 x; \8 m( s5 y1 a8 X0 g- cldr r1,[PC+#offset]:[PC+#offset]也是相对地址,表示把偏移offset处的地址上的数据加载到 r1;
  `) t- }& ]' }: [3 j
" f2 k3 L. m* ?7 S+ Y* \1 q5 r/ V现在继续:
  s: P& |1 E1 v  `+ A  P
; Q% Z: ?/ F2 U6 x# u    刚才分析所得到的矛盾,肯定是在认识上存在的偏差,经过把U-BOOT进行make后,从所生成的两个.map文件来看(~/u-boot.map和 Systen.map),所有的地址标号都是从0x33f80000开始的,就是从SDRAM的高地址开始,等于TEXT_BASE的值,也就是说,链接器是从0x33F80000开始来链接所编译生成的目标文件的,而不是从0地址开始,经过查看,start_armboot=0x33f80d9c,就是说void start_armboot (void)函数的入口地址在SDRAM中(链接器决定),所以执行
$ m# N* I9 r) @9 R
' s4 J; t  I, T- C" j' I2 t        ldr pc, _start_armboot% [6 R) U/ E* d0 M
1 }: t5 R2 p1 ~, `/ X
        _start_armboot: .word start_armboot,
% y3 M3 X# K! @" l& M: ~2 \* A% t9 M0 y7 p/ e3 K+ g6 a
PC指针肯定就指向了SDRAM中,换句话就是说进入到SDRAM中了,对于ldr pc, _start_armboot,其仍然是GNU中使用程序相对偏移的方式加载数据,翻译一下就是ldr pc, [pc+pc到_start_armboot的偏移值],结果就把_start_armboot地址中的数start_armboot放入pc中完成了跳转,而 start_armboot 的值(函数地址)是在链接时就确定了,是相对于 TEXT_BASE 的。因为在整个UBOOT的阶段1中所有的寻址都是相对位置的寻址(虽然链接器认为是阶段1的代码是从地址0x3ff80000中开始链接的),把阶段1 的代码放在0地址开始的FLASH中也是可以正确的运行的,如果ARM的复位向量是在0x00000001(假设),那么把代码烧写到从 0x00000004处开始的地方,上电时也可以正确的运行(假设ARM的复位向量是在0x00000004成立),当然ARM的复位向量不在这里,只是以此假设来说明以上的对于阶段1的分析。- ~4 a! [( W; V* a: O% n

' o* l2 y; r. O7 [) s" c2 a6 G    现在最后一个矛盾就是链接脚本(~/board/smdk2410/u-boot.lds)所描述的链接地址与实际的链接地址不相同的问题,因为根据链接脚本,所有的地址标号应该从0地址开始计数的,然而不是。经过查找Makefile文件,在顶层的Makefile文件中,在166行中链接是的链接命令:5 K+ A" `0 E0 W5 I3 X$ k$ V9 U; w6 r

) |# N: H) |' R. ^& N$(LD) $(LDFLAGS) $$UNDEF_SYM $(OBJS) /,
2 Y0 J% j  `# [' j# V4 f8 x' A( I
% W4 t+ J; S) V* i# G9 I其中的LDFLAGS在定义在顶层的config.mk中的145行:LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS),
/ g$ w5 w0 s$ `- b7 `. i! d8 E% J: r7 t8 p" j9 y/ D% _9 Q
最关键的就是 -Ttext $(TEXT_BASE)命令了,他的含义就是说,起始地址在TEXT_BASE,而TEXT_BASE在~/board/smdk2410/config.mk中TEXT_BASE = 0x3FF80000;
2 A$ L6 t1 W. T! m3 @
* I. e! W% s7 S+ a# Z) k, P+ e, z到此就弄清楚为什么链接从0x3ff80000开始的了,至于链接脚本,其主要作用是用来指明各个*.o文件的顺序,如入口地址标号(_start)等,以及使两个地址标号得到当前的地址
: J! {9 n( u9 b8 c5 N" H
: l8 @0 }% A, i# j  o    __u_boot_cmd_start = .;    *.u_boot_cmd段的起始地址# S% v6 N6 ^2 b/ m  P7 u# y
/ K$ T# U, W/ m
    .u_boot_cmd : { *(.u_boot_cmd) }) h8 B! g" r" M+ x
    __u_boot_cmd_end = .;       *.u_boot_cmd段的结束地址
: Y+ z; u% w; v$ w% Z( {( L/ h+ }
+ M# P9 M! @. o; u以供C程序使用。 __u_boot_cmd_start和__u_boot_cmd_end可以作为全局的一个常数使用。3 ^: O$ t- n, u  x$ ~( L
' L* x3 V( B3 a# Z, a
总结:/ @; p7 Z1 @6 U( W- c2 O
' Y+ D) K! }3 [
    因为-Ttext $(TEXT_BASE)命令的使用,链接器把UBOOT从地址0x3ff80000开始连接,在第一阶段中,所有使用的目标地址寻址都是使用当前PC值加减偏移量的方法,所以把UBOOT烧写到0地址开始的FLASH中,不影响第一阶段的正确执行。

本版积分规则

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

GMT+8, 2024-4-24 08:21 , Processed in 0.043583 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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