U-Boot完美解讀(1)——程序的佈局和地址解析

0、前言

        在網上看到N多“大牛”們的移植文檔,什麼S3C2410,S3C6410,ARM7,ARM9的,一大堆一大堆,大致看了下,百分之九十以上的文章都是介紹了在哪兒修改代碼,至於爲什麼這樣修改卻是隻字未提。當然,這樣的文檔對於做產品是好樣的,因爲產品只追求結果,開發人員如何實現,爲什麼要這樣實現已經不重要了。所以,本系列計劃與大家分享移植如何實現,爲什麼要這樣移植作詳細介紹,由於個人並非天才,所以在寫作過程中需要閱讀大量的datasheet,甚至於反覆讀,所以更新不會太快。當然如有不當之處,敬請各位不吝指正。

 

1、入門第一天得學會站好位置

1.1、同學們都在哪兒呢?

剛入手bootloader的人可能都在想這樣一個問題,把編譯好的編程下載到目標平臺後,目標板怎麼知道從哪兒運行呢?剛開始時,我也在想這個的問題,有問題可不是什麼丟人的問題喲,關鍵不知道而且還假裝沒有問題可就有點業餘了喲!廢話少說,直接讓內存集合,看看U-Boot把內存分配並組織起來。

有過Linux編程的朋友都知道,單一文件可以直接通過GCC命令行方式進行,但如果有多個文件或者是上百個以至於上千文件的項目,用這種方式只能對他說一句“兄弟,我真的服咯U”。Makefile提供了對源文件進行管理的方式,通過查看U-Boot根目錄下的Makefile文件,可以看到U-Boot的編譯過程。對於內存的管理,我們只需要關注最後的鏈接方式,在Makefile中有如下部分:

$(obj)u-boot: depend $(SUBDIRS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds
  $(GEN_UBOOT)
ifeq ($(CONFIG_KALLSYMS),y)
  smap=`$(call SYSTEM_MAP,u-boot) | \
   awk '$$2 ~ /[tTwW]/ {printf $$1 $$3 "\\\\000"}'` ; \
  $(CC) $(CFLAGS) -DSYSTEM_MAP="\"$${smap}\"" \
   -c common/system_map.c -o $(obj)common/system_map.o
  $(GEN_UBOOT) $(obj)common/system_map.o
endif

不知道有沒有看到一個u-boot.lds呢,當然如果直接在源碼中找的話,會找到一大堆的鏈接文件,這個是需要配置的,需要移植到什麼樣的平臺,可以查看源碼根目錄cpu/對應平臺/u-boot.lds即可。如果目標板是ARM的處理器,可以到cpu/arm926ejs、arm920t等目錄下查找與處理器對應的文件,如果是mips處理器,則對應到cpu/mips下查找。相信學嵌入式的都是強人,所以此處略去三千字的說明。偷懶的感覺就是好,嘿嘿........

1.2、到操場集合了

 選擇一個文件打開,這裏以cpu\arm_cortexa8\u-boot.lds爲例,其它文件類似。打開文件後,代碼並不多,加文件說明、空行、括號都不足百行,是不是覺得有點竊喜呢?既然如此,親,我就直接把源碼貼出來了喲,一會再逐個“包郵”喲!

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
	. = 0x00000000;

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

	. = ALIGN(4);
	.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.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 = .;
}

 

1.3、對號入座

直接貼出上面的一段代碼,不知道親們看明白了多少呢?如果能很輕鬆過了,那隻能說恭喜你,已經入門了,下面的內容請直接跳過。上一節賣了一個關子,接下來針對不明白的親們進行逐行分析:

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm"")
該行代碼指定輸出的可執行文件是elf格式(理解爲二進制的文件就行),32位ARM指令,其中處理器支持爲小端(目前ARM平臺均爲小端模式,大端與小端的差別請參考大小端字節序)
OUTPUT_ARCH(arm)
指定輸出可執行文件的運行平臺爲ARM處理器平臺,當然如果爲其它平臺如mips,ppc等,這裏作相應的變更。
ENTRY(_start)
指定輸出可執行文件的起始代碼段爲_start.
SECTIONS
{
        . = 0x00000000 
指明目標代碼的起始地址從0x0位置開始,其中前面的"."代表的是當前位置,ARM平臺起始地址都是從0x00000000開始執行,這是由硬件屬性決定的,是平臺上後上能找到的第一個地址。
        . = ALIGN(4) 
代碼對齊方式,這裏是以4字節對齊,如果設置過大,則會找成資源浪費,如果設置過小,則降低系統性能,如取一個數據時需要多次訪問內存,後面重複該代碼將不再作解釋。
        .text :
        {
          cpu/arm_cortexa8/start.o (.text) 
          *(.text)
        }
其中.text是聲明爲代碼段,在存儲裏的代碼段,數據段等分別用不同的符號說明,而cpu/arm_cortexa8/start.o (.text)是指定的一個目錄,這裏是針對cortex-A8的平臺,在對應u-boot根目錄下,可以找到所指的文件,這裏說明引導文件的位置在cpu/arm_cortexa8/下(由於U-Boot對代碼組織結構進行調整,新U-Boot位置位於arch/arm/cup/arm_cortexa8),對應start.s編譯後的目標文件,start.s文件將在下一節作詳細分析。
       .rodata : { *(.rodata) }
指定只讀數據段,RO段。
        .data : { *(.data) } 
指定讀/寫數據段,RW段。
        .got : { *(.got) } 
指定got段, got段式是uboot自定義的一個段, 非標準段。
        __u_boot_cmd_start = .   
右邊的“.”在前面有解釋,表示當前位置,這裏把__u_boot_cmd_start賦值爲當前位置,即起始位置。
       .u_boot_cmd : { *(.u_boot_cmd) }
指定u_boot_cmd段,,uboot把所有的uboot命令放在該段,除了uboot默認的命令外,可以自己增加新的命令,後面再作詳細介紹。
        __u_boot_cmd_end = .
意思同上,把__u_boot_cmd_end賦值爲當前位置,即結束位置。
        __bss_start = .
把__bss_start賦值爲當前位置,即bss段的開始位置,BSS段的作用參見內存管理。
       .bss : { *(.bss) }
指定bss段
       _end = .
}
把_end賦值爲當前位置,即bss段的結束位置,最後一個括號表示定義的SECTIOS段的結束

OK,本節提到的還留下幾個問題。

問題一:大小端字節序,將隨後補一文章單獨介紹。

問題二:start.s文件的解析,將在下一節作詳細分析。

 問題三:BSS段,堆,棧段的介紹,將隨後補一文章內存管理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章