一乐电子

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

QQ登录

只需一步,快速开始

微信扫码登录

手机号码,快捷登录

手机号码,快捷登录

搜索
查看: 3978|回复: 0

uboot中的TEXT_BASE

[复制链接]
发表于 2017-6-10 16:48 | 显示全部楼层 |阅读模式
2 z- Z: `& N3 a( N+ V% P* q
uboot中的TEXT_BASE6 P7 E2 T6 {8 v, H# e$ Q

0 o, y, h3 \8 Q- o7 J3 B0 M( e都知道U-BOOT分为两个阶段,第一阶段是(~/cpu/arm920t/start.S中)在FLASH上运行(一般情况下),完成对硬件的初始化,包括看门狗,中断缓存等,并且负责把代码搬移到SDRAM中(在搬移的时候检查自身代码是否在SDRAM中),然后完成C程序运行所需要环境的建立,包括堆栈的初始化等,最后执行一句跳转指令:) F$ }. @" H3 C3 Z4 X7 |

0 }# i/ v" }" M& S        ldr pc, _start_armboot: w! D$ i+ Y+ h; E' ]
1 E% x; k" _; W
        _start_armboot: .word start_armboot,
, D: Y% |3 }- Z8 h4 p& G4 |9 v# S) k" t6 l5 [
进入到/lib_arm/board.c中的函数void start_armboot (void),从此就进入了第二阶段。这是在很多资料上都有讲述的,所以勿需多言了。
- h6 i: w# [9 m% U! L5 L
, }/ b: G( ]8 @    现在对于第一阶段有几个问题,以前我一直是没有搞明白的,既然在FLASH中的代码是把自己拷贝到SDRAM中,那么在S3C2410的内存地址空间,就有两份的启动代码,第一份就是在FLASH中,第二份就是在SDRAM中。根据链接脚本文件(~/board/smdk2410/u-boot.lds)9 W4 v, _  r3 h0 N9 b& V4 v9 ]* ]: G

$ s; J5 l$ h; QOUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
% R0 _/ Z8 U. N+ s/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/3 \6 A- w# d" p. p/ a
OUTPUT_ARCH(arm); t, L* s9 j8 e3 ?& U
ENTRY(_start)
5 m" |  n$ l* _! y! s8 H& pSECTIONS" {3 p  h! O) w" Z9 ^) |  U
{
5 v& M( C7 F8 F+ S . = 0x00000000;    /* 后记:这个链接起始地址实际上被-Ttest $(TEST_BASE)更新了*/
' P& O% ^: f# u) @- w  A
' {) Q+ M" m" V0 l . = ALIGN(4);9 ?5 O! n. Y6 Q1 R9 @
.text      :% k3 g+ F$ i3 y$ X
{
+ X8 |1 H: t6 ?0 p3 e$ u8 l+ Y   cpu/arm920t/start.o (.text)% d4 n4 Q, _& T& E2 {9 y
   *(.text); S  K/ Z+ s' d& s& C1 r0 x0 _
}9 S0 s7 g' T* \

1 e1 S7 [0 n# w% [ . = ALIGN(4);& o, k2 S, j) D7 K$ X/ `) I6 e
.rodata : { *(.rodata) }
4 s$ t" h6 u0 {. q
9 A- |4 R/ n, }: p1 B . = ALIGN(4);% N4 z, E  B$ q# M
.data : { *(.data) }5 h) Z, D% _# \. s0 @. x
3 V3 ^6 k/ B+ K: Y0 A
. = ALIGN(4);' _: E! H$ U- y/ c5 F: X, E
.got : { *(.got) }* Y! m4 k, k4 P" t8 T: h
0 `; r$ z" P. I1 x, N
. = .;) ^1 N4 ?9 C$ e: u3 ^9 l
__u_boot_cmd_start = .;
1 H5 a3 M/ t1 D, i5 q+ W; c. [& a% | .u_boot_cmd : { *(.u_boot_cmd) }( f. e% V3 T- V+ c6 D7 @+ C
__u_boot_cmd_end = .;% E% A) \  ?5 O5 N+ f% T

7 N5 s  x! q4 E. r . = ALIGN(4);4 I3 c1 }  `) G
__bss_start = .;& @) v/ X. e% z! f5 _3 t6 Y
.bss : { *(.bss) }4 @3 @/ I- z9 @$ Z/ C6 @
_end = .;
/ [" u. G7 f6 _! u
% x# q, i- k6 A  A: G3 B* R}
5 W/ E. Q+ d, D( ]% T    其中的链接命令 . = 0x00000000;表示地址计数器从0地址开始计数,而且_start 是程序代码段的入口,那么*.text中的所有地址标号(cpu/arm920t/start.S中定义的)就应该从0地址开始计数,那么标号 start_armboot(就是void start_armboot (void)函数的入口地址)应该在FLASH中才对啊,所以按照上边的分析,
. r0 d, y4 J* K& U  s+ x- l
5 \+ C% O: j4 K, q+ y7 }        ldr pc, _start_armboot
. z& ^' f: p% @, h9 G- s3 U5 X/ P7 C% p1 Z
        _start_armboot: .word start_armboot5 v5 G( m* e+ q2 O
+ r; D! S5 c/ [
此条语句后,并没有跳转到SDRAM中的void start_armboot (void),而是跳转到了FLASH中的void start_armboot (void)中。
+ B3 c3 u. l& G, W. l% _# n
0 e. {+ V* d8 z) W3 g所以就出现了这样的矛盾,在FLASH中有一段代码把自己拷贝到SDRAM中,产生了两份UBOOT可执行的指令流,但是最后却没有跳转到SDRAM中去运行以提高指令执行的速度。
3 u$ }3 ~& K6 ?. T' F' S& x/ A0 y( }! W
产生以上的认识是基于以下几个认识(肯定是错误的):2 {& X, b. I+ {- `3 _# K
! s3 _1 T& O# o0 ?
1.*.text中的所有地址标号(在链接时确定)是从0地址开始生成的。
7 L. {* w# |9 }* O2 h- J2 Y7 c
! d9 O  t  i& z# s1 q7 C3 a      实际上在arm-linux-ld 执行时,原来定义的0x0地址被更新为TEXT_BASE定义的地址。
* J' `" F, j6 v3 B& i, p& Z
4 r( b) V4 y% H7 j1 t' t- w2.relocate:    /* relocate U-Boot to RAM     */
1 F7 a1 C1 g7 |$ {+ o) Z; G( ]   adr r0, _start  /* r0 <- current position of code   */
5 t5 m" w  \+ K/ l) h' H9 R% l   ldr r1, _TEXT_BASE  /* test if we run from flash or RAM */2 D2 O/ d, }0 i* E$ u3 p/ u
   cmp     r0, r1                  /* don't reloc during debug         */4 Y, C& U& p% D6 S  {5 [
   beq     stack_setup6 |4 I# {% B4 s3 D0 ?" b
/ V6 X) o9 U6 ]- s& J; R& A, ?9 `
   ldr r2, _armboot_start6 I8 U' q& \! l) c9 w  w
   ldr r3, _bss_start
- h7 ^$ G8 C+ z, V9 A. p   sub r2, r3, r2  /* r2 <- size of armboot            */: _/ W! w' i7 u2 u/ ]) I
   add r2, r0, r2  /* r2 <- source end address         */9 I" q0 a! S* @+ z
6 j; j' W  O* J' }! y5 j
如果不是出于调试阶段,这段搬移代码中的r0和r1肯定不相等的,r0=#0,r1=#TEXT_BASE: 0x33F80000(在./board/smdk2410/config.mk中),所以执行代码的自身拷贝与搬移。' T* Q* [7 O' x/ ^( ]2 U9 V9 Y) j4 d: R

2 p# t, r6 q$ P1 P8 U. {7 p& P注意:在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页。) N1 R/ m& ~- X6 e) ^! i# S" S
) X! ?% v  G, K' U- f) g7 \

* M; t2 l% C  f0 M, f' q# B比较一下:
/ p6 W8 s4 x) I. N% f8 k0 ?" f: H
5 a2 i- U6 v4 C, S! ?; [5 nadd r0,(PC+#offset):(PC+#offset)是相对地址,表示把本指令上溯或下溯offset处的地址加载到 r0;/ K; V% O- a! A- ]
; X% g. B5 l+ Y$ |
ldr r1,[PC+#offset]:[PC+#offset]也是相对地址,表示把偏移offset处的地址上的数据加载到 r1;/ v% e# x: F* R! }
! D5 g1 m. n( D0 [7 j5 E: E5 r& E
现在继续:
, ^4 `3 f1 [1 }1 f
& r0 w& N9 f# [( T, Z& Y; 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 n/ n1 w  X, r; T
& A: b- C$ k. a1 {# |1 T        ldr pc, _start_armboot0 ?* X. o, }+ \- h# Y6 r3 J1 q

! @9 C7 h# A+ i" z8 {& T        _start_armboot: .word start_armboot,
; D8 Z& {/ Q2 ?; z, L/ p0 X' |& R. Z% g% e. d
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的分析。
' h* v( D! E* N# r8 V1 q. G
+ S  \' ]  {1 M) _    现在最后一个矛盾就是链接脚本(~/board/smdk2410/u-boot.lds)所描述的链接地址与实际的链接地址不相同的问题,因为根据链接脚本,所有的地址标号应该从0地址开始计数的,然而不是。经过查找Makefile文件,在顶层的Makefile文件中,在166行中链接是的链接命令:  k1 s) g0 M  P+ b

: `& z2 E! A5 {+ O7 t) Z$(LD) $(LDFLAGS) $$UNDEF_SYM $(OBJS) /,
  U4 Z& ^8 k7 P% x: O, W1 A- t3 I* J; M3 e: Z5 R  w) j
其中的LDFLAGS在定义在顶层的config.mk中的145行:LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS),3 h: t- r& t* A+ t% E% ?
1 x, h2 }4 [; \  Y
最关键的就是 -Ttext $(TEXT_BASE)命令了,他的含义就是说,起始地址在TEXT_BASE,而TEXT_BASE在~/board/smdk2410/config.mk中TEXT_BASE = 0x3FF80000;+ u! G" h6 v( q9 r$ M! b3 t
& r- }" y7 p3 \9 u* A( N7 H
到此就弄清楚为什么链接从0x3ff80000开始的了,至于链接脚本,其主要作用是用来指明各个*.o文件的顺序,如入口地址标号(_start)等,以及使两个地址标号得到当前的地址7 Z0 [! T# ]* v, K
( p9 x; C: f8 u9 d' A6 j1 k
    __u_boot_cmd_start = .;    *.u_boot_cmd段的起始地址
% x8 _$ I( h; }% O' U9 {6 N" y5 n- g, ^8 }( J7 q* c
    .u_boot_cmd : { *(.u_boot_cmd) }3 G, N* g5 R0 O1 `! J* X. L, t& L9 P
    __u_boot_cmd_end = .;       *.u_boot_cmd段的结束地址" p, B8 ?4 I! D7 E

/ E* I, V% _$ @( I: W以供C程序使用。 __u_boot_cmd_start和__u_boot_cmd_end可以作为全局的一个常数使用。0 ?2 K. t- i4 W' @8 |  k4 N

5 W! S1 x. ?4 U& U+ R4 X/ k" z总结:
8 i0 n% l+ p  T6 L" M6 D$ ]1 ]0 f0 `
0 q) H9 d6 C' v" F    因为-Ttext $(TEXT_BASE)命令的使用,链接器把UBOOT从地址0x3ff80000开始连接,在第一阶段中,所有使用的目标地址寻址都是使用当前PC值加减偏移量的方法,所以把UBOOT烧写到0地址开始的FLASH中,不影响第一阶段的正确执行。

本版积分规则

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

GMT+8, 2025-10-28 06:03 , Processed in 0.029909 second(s), 19 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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