一乐电子

一乐电子百科

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

QQ登录

只需一步,快速开始

快捷登录

手机号码,快捷登录

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

uboot中的TEXT_BASE

[复制链接]
发表于 2017-6-10 16:48 | 显示全部楼层 |阅读模式
1 j$ P' e5 K0 I- h8 m' j
uboot中的TEXT_BASE
4 E9 E/ Y. M0 s7 o$ a0 \
' w9 I7 K2 }6 |都知道U-BOOT分为两个阶段,第一阶段是(~/cpu/ARM920t/start.S中)在FLASH上运行(一般情况下),完成对硬件的初始化,包括看门狗,中断缓存等,并且负责把代码搬移到SDRAM中(在搬移的时候检查自身代码是否在SDRAM中),然后完成C程序运行所需要环境的建立,包括堆栈的初始化等,最后执行一句跳转指令:5 r: W6 z, s( i0 Y4 C' h' r  J
6 O1 u2 K% K4 _' c
        ldr pc, _start_armboot# w  z. F( [& q) F
* a, y9 H  ?3 r1 H
        _start_armboot: .word start_armboot,
# A* i; ^3 ^3 ]/ Z
% h; J* E6 ^5 _% f进入到/lib_arm/board.c中的函数void start_armboot (void),从此就进入了第二阶段。这是在很多资料上都有讲述的,所以勿需多言了。
# p  |9 P9 d* {$ E
5 A5 S* T$ h8 d9 x" L2 ?    现在对于第一阶段有几个问题,以前我一直是没有搞明白的,既然在FLASH中的代码是把自己拷贝到SDRAM中,那么在S3C2410的内存地址空间,就有两份的启动代码,第一份就是在FLASH中,第二份就是在SDRAM中。根据链接脚本文件(~/board/smdk2410/u-boot.lds)3 |( ]' S" T7 Q0 P

+ {, O1 z* d' FOUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
( J8 F4 R. ^* @& t7 i1 R' Z/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/) ]0 }" [) b: M* \; Z/ _0 F' N
OUTPUT_ARCH(arm)
9 y8 C7 V& q) K  f- S9 {ENTRY(_start)
2 b& E+ i3 Y  V( u# \5 sSECTIONS
+ i$ [8 [2 N: U/ l4 n{/ j2 I; C9 C, J. U" r! ~" c! B
. = 0x00000000;    /* 后记:这个链接起始地址实际上被-Ttest $(TEST_BASE)更新了*/2 r" Z9 ^1 z. }6 n0 K& J

& W1 F0 d; f0 e3 q9 T . = ALIGN(4);: _- ]' f2 x; a- p6 ~
.text      :
! y: X5 G& T: s  }, a* W {
9 j& u. k# v! j* h( l' q   cpu/arm920t/start.o (.text)
: I8 t! j6 ?6 v; f" T   *(.text)
0 y' T1 E/ ?7 ?) n }2 J) R2 y- d/ V+ b9 |

. J0 I/ j; ?5 S: F  Z . = ALIGN(4);
( O+ }* w4 y8 v2 d  u .rodata : { *(.rodata) }
# C5 j& `  ]# v3 I; K2 b3 y
8 z, U8 Y6 @& p+ I8 J9 \ . = ALIGN(4);
7 \* B& G9 x% ^ .data : { *(.data) }+ p4 V7 x4 y; |# L

  w- Z! T1 x8 G' p( A: m5 L  L0 H . = ALIGN(4);$ I/ U6 c( D/ y
.got : { *(.got) }; Y$ ~5 }/ N2 F" T

6 G$ k' C) \& o5 f( h . = .;1 e9 ^# t9 `& \2 C
__u_boot_cmd_start = .;
- u! V% [( N3 T2 Y8 a! R9 l1 a .u_boot_cmd : { *(.u_boot_cmd) }& ^  ~! g2 A' L" s7 e* L" e
__u_boot_cmd_end = .;
/ X, S( h$ B( h  S: z+ ]" l0 s' w' g. q
. = ALIGN(4);
9 P  \7 X' w# B! }, B- a" q2 Q5 Y+ N __bss_start = .;
' K; {7 C8 q) r0 T .bss : { *(.bss) }
; S; Q6 I/ y" k0 |% V9 e _end = .;8 u: e# j0 p, g; P; V+ g  Y( M
/ v) V+ v- ]- j9 B2 k% G* y+ e
}; W4 T  N  ?+ Q1 C
    其中的链接命令 . = 0x00000000;表示地址计数器从0地址开始计数,而且_start 是程序代码段的入口,那么*.text中的所有地址标号(cpu/arm920t/start.S中定义的)就应该从0地址开始计数,那么标号 start_armboot(就是void start_armboot (void)函数的入口地址)应该在FLASH中才对啊,所以按照上边的分析,8 @2 y; v0 L! P" f- s5 D
9 r9 m2 g+ }; A6 _/ L
        ldr pc, _start_armboot
; Q1 {& i) S/ b& w& M# O5 X
0 V+ K! F% V3 i# F8 d6 S        _start_armboot: .word start_armboot
: s8 N/ y% q$ V4 e
' R& s) @  \3 u/ G' ~此条语句后,并没有跳转到SDRAM中的void start_armboot (void),而是跳转到了FLASH中的void start_armboot (void)中。" `! l, E; E8 w* v1 c6 E

  H9 y9 R, q! l9 L所以就出现了这样的矛盾,在FLASH中有一段代码把自己拷贝到SDRAM中,产生了两份UBOOT可执行的指令流,但是最后却没有跳转到SDRAM中去运行以提高指令执行的速度。
: {& F/ s; ~) _6 S) B" l2 c$ G+ J9 e
产生以上的认识是基于以下几个认识(肯定是错误的):
1 u3 b5 _2 ^3 L& e0 \( U
5 d6 A  L% U% i! [6 f# _: I" O1.*.text中的所有地址标号(在链接时确定)是从0地址开始生成的。$ B' ^' m  \) x% `0 R
. r7 ^4 G- Y7 E+ I0 n
      实际上在arm-linux-ld 执行时,原来定义的0x0地址被更新为TEXT_BASE定义的地址。& x! ^3 f- F% U. y$ e8 w$ q# l) J
% I- D$ k+ }+ }3 K* D1 {4 p- ^6 i
2.relocate:    /* relocate U-Boot to RAM     */( ], g2 b' e  _) r- _
   adr r0, _start  /* r0 <- current position of code   */: v- Z3 p4 Q7 G8 e% L
   ldr r1, _TEXT_BASE  /* test if we run from flash or RAM */# i% H3 D, k- h5 W' k2 X4 [
   cmp     r0, r1                  /* don't reloc during debug         */
% m2 e! h' m  v7 ~" B   beq     stack_setup0 L) p/ \6 R& _& [
' ~1 M& {: e/ \- l8 R# ?' \7 r) _
   ldr r2, _armboot_start1 W) }: b* K) E3 d9 Z
   ldr r3, _bss_start" z* D9 |5 Q; F( N; l
   sub r2, r3, r2  /* r2 <- size of armboot            */1 _; [; z0 ~3 ?; Y! H
   add r2, r0, r2  /* r2 <- source end address         */  M; e' f6 [; o- N5 v+ q) f
1 Y% a& D' j- E) _' K( K, w% G0 x
如果不是出于调试阶段,这段搬移代码中的r0和r1肯定不相等的,r0=#0,r1=#TEXT_BASE: 0x33F80000(在./board/smdk2410/config.mk中),所以执行代码的自身拷贝与搬移。
8 E9 V; I3 ^6 w/ s: N3 G
1 @2 s7 g+ t  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页。  G/ L" ?' r7 T( e- A8 n

: ?0 S* `8 g& }+ U  F$ E
8 L# h0 ?% q4 i3 n比较一下:2 T; n. q- u% U4 x
/ V* \. R- I+ s8 m
add r0,(PC+#offset):(PC+#offset)是相对地址,表示把本指令上溯或下溯offset处的地址加载到 r0;% b4 ^8 O5 r, r# ^! I
0 i& F& D7 |. y* e; {- [( R
ldr r1,[PC+#offset]:[PC+#offset]也是相对地址,表示把偏移offset处的地址上的数据加载到 r1;
/ j( P5 L" N- H7 k) N' V( Z* Z2 ?+ c3 ^9 W
现在继续:
3 Q' ^4 B% h) `) u4 i! M7 e( k" E$ D0 }5 H
    刚才分析所得到的矛盾,肯定是在认识上存在的偏差,经过把U-BOOT进行make后,从所生成的两个.map文件来看(~/u-boot.map和 Systen.map),所有的地址标号都是从0x33f80000开始的,就是从SDRAM的高地址开始,等于TEXT_BASE的值,也就是说,链接器是从0x33F80000开始来链接所编译生成的目标文件的,而不是从0地址开始,经过查看,start_armboot=0x33f80d9c,就是说void start_armboot (void)函数的入口地址在SDRAM中(链接器决定),所以执行$ |3 R) T& i! N2 _: _' f
( p0 j0 X1 S8 f+ W: _/ K$ }9 o
        ldr pc, _start_armboot7 @1 S4 ~9 D. m3 N$ `9 O7 j
# f0 i# a/ N  ?, l4 }
        _start_armboot: .word start_armboot,6 G! H5 K& k, s' x
, a1 X  E+ Q9 Z& E% _9 N9 T
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的分析。
3 h: ]* n8 c& B5 |" O3 s0 z. a2 F( C9 B  s/ G0 c
    现在最后一个矛盾就是链接脚本(~/board/smdk2410/u-boot.lds)所描述的链接地址与实际的链接地址不相同的问题,因为根据链接脚本,所有的地址标号应该从0地址开始计数的,然而不是。经过查找Makefile文件,在顶层的Makefile文件中,在166行中链接是的链接命令:4 O" Y+ R1 N9 T* R1 v

9 P) w) }& E4 V" S5 N) I$(LD) $(LDFLAGS) $$UNDEF_SYM $(OBJS) /,! T" w; P4 i. I
9 Z9 g& J7 Q0 d) S
其中的LDFLAGS在定义在顶层的config.mk中的145行:LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS),1 c: j. t1 `- j, i+ {
. z; k4 y7 q- ^3 R! V# u1 h
最关键的就是 -Ttext $(TEXT_BASE)命令了,他的含义就是说,起始地址在TEXT_BASE,而TEXT_BASE在~/board/smdk2410/config.mk中TEXT_BASE = 0x3FF80000;9 M! ?! I) }' v: p
9 [; }# S$ {. H! G0 n+ L+ L: I
到此就弄清楚为什么链接从0x3ff80000开始的了,至于链接脚本,其主要作用是用来指明各个*.o文件的顺序,如入口地址标号(_start)等,以及使两个地址标号得到当前的地址8 e; k+ W$ p  T4 t  D! z: ^

' g. T# }1 y. F7 Z- N    __u_boot_cmd_start = .;    *.u_boot_cmd段的起始地址
5 c4 k% [0 N) J" y0 s8 L; m& h5 l# S2 Y% ?" T
    .u_boot_cmd : { *(.u_boot_cmd) }% a# O4 `1 |, o* R! d
    __u_boot_cmd_end = .;       *.u_boot_cmd段的结束地址4 q4 d- i3 j% [4 {( v
7 x# `0 Y( @* o$ c  Q/ G  v  @  |
以供C程序使用。 __u_boot_cmd_start和__u_boot_cmd_end可以作为全局的一个常数使用。
5 P! N9 i5 N$ F/ m& ], V' n" k& F
总结:4 n  F$ O5 ~* M5 j  J( c
- W- j. g6 v# K% {) r  N
    因为-Ttext $(TEXT_BASE)命令的使用,链接器把UBOOT从地址0x3ff80000开始连接,在第一阶段中,所有使用的目标地址寻址都是使用当前PC值加减偏移量的方法,所以把UBOOT烧写到0地址开始的FLASH中,不影响第一阶段的正确执行。

本版积分规则

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

GMT+8, 2024-3-28 22:07 , Processed in 0.043893 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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