一乐电子

一乐电子百科

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

QQ登录

只需一步,快速开始

快捷登录

手机号码,快捷登录

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

uboot中的TEXT_BASE

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

6 z4 }6 b* P2 N5 }' U* |% [uboot中的TEXT_BASE
3 W: c0 _+ d% ?: g. {' B/ d! q6 o% U8 i2 Q9 N: W7 V' e7 M, v6 ~/ T( @
都知道U-BOOT分为两个阶段,第一阶段是(~/cpu/ARM920t/start.S中)在FLASH上运行(一般情况下),完成对硬件的初始化,包括看门狗,中断缓存等,并且负责把代码搬移到SDRAM中(在搬移的时候检查自身代码是否在SDRAM中),然后完成C程序运行所需要环境的建立,包括堆栈的初始化等,最后执行一句跳转指令:' y. k& s2 R' N

+ O1 z, @' l+ O9 x$ @        ldr pc, _start_armboot0 p' m& J; }- d- N& L

" `# I, `& L1 e. y        _start_armboot: .word start_armboot,
+ E% Y4 c* r, W; B2 P' f6 j
$ M% i8 j4 A6 o2 H- N. Z( n0 G9 A进入到/lib_arm/board.c中的函数void start_armboot (void),从此就进入了第二阶段。这是在很多资料上都有讲述的,所以勿需多言了。$ @! M) W& t5 Z. c2 A7 s

0 C! v5 Q& `0 W/ A0 G    现在对于第一阶段有几个问题,以前我一直是没有搞明白的,既然在FLASH中的代码是把自己拷贝到SDRAM中,那么在S3C2410的内存地址空间,就有两份的启动代码,第一份就是在FLASH中,第二份就是在SDRAM中。根据链接脚本文件(~/board/smdk2410/u-boot.lds)
  j6 y$ B# g! o& p) w% S8 a% h+ }! J9 C1 a( F6 ~! F
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
4 z9 ]8 s; v! B$ Q& C8 o0 Q/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/- |! g. _8 g) F( Q* l' T
OUTPUT_ARCH(arm)  h# ~: R1 g* i8 k; q
ENTRY(_start)
4 K1 a9 D5 D8 C; [SECTIONS2 ~" S/ o- R3 v, |8 j( R! D
{
2 p/ y* F, ^2 G . = 0x00000000;    /* 后记:这个链接起始地址实际上被-Ttest $(TEST_BASE)更新了*/; }" W/ \6 E( n; ^+ L& v
3 F$ K; a# x2 W  I, Z' s# g- ?" ~- X( F
. = ALIGN(4);
# @7 |$ ~; c9 R. G .text      :
6 U; r. S+ ]3 @( S7 z1 e {
( u' t% I- ^0 _1 f* c   cpu/arm920t/start.o (.text)
1 p; k) `. z3 b; ~- O6 R7 S+ u   *(.text)3 ^5 Y" o2 j, Y/ E+ B4 D$ @
}
4 l+ C/ M. @* {8 j' a1 Q( h" ?% y  Y
. = ALIGN(4);
/ E8 c7 ^. t$ U" G .rodata : { *(.rodata) }
% w1 B4 o) a3 W& N5 Y
8 @! g+ ]5 j' m0 ~8 x; Q  J$ M . = ALIGN(4);
- h$ r3 t, X. \; {; F0 Z. h .data : { *(.data) }: T& B+ r' z+ V7 v. [7 `- h5 {

$ J3 n5 o5 M" d! e% e3 ]2 Z . = ALIGN(4);# k+ v) A, S; r7 r2 B
.got : { *(.got) }" [* l- {1 S+ f- h+ A% I% `7 s# L

, @& @! S- X3 j  }, u . = .;9 O0 @8 S1 ^! j' _9 p
__u_boot_cmd_start = .;2 t- h. x3 @# [2 S- U0 {; x
.u_boot_cmd : { *(.u_boot_cmd) }: o* {0 T1 J! M6 p
__u_boot_cmd_end = .;
# y( X$ V6 o7 S4 M4 S. s& m  Q) t5 ?" \
. = ALIGN(4);" \  Q# E+ d+ F5 m+ r* p" `- V
__bss_start = .;
( n- u% ]  k0 n# W .bss : { *(.bss) }" I. w4 ]0 p. j% Y! B: J
_end = .;
# Q1 H4 N5 {: N4 C4 z
0 {# z. d) P* |! n/ M5 V}8 \/ U3 c4 B* Z: s2 h
    其中的链接命令 . = 0x00000000;表示地址计数器从0地址开始计数,而且_start 是程序代码段的入口,那么*.text中的所有地址标号(cpu/arm920t/start.S中定义的)就应该从0地址开始计数,那么标号 start_armboot(就是void start_armboot (void)函数的入口地址)应该在FLASH中才对啊,所以按照上边的分析,
' ?; g5 b7 a# \5 [( Q/ a: f4 e' y
$ t9 ?8 H1 l+ T" t, ~2 K/ Y        ldr pc, _start_armboot# ^! U6 k. p& P8 Y9 ~

) U. ~: \9 j- N        _start_armboot: .word start_armboot1 z+ J- J- I  N: K) f
, O- V* q, {. o5 Z7 E9 T4 Z
此条语句后,并没有跳转到SDRAM中的void start_armboot (void),而是跳转到了FLASH中的void start_armboot (void)中。
# h0 _  ^) Z3 M9 i. g" f
$ p& d% Y# f( R6 R2 ]% j& e& s1 }所以就出现了这样的矛盾,在FLASH中有一段代码把自己拷贝到SDRAM中,产生了两份UBOOT可执行的指令流,但是最后却没有跳转到SDRAM中去运行以提高指令执行的速度。
! J3 @/ b% y" a# _8 s4 X8 |
5 @, X5 f# z; b9 P# z) \  X) V4 S/ m: b产生以上的认识是基于以下几个认识(肯定是错误的):
4 `7 t: f. y# x4 E: K- G1 D; w+ A5 \. x6 O
1.*.text中的所有地址标号(在链接时确定)是从0地址开始生成的。* Y( h8 p% T$ v& d1 V! R

4 e7 V, l' Z6 g5 e% U      实际上在arm-linux-ld 执行时,原来定义的0x0地址被更新为TEXT_BASE定义的地址。9 j* ?* c; Z" l& X+ u  Z

6 j# @: Q2 o, ~1 g+ f2 [. G2.relocate:    /* relocate U-Boot to RAM     */
* F6 H4 @1 S" `   adr r0, _start  /* r0 <- current position of code   */' F+ j0 e" P! K. W
   ldr r1, _TEXT_BASE  /* test if we run from flash or RAM */5 O9 ?, }5 m$ F- {( `- @
   cmp     r0, r1                  /* don't reloc during debug         */  g5 b2 e! [8 q6 U  P0 o) |
   beq     stack_setup; \6 ]2 z  e: g9 u* M
! g; Q# q; }1 ~1 z
   ldr r2, _armboot_start
( v9 r. }) s* G   ldr r3, _bss_start; i' f7 i* Y" U7 B8 {. v5 q  L5 p
   sub r2, r3, r2  /* r2 <- size of armboot            */
$ r, j0 h  v2 i4 R+ j1 n   add r2, r0, r2  /* r2 <- source end address         */2 R2 o% B/ Z) D$ d

5 W& m1 b) v, B- x+ y8 ~& |3 F如果不是出于调试阶段,这段搬移代码中的r0和r1肯定不相等的,r0=#0,r1=#TEXT_BASE: 0x33F80000(在./board/smdk2410/config.mk中),所以执行代码的自身拷贝与搬移。" i+ m. Z4 V; D3 m0 J( J$ [: D
) v$ t% ~2 R( b) d
注意:在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页。- I6 o5 O( {$ U$ \

" c; V& G3 X- G! |1 V4 d/ z4 C  r: S* L6 `# B9 J( d
比较一下:2 Y) V* e3 Z' s8 v# d: V  Y6 Y

5 Q0 u) I; v1 \; ^% Aadd r0,(PC+#offset):(PC+#offset)是相对地址,表示把本指令上溯或下溯offset处的地址加载到 r0;5 s: F' Z+ [! }3 ^
9 o7 |+ O8 x$ |# q! g5 C5 W
ldr r1,[PC+#offset]:[PC+#offset]也是相对地址,表示把偏移offset处的地址上的数据加载到 r1;
/ ?* k7 V& w% [7 L  `5 H( Z: P0 e5 Z5 q7 A% U# W! d
现在继续:
- }( m+ o, m0 R) L( h) I4 ^9 r% N- N. d6 g$ x" Q" E# W; Y
    刚才分析所得到的矛盾,肯定是在认识上存在的偏差,经过把U-BOOT进行make后,从所生成的两个.map文件来看(~/u-boot.map和 Systen.map),所有的地址标号都是从0x33f80000开始的,就是从SDRAM的高地址开始,等于TEXT_BASE的值,也就是说,链接器是从0x33F80000开始来链接所编译生成的目标文件的,而不是从0地址开始,经过查看,start_armboot=0x33f80d9c,就是说void start_armboot (void)函数的入口地址在SDRAM中(链接器决定),所以执行
- }  a" g0 o2 C! l5 D$ h9 {/ X8 Q1 ]: ^# z; H- H+ S
        ldr pc, _start_armboot
4 m( O9 A( b9 l
! }5 _. B; {: \8 V        _start_armboot: .word start_armboot,
5 w& ?, j$ }% }7 M5 O( ?/ B7 \$ S2 R- t  ^' M- o
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的分析。
  z: \, S  f0 l0 ~. Q7 W' T  x& I9 q6 `1 |' u6 {
    现在最后一个矛盾就是链接脚本(~/board/smdk2410/u-boot.lds)所描述的链接地址与实际的链接地址不相同的问题,因为根据链接脚本,所有的地址标号应该从0地址开始计数的,然而不是。经过查找Makefile文件,在顶层的Makefile文件中,在166行中链接是的链接命令:
6 j9 g4 ^. R. O6 x7 C/ ]. U0 g+ N5 t
$(LD) $(LDFLAGS) $$UNDEF_SYM $(OBJS) /,  s* H/ Z& Q0 _, r) V" w% u
4 t0 X; G0 Q1 M
其中的LDFLAGS在定义在顶层的config.mk中的145行:LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS),
, M3 X! c3 k1 |
& N7 n. C. R8 `最关键的就是 -Ttext $(TEXT_BASE)命令了,他的含义就是说,起始地址在TEXT_BASE,而TEXT_BASE在~/board/smdk2410/config.mk中TEXT_BASE = 0x3FF80000;
1 V; I: n! x) u
( X6 l0 M: `8 ?' B, h9 h6 |( h  O到此就弄清楚为什么链接从0x3ff80000开始的了,至于链接脚本,其主要作用是用来指明各个*.o文件的顺序,如入口地址标号(_start)等,以及使两个地址标号得到当前的地址" R# f3 h' ~! [% i( X7 z
0 ]. D% w( Z% q% Y4 F8 |
    __u_boot_cmd_start = .;    *.u_boot_cmd段的起始地址8 y5 `0 z$ O- w" `) Q" x6 i2 ?
: T% C$ J3 J! d) ~
    .u_boot_cmd : { *(.u_boot_cmd) }7 o* W' ^; n  F3 d* r, B  A
    __u_boot_cmd_end = .;       *.u_boot_cmd段的结束地址
4 s8 [; w8 E8 _! J7 Z8 B  T6 p. I) G, u" t
以供C程序使用。 __u_boot_cmd_start和__u_boot_cmd_end可以作为全局的一个常数使用。6 u9 B) t1 a4 P9 m6 E9 ]
% z6 J2 t! q. _" q8 S7 ~1 S
总结:
0 w$ n( H# h9 e' n5 W- r, V: m% ?! m; X4 R
    因为-Ttext $(TEXT_BASE)命令的使用,链接器把UBOOT从地址0x3ff80000开始连接,在第一阶段中,所有使用的目标地址寻址都是使用当前PC值加减偏移量的方法,所以把UBOOT烧写到0地址开始的FLASH中,不影响第一阶段的正确执行。

本版积分规则

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

GMT+8, 2024-3-28 17:27 , Processed in 0.058867 second(s), 31 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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