本文轉載自:http://blog.csdn.net/czg13548930186/article/details/53434566
uboot主Makefile分析1
1、uboot version確定(Makefile的24-29行)
Makefile代碼部分:
- VERSION = 1
- PATCHLEVEL = 30
- SUBLEVEL = 4
- EXTRAVERSION =
- U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
- VERSION_FILE = $(obj)include/version_autogenerated.h
(1)uboot的版本號分3個級別:
VERSION:主板本號
PATCHLEVEL:次版本號
SUBLEVEL:再次版本號
EXTRAVERSION:另外附加的版本信息
這4個用.分隔開共同構成了最終的版本號U_BOOT_VERSION ,這個變量記錄了Makefile中配置的版本號。
2、include/version_autogenerated.h文件是編譯過程中自動生成的一個文件,所以源目錄中沒有,但是編譯過后的uboot中就有了。它里面的內容是一個宏定義,宏定義的值內容就是我們在Makefile中配置的uboot的版本號。
2、HOSTARCH和HOSTOS
- Makefile代碼部分:
- HOSTARCH := $(shell uname -m | \
- sed -e s/i.86/i386/ \
- -e s/sun4u/sparc64/ \
- -e s/arm.*/arm/ \
- -e s/sa110/arm/ \
- -e s/powerpc/ppc/ \
- -e s/ppc64/ppc/ \
- -e s/macppc/ppc/)
- HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
- sed -e 's/cygwin.*/cygwin/')
- 注:sed的替換功能
- test = abcdefgabc
- Test1 = $(test) | sed -e s/abc/123/
- Test2 = $(test) | sed -e s/abc/123/g
- @echo $(Test1 )
- @echo $(Test2 )
- 結果:
- 123defabc
- 123def123
abc被替換成了123,如果不加字母g,結果就變成了只有第一個abc被替換
1、HOSTARCH這個名字:HOST是主機,就是當前在做開發用的這台電腦就叫主機;ARCH是architecture(架構)的縮寫,表示CPU的架構。所以HOSTARCH就表示主機的CPU的架構。
2、直接在shell中執行uname -m得到i686,得到的值其實你當前執行這個命令的電腦的CPU的版本號。
3、shell中的|叫做管道,管道的作用就是把管道前面一個運算式的輸出作為后面一個的輸入再去做處理,最終的輸出才是我們整個式子的輸出。
4、這兩個環境變量是主機的操作系統和主機的CPU架構,得出后保存備用,后面自然會用到。
uboot主Makefile分析2
1、靜默編譯(50-54行)
Makefile代碼部分:
- #################################################################
- # Allow for silent builds
- ifeq (,$(findstring s,$(MAKEFLAGS)))
- XECHO = echo
- else
- XECHO = :
- endif
- #################################################################
1、平時默認編譯時命令行會打印出來很多編譯信息。但是有時候我們不希望看到這些編譯信息,就后台編譯即可。這就叫靜默編譯。
2、使用方法就是編譯時make -s,-s會作為MAKEFLAGS傳給Makefile,在50-54行這段代碼作用下XECHO變量就會被變成空(默認等於echo),於是實現了靜默編譯。
2、2種編譯方法(原地編譯和單獨輸出文件夾編譯)
1、編譯復雜項目,Makefile提供2種編譯管理方法。默認情況下是當前文件夾中的.c文件,編譯出來的.o文件會放在同一文件夾下。這種方式叫原地編譯。原地編譯的好處就是處理起來簡單。
2、原地編譯有一些壞處:第一,污染了源文件目錄。第二的缺陷就是一套源代碼只能按照一種配置和編譯方法進行處理,無法同時維護2個或2個以上的配置編譯方式。
3、為了解決以上2種缺陷,uboot支持單獨輸出文件夾方式的編譯(Linux kernel也支持,而且uboot的這種技術就是從linux kernel學習來的)。基本思路就是在編譯時另外指定一個輸出目錄,將來所有的編譯生成的.o文件或生成的其他文件全部丟到那個輸出目錄下去。源代碼目錄不做任何污染,這樣輸出目錄就承載了本次配置編譯的所有結果。
(4)具體用法:默認的就是原地編譯。如果需要指定具體的輸出目錄編譯則有2種方式來指定輸出目錄。(具體參考Makefile 56-76行注釋內容)
第一種:make O=輸出目錄
第二種:export BUILD_DIR=輸出目錄 然后再make
如果兩個都指定了(既有BUILD_DIR環境變量存在,又有O=xx),則O=xx具有更高優先級,聽他的。
(5)兩種編譯的實現代碼在Makefile的78-123行,如下
- Makefile代碼部分:
- #########################################################################
- #
- # U-boot build supports producing a object files to the separate external
- # directory. Two use cases are supported:
- #
- # 1) Add O= to the make command line
- # 'make O=/tmp/build all'
- #
- # 2) Set environement variable BUILD_DIR to point to the desired location
- # 'export BUILD_DIR=/tmp/build'
- # 'make'
- #
- # The second approach can also be used with a MAKEALL script
- # 'export BUILD_DIR=/tmp/build'
- # './MAKEALL'
- #
- # Command line 'O=' setting overrides BUILD_DIR environent variable.
- #
- # When none of the above methods is used the local build is performed and
- # the object files are placed in the source directory.
- #
- ifdef O
- ifeq ("$(origin O)", "command line")
- BUILD_DIR := $(O)
- endif
- endif
- ifneq ($(BUILD_DIR),)
- saved-output := $(BUILD_DIR)
- # Attempt to create a output directory.
- $(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})
- # Verify if it was successful.
- BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd)
- $(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist))
- endif # ifneq ($(BUILD_DIR),)
- OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
- SRCTREE := $(CURDIR)
- TOPDIR := $(SRCTREE)
- LNDIR := $(OBJTREE)
- export TOPDIR SRCTREE OBJTREE
- MKCONFIG := $(SRCTREE)/mkconfig
- export MKCONFIG
- ifneq ($(OBJTREE),$(SRCTREE))
- REMOTE_BUILD := 1
- export REMOTE_BUILD
- endif
- # $(obj) and (src) are defined in config.mk but here in main Makefile
- # we also need them before config.mk is included which is the case for
- # some targets like unconfig, clean, clobber, distclean, etc.
- ifneq ($(OBJTREE),$(SRCTREE))
- obj := $(OBJTREE)/
- src := $(SRCTREE)/
- else
- obj :=
- src :=
- endif
- export obj src
- # Make sure CDPATH settings don't interfere
- unexport CDPATH
- #########################################################################
3.uboot主Makefile分析3
1、OBJTREE、SRCTREE、TOPDIR
(1)OBJTREE:編譯出的.o文件存放的目錄的根目錄。
在默認編譯下,OBJTREE等於當前目錄;
在O=xx編譯下,OBJTREE就等於我們設置的那個輸出目錄。
(2)SRCTREE: 源碼目錄,其實就是源代碼的根目錄,也就是當前目錄。
總結:在默認編譯下,OBJTREE和SRCTREE相等;在O=xx這種編譯下OBJTREE和SRCTREE不相等。Makefile中定義這兩個變量,其實就是為了記錄編譯后的.o文件往哪里放,就是為了實現O=xx的這種編譯方式的。
2、MKCONFIG(Makefile的101行)
Makefile中定義的一個變量(在這里定義,在后面使用),它的值就是我們源碼根目錄下面的mkconfig。這個mkconfig是一個腳本,這個腳本就是uboot配置階段的配置腳本。
3、include $(obj)include/config.mk(133行)
- Makefile代碼部分:
- # load ARCH, BOARD, and CPU configuration
- include $(obj)include/config.mk
- export ARCH CPU BOARD VENDOR SOC
(1)include/config.mk不是源碼自帶的(你在沒有編譯過的源碼目錄下是找不到這個文件的),要在配置過程(make x210_sd_config)中才會生成這個文件。因此這個文件的值和我們配置過程有關,是由配置過程根據我們的配置自動生成的。
(2)我們X210在iNand情況下配置生成的config.mk內容為:
ARCH = arm
CPU = s5pc11x
BOARD = x210
VENDOR = samsung
SOC = s5pc110
(3)我們在下一行(134行)export導出了這5個變量作為環境變量。所以着兩行加起來其實就是為當前makefile定義了5個環境變量而已。之所以不直接給出這5個環境變量的值,是因為我們希望這5個值是可以被人很容易的、集中的配置的。
(4)這里的配置值來自於2589行那里的配置項。如果我們要更改這里的某個配置值要到2589行那里調用MKCONFIG腳本傳參時的參數。
- Makefile代碼部分:
- x210_sd_config : unconfig
- @$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110
- @echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk
4、ARCH CROSS_COMPILE
(1)接下來有2個很重要的環境變量。一個是ARCH,上面導出的,值來自於我們的配置過程,它的值會影響后面的CROSS_COMPILE環境變量的值。ARCH的意義是定義當前編譯的目標CPU的架構。
(2)CROSS_COMPILE是定義交叉編譯工具鏈的前綴的。定義這些前綴是為了在后面用(用前綴加上后綴來定義編譯過程中用到的各種工具鏈中的工具)。我們把前綴和后綴分開還有一個原因就是:在不同CPU架構上的交叉編譯工具鏈,只是前綴不一樣,后綴都是一樣的。因此定義時把前綴和后綴分開,只需要在定義前綴時區分各種架構即可實現可移植性。
(3)CROSS_COMPILE在136-182行來確定。CROSS_COMPILE是被ARCH所確定的,只要配置了ARCH=arm,那么我們就只能在ARM的那個分支去設置CROSS_COMPILE的值。這個設置值只要能保證找到那個交叉編譯工具鏈即可,不一定非得是全路徑的,相對路徑也可以。(如果已經將工具鏈導出到環境變量,並且設置了符號鏈接,這樣CROSS_COMPILE = arm-linux-就可以)
- Makefile代碼部分:
- ifndef CROSS_COMPILE
- ifeq ($(HOSTARCH),$(ARCH))
- CROSS_COMPILE =
- else
- ifeq ($(ARCH),ppc)
- CROSS_COMPILE = ppc_8xx-
- endif
- ifeq ($(ARCH),arm)
- #CROSS_COMPILE = arm-linux-
- #CROSS_COMPILE = /usr/local/arm/4.4.1-eabi-cortex-a8/usr/bin/arm-linux-
- #CROSS_COMPILE = /usr/local/arm/4.2.2-eabi/usr/bin/arm-linux-
- CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-
- endif
- ifeq ($(ARCH),i386)
- CROSS_COMPILE = i386-linux-
- endif
- ifeq ($(ARCH),mips)
- CROSS_COMPILE = mips_4KC-
- endif
- ifeq ($(ARCH),nios)
- CROSS_COMPILE = nios-elf-
- endif
- ifeq ($(ARCH),nios2)
- CROSS_COMPILE = nios2-elf-
- endif
- ifeq ($(ARCH),m68k)
- CROSS_COMPILE = m68k-elf-
- endif
- ifeq ($(ARCH),microblaze)
- CROSS_COMPILE = mb-
- endif
- ifeq ($(ARCH),blackfin)
- CROSS_COMPILE = bfin-uclinux-
- endif
- ifeq ($(ARCH),avr32)
- CROSS_COMPILE = avr32-linux-
- endif
- ifeq ($(ARCH),sh)
- CROSS_COMPILE = sh4-linux-
- endif
- ifeq ($(ARCH),sparc)
- CROSS_COMPILE = sparc-elf-
- endif # sparc
- endif # HOSTARCH,ARCH
- endif # CROSS_COMPILE
- export CROSS_COMPILE
(4)實際運用時,我們可以在Makefile中去更改設置CROSS_COMPILE的值,也可以在編譯時用make CROSS_COMPILE=xxxx來設置,而且編譯時傳參的方法可以覆蓋Makefile里面的設置。
4.uboot主Makefile分析4
1、$(TOPDIR)/config.mk(主Makefile的185行)
2、編譯工具定義(config.mk 94-107行)
3、包含開發板配置項目(config.mk, 112行)
(1)autoconfig.mk文件不是源碼提供的,是配置過程自動生成的。
(2)這個文件的作用就是用來指導整個uboot的編譯過程。這個文件的內容其實就是很多CONFIG_開頭的宏(可以理解為變量),這些宏/變量會影響我們uboot編譯過程的走向(原理就是條件編譯)。在uboot代碼中有很多地方使用條件編譯進行編寫,這個條件編譯是用來實現可移植性的。(可以說uboot的源代碼在很大程度來說是拼湊起來的,同一個代碼包含了各種不同開發板的適用代碼,用條件編譯進行區別。)
(3)這個文件不是憑空產生的,配置過程也是需要原材料來產生這個文件的。原材料在源碼目錄的inlcude/configs/xxx.h頭文件。(X210開發板中為include/configs/x210_sd.h)。這個h頭文件里面全都是宏定義,這些宏定義就是我們對當前開發板的移植。每一個開發板的移植都對應這個目錄下的一個頭文件,這個頭文件里每一個宏定義都很重要,這些配置的宏定義就是我們移植uboot的關鍵所在。
5.uboot主Makefile分析5
1、鏈接腳本(config.mk 142-149行)
(1)如果定義了CONFIG_NAND_U_BOOT宏,則鏈接腳本叫u-boot-nand.lds,如果未定義這個宏則鏈接腳本叫u-boot.lds。
(2)從字面意思分析,即可知:CONFIG_NAND_U_BOOT是在Nand版本情況下才使用的,我們使用的X210都是iNand版本的,因此這個宏沒有的。
(3)實際在board\samsung\x210目錄下有u-boot.lds,這個就是鏈接腳本。我們在分析uboot的編譯鏈接過程時就要考慮這個鏈接腳本。
2、TEXT_BASE(config.mk 156-158行)
(1)Makefile中在配置X210開發板時,在board/samsung/x210目錄下生成了一個文件config.mk,其中的內容就是:TEXT_BASE = 0xc3e00000相當於定義了一個變量。
(2)TEXT_BASE是將來我們整個uboot鏈接時指定的鏈接地址。因為uboot中啟用了虛擬地址映射,因此這個C3E00000地址就等於0x23E00000(也可能是33E00000具體地址要取決於uboot中做的虛擬地址映射關系)。
(3)回顧裸機中講的鏈接地址的問題,再想想dnw方式先下載x210_usb.bin然后再下載uboot.bin時為什么第二個地址是23E00000.
uboot主Makefile分析6
- Makefile代碼部分:
- ALL += $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND) $(U_BOOT_ONENAND) $(obj)u-boot.dis
- ifeq ($(ARCH),blackfin)
- ALL += $(obj)u-boot.ldr
- endif
- all: $(ALL)
(1)291行出現了整個主Makefile中第一個目標all(也就是默認目標,我們直接在uboot根目錄下make其實就等於make all,就等於make這個目標)
(2)目標中有一些比較重要的。譬如:u-boot是最終編譯鏈接生成的elf格式的可執行文件,
(3)unconfig字面意思來理解就是未配置。這個符號用來做為我們各個開發板配置目標的依賴。目標是當我們已經配置過一個開發板后再次去配置時還可以配置。
(4)我們配置開發板時使用:make x210_sd_config,因此分析x210_sd_config肯定是主Makefile中的一個目標。
uboot配置過程詳解1
(1)mkconfig腳本的6個參數
$(@:_config=) arm s5pc11x x210 samsung s5pc110
x210_sd_config里的_config部分用空替換,得到:x210_sd,這就是第一個參數,所以:
$1: x210_sd
$2: arm
$3: s5pc11x
$4: x210
$5: samsumg
$6: s5pc110
所以,$# = 6
(2)第23行:[ "${BOARD_NAME}" ] || BOARD_NAME="$1"
其實就是看BOARD_NAME變量是否有值,如果有值就維持不變;如果無值就給他賦值為$1,實際分析結果:BOARD_NAME=x210_sd
(3)第25行:[ $# -lt 4 ] && exit 1
如果$#小於4,則exit 1(mkconfig腳本返回1)
(4)第26行:[ $# -gt 6 ] && exit 1
如果$#大於6,則也返回1.
所以:mkconfig腳本傳參只能是4、5、6,如果大於6或者小於4都不行。
(5)從第33行到第118行,
(6)都是在創建符號鏈接。為什么要創建符號鏈接?這些符號鏈接文件的存在就是整個配置過程的核心,這些符號鏈接文件(文件夾)的主要作用是給頭文件包含等過程提供指向性連接。根本目的是讓uboot具有可移植性。
uboot可移植性的實現原理:在uboot中有很多彼此平行的代碼,各自屬於各自不同的架構/CPU/開發板,我們在具體到一個開發板的編譯時用符號連接的方式提供一個具體的名字的文件夾供編譯時使用。這樣就可以在配置的過程中通過不同的配置使用不同的文件,就可以正確的包含正確的文件。
創建的符號鏈接:
第一個:
- cd ./include
- rm -f asm
- ln -s asm-$2 asm
在include目錄下創建asm文件,指向asm-arm。(46-48行)
第二個:ln -s ${LNPREFIX}arch-$3 asm-$2/arch
在inlcude/asm-arm下創建一個arch文件,指向include/asm-arm/arch-s5pc110
第三個:
- # create link for s5pc1xx SoC
- if [ "$3" = "s5pc1xx" ] ; then
- rm -f regs.h
- ln -s $6.h regs.h
- rm -f asm-$2/arch
- ln -s arch-$3 asm-$2/arch
- fi
在include目錄下創建regs.h文件,指向include/s5pc110.h
刪除第二個
在inlcude/asm-arm下創建一個arch文件,指向include/asm-arm/arch-s5pc11x
第四個:在include/asm-arm下創建一個proc文件,指向include/asm-arm/proc-armv
總結:一共創建了4個符號鏈接。這4個符號鏈接將來在寫代碼過程中,頭文件包含時非常有用。譬如一個頭文件包含可能是:#include <asm/xx.h>
uboot配置過程詳解2
- #
- # Create include file for Make
- #
- echo "ARCH = $2" > config.mk
- echo "CPU = $3" >> config.mk
- echo "BOARD = $4" >> config.mk
- [ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk
- [ "$6" ] && [ "$6" != "NULL" ] && echo "SOC = $6" >> config.mk
(1)創建include/config.mk文件(mkconfig文件123-129行)
(2)創建include/config.mk文件是為了讓主Makefile在第133行去包含的
(3)思考:uboot的配置和編譯過程的配合。編譯的時候需要ARCH=arm、CPU=xx等這些變量來指導編譯,配置的時候就是為編譯階段提供這些變量。那為什么不在Makefile中直接定義這些變量去使用,而要在mkconfig腳本中創建config.mk文件然后又在Makefile中include這些文件呢?
(4)理解這些腳本時,時刻要注意自己當前所處的路徑。
(5)創建(默認情況)/追加(make -a時追加)include/config.h文件(mkconfig文件的134-141行)。
(6)這個文件里面的內容就一行#include <configs/x210_sd.h>,這個頭文件是我們移植x210開發板時,對開發板的宏定義配置文件。這個文件是我們移植x210時最主要的文件。
(7)x210_sd.h文件會被用來生成一個autoconfig.mk文件,這個文件會被主Makefile引入,指導整個編譯過程。這里面的這些宏定義會影響我們對uboot中大部分.c文件中一些條件編譯的選擇。從而實現最終的可移植性。
注意:uboot的整個配置過程,很多文件之間是有關聯的(有時候這個文件是在那個文件中創建出來的;有時候這個文件被那個文件包含進去;有時候這個文件是由那個文件的內容生成的決定的)
注意:uboot中配置和編譯過程,所有的文件或者全局變量都是字符串形式的(不是指的C語言字符串的概念,指的是都是字符組成的序列)。這意味着我們整個uboot的配置過程都是字符串匹配的,所以一定要細節,注意大小寫,要注意不要輸錯字符,因為一旦錯一個最后會出現一些莫名其妙的錯誤,很難排查,這個是uboot移植過程中新手來說最難的地方。
uboot的鏈接腳本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
/*
* (C) Copyright 2002
* Gary Jennejohn, DENX Software Engineering, <gj@denx.de>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
cpu/s5pc11x/start.o (.text)
cpu/s5pc11x/s5pc110/cpu_init.o (.text)
board/samsung/x210/lowlevel_init.o (.text)
cpu/s5pc11x/onenand_cp.o (.text)
cpu/s5pc11x/nand_cp.o (.text)
cpu/s5pc11x/movi.o (.text)
common/secure_boot.o (.text)
common/ace_sha1.o (.text)
cpu/s5pc11x/pmic.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);
.mmudata : { *(.mmudata) }
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
_end = .;
}
|
來自CODE的代碼片
(1)uboot的鏈接腳本和我們之前裸機中的鏈接腳本並沒有本質區別,只是復雜度高一些,文件多一些,使用到的技巧多一些。
(2)ENTRY(_start)用來指定整個程序的入口地址。所謂入口地址就是整個程序的開頭地址,可以認為就是整個程序的第一句指令。有點像C語言中的main。
(3)之前在裸機中告訴大家,指定程序的鏈接地址有2種方法:一種是在Makefile中ld的flags用-Ttext 0x20000000來指定;第二種是在鏈接腳本的SECTIONS開頭用.=0x20000000來指定。兩種都可以實現相同效果。其實,這兩種技巧是可以共同配合使用的,也就是說既在鏈接腳本中指定也在ld flags中用-Ttext來指定。兩個都指定以后以-Ttext指定的為准。
(4)uboot的最終鏈接起始地址就是在Makefile中用-Ttext 來指定的,具體參見2.4.5.2節,注意TEXT_BASE變量。最終來源是Makefile中配置對應的命令中,在make xxx_config時得到的。
(5)在代碼段中注意文件排列的順序。指定必須放在前面部分的那些文件就是那些必須安排在前16KB內的文件,這些文件中的函數在前16KB會被調用。在后面第二部分(16KB之后)中調用的程序,前后順序就無所謂了。
(6)鏈接腳本中除了.text .data .rodata .bss段等編譯工具自帶的段之外,編譯工具還允許我們自定義段。譬如uboot總的.u_boot_cmd段就是自定義段。自定義段很重要。