arch linux arm下载_「正点原子Linux连载」第三十五章Linux内核顶层Makefile详解(二)...

1)实验平台:正点原子Linux开发板

2)摘自《正点原子I.MX6U嵌入式Linux驱动开发指南

关注官方微信号公众号,获取更多资料:正点原子

f7e9a4e1c64138f782089305247c8a57.png

8、设置目标架构和交叉编译器

同uboot一样,Linux编译的时候需要设置目标板架构ARCH和交叉编译器CROSS_COMPILE,在顶层Makefile中代码如下:

示例代码35.5.8 顶层Makefile代码段

252 ARCH ?= $(SUBARCH)

253 CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)

为了方便,一般直接修改顶层Makefile中的ARCH和CROSS_COMPILE,直接将其设置为对应的架构和编译器,比如本教程将ARCH设置为为arm,CROSS_COMPILE设置为arm-linux-gnueabihf-,如下所示:

示例代码35.5.9 顶层Makefile代码段

252 ARCH ?=arm

253 CROSS_COMPILE ?= arm-linux-gnueabihf-

设置好以后我们就可以使用如下命令编译Linux了:

make xxx_defconfig //使用默认配置文件配置Linux

make menuconfig //启动图形化配置界面

make -j16 //编译Linux

9、调用scripts/Kbuild.include文件

同uboot一样,Linux顶层Makefile也会调用文件scripts/Kbuild.include,顶层Makefile相应代码如下:

示例代码35.5.10 顶层Makefile代码段

348 # We need some generic definitions (do not try to remake the file).

349 scripts/Kbuild.include:;

350 include scripts/Kbuild.include

10、交叉编译工具变量设置

顶层Makefile中其他和交叉编译器有关的变量设置如下:

示例代码35.5.11 顶层Makefile代码段

353 AS = $(CROSS_COMPILE)as

354 LD = $(CROSS_COMPILE)ld

355 CC = $(CROSS_COMPILE)gcc

356 CPP = $(CC)-E

357 AR = $(CROSS_COMPILE)ar

358 NM = $(CROSS_COMPILE)nm

359 STRIP = $(CROSS_COMPILE)strip

360 OBJCOPY = $(CROSS_COMPILE)objcopy

361 OBJDUMP = $(CROSS_COMPILE)objdump

LA、LD、CC等这些都是交叉编译器所使用的工具。

11、头文件路径变量

顶层Makefile定义了两个变量保存头文件路径:USERINCLUDE和LINUXINCLUDE,相关代码如下:

示例代码35.5.12 顶层Makefile代码段

381 USERINCLUDE :=

382-I$(srctree)/arch/$(hdr-arch)/include/uapi

383-Iarch/$(hdr-arch)/include/generated/uapi

384-I$(srctree)/include/uapi

385-Iinclude/generated/uapi

386-include $(srctree)/include/linux/kconfig.h

387

388 # Use LINUXINCLUDE when you must reference the include/ directory.

389 # Needed to be compatible with the O= option

390 LINUXINCLUDE :=

391-I$(srctree)/arch/$(hdr-arch)/include

392-Iarch/$(hdr-arch)/include/generated/uapi

393-Iarch/$(hdr-arch)/include/generated

394 $(if $(KBUILD_SRC),-I$(srctree)/include)

395-Iinclude

396 $(USERINCLUDE)

第381~386行是USERINCLUDE是UAPI相关的头文件路径,第390~396行是LINUXINCLUDE是Linux内核源码的头文件路径。重点来看一下LINUXINCLUDE,其中srctree=.,hdr-arch=arm,KBUILD_SRC为空,因此,将USERINCLUDE和LINUXINCLUDE展开以后为:

USERINCLUDE :=

-I./arch/arm/include/uapi

-Iarch/arm/include/generated/uapi

-I./include/uapi

-Iinclude/generated/uapi

-include ./include/linux/kconfig.h

LINUXINCLUDE :=

-I./arch/arm/include

-Iarch/arm/include/generated/uapi

-Iarch/arm/include/generated

-Iinclude

-I./arch/arm/include/uapi

-Iarch/arm/include/generated/uapi

-I./include/uapi

-Iinclude/generated/uapi

-include ./include/linux/kconfig.h

12、导出变量

顶层Makefile会导出很多变量给子Makefile使用,导出的这些变量如下:

示例代码35.5.13 顶层Makefile代码段

417 export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION

418 export ARCH SRCARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC

419 export CPP AR NM STRIP OBJCOPY OBJDUMP

420 export MAKE AWK GENKSYMS INSTALLKERNEL PERL PYTHON UTS_MACHINE

421 export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS

422

423 export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS

424 export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE CFLAGS_GCOV CFLAGS_KASAN

425 export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE

426 export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE

427 export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL

428 export KBUILD_ARFLAGS

35.5.1 makexxx_defconfig过程

第一次编译Linux之前都要使用"makexxx_defconfig"先配置Linux内核,在顶层Makefile中有"%config"这个目标,如下所示:

示例代码35.5.1.1 顶层Makefile代码段

490 config-targets :=0

491 mixed-targets :=0

492 dot-config :=1

493

494 ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)

495 ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)

496 dot-config :=0

497 endif

498 endif

499

500 ifeq ($(KBUILD_EXTMOD),)

501 ifneq ($(filter config %config,$(MAKECMDGOALS)),)

502 config-targets :=1

503 ifneq ($(words $(MAKECMDGOALS)),1)

504 mixed-targets :=1

505 endif

506 endif

507 endif

508

509 ifeq ($(mixed-targets),1)

510 # =================================================================

511 # We're called with mixed targets (*config and build targets).

512 # Handle them one by one.

513

514 PHONY += $(MAKECMDGOALS) __build_one_by_one

515

516 $(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one

517 @:

518

519 __build_one_by_one:

520 $(Q)set -e;

521for i in $(MAKECMDGOALS);do

522 $(MAKE)-f $(srctree)/Makefile $$i;

523 done

524

525else

526 ifeq ($(config-targets),1)

527 # ================================================================

528 # *config targets only - make sure prerequisites are updated, and

529 # descend in scripts/kconfig to make the *config target

530

531 # Read arch specific Makefile to set KBUILD_DEFCONFIG as needed.

532 # KBUILD_DEFCONFIG may point out an alternative default

533 # configuration used for'make defconfig'

534 include arch/$(SRCARCH)/Makefile

535 export KBUILD_DEFCONFIG KBUILD_KCONFIG

536

537 config: scripts_basic outputmakefile FORCE

538 $(Q)$(MAKE) $(build)=scripts/kconfig $@

539

540%config: scripts_basic outputmakefile FORCE

541 $(Q)$(MAKE) $(build)=scripts/kconfig $@

542

543else

......

563 endif # KBUILD_EXTMOD

第490~507行和uboot一样,都是设置定义变量config-targets、mixed-targets和dot-config的值,最终这三个变量的值为:

config-targets= 1

mixed-targets= 0

dot-config= 1

因为config-targets=1,因此第534行~541行成立。第534行引用arch/arm/Makefile这个文件,这个文件很重要,因为zImage、uImage等这些文件就是由arch/arm/Makefile来生成的。

第535行导出变量KBUILD_DEFCONFIG KBUILD_KCONFIG。

第537行,没有目标与之匹配,因此不执行。

第540行,"makexxx_defconfig"与目标"%config"匹配,因此执行。"%config"依赖scripts_basic、outputmakefile和FORCE,"%config"真正有意义的依赖就只有scripts_basic,scripts_basic的规则如下:

示例代码35.5.1.2 顶层Makefile代码段

448 scripts_basic:

449 $(Q)$(MAKE) $(build)=scripts/basic

450 $(Q)rm -f .tmp_quiet_recordmcount

build定义在文件scripts/Kbuild.include中,值为build := -f $(srctree)/scripts/Makefile.build obj,因此将示例代码35.5.1.2展开就是:

scripts_basic:

@make -f ./scripts/Makefile.build obj=scripts/basic //也可以没有@,视配置而定

@rm -f . tmp_quiet_recordmcount //也可以没有@

接着回到示例代码35.5.1.1的目标"%config"处,内容如下:

%config: scripts_basic outputmakefile FORCE

$(Q)$(MAKE) $(build)=scripts/kconfig $@

将命令展开就是:

@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig

35.5.2 Makefile.build脚本分析

从上一小节可知,"makexxx_defconfig"配置Linux的时候如下两行命令会执行脚本scripts/Makefile.build:

@make -f ./scripts/Makefile.build obj=scripts/basic

@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig

我们依次来分析一下:

1、scripts_basic目标对应的命令

scripts_basic目标对应的命令为:@make -f ./scripts/Makefile.build obj=scripts/basic。打开文件scripts/Makefile.build,有如下代码:

示例代码35.5.2.1 Makefile.build代码段

41 # The filename Kbuild has precedence over Makefile

42 kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))

43 kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)

44 include $(kbuild-file)

将kbuild-dir展开后为:

kbuild-dir=./scripts/basic

将kbuild-file展开后为:

kbuild-file= ./scripts/basic/Makefile

最后将59行展开,即:

include./scripts/basic/Makefile

继续分析scripts/Makefile.build,如下代码:

示例代码35.5.2.2 Makefile.build代码段

94 __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y))

95 $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target))

96 $(subdir-ym) $(always)

97 @:

__build是默认目标,因为命令"@make -f ./scripts/Makefile.build obj=scripts/basic"没有指定目标,所以会使用到默认目标__build。在顶层Makefile中,KBUILD_BUILTIN为1,KBUILD_MODULES为空,因此展开后目标__build为:

__build:$(builtin-target) $(lib-target) $(extra-y)) $(subdir-ym) $(always)

@:

可以看出目标__build有5个依赖:builtin-target、lib-target、extra-y、subdir-ym和always。这5个依赖的具体内容如下:

builtin-target =

lib-target =

extra-y =

subdir-ym =

always = scripts/basic/fixdep scripts/basic/bin2c

只有always有效,因此__build最终为:

__build: scripts/basic/fixdep scripts/basic/bin2c

@:

__build依赖于scripts/basic/fixdep和scripts/basic/bin2c,所以要先将scripts/basic/fixdep和scripts/basic/bin2c.c这两个文件编译成fixdep和bin2c。

综上所述,scripts_basic目标的作用就是编译出scripts/basic/fixdep和scripts/basic/bin2c这两个软件。

2、 %config目标对应的命令

%config目标对应的命令为:@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig,此命令会使用到的各个变量值如下:

src= scripts/kconfig

kbuild-dir =./scripts/kconfig

kbuild-file =./scripts/kconfig/Makefile

include ./scripts/kconfig/Makefile

可以看出,Makefile.build会读取scripts/kconfig/Makefile中的内容,此文件有如下所示内容:

示例代码35.5.2.3 scripts/kconfig/Makefile代码段

113%_defconfig: $(obj)/conf

114 $(Q)$< $(silent)--defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)

目标%_defconfig与 xxx_defconfig匹配,所以会执行这条规则,将其展开就是:

%_defconfig: scripts/kconfig/conf

@ scripts/kconfig/conf --defconfig=arch/arm/configs/%_defconfig Kconfig

%_defconfig依赖scripts/kconfig/conf,所以会编译scripts/kconfig/conf.c生成conf这个软件。此软件就会将%_defconfig中的配置输出到.config文件中,最终生成Linuxkernel根目录下的.config文件。

35.5.3 make过程

使用命令"makexxx_defconfig"配置好Linux内核以后就可以使用"make"或者"makeall"命令进行编译。顶层Makefile有如下代码:

示例代码35.5.3.1 顶层Makefile代码段

125 PHONY := _all

126 _all:

......

192 PHONY += all

193 ifeq ($(KBUILD_EXTMOD),)

194 _all: all

195else

196 _all: modules

197 endif

......

608 all: vmlinux

第126行,_all是默认目标,如果使用命令"make"编译Linux的话此目标就会被匹配。

第193行,如果KBUILD_EXTMOD为空的话194行的代码成立。

第194行,默认目标_all依赖all。

第608行,目标all依赖vmlinux,所以接下来的重点就是vmlinux!

顶层Makefile中有如下代码:

示例代码35.5.3.2 顶层Makefile代码段

904 # Externally visible symbols (used by link-vmlinux.sh)

905 export KBUILD_VMLINUX_INIT := $(head-y) $(init-y)

906 export KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y) $(drivers-y) $(net-y)

907 export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds

908 export LDFLAGS_vmlinux

909 # used by scripts/pacmage/Makefile

910 export KBUILD_ALLDIRS := $(sort $(filter-out arch/%,$(vmlinux-alldirs)) arch Documentation include samples scripts tools virt)

911

912 vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)

913

914 # Final link of vmlinux

915 cmd_link-vmlinux = $(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux)

916 quiet_cmd_link-vmlinux = LINK $@

917

918 # Include targets which we want to

919 # execute if the rest of the kernel build went well.

920 vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) FORCE

921 ifdef CONFIG_HEADERS_CHECK

922 $(Q)$(MAKE)-f $(srctree)/Makefile headers_check

923 endif

924 ifdef CONFIG_SAMPLES

925 $(Q)$(MAKE) $(build)=samples

926 endif

927 ifdef CONFIG_BUILD_DOCSRC

928 $(Q)$(MAKE) $(build)=Documentation

929 endif

930 ifdef CONFIG_GDB_SCRIPTS

931 $(Q)ln -fsn `cd $(srctree)&&/bin/pwd`/scripts/gdb/vmlinux-gdb.py

932 endif

933+$(call if_changed,link-vmlinux)

从第920行可以看出目标vmlinux依赖scripts/link-vmlinux.sh $(vmlinux-deps) FORCE。第912行定义了vmlinux-deps,值为:

vmlinux-deps= $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)

第905行,KBUILD_VMLINUX_INIT= $(head-y) $(init-y)。

第906行,KBUILD_VMLINUX_MAIN = $(core-y) $(libs-y) $(drivers-y) $(net-y)。

第907行,KBUILD_LDS= arch/$(SRCARCH)/kernel/vmlinux.lds,其中SRCARCH=arm,因此KBUILD_LDS= arch/arm/kernel/vmlinux.lds。

综上所述,vmlinux的依赖为:scripts/link-vmlinux.sh、$(head-y) 、$(init-y)、$(core-y) 、$(libs-y) 、$(drivers-y) 、$(net-y)、arch/arm/kernel/vmlinux.lds和FORCE。

第933行的命令用于链接生成vmlinux。

重点来看一下$(head-y) 、$(init-y)、$(core-y) 、$(libs-y) 、$(drivers-y) 和$(net-y)这六个变量的值。

1、head-y

head-y定义在文件arch/arm/Makefile中,内容如下:

示例代码35.5.3.3 arch/arm/Makefile代码段

135 head-y := arch/arm/kernel/head$(MMUEXT).o

当不使能MMU的话MMUEXT=-nommu,如果使能MMU的话为空,因此head-y最终的值为:

head-y = arch/arm/kernel/head.o

2、init-y、drivers-y和net-y

在顶层Makefile中有如下代码:

示例代码35.5.3.4 顶层Makefile代码段

558 init-y := init/

559 drivers-y := drivers/ sound/ firmware/

560 net-y := net/

......

896 init-y := $(patsubst %/, %/built-in.o, $(init-y))

898 drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))

899 net-y := $(patsubst %/, %/built-in.o, $(net-y))

从示例代码35.5.3.4可知,init-y、libs-y、drivers-y和net-y最终的值为:

init-y = init/built-in.o

drivers-y = drivers/built-in.o sound/built-in.o firmware/built-in.o

net-y = net/built-in.o

3、libs-y

libs-y基本和init-y一样,在顶层Makefile中存在如下代码:

示例代码35.5.3.5 顶层Makefile代码段

561 libs-y := lib/

......

900 libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))

901 libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y))

902 libs-y := $(libs-y1) $(libs-y2)

根据示例代码35.5.3.5可知,libs-y应该等于"lib.a built-in.o",这个只正确了一部分!因为在arch/arm/Makefile中会向libs-y中追加一些值,代码如下:

示例代码35.5.3.6 arch/arm/Makefile代码段

286 libs-y := arch/arm/lib/ $(libs-y)

arch/arm/Makefile将libs-y的值改为了:arch/arm/lib $(libs-y),展开以后为:

libs-y = arch/arm/lib lib/

因此根据示例代码35.5.3.5的第900~902行可知,libs-y最终应该为:

libs-y = arch/arm/lib/lib.a lib/lib.a arch/arm/lib/built-in.o lib/built-in.o

4、core-y

core-y和init-y也一样,在顶层Makefile中有如下代码:

示例代码35.5.3.7 顶层Makefile代码段

532 core-y := usr/

......

887 core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/

但是在arch/arm/Makefile中会对core-y进行追加,代码如下:

示例代码35.5.3.8 arch/arm/Makefile代码段

269 core-$(CONFIG_FPE_NWFPE) += arch/arm/nwfpe/

270 core-$(CONFIG_FPE_FASTFPE) += $(FASTFPE_OBJ)

271 core-$(CONFIG_VFP) += arch/arm/vfp/

272 core-$(CONFIG_XEN) += arch/arm/xen/

273 core-$(CONFIG_KVM_ARM_HOST) += arch/arm/kvm/

274 core-$(CONFIG_VDSO) += arch/arm/vdso/

275

276 # If we have a machine-specific directory, then include it in the build.

277 core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/

278 core-y += arch/arm/probes/

279 core-y += arch/arm/net/

280 core-y += arch/arm/crypto/

281 core-y += arch/arm/firmware/

282 core-y += $(machdirs) $(platdirs)

第269~274行根据不同的配置向core-y追加不同的值,比如使能VFP的话就会在.config中有CONFIG_VFP=y这一行,那么core-y就会追加"arch/arm/vfp/"。

第277~282行就是对core-y直接追加的值。

在顶层Makefile中有如下一行:

示例代码35.5.3.9 顶层Makefile代码段

897 core-y := $(patsubst %/, %/built-in.o, $(core-y))

经过上述代码的转换,最终core-y的值为:

core-y = usr/built-in.o arch/arm/vfp/built-in.o

arch/arm/vdso/built-in.o arch/arm/kernel/built-in.o

arch/arm/mm/built-in.o arch/arm/common/built-in.o

arch/arm/probes/built-in.o arch/arm/net/built-in.o

arch/arm/crypto/built-in.o arch/arm/firmware/built-in.o

arch/arm/mach-imx/built-in.o kernel/built-in.o

mm/built-in.o fs/built-in.o

ipc/built-in.o security/built-in.o

crypto/built-in.o block/built-in.o

关于head-y 、init-y、core-y 、libs-y 、drivers-y和net-y这6个变量就讲解到这里。这些变量都是一些built-in.o或.a等文件,这个和uboot一样,都是将相应目录中的源码文件进行编译,然后在各自目录下生成built-in.o文件,有些生成了.a库文件。最终将这些built-in.o和.a文件进行链接即可形成ELF格式的可执行文件,也就是vmlinux!但是链接是需要连接脚本的,vmlinux的依赖arch/arm/kernel/vmlinux.lds就是整个Linux的链接脚本。

示例代码35.5.3.2第933行的命令"+$(call if_changed,link-vmlinux)"表示将$(call if_changed,link-vmlinux)的结果作为最终生成vmlinux的命令,前面的"+"表示该命令结果不可忽略。$(call if_changed,link-vmlinux)是调用函数if_changed,link-vmlinux是函数if_changed的参数,函数if_changed定义在文件scripts/Kbuild.include中,如下所示:

示例代码35.5.3.10 scripts/Kbuild.include代码段

247 if_changed = $(if $(strip $(any-prereq) $(arg-check)),

248 @set -e;

249 $(echo-cmd) $(cmd_$(1));

250 printf '%s''cmd_$@ := $(make-cmd)'> $(dot-target).cmd)

any-prereq用于检查依赖文件是否有变化,如果依赖文件有变化那么any-prereq就不为空,否则就为空。arg-check用于检查参数是否有变化,如果没有变化那么arg-check就为空。

第248行,"@set -e"告诉bash,如果任何语句的执行结果不为true(也就是执行出错)的话就直接退出。

第249行,$(echo-cmd)用于打印命令执行过程,比如在链接vmlinux的时候就会输出"LINK vmlinux"。$(cmd_$(1))中的$(1)表示参数,也就是link-vmlinux,因此$(cmd_$(1))表示执行cmd_link-vmlinux的内容。cmd_link-vmlinux在顶层Makefile中有如下所示定义:

示例代码35.5.3.11 顶层Makefile代码段

914 # Final link of vmlinux

915 cmd_link-vmlinux = $(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux)

916 quiet_cmd_link-vmlinux = LINK $@

第915行就是cmd_link-vmlinux的值,其中CONFIG_SHELL=/bin/bash,$

LD= arm-linux-gnueabihf-ld -EL,LDFLAGS为空。LDFLAGS_vmlinux的值由顶层Makefile和arch/arm/Makefile这两个文件共同决定,最终LDFLAGS_vmlinux=-p --no-undefined -X --pic-veneer --build-id。因此cmd_link-vmlinux最终的值为:

cmd_link-vmlinux = /bin/bash scripts/link-vmlinux.sh arm-linux-gnueabihf-ld -EL -p --no-undefined -X --pic-veneer --build-id

cmd_link-vmlinux会调用scripts/link-vmlinux.sh这个脚本来链接出vmlinux!在link-vmlinux.sh中有如下所示代码:

示例代码35.5.3.12 scripts/link-vmlinux.sh代码段

51 vmlinux_link()

52{

53 local lds="${objtree}/${KBUILD_LDS}"

54

55if["${SRCARCH}"!="um"]; then

56 ${LD} ${LDFLAGS} ${LDFLAGS_vmlinux}-o ${2}

57-T ${lds} ${KBUILD_VMLINUX_INIT}

58--start-group ${KBUILD_VMLINUX_MAIN}--end-group ${1}

59else

60 ${CC} ${CFLAGS_vmlinux}-o ${2}

61-Wl,-T,${lds} ${KBUILD_VMLINUX_INIT}

62-Wl,--start-group

63 ${KBUILD_VMLINUX_MAIN}

64-Wl,--end-group

65-lutil ${1}

66 rm -f linux

67 fi

68}

......

216 info LD vmlinux

217 vmlinux_link "${kallsymso}" vmlinux

vmliux_link就是最终链接出vmlinux的函数,第55行判断SRCARCH是否等于"um",如果不相等的话就执行56~58行的代码。因为SRCARCH=arm,因此条件成立,执行56~58行的代码。这三行代码就应该很熟悉了!就是普通的链接操作,连接脚本为lds= ./arch/arm/kernel/vmlinux.lds,需要链接的文件由变量KBUILD_VMLINUX_INIT和KBUILD_VMLINUX_MAIN来决定,这两个变量在示例代码35.5.3.2中已经讲解过了。

第217行调用vmlinux_link函数来链接出vmlinux。

使用命令"make V=1"编译Linux,会有如图35.5.3.1所示的编译信息:

8a181a12c233b2eda8327fe1fa7e05ad.png

图35.5.3.1 link-vmlinux.sh链接vmlinux过程

至此我们基本理清了make的过程,重点就是将各个子目录下的built-in.o、.a等文件链接在一起,最终生成vmlinux这个ELF格式的可执行文件。链接脚本为arch/arm/kernel/vmlinux.lds,链接过程是由shell脚本scripts/link-vmlinux.s来完成的。接下来的问题就是这些子目录下的built-in.o、.a等文件又是如何编译出来的呢?

35.5.4 built-in.o文件编译生成过程

根据示例代码35.5.3.2第920行可知,vmliux依赖vmlinux-deps,而vmlinux-deps= $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN),KBUILD_LDS是连接脚本,这里不考虑,剩下的KBUILD_VMLINUX_INIT和KBUILD_VMLINUX_MAIN就是各个子目录下的built-in.o、.a等文件。最终vmlinux-deps的值如下:

vmlinux-deps = arch/arm/kernel/vmlinux.lds arch/arm/kernel/head.o

init/built-in.o usr/built-in.o

arch/arm/vfp/built-in.o arch/arm/vdso/built-in.o

arch/arm/kernel/built-in.o arch/arm/mm/built-in.o

arch/arm/common/built-in.o arch/arm/probes/built-in.o

arch/arm/net/built-in.o arch/arm/crypto/built-in.o

arch/arm/firmware/built-in.o arch/arm/mach-imx/built-in.o

kernel/built-in.o mm/built-in.o

fs/built-in.o ipc/built-in.o

security/built-in.o crypto/built-in.o

block/built-in.o arch/arm/lib/lib.a

lib/lib.a arch/arm/lib/built-in.o

lib/built-in.o drivers/built-in.o

sound/built-in.o firmware/built-in.o

net/built-in.o

除了arch/arm/kernel/vmlinux.lds以外,其他都是要编译链接生成的。在顶层Makefile中有如下代码:

示例代码35.5.4.1 顶层Makefile代码段

937 $(sort $(vmlinux-deps)): $(vmlinux-dirs);

sort是排序函数,用于对vmlinux-deps的字符串列表进行排序,并且去掉重复的单词。可以看出vmlinux-deps依赖vmlinux-dirs,vmlinux-dirs也定义在顶层Makefile中,定义如下:

示例代码35.5.4.2 顶层Makefile代码段

889 vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m)

890 $(core-y) $(core-m) $(drivers-y) $(drivers-m)

891 $(net-y) $(net-m) $(libs-y) $(libs-m)))

vmlinux-dirs看名字就知道和目录有关,此变量保存着生成vmlinux所需源码文件的目录,值如下:

vmlinux-dirs = init usr arch/arm/vfp

arch/arm/vdso arch/arm/kernel arch/arm/mm

arch/arm/common arch/arm/probes arch/arm/net

arch/arm/crypto arch/arm/firmware arch/arm/mach-imx

kernel mm fs

ipc security crypto

block drivers sound

firmware net arch/arm/lib

lib

在顶层Makefile中有如下代码:

示例代码35.5.4.3 顶层Makefile代码段

946 $(vmlinux-dirs): prepare scripts

947 $(Q)$(MAKE) $(build)=$@

目标vmlinux-dirs依赖prepare和scripts,这两个依赖不去浪费时间了,重点看一下第947行的命令。build前面已经说了,值为"-f ./scripts/Makefile.build obj",因此将947行的命令展开就是:

@ make -f ./scripts/Makefile.build obj=$@

$@表示目标文件,也就是vmlinux-dirs的值,将vmlinux-dirs中的这些目录全部带入到命令中,结果如下:

@ make -f ./scripts/Makefile.build obj=init

@ make -f ./scripts/Makefile.build obj=usr

@ make -f ./scripts/Makefile.build obj=arch/arm/vfp

@ make -f ./scripts/Makefile.build obj=arch/arm/vdso

@ make -f ./scripts/Makefile.build obj=arch/arm/kernel

@ make -f ./scripts/Makefile.build obj=arch/arm/mm

@ make -f ./scripts/Makefile.build obj=arch/arm/common

@ make -f ./scripts/Makefile.build obj=arch/arm/probes

@ make -f ./scripts/Makefile.build obj=arch/arm/net

@ make -f ./scripts/Makefile.build obj=arch/arm/crypto

@ make -f ./scripts/Makefile.build obj=arch/arm/firmware

@ make -f ./scripts/Makefile.build obj=arch/arm/mach-imx

@ make -f ./scripts/Makefile.build obj=kernel

@ make -f ./scripts/Makefile.build obj=mm

@ make -f ./scripts/Makefile.build obj=fs

@ make -f ./scripts/Makefile.build obj=ipc

@ make -f ./scripts/Makefile.build obj=security

@ make -f ./scripts/Makefile.build obj=crypto

@ make -f ./scripts/Makefile.build obj=block

@ make -f ./scripts/Makefile.build obj=drivers

@ make -f ./scripts/Makefile.build obj=sound

@ make -f ./scripts/Makefile.build obj=firmware

@ make -f ./scripts/Makefile.build obj=net

@ make -f ./scripts/Makefile.build obj=arch/arm/lib

@ make -f ./scripts/Makefile.build obj=lib

这些命令运行过程其实都是一样的,我们就以"@ make -f ./scripts/Makefile.build obj=init"这个命令为例,讲解一下详细的运行过程。这里又要用到Makefile.build这个脚本了,此脚本默认目标为__build,这个在35.5.2小节已经讲过了,我们再来看一下,__build目标对应的规则如下:

示例代码35.5.4.4 scripts/Makefile.build代码段

94 __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y))

95 $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target))

96 $(subdir-ym) $(always)

97 @:

当只编译Linux内核镜像文件,也就是使用"makezImage"编译的时候,KBUILD_BUILTIN=1,KBUILD_MODULES为空。"make"命令是会编译所有的东西,包括Linux内核镜像文件和一些模块文件。如果只编译Linux内核镜像的话,__build目标简化为:

__build: $(builtin-target) $(lib-target) $(extra-y))$(subdir-ym) $(always)

@:

重点来看一下builtin-target这个依赖,builtin-target同样定义在文件scripts/Makefile.build中,定义如下:

示例代码35.5.4.5 scripts/Makefile.build代码段

86 ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)

87 builtin-target := $(obj)/built-in.o

88 endif

第87行就是builtin-target变量的值,为"$(obj)/built-in.o",这就是这些built-in.o的来源了。要生成built-in.o,要求obj-y、obj-m、obj-、subdir-m和lib-target这些变量不能全部为空。最后一个问题:built-in.o是怎么生成的?在文件scripts/Makefile.build中有如下代码:

示例代码35.5.4.6 顶层Makefile代码段

325 #

326 # Rule to compile a set of .o files into one .o file

327 #

328 ifdef builtin-target

329 quiet_cmd_link_o_target = LD $@

330 # If the list of objects to link is empty, just create an empty built-in.o

331 cmd_link_o_target = $(if $(strip $(obj-y)),

332 $(LD) $(ld_flags)-r -o $@ $(filter $(obj-y), $^)

333 $(cmd_secanalysis),

334 rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS) $@)

335

336 $(builtin-target): $(obj-y) FORCE

337 $(call if_changed,link_o_target)

338

339 targets += $(builtin-target)

340 endif # builtin-target

第336行的目标就是builtin-target,依赖为obj-y,命令为"$(call if_changed,link_o_target)",也就是调用函数if_changed,参数为link_o_target,其返回值就是具体的命令。前面讲过了if_changed,它会调用cmd_$(1)所对应的命令($(1)就是函数的第1个参数),在在这里就是调用cmd_link_o_target所对应的命令,也就是第331~334行的命令。cmd_link_o_target就是使用LD将某个目录下的所有.o文件链接在一起,最终形成built-in.o。

35.5.5 makezImage过程

1、vmlinux、Image,zImage、uImage的区别

前面几小节重点是讲vmlinux是如何编译出来的,vmlinux是ELF格式的文件,但是在实际中我们不会使用vmlinux,而是使用zImage或uImage这样的Linux内核镜像文件。那么vmlinux、zImage、uImage他们之间有什么区别呢?

①、vmlinux是编译出来的最原始的内核文件,是未压缩的,比如正点原子提供的Linux源码编译出来的vmlinux差不多有16MB,如图35.5.5.1所示:

1d0832db300abb8e638bf59d37075297.png

图35.5.5.1 vmlinux信息

②、Image是Linux内核镜像文件,但是Image仅包含可执行的二进制数据。Image就是使用objcopy取消掉vmlinux中的一些其他信息,比如符号表什么的。但是Image是没有压缩过的,Image保存在arch/arm/boot目录下,其大小大概在12MB左右如图35.5.5.2所示:

4dae5be8dee86cf469cc1501d264573d.png

图35.5.5.2 Image镜像信息

相比vmlinux的16MB,Image缩小到了12MB。

③、zImage是经过gzip压缩后的Image,经过压缩以后其大小大概在6MB左右,如图35.5.5.3所示:

4cc0545887f5002396cf9774fe073b8f.png

图35.5.5.3 zImage镜像信息

④、uImage是老版本uboot专用的镜像文件,uImag是在zImage前面加了一个长度为64字节的"头",这个头信息描述了该镜像文件的类型、加载位置、生成时间、大小等信息。但是新的uboot已经支持了zImage启动!所以已经很少用到uImage了,除非你用的很古老的uboot。

使用"make"、"makeall"、"makezImage"这些命令就可以编译出zImage镜像,在arch/arm/Makefile中有如下代码:

示例代码35.5.5.1 顶层Makefile代码段

310 BOOT_TARGETS = zImage Image xipImage bootpImage uImage

......

315 $(BOOT_TARGETS): vmlinux

316 $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@

第310行,变量BOOT_TARGETS包含zImage,Image,xipImage等镜像文件。

第315行,BOOT_TARGETS依赖vmlinux,因此如果使用"makezImage"编译的Linux内核的话,首先肯定要先编译出vmlinux。

第316行,具体的命令,比如要编译zImage,那么命令展开以后如下所示:

@ make -f ./scripts/Makefile.build obj=arch/arm/boot MACHINE=arch/arm/boot/zImage

看来又是使用scripts/Makefile.build文件来完成vmliux到zImage的转换。

关于Linux顶层Makefile就讲解到这里,基本和uboot的顶层Makefile一样,重点在于vmlinux的生成。最后将vmlinux压缩成我们最常用的zImage或uImage等文件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值