uboot中的TEXT_BASE

來源:互聯網
上載者:User

都知道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中,不影響第一階段的正確執行。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.