一乐电子

一乐电子百科

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

QQ登录

只需一步,快速开始

快捷登录

手机号码,快捷登录

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

uboot中的TEXT_BASE

[复制链接]
发表于 2017-6-10 16:48 | 显示全部楼层 |阅读模式

6 v) p& B$ A' {) _uboot中的TEXT_BASE+ S* \9 s3 t/ B( }; H9 b: Q- j
9 g  g8 C/ z. t4 W3 |4 T
都知道U-BOOT分为两个阶段,第一阶段是(~/cpu/ARM920t/start.S中)在FLASH上运行(一般情况下),完成对硬件的初始化,包括看门狗,中断缓存等,并且负责把代码搬移到SDRAM中(在搬移的时候检查自身代码是否在SDRAM中),然后完成C程序运行所需要环境的建立,包括堆栈的初始化等,最后执行一句跳转指令:
* g& x. N6 J1 G' Y* e" R
+ S6 a$ @7 f" v, X9 k* `        ldr pc, _start_armboot" b* j3 L$ l3 L1 ]1 U% T
$ X$ j/ W) w4 L) C
        _start_armboot: .word start_armboot,0 \- Y- X+ q2 o0 Y$ `, y/ o1 _

' X) @& Y" T2 i& @& U* G6 O4 q1 ?进入到/lib_arm/board.c中的函数void start_armboot (void),从此就进入了第二阶段。这是在很多资料上都有讲述的,所以勿需多言了。2 h% d3 r. t0 m% H* v
( i5 k1 n8 _' V% ^- a) O
    现在对于第一阶段有几个问题,以前我一直是没有搞明白的,既然在FLASH中的代码是把自己拷贝到SDRAM中,那么在S3C2410的内存地址空间,就有两份的启动代码,第一份就是在FLASH中,第二份就是在SDRAM中。根据链接脚本文件(~/board/smdk2410/u-boot.lds), K% ^$ }4 c+ z& n, Y8 A
% v1 P& [+ M5 M& Y0 T; u9 Z; Y
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")% D  Y1 e9 p+ q3 E* G! v+ g
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
8 d" G4 y2 t& _1 c( ~OUTPUT_ARCH(arm)
9 y% l  j/ G3 R$ |# XENTRY(_start)
7 v. h6 T* J% o7 D( `SECTIONS
8 i! n' u: O# W4 h* e{
$ J/ F+ m& H; v: w . = 0x00000000;    /* 后记:这个链接起始地址实际上被-Ttest $(TEST_BASE)更新了*/
% D# m+ G$ X- l, z# B' o% r) ]) I; c7 m6 Q  B+ |
. = ALIGN(4);/ s2 r" M9 R# Q8 q6 ^1 _
.text      :
& P  Q- g1 v1 n, o) t0 ]7 b! O& v {
0 z+ r, a0 L/ X! E$ x   cpu/arm920t/start.o (.text)2 {$ h) P4 z. e5 z: Z1 q
   *(.text)
$ B! @) [/ V" K" y }2 D% K6 |' m9 K4 b
5 r( \0 B6 b2 c7 Q' L- q
. = ALIGN(4);; V: V6 k* |% G, \5 E7 r
.rodata : { *(.rodata) }! ]4 j" F$ Z7 v7 }

) G/ C' f$ G5 u5 }3 a8 U . = ALIGN(4);
. s& W6 `* [2 G8 }, [: p- \7 Z .data : { *(.data) }1 X& d" J, q5 p' {

; w- c' g& w) t+ A2 |  O . = ALIGN(4);; z# w5 K2 f" L" j
.got : { *(.got) }6 f# C5 F8 f1 a! J3 D- [$ C( w

; e0 F5 m; v# ~8 M . = .;# k) J7 e" Z4 o  m7 l
__u_boot_cmd_start = .;" U- L' ?6 u6 P( U1 m
.u_boot_cmd : { *(.u_boot_cmd) }
  Q: s: s# f  _& R- l: A0 F, T __u_boot_cmd_end = .;
3 |% T4 v: e0 u  K! k6 r2 w" A4 `9 k' @* e* W6 T
. = ALIGN(4);
. k' e* i, ^8 L1 D( w) O2 ? __bss_start = .;
3 U3 F0 E6 z) [8 j8 U .bss : { *(.bss) }+ ]' \+ h9 c; {7 P: H
_end = .;% Z' U7 ~: b: d' a& M

& l+ P# p# U+ ~: O}
6 o, g3 ?4 H9 q! e  C    其中的链接命令 . = 0x00000000;表示地址计数器从0地址开始计数,而且_start 是程序代码段的入口,那么*.text中的所有地址标号(cpu/arm920t/start.S中定义的)就应该从0地址开始计数,那么标号 start_armboot(就是void start_armboot (void)函数的入口地址)应该在FLASH中才对啊,所以按照上边的分析,
6 V% A4 u6 O2 b" ]
9 U" [% ?5 W: }, E6 j  t        ldr pc, _start_armboot  P% |" m0 W! P2 U
5 Z* x. q4 Z8 j; I* a: L" c2 @
        _start_armboot: .word start_armboot
) Y$ w. s" N' M% b
9 J  V# F# j0 [/ m, E; M此条语句后,并没有跳转到SDRAM中的void start_armboot (void),而是跳转到了FLASH中的void start_armboot (void)中。
8 j! e- S/ Y9 z9 P5 A( v; S! d$ @2 `, I& l7 S$ _7 C
所以就出现了这样的矛盾,在FLASH中有一段代码把自己拷贝到SDRAM中,产生了两份UBOOT可执行的指令流,但是最后却没有跳转到SDRAM中去运行以提高指令执行的速度。
# |. S- ^  \" X/ r0 o# t7 C8 J$ b, P( P: J
产生以上的认识是基于以下几个认识(肯定是错误的):) e  u( t1 h  T6 f# E
+ l1 [* ?* i- k1 F6 `4 s. l2 Y
1.*.text中的所有地址标号(在链接时确定)是从0地址开始生成的。' r. w% a) D' d5 |# t5 z
! {  d4 G+ ~' A& _! t0 F
      实际上在arm-linux-ld 执行时,原来定义的0x0地址被更新为TEXT_BASE定义的地址。
7 S3 b+ a# a  W. i& ]5 `. ~6 P
# e9 V- f- W" G% I# u- a9 p2.relocate:    /* relocate U-Boot to RAM     */
/ \( {, Z1 U0 }6 G, g& C. `   adr r0, _start  /* r0 <- current position of code   */0 ~3 o2 b5 C1 \' K: k/ r1 i
   ldr r1, _TEXT_BASE  /* test if we run from flash or RAM */
  F6 E2 L! Y7 A# H   cmp     r0, r1                  /* don't reloc during debug         */1 d3 `6 ]: C: _/ _$ o' p
   beq     stack_setup
- i! h; b0 ~! }) e
% @9 N$ _! D% K5 j7 k   ldr r2, _armboot_start: U! d. c/ m, ^
   ldr r3, _bss_start
' _$ z& H! W1 @3 H9 N7 v/ ^   sub r2, r3, r2  /* r2 <- size of armboot            */' C0 \8 j3 c( a' J/ G& x/ ]: D
   add r2, r0, r2  /* r2 <- source end address         */
/ @' H- |4 S% x+ \( t6 S+ j/ x8 N1 Q; F8 V. f1 P! S5 {
如果不是出于调试阶段,这段搬移代码中的r0和r1肯定不相等的,r0=#0,r1=#TEXT_BASE: 0x33F80000(在./board/smdk2410/config.mk中),所以执行代码的自身拷贝与搬移。; O% U9 A7 M5 J. c" @

1 n* `: X2 `" f注意:在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页。
& }5 P6 L3 a* s; e6 T1 d
8 x( O4 W: M. |2 t2 T
, K  F7 Z0 J; W! u6 O0 }比较一下:
) f8 Z0 H7 o5 ]! Q. T6 z6 a! T7 [; Z# w2 J5 |
add r0,(PC+#offset):(PC+#offset)是相对地址,表示把本指令上溯或下溯offset处的地址加载到 r0;# p! g' M7 X! A" r5 q$ N: F
6 `3 e- O" D( D; i% b
ldr r1,[PC+#offset]:[PC+#offset]也是相对地址,表示把偏移offset处的地址上的数据加载到 r1;" I+ L% j3 \2 {/ L

2 `+ Y# g5 r! B3 n0 m5 I现在继续:9 k! {3 Y( B9 m( ^* E+ k

7 i8 c* d5 N9 l. E" |+ @. O" x    刚才分析所得到的矛盾,肯定是在认识上存在的偏差,经过把U-BOOT进行make后,从所生成的两个.map文件来看(~/u-boot.map和 Systen.map),所有的地址标号都是从0x33f80000开始的,就是从SDRAM的高地址开始,等于TEXT_BASE的值,也就是说,链接器是从0x33F80000开始来链接所编译生成的目标文件的,而不是从0地址开始,经过查看,start_armboot=0x33f80d9c,就是说void start_armboot (void)函数的入口地址在SDRAM中(链接器决定),所以执行
6 \5 X+ \7 d$ a/ d" \) Q- f' b  l0 ~0 \
        ldr pc, _start_armboot
  b5 |! [" S/ j& o3 G5 \/ c7 W1 \( D6 v4 s4 k3 R  A
        _start_armboot: .word start_armboot,
. m" Q& j' V  Q5 s; A5 }  b
! W' p) d& {1 j) yPC指针肯定就指向了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的分析。
. P, E1 L( G% E# h. w, {3 ]- `' o! T& Y8 E) s5 x8 S# F8 O
    现在最后一个矛盾就是链接脚本(~/board/smdk2410/u-boot.lds)所描述的链接地址与实际的链接地址不相同的问题,因为根据链接脚本,所有的地址标号应该从0地址开始计数的,然而不是。经过查找Makefile文件,在顶层的Makefile文件中,在166行中链接是的链接命令:/ Y8 g1 o( T& E& l1 A4 y

# W+ B- z4 W  s/ ?$(LD) $(LDFLAGS) $$UNDEF_SYM $(OBJS) /,! K! {/ g  T4 N$ |6 I0 o# v
; ?* e4 `7 Q$ Y2 V" d
其中的LDFLAGS在定义在顶层的config.mk中的145行:LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS),7 o9 _; ~/ U9 O: R4 w9 x
2 \1 Z# j) b: d
最关键的就是 -Ttext $(TEXT_BASE)命令了,他的含义就是说,起始地址在TEXT_BASE,而TEXT_BASE在~/board/smdk2410/config.mk中TEXT_BASE = 0x3FF80000;9 y7 ?+ q  J, ^9 E  k0 E
/ p# J; w' d% d3 k% K( \7 [( a
到此就弄清楚为什么链接从0x3ff80000开始的了,至于链接脚本,其主要作用是用来指明各个*.o文件的顺序,如入口地址标号(_start)等,以及使两个地址标号得到当前的地址4 `4 [0 G6 f& O7 v4 `

! O" h$ D$ T" W! |7 w    __u_boot_cmd_start = .;    *.u_boot_cmd段的起始地址
' O4 z: x  S0 t* J$ E0 H
/ k! i" E; F* `3 b$ H1 n( F+ g    .u_boot_cmd : { *(.u_boot_cmd) }% k$ i, b& A% q
    __u_boot_cmd_end = .;       *.u_boot_cmd段的结束地址, D3 M  a* G7 w* s" \

4 g/ E4 r: d3 P以供C程序使用。 __u_boot_cmd_start和__u_boot_cmd_end可以作为全局的一个常数使用。' A( B- v- Z" @9 a4 d0 V
: O5 p9 W" f1 T8 F/ q0 h3 y
总结:* [$ B9 ~8 t2 L0 w# h: u: Z
/ [3 r- l+ ?+ r# ~6 {# E4 w, Z
    因为-Ttext $(TEXT_BASE)命令的使用,链接器把UBOOT从地址0x3ff80000开始连接,在第一阶段中,所有使用的目标地址寻址都是使用当前PC值加减偏移量的方法,所以把UBOOT烧写到0地址开始的FLASH中,不影响第一阶段的正确执行。

本版积分规则

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

GMT+8, 2025-4-28 10:32 , Processed in 0.047857 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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