ALL-y += u-boot.srec u-boot.bin System.map binary_size_check
u-boot.srec: u-boot FORCE
$(call if_changed,objcopy)
u-boot.bin: u-boot FORCE
$(call if_changed,objcopy)
$(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))
$(BOARD_SIZE_CHECK)
System.map: u-boot
@$(call SYSTEM_MAP,$<) > $@
@file_size=$(shell wc -c u-boot.bin | awk '{print $$1}') ; \
map_size=$(shell cat u-boot.map | \
awk '/_image_copy_start/ {start = $$1} /_image_binary_end/ {end = $$1} END {if (start != "" && end != "") print "ibase=16; " toupper(end) " - " toupper(start)}' \
| sed 's/0X//g' \
| bc); \
if [ "" != "$$map_size" ]; then \
if test $$map_size -ne $$file_size; then \
echo "u-boot.map shows a binary size of $$map_size" >&2 ; \
echo " but u-boot.bin shows $$file_size" >&2 ; \
exit 1; \
fi \
fi
u-boot:$(u-boot-init) $(u-boot-main) u-boot.lds
$(call if_changed,u-boot__)
u-boot-init := $(head-y)
head-y := $(CPUDIR)/start.o
u-boot-main := $(libs-y)
libs-y += lib/
libs-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/
libs-y += $(CPUDIR)/
ifdef SOC
libs-y += $(CPUDIR)/$(SOC)/
endif
libs-y += arch/$(ARCH)/lib/
include$(srctree)/config.mk
u-boot.lds: $(LDSCRIPT) prepare FORCE
$(call if_changed_dep,cpp_lds)
514 # If there is no specified link script, we look in a number of places for it
515 ifndef LDSCRIPT
516 ifeq ($(wildcard $(LDSCRIPT)),)
517 LDSCRIPT := $(srctree)/board/$(BOARDDIR)/u-boot.lds
518 endif
519 ifeq ($(wildcard $(LDSCRIPT)),)
520 LDSCRIPT := $(srctree)/$(CPUDIR)/u-boot.lds
521 endif
522 ifeq ($(wildcard $(LDSCRIPT)),)
523 LDSCRIPT := $(srctree)/arch/$(ARCH)/cpu/u-boot.lds
524 endif
525 endif
prepare:prepare0
prepare0: archprepare FORCE
$(Q)$(MAKE)$(build)=.
archprepare: prepare1 scripts_basic
$(version_h): include/config/uboot.release FORCE
$(call filechk,version.h)
version_h:= include/generated/version_autogenerated.h
$(timestamp_h): $(srctree)/Makefile FORCE
$(call filechk,timestamp.h)
timestamp_h := include/generated/timestamp_autogenerated.h
prepare2: prepare3 outputmakefile
prepare3: include/config/uboot.release
include/config/%.conf:$(KCONFIG_CONFIG) include/config/auto.conf.cmd
$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
現在仍然假設 auto.conf 和 auto.conf.cmd 還沒有生成,那么由上面的 $(KCONFIG_CONFIG) include/config/auto.conf.cmd: ; 這條語句知道,該語句中的目標沒有依賴,也沒有生成它的規則命令,所以可想 GNU Make 本身無法生成 auto.conf.cmd 的。然后該條語句后面的一個分號表明,這兩個目標被強制是最新的,所以下面這條命令得以執行:
$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
這里我們看到要生成一個目標 silentoldconfig ,這個目標定義在 scripts/kconfig/Makefile 中。因為這里使用 -f 選項重新指定了頂層 Makefile,而目標又是 silentoldconfig ,所以該命令最終會在頂層 Makefile 的 462-464 這里執行:
1
2
3
|
%config: scripts_basic outputmakefile FORCE
$(Q)
mkdir
-p
include
/linux
include
/config
$(Q)
$(MAKE)
$(build)
=scripts
/kconfig
$@
|
這時,我們來到 scripts/kconfig/Makefile 文件里。在該文件的 32-34 行看到:
1
2
3
|
silentoldconfig:
$(obj)
/conf
$(Q)
mkdir
-p
include
/generated
$< -s
$(Kconfig)
|
從上面看到,silentoldconfig 目標需要依賴 conf 這個程序,該程序也在 scripts/kconfig 目錄下生成。
$< -s $(Kconfig) 該條命令相當於 conf -s $(Kconfig) ,這里 $(Kconfig) 是位於不同平台目錄下的 Kconfig 文件,比如在 x86 平台就是 arch/x86/Kconfig 。
conf 程序的源代碼的主函數在同目錄的 conf.c 文件中,在 main() 函數中看到:
1
2
3
4
5
6
7
8
9
10
|
while
((opt = getopt(ac, av,
"osdD:nmyrh"
)) != -1) {
switch
(opt) {
case
'o'
:
input_mode = ask_silent;
break
;
case
's'
:
input_mode = ask_silent;
sync_kconfig = 1;
break
;
... ...
|
所以,在使用 s 參數時,sync_kconfig 這個變量會為 1 。同樣在 main() 函數還看到:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
if
(sync_kconfig) {
name = conf_get_configname();
if
(stat(name, &tmpstat)) {
fprintf
(stderr, _(
"***\n"
"*** You have not yet configured your kernel!\n"
"*** (missing kernel config file \"%s\")\n"
"***\n"
"*** Please run some configurator (e.g. \"make oldconfig\" or\n"
"*** \"make menuconfig\" or \"make xconfig\").\n"
"***\n"
), name);
exit
(1);
}
}
|
上面代碼中,如果我們從未配置過內核,那么就會打印出錯誤信息,然后退出。這里假設已經配置過內核,並生成了 .config 文件,那么在 main() 函數中會來到:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
switch
(input_mode) {
case
set_default:
if
(!defconfig_file)
defconfig_file = conf_get_default_confname();
if
(conf_read(defconfig_file)) {
printf
(_(
"***\n"
"*** Can't find default configuration \"%s\"!\n"
"***\n"
), defconfig_file);
exit
(1);
}
break
;
case
ask_silent:
case
ask_all:
case
ask_new:
conf_read(NULL);
break
;
... ...
|
由於使用 s 選項,則 input_mode 為 ask_silent,所以這里會執行 conf_read(NULL); 函數。
conf_read(NULL); 函數用來讀取 .config 文件。讀取的各種相關內容主要存放在一個 struct symbol 結構鏈表里,而各個結構的相關指針則放在一個 symbol_hash[] 的數組中,對於數組中元素的尋找通過 fnv32 哈希算法進行定位。
最后會來到 conf.c 中的底部:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
if
(sync_kconfig) {
/* silentoldconfig is used during the build so we shall update autoconf.
* All other commands are only used to generate a config.
*/
if
(conf_get_changed() && conf_write(NULL)) {
fprintf
(stderr, _(
"\n*** Error during writing of the kernel configuration.\n\n"
));
exit
(1);
}
if
(conf_write_autoconf()) {
fprintf
(stderr, _(
"\n*** Error during update of the kernel configuration.\n\n"
));
return
1;
}
}
else
{
if
(conf_write(NULL)) {
fprintf
(stderr, _(
"\n*** Error during writing of the kernel configuration.\n\n"
));
exit
(1);
}
}
|
在 if (conf_get_changed() && conf_write(NULL)) 這個判斷里,conf_get_changed() 函數判斷 .config 文件是否做過變動,如果是,那么會調用 conf_write(NULL) 來重新寫 .config 文件。實際上,對於 defconfig, oldconfig, menuconfig 等目標來說,conf 程序最終也是調用 conf_write() 函數將配置結果寫入 .config 文件中(最后那個 else 里的內容便是)。
確保了 .config 已經最新后,那么調用 conf_write_autoconf() 生成 auto.conf,auto.conf.cmd 以及 autoconf.h 這 3 個文件。
來到 conf_write_autoconf() 函數:j
在 conf_write_autoconf() 里,調用 file_write_dep("include/config/auto.conf.cmd"); 函數將相關內容寫入 auto.conf.cmd 文件。在生成的 auto.conf.cmd 文件中可以看到:
1
2
|
include
/config/auto
.conf: \
$(deps_config)
|
可以看到 auto.conf 文件中的內容依賴於 $(deps_config) 變量里定義的東西,這些東西基本上是各個目錄下的 Kconfig 以及其它一些相關文件。
auto.config 和 .config 的差別是在 auto.config 里去掉了 .config 中的注釋項目以及空格行,其它的都一樣。
仍然在 conf_write_autoconf() 里,分別建立了 .tmpconfig,.tmpconfig_tristate 和 .tmpconfig.h 這 3 個臨時文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
out =
fopen
(
".tmpconfig"
,
"w"
);
if
(!out)
return
1;
tristate =
fopen
(
".tmpconfig_tristate"
,
"w"
);
if
(!tristate) {
fclose
(out);
return
1;
}
out_h =
fopen
(
".tmpconfig.h"
,
"w"
);
if
(!out_h) {
fclose
(out);
fclose
(tristate);
return
1;
}
|
然后將文件頭的注釋部分分別寫入到這幾個臨時文件中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
sym = sym_lookup(
"KERNELVERSION"
, 0);
sym_calc_value(sym);
time
(&now);
fprintf
(out,
"#\n"
"# Automatically generated make config: don't edit\n"
"# Linux kernel version: %s\n"
"# %s"
"#\n"
,
sym_get_string_value(sym),
ctime
(&now));
fprintf
(tristate,
"#\n"
"# Automatically generated - do not edit\n"
"\n"
);
fprintf
(out_h,
"/*\n"
" * Automatically generated C config: don't edit\n"
" * Linux kernel version: %s\n"
" * %s"
" */\n"
"#define AUTOCONF_INCLUDED\n"
,
sym_get_string_value(sym),
ctime
(&now));
|
接着在 for_all_symbols(i, sym) 這個循環里(是一個宏)里將相關內容分別寫入到這幾個文件中。
在最后一段代碼中,將這幾個臨時文件進行改名:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
name =
getenv
(
"KCONFIG_AUTOHEADER"
);
if
(!name)
name =
"include/generated/autoconf.h"
;
if
(
rename
(
".tmpconfig.h"
, name))
return
1;
name =
getenv
(
"KCONFIG_TRISTATE"
);
if
(!name)
name =
"include/config/tristate.conf"
;
if
(
rename
(
".tmpconfig_tristate"
, name))
return
1;
name = conf_get_autoconfig_name();
/*
* This must be the last step, kbuild has a dependency on auto.conf
* and this marks the successful completion of the previous steps.
*/
if
(
rename
(
".tmpconfig"
, name))
return
1;
|
上面代碼中的 conf_get_autoconfig_name() 實現為:
1
2
3
4
5
6
|
const
char
*conf_get_autoconfig_name(
void
)
{
char
*name =
getenv
(
"KCONFIG_AUTOCONFIG"
);
return
name ? name :
"include/config/auto.conf"
;
}
|
從上面可以看到,分別生成了以下幾個文件:
引用include/generated/autoconf.h
include/config/tristate.conf
include/config/auto.conf
其中 include/generated/autoconf.h 頭文件由內核本身使用,主要用來預處理 C 代碼。比如在 .config 或 auto.conf 中定義要編譯為模塊的項,如:
CONFIG_DEBUG_NX_TEST=m
在 autoconf.h 中會被定義為:
#define CONFIG_DEBUG_NX_TEST_ MODULE 1
在 .config 或 auto.conf 后接字符串值的項,如:
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
在 autoconfig.h 中會被定義為:
#define CONFIG_DEFCONFIG_LIST "/lib/modules/$UNAME_RELEASE/.config"
同樣對應於 int 型的項如 CONFIG_HZ=1000 在 autoconf.h 中被定義為 #define CONFIG_HZ 1000 。