转载:http://blog.csdn.net/xxblinux/article/details/6281295

  我们都知道U-BOOT分为两个阶段,第一阶段是(~/cpu/arm920t/start.S中)在FLASH上运行(一般情况下),完成对硬件的初始化,包括看门狗,中断缓存等,并且负责把代码搬移到SDRAM中(在搬移的时候检查自身代码是否在SDRAM中),然后完成C程序运行所需要环境的建立,包括堆栈的初始化等,最后执行一句跳转指令:

ldr pc, _start_armboot

_start_armboot: .word start_armboot,

进入到/lib_arm/board.c中的函数void start_armboot (void),从此就进入了第二阶段。这是在很多资料上都有讲述的,所以勿需多言了。

现在对于第一阶段有几个问题,以前我一直是没有搞明白的,既然在FLASH中的代码是把自己拷贝到SDRAM中,那么在S3C2410的内存地址空间,就有两份的启动代码,第一份就是在FLASH中,第二份就是在SDRAM中。根据链接脚本文件(~/board/smdk2410/u-boot.lds)

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
 . = 0x00000000;    /* 后记:这个链接起始地址实际上被-Ttest $(TEST_BASE)更新了*/

. = ALIGN(4);
 .text      :
 {
   cpu/arm920t/start.o (.text)
   *(.text)
 }

. = ALIGN(4);
 .rodata : { *(.rodata) }

. = ALIGN(4);
 .data : { *(.data) }

. = ALIGN(4);
 .got : { *(.got) }

. = .;
 __u_boot_cmd_start = .;
 .u_boot_cmd : { *(.u_boot_cmd) }
 __u_boot_cmd_end = .;

. = ALIGN(4);
 __bss_start = .;
 .bss : { *(.bss) }
 _end = .;

}
    其中的链接命令 . = 0x00000000;表示地址计数器从0地址开始计数,而且_start 是程序代码段的入口,那么*.text中的所有地址标号(cpu/arm920t/start.S中定义的)就应该从0地址开始计数,那么标号 start_armboot(就是void start_armboot (void)函数的入口地址)应该在FLASH中才对啊,所以按照上边的分析,

ldr pc, _start_armboot

_start_armboot: .word start_armboot

此条语句后,并没有跳转到SDRAM中的void start_armboot (void),而是跳转到了FLASH中的void start_armboot (void)中。

所以就出现了这样的矛盾,在FLASH中有一段代码把自己拷贝到SDRAM中,产生了两份UBOOT可执行的指令流,但是最后却没有跳转到SDRAM中去运行以提高指令执行的速度。

产生以上的认识是基于以下几个认识(肯定是错误的):

1.*.text中的所有地址标号(在链接时确定)是从0地址开始生成的。

实际上在arm-linux-ld 执行时,原来定义的0x0地址被更新为TEXT_BASE定义的地址。

2.relocate:    /* relocate U-Boot to RAM     */
   adr r0, _start  /* r0 <- current position of code   */
   ldr r1, _TEXT_BASE  /* test if we run from flash or RAM */
   cmp     r0, r1                  /* don't reloc during debug         */
   beq     stack_setup

ldr r2, _armboot_start
   ldr r3, _bss_start
   sub r2, r3, r2  /* r2 <- size of armboot            */
   add r2, r0, r2  /* r2 <- source end address         */

如果不是出于调试阶段,这段搬移代码中的r0和r1肯定不相等的,r0=#0,r1=#TEXT_BASE: 0x33F80000(在./board/smdk2410/config.mk中),所以执行代码的自身拷贝与搬移。

注意:在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页。

比较一下:

add r0,(PC+#offset):(PC+#offset)是相对地址,表示把本指令上溯或下溯offset处的地址加载到 r0;

ldr r1,[PC+#offset]:[PC+#offset]也是相对地址,表示把偏移offset处的地址上的数据加载到 r1;

现在继续:

刚才分析所得到的矛盾,肯定是在认识上存在的偏差,经过把U-BOOT进行make后,从所生成的两个.map文件来看(~/u-boot.map和 Systen.map),所有的地址标号都是从0x33f80000开始的,就是从SDRAM的高地址开始,等于TEXT_BASE的值,也就是说,链接器是从0x33F80000开始来链接所编译生成的目标文件的,而不是从0地址开始,经过查看,start_armboot=0x33f80d9c,就是说void start_armboot (void)函数的入口地址在SDRAM中(链接器决定),所以执行

ldr pc, _start_armboot

_start_armboot: .word start_armboot,

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的分析。

现在最后一个矛盾就是链接脚本(~/board/smdk2410/u-boot.lds)所描述的链接地址与实际的链接地址不相同的问题,因为根据链接脚本,所有的地址标号应该从0地址开始计数的,然而不是。经过查找Makefile文件,在顶层的Makefile文件中,在166行中链接是的链接命令:

$(LD) $(LDFLAGS) $$UNDEF_SYM $(OBJS) /,

其中的LDFLAGS在定义在顶层的config.mk中的145行:LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS),

最关键的就是 -Ttext $(TEXT_BASE)命令了,他的含义就是说,起始地址在TEXT_BASE,而TEXT_BASE在~/board/smdk2410/config.mk中TEXT_BASE = 0x3FF80000;

到此就弄清楚为什么链接从0x3ff80000开始的了,至于链接脚本,其主要作用是用来指明各个*.o文件的顺序,如入口地址标号(_start)等,以及使两个地址标号得到当前的地址

__u_boot_cmd_start = .;    *.u_boot_cmd段的起始地址

.u_boot_cmd : { *(.u_boot_cmd) }
    __u_boot_cmd_end = .;       *.u_boot_cmd段的结束地址

以供C程序使用。 __u_boot_cmd_start和__u_boot_cmd_end可以作为全局的一个常数使用。

总结:

因为-Ttext $(TEXT_BASE)命令的使用,链接器把UBOOT从地址0x3ff80000开始连接,在第一阶段中,所有使用的目标地址寻址都是使用当前PC值加减偏移量的方法,所以把UBOOT烧写到0地址开始的FLASH中,不影响第一阶段的正确执行。

uboot中的TEXT_BASE的更多相关文章

  1. U-Boot中关于TEXT_BASE,代码重定位,链接地址相关说明

    都知道U-BOOT分为两个阶段,第一阶段是(~/cpu/arm920t/start.S中)在FLASH上运行(一般情况 下),完成对硬件的初始化,包括看门狗,中断缓存等,并且负责把代码搬移到SDRAM ...

  2. u-boot中的Makefile

    在windos下,pc机上电之后,BIOS会初始化硬件配置,为内核传递参数,引导操作系统启动,并且识别C盘.D盘.等整个操作系统启动起来之后,才可以运行应用程序比如QQ.QQ音影.同理,在嵌入式Lin ...

  3. Uboot中start.S源码的指令级的详尽解析【转】

    本文转载自:http://www.crifan.com/files/doc/docbook/uboot_starts_analysis/release/html/uboot_starts_analys ...

  4. uboot中添加FIQ中断及相关问题

    本文主要说明了在uboot中添加FIQ中断时遇到的问题以及对应的解决办法. 首先交代一下项目的软硬件环境.硬件方面,使用s3c2440作为主控芯片,外接串口.网卡等设备.软件方面,主控芯片上电后运行u ...

  5. u-boot中nandflash初始化流程分析(转)

    u-boot中nandflash初始化流程分析(转) 原文地址http://zhuairlunjj.blog.163.com/blog/static/80050945201092011249136/ ...

  6. (四)ubuntu学习前传—uboot中对Flash和DDR的管理

    1.uboot阶段Flash的分区 (1)所谓分区,就是说对Flash进行分块管理.(2)PC机等产品中,因为大家都是在操作系统下使用硬盘的,整个硬盘由操作系统统一管理,操作系统会使用文件系统帮我们管 ...

  7. uboot中gd的定义和使用

    近期在做uboot中nand启动相关的工作,遇到一个问题一直纠结着.如今最终明确了这个问题,想想还有好多兄弟在某个黑暗的角落里或者某台电脑前纠结着呢,所以赶紧写下来以供查阅. uboot versio ...

  8. u-boot中分区和内核MTD分区关系

    一.u-boot中环境变量与uImage中MTD的分区关系 分区只是内核的概念,就是说A-B地址放内核,C-D地址放文件系统,(也就是规定哪个地址区间放内核或者文件系统)等等. 一般我们只需要分3-4 ...

  9. 关于Yaffs2在u-boot中的支持

    开发板是一块2G的MLC的NandFlash,页大小8k+512,为其移植u-boot到yaffs2这了.以前在Mini2440上移植过2k+64的slc的NandFlash的Yaffs2支持,当然也 ...

随机推荐

  1. 我到 vim 配置文件---------修改从---http://www.cnblogs.com/ma6174/archive/2011/12/10/2283393.html

    """"""""""""""""&quo ...

  2. 使用 Apache Spark 让 MySQL 查询速度提升 10 倍以上

    转: https://coyee.com/article/11012-how-apache-spark-makes-your-slow-mysql-queries-10x-faster-or-more ...

  3. docker官方镜像库 搭建 jekins

    先去docker hub 镜像官网下载jenkins 镜像(https://hub.docker.com/_/jenkins/): 其实就是在docker 中执行命令:docker pull jenk ...

  4. 笔记<c# 调用DLL解密密文>

    using DTcms.Common; using System; using System.Collections.Generic; using System.Linq; using System. ...

  5. 如何编写 Makefile

     1. 目标 依赖 命令 make会比较targets文件和prerequisites文件的修改日期,如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的 ...

  6. Ant入门

    一.Ant介绍 Ant是Java的生成工具,是Apache的核心项目:直接在apache官网下载即可: Ant类似于Unix中的Make工具,都是用来编译.生成: Ant是跨平台的,而Make不能: ...

  7. 利用Swoole实现PHP+websocket直播,即使通讯代码,及linux下swoole安装基本配置

    swoole安装基本配置 php安装swoole 1. 下载swoole安装 wget http://pecl.php.net/get/swoole-1.9.1.tgz tar -zxvf swool ...

  8. tortoisegit推送ssh-key需要输入用户信息

    修改了测试代码,却在提交代码时候又跳出来请输入用户名和密码, 后来发现,github push有两种方式,ssh方式和https方式.而https方式是不同的,具体来说,就是url信息的不同,实际的验 ...

  9. hdu 5242 Game(树链剖分,贪心¥)

    Game Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  10. SpringBoot WebSocket实现

    1.添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...