zoukankan      html  css  js  c++  java
  • U-Boot中关于TEXT_BASE,代码重定位,链接地址相关说明

    都知道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中,不影响第一阶段的正确执行。

  • 相关阅读:
    win7网络共享原来如此简单,WiFi共享精灵开启半天都弱爆了!
    JQUERY UI Datepicker Demo
    Official online document, install svn server in centOS
    JAVE not work in linux
    AMR 转mp3 失败
    XD, XR, DR 股票
    Linux 下MySql 重置密码
    Difinition Of Done
    Apache, Tomcat, JK Configuration Example
    Linux 安装tomcat
  • 原文地址:https://www.cnblogs.com/pengdonglin137/p/3791581.html
Copyright © 2011-2022 走看看