都知道u-boot分為兩個階段,第一 t階段是(~/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
. = align(4);
.rodata :
. = align(4);
.data :
. = align(4);
.got :
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd :
__u_boot_cmd_end = .;
. = align(4);
__bss_start = .;
.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中去執行以提高指令執行的速度。
產生以上的認識是基於以下幾個認識(肯定是錯誤的):
實際上在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中的情況可以參考杜春雷
比較一下:
現在繼續:
剛才分析所得到的矛盾,肯定是在認識上存在的偏差,經過把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;
__u_boot_cmd_start = .; *.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中,不影響第一階段的正確執行。
u boot 中的命令實現
u boot 中的命令實現 我們知道,u boot的執行過程是首先進行一些初始化化工作,然後在乙個死 迴圈中不斷接收串列埠的命令並進行解釋執行,下面我們就看看執行部分 的實 現,見common main.c中的run command int run command const char cmd,in...
u boot中重定位
在學習 u boot的時候,對程式重定位不理解,不知道為什麼要進行重定位,在網上查詢資料學習了一下。首先,要了解一下乙個程式的生成的四個步驟 預處理 編譯 彙編 鏈結。經過這四步,最終才生成可執行檔案bin檔案。預處理主要是巨集定義的展開,編譯主要是進行語法此分析,如我們常見的語法錯誤,某些函式未定...
u boot中新增命令
看下ping命令實現的例子 檔案common cmd net.c static int do ping cmd tbl t cmdtp,int flag,int argc,char const argv printf host s is alive n argv 1 return 0 u boot ...