**
Xilinx ZYNQ 7020 ARM 内核kernel源码解析
**
还记得2018年的时候,kernel还是4.9.0 ,到了2022变成了5.15了,三年疫情过去了,我们的技术一直在精进。国产加油,中国加油。无论如何变,它还是LINUX内核,你要了解了,什么版本都一样,一通百通。你要是不懂这些知识,就等着主管K你吧,这是你应该受的。
上链接:https://gitcode.net/mirrors/xilinx/linux-xlnx
此文档,还是以4.9.0来讲解,大家可以下载到这个版本。还是那句话:你强就不用petalinux.
Kernel 顶层 Makefile
之前分析过 uboot 源码目录下的顶层 makefile 文件,uboot 的顶层 makefile 参考了了许多 linux 顶层 makefile 的代码,前面有很多代码思路基本差不多
一、版本号
VERSION = 4
PATCHLEVEL = 9
SUBLEVEL = 0
EXTRAVERSION =
NAME = Roaring Lionus
二、MAKEFLAGS 变量
MAKEFLAGS用来查找相对于内核 src 根目录的 make include 文件,他的值 始终自动的传递给子 make,我们可以在代码中看到 MAKEFLAGS:
MAKEFLAGS += -rR --include-dir=$(CURDIR)
MAKEFLAGS += --no-print-directory
Clear a bunch of variables before executing the submake
tools/: FORCE
$(Q)mkdir -p $(objtree)/tools
(
Q
)
(Q)
(Q)(MAKE) LDFLAGS= MAKEFLAGS=“
(
f
i
l
t
e
r
−
−
j
(filter --j% -j,
(filter−−j(MAKEFLAGS))” O=$(shell cd $(objtree) && /bin/pwd) subdir=tools -C $(src)/tools/
tools/%: FORCE
$(Q)mkdir -p $(objtree)/tools
(
Q
)
(Q)
(Q)(MAKE) LDFLAGS= MAKEFLAGS=“
(
f
i
l
t
e
r
−
−
j
(filter --j% -j,
(filter−−j(MAKEFLAGS))” O=$(shell cd $(objtree) && /bin/pwd) subdir=tools -C $(src)/tools/ $*
三、命令输出
Linux 编译的时候可以通过 “V=1” 来输出完整的命令,和 uboot 一样
Use ‘make V=1’ to see the full commands
ifeq (“$(origin V)”, “command line”)
KBUILD_VERBOSE = $(V)
endif
ifndef KBUILD_VERBOSE
KBUILD_VERBOSE = 0
endif
ifeq ($(KBUILD_VERBOSE),1)
quiet =
Q =
else
quiet=quiet_
Q = @
endif
If the user is running make -s (silent mode), suppress echoing of # commands
ifneq (
(
f
i
l
t
e
r
4.
(filter 4.%,
(filter4.(MAKE_VERSION)),) # make-4
ifneq (
(
f
i
l
t
e
r
(filter %s ,
(filter(firstword xKaTeX parse error: Expected 'EOF', got '#' at position 48: …endif else #̲ make-3.8x ifne…(filter s% -s%,$(MAKEFLAGS)),)
quiet=silent_
endif
endif
export quiet Q KBUILD_VERBOSE
V 为 1, quiet 和 Q 为空,输出完整命令,V 为 0 ,则 quiet=quiet_、Q = @,屏蔽掉完整命令
四、静默输出
编译的时候使用 “make -s” 就可实现静默编译,编译的时候就不会打印任何的信息:
If the user is running make -s (silent mode), suppress echoing of # commands
ifneq (
(
f
i
l
t
e
r
4.
(filter 4.%,
(filter4.(MAKE_VERSION)),) # make-4
ifneq (
(
f
i
l
t
e
r
(filter %s ,
(filter(firstword xKaTeX parse error: Expected 'EOF', got '#' at position 48: …endif else #̲ make-3.8x ifne…(filter s% -s%,$(MAKEFLAGS)),)
quiet=silent_
endif
endif
quiet 为 silent_ 时不会打印任何信息
后面还有一行
export quiet Q KBUILD_VERBOSE用来传递参数给子 make
五、编译结果输出目录
编译时使用 “O=xxx” 可将编译产生的过程文件输出到指定的目录中
ifeq ($(KBUILD_SRC),)
OK, Make called in directory where kernel src resides # Do we want to locate output files in a separate directory?
ifeq (“$(origin O)”, “command line”)
KBUILD_OUTPUT := $(O)
endif
That’s our default target when none is given on the command line
PHONY := _all
_all:
Cancel implicit rules on top Makefile
$(CURDIR)/Makefile Makefile: ;
代码判断 O 存在且来自命令行后就会将 O 的值赋值给 KBUILD_OUTPUT (编译输出目录),然后判断 KBUILD_OUTPUT 不为空的话,则创建该目录,并将所有的 make 结果设置为该目录
ifneq ($(KBUILD_OUTPUT),)
Invoke a second make in the output directory, passing relevant variables
check that the output directory actually exists
saved-output := $(KBUILD_OUTPUT)
KBUILD_OUTPUT := $(shell mkdir -p $(KBUILD_OUTPUT) && cd $(KBUILD_OUTPUT)
&& /bin/pwd)
$(if $(KBUILD_OUTPUT),
(
e
r
r
o
r
f
a
i
l
e
d
t
o
c
r
e
a
t
e
o
u
t
p
u
t
d
i
r
e
c
t
o
r
y
"
(error failed to create output directory "
(errorfailedtocreateoutputdirectory"(saved-output)"))
PHONY += $(MAKECMDGOALS) sub-make
$(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make
@:
sub-make:
(
Q
)
(Q)
(Q)(MAKE) -C
(
K
B
U
I
L
D
O
U
T
P
U
T
)
K
B
U
I
L
D
S
R
C
=
(KBUILD_OUTPUT) KBUILD_SRC=
(KBUILDOUTPUT)KBUILDSRC=(CURDIR)
-f $(CURDIR)/Makefile
(
f
i
l
t
e
r
−
o
u
t
a
l
l
s
u
b
−
m
a
k
e
,
(filter-out _all sub-make,
(filter−outallsub−make,(MAKECMDGOALS))
Leave processing to above invocation of make
skip-makefile := 1
endif # ifneq (KaTeX parse error: Expected 'EOF', got '#' at position 25: …UTPUT),) endif #̲ ifeq ((KBUILD_SRC),)
六、代码检查
使用参数 “ C=1” 使能代码检查,检查那些需要重新编译的文件,“C=2”用于检查所有的源码文件
ifeq (“$(origin C)”, “command line”)
KBUILD_CHECKSRC = $©
endif
ifndef KBUILD_CHECKSRC
KBUILD_CHECKSRC = 0
endif
#lai add it//希望我的姓可以明扬天下。为中国1亿技术人加油。
first_a = $(CXX)
second_a = g++
ifeq (first_a, second_a)
PHONY += all
Else
代码逻辑和前面的相同
七、模块编译
Linux 允许单独编译某个模块,使用命令 “make M=dir” 或者旧语法 “make SUBDIRS=dir”,相关代码如下:
if SUBDIRS
KBUILD_EXTMOD ?= $(SUBDIRS)
endifdef
ifeq (“$(origin M)”, “command line”)
KBUILD_EXTMOD :=
(
M
)
e
n
d
i
f
代
码
对
a
l
l
和
M
两
个
指
令
都
支
持
P
H
O
N
Y
+
=
a
l
l
i
f
e
q
(
(M) endif 代码对all和M两个指令都支持 PHONY += all ifeq (
(M)endif代码对all和M两个指令都支持PHONY+=allifeq((KBUILD_EXTMOD),)
_all: all
else
_all: modules
endif
根据 KBUILD_EXTMOD 决定编译部分还是全部模块,然后设置 srctree、objtree、VPATH 这三个变量并输出
ifeq (KaTeX parse error: Expected 'EOF', got '#' at position 24: …SRC),) #̲ building in th…(KBUILD_SRC)/,$(dir $(CURDIR)))
# building in a subdirectory of the source tree
srctree := …
else
srctree := $(KBUILD_SRC)
endif
endif
objtree := .
src := $(srctree)
obj := $(objtree)
VPATH := ( s r c t r e e ) (srctree) (srctree)(if ( K B U I L D E X T M O D ) , : (KBUILD_EXTMOD),: (KBUILDEXTMOD),:(KBUILD_EXTMOD))
八、设置目标架构和交叉编译器
Linux 编译要设置目标板架构 ARCH 和交叉编译器 CROSS_COMPILE
CROSS_COMPILE specify the prefix used for all executables used
during compilation. Only gcc and related bin-utils executables # are prefixed with $(CROSS_COMPILE). # CROSS_COMPILE can be set on the command line # make CROSS_COMPILE=ia64-linux- # Alternatively CROSS_COMPILE can be set in the environment. # A third alternative is to store a setting in .config so that plain # “make” in the configured kernel build directory always uses that. # Default value for CROSS_COMPILE is not to prefix executables # Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile
ARCH ?= $(SUBARCH)
CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:“%”=%)
编译时要把这两个参数传进去.
我们在实际操作中,一般会如下:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zynq _defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8
九、调用构建文件
Linux 顶层 Makefile 也会调用文件 scripts/Kbuild.include,里面是一些辅助调用的代码:
scripts/Kbuild.include: ;
include scripts/Kbuild.include
十、交叉编译工具链设置
下面的代码是配置交叉编译工具的代码,比如 LD-链接器、gcc 编译器等等
Make variables (CC, etc…)
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
AWK = awk
GENKSYMS = scripts/genksyms/genksyms
INSTALLKERNEL := installkernel
DEPMOD = /sbin/depmod
PERL = perl
PYTHON = python
CHECK = sparse
CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__
-Wbitwise -Wno-return-void $(CF)
NOSTDINC_FLAGS =
CFLAGS_MODULE =
AFLAGS_MODULE =
LDFLAGS_MODULE =
CFLAGS_KERNEL =
AFLAGS_KERNEL =
LDFLAGS_vmlinux =
CFLAGS_GCOV = -fprofile-arcs -ftest-coverage -fno-tree-loop-im -Wno-maybe-uninitialized
CFLAGS_KCOV := $(call cc-option,-fsanitize-coverage=trace-pc,)
十一、头文件路径变量
顶层 Makefile 定义了两个变量 保存头文件路径:USERINCLUDE 和 LINUXINCLUDE
Use USERINCLUDE when you must reference the UAPI directories only.
USERINCLUDE :=
-I
(
s
r
c
t
r
e
e
)
/
a
r
c
h
/
(srctree)/arch/
(srctree)/arch/(hdr-arch)/include/uapi
-I
(
o
b
j
t
r
e
e
)
/
a
r
c
h
/
(objtree)/arch/
(objtree)/arch/(hdr-arch)/include/generated/uapi
-IKaTeX parse error: Undefined control sequence: \ at position 24: …)/include/uapi \̲ ̲ -I(objtree)/include/generated/uapi
-include $(srctree)/include/linux/kconfig.h
Use LINUXINCLUDE when you must reference the include/ directory. # Needed to be compatible with the O= option
LINUXINCLUDE :=
-I
(
s
r
c
t
r
e
e
)
/
a
r
c
h
/
(srctree)/arch/
(srctree)/arch/(hdr-arch)/include
-I
(
s
r
c
t
r
e
e
)
/
a
r
c
h
/
(srctree)/arch/
(srctree)/arch/(hdr-arch)/mach-zynq
-I
(
o
b
j
t
r
e
e
)
/
a
r
c
h
/
(objtree)/arch/
(objtree)/arch/(hdr-arch)/include/generated/uapi
-I
(
o
b
j
t
r
e
e
)
/
a
r
c
h
/
(objtree)/arch/
(objtree)/arch/(hdr-arch)/include/generated
$(if
(
K
B
U
I
L
D
S
R
C
)
,
−
I
(KBUILD_SRC), -I
(KBUILDSRC),−I(srctree)/include)
-I$(objtree)/include
LINUXINCLUDE += $(filter-out
(
L
I
N
U
X
I
N
C
L
U
D
E
)
,
(LINUXINCLUDE),
(LINUXINCLUDE),(USERINCLUDE))
在默认输入情况下,srctree=.,hdr-arch=arm,KBUILD_SRC 为空,带入后展开代码:
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
十二、导出变量
后面就是一堆导出变量,用于给子 make 使用
export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION
export ARCH SRCARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC
export CPP AR NM STRIP OBJCOPY OBJDUMP
export MAKE AWK GENKSYMS INSTALLKERNEL PERL PYTHON UTS_MACHINE
export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS
export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS
export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE CFLAGS_GCOV CFLAGS_KCOV CFLAGS_KASAN CFLAGS_UBSAN
export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE
export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL
export KBUILD_ARFLAGS
When compiling out-of-tree modules, put MODVERDIR in the module # tree rather than in the kernel tree. The kernel tree might # even be read-only.
export MODVERDIR := $(if ( K B U I L D E X T M O D ) , (KBUILD_EXTMOD), (KBUILDEXTMOD),(firstword $(KBUILD_EXTMOD))/).tmp_versions
Files to ignore in find … statements
export RCS_FIND_IGNORE := ( -name SCCS -o -name BitKeeper -o -name .svn -o
-name CVS -o -name .pc -o -name .hg -o -name .git )
-prune -o
export RCS_TAR_IGNORE := --exclude SCCS --exclude BitKeeper --exclude .svn
–exclude CVS --exclude .pc --exclude .hg --exclude .git
(未完,待更新)