1. 移植准备工作1.1 安装必要的依赖工具1.2 下载源码1.2 配置交叉编译工具链1.3 构建内核源码补丁文件的默认配置1.4 拷贝设备树文件2. 首次Linux内核的编译运行2.1 首次编译2.2 安装镜像到开发板3. 解决内核错误3.1 修改设备树文件3.2 重新编译并拷贝镜像和设备树文件4. make menuconfig 的执行过程4.1 Makefile分析4.2 scirpts/Makefile.build分析4.3 script/kconfig/Makefile分析5. 配置文件5.1 三个配置文件的关系5.2 Kconfig 语法6. 编译外设驱动7. 采用模块化的方法编译设备驱动8. 镜像编译过程分析8.1 uImage镜像文件的构建8.2 zImage镜像文件的构建8.3 $(obj)/compressed/vmlinux镜像文件的构建8.4 Image镜像文件的构建8.5 镜像文件的构建过程8.6 vmlinux镜像文件的构建
1. 移植准备工作
1.1 安装必要的依赖工具
$ sudo apt-get install bison flex libelf-dev libssl-dev u-boot-tools libncurses-dev
1.2 下载源码
- 数据包下载:STM32MP1Dev 页面中的 Yocto_SDK
- 本文使用 3.1 版本,对应 Linux 5.10.61 版本内核
- 文件名为
en.SOURCES-stm32mp1-openstlinux-5.10-dunfell-mp1-21-11-17_tar_v3.1.0.xz
- 解压后拷贝出
sources/arm-ost1-linux-gnueabi/linux-stm32mp-5.10.61-stm32mp-r2-r0
- 该目录下有:
.patch
:补丁文件.config
:补丁配置列表linux-5.10.61.tar.xz
:Linux 系统源码README.HOW_TO.txt
series
:补丁文件列表
- 解压
linux-5.10.61
,主版本号.次版本号.修订版本号
- 进入解压后的源码目录,打上补丁(参考
README
)
for p in `ls -1 ../*.patch`; do patch -p1 < $p; done
linux-5.10.61.tar.xz
是单纯的 Linux 源码,打补丁的作用是添加 ST 的各个设备的设备树文件
1.2 配置交叉编译工具链
- 打开
Makefile
编辑如下行:
370 ARCH ?= arm 371 CROSS_COMPILE:=arm-linux-gnueabihf-
arm
交叉编译工具链的参考:ARM 交叉工具链安装
1.3 构建内核源码补丁文件的默认配置
make ARCH=arm multi_v7_defconfig fragment*.config # 成功现象 # (中间省略) # # configuration written to .config #
1.4 拷贝设备树文件
- FSMP1A 是从 DK1 公版基础上修改的,所以可以从 DK1 的设备树文件修改
cd arch/arm/boot/dts cp stm32mp157a-dk1.dts stm32mp157a-fsmp1a.dts cp stm32mp15xx-dkx.dtsi stm32mp15xx-fsmp1x.dtsi
- 编辑
stm32mp157a-fsmp1a.dts
,修改依赖的头文件:
13 #include "stm32mp15xx-fsmp1x.dtsi" 14 15 / { 16 model = "HQYJ STM32MP157A-FSMP1A Discovery Board"; 17 compatible = "hqyj,stm32mp157a-fsmp1a", "hqyj,stm32mp157";
- 编辑
arm/boot/dts/Makefile
,添加到编译的列表
1097 stm32mp157a-dk1.dtb \\ 1098 stm32mp157a-fsmp1a.dtb \\ ------> 这一行为需要添加内容
2. 首次Linux内核的编译运行
2.1 首次编译
- 可以参考
README
的第六章节
time make -j4 ARCH=arm uImage vmlinux dtbs LOADADDR=0xC2000000
time
:对编译进行计时
ARCH=arm
:指定arm
架构
uImage
:编译生成的镜像文件
vmlinux
:指定 elf 镜像文件
dtbs
:编译设备树文件
LOADADDR=0xC20000000
:指定的加载地址
- 编译成功后显示:
OBJCOPY arch/arm/boot/zImage Kernel: arch/arm/boot/zImage is ready UIMAGE arch/arm/boot/uImage Image Name: Linux-5.10.61 Created: Tue May 9 17:10:31 2023 Image Type: ARM Linux Kernel Image (uncompressed) Data Size: 7172240 Bytes = 7004.14 KiB = 6.84 MiB Load Address: c2000000 Entry Point: c2000000 Kernel: arch/arm/boot/uImage is ready
2.2 安装镜像到开发板
- 通过 tftp 服务器下载镜像到开发板的方法安装
- 需要开发板和主机在同一局域网
- 需要配置好 tftp 服务器:TFTP 环境配置
- 步骤一:打开串口工具连接到开发板 u-boot,查看并设置
bootcmd
和bootargs
(⚠️一定要注意对应自己的路径)
FSMP1A> pri bootcmd bootcmd=tftp 0xc2000000 uImage;tftp 0xc4000000 stm32mp157a-fsmp1a.dtb;bootm 0xc2000000 - 0xc4000000 FSMP1A> pri bootargs bootargs=root=/dev/nfs nfsroot=192.168.1.250:/home/linux/nfs/rootfs,tcp,v4 rw console=ttySTM0,115200 init=/linuxrc ip=192.168.1.100
bootcmd
即开机自动运行的脚本,也可以在 u-boot 交互中手动输入这些代码
- 指令解析:
tftp 下载地址 镜像
,tftp 下载地址 设备树文件
,bootm 启动范围
0x20000000
需要对应首次编译指令中输入的地址,0x40000000
设置一个距离一个距离0x20000000
足够远的地址,没有特定nfsroot
:通过 nfs 挂载rootfs
根文件系统- 需要先配置好 nfs 服务器:NFS 服务器环境配置
192.168.1.250
:主机地址,nfs 服务器地址192.168.1.100
:开发板设置的局域网地址/home/linux/nfs/rootfs
:主机中 rootfs 的目录- 需要对应自己主机配置文件中设置地址
rootfs
根目录的移植在rootfs 根文件系统制作 笔记中学习- 先可以使用 rootfs-ok.tar.xz 这个构建好的
- 在开发板 u-boot 确认网络相关配置:
FSMP1A> pri serverip serverip=192.168.1.250 FSMP1A> pri netmask netmask=255.255.255.0 FSMP1A> pri gatewayip gatewayip=192.168.1.1 FSMP1A> pri ipaddr ipaddr=192.168.1.100 FSMP1A> ping 192.168.1.250 ethernet@5800a000 Waiting for PHY auto negotiation to complete... done Using ethernet@5800a000 device host 192.168.1.250 is alive
- 确认服务器地址(即主机的地址),确认子网掩码,确认网关 IP
- 通过
ping
测试,显示alive
即为网络连接 OK
- 接下来,拷贝编好的镜像文件和设备树文件到 tftp 目录中
cp arch/arm/boot/uImage ~/tftpboot/ cp arch/arm/boot/dts/stm32mp157a-fsmp1a.dtb ~/tftpboot/
- 复位开发板,等待 u-boot 倒计时后自动运行
bootcmd
下载镜像并运行:
[ 5.637332] IP-Config: Fail to open eth0 [ 5.641332] IP-Config: No netwrok devices avaliable
- 会跳出熟悉的 Linux 启动日志,但最终会停止在网络驱动错误中(这是正常的,因为还没有对设备树文件修改)
3. 解决内核错误
3.1 修改设备树文件
- 将 stm32mp157a-fsmp1a-dts.zip 解压覆盖到
/arch/arm/boot/dts
,这是写好的设备树文件
- 将 0001-linux-5.10.61-maxio0621.patch 拷贝到源码目录中,这是 v3.1 版本开发板的网卡驱动
- 执行
patch -Np1 < 0001-linux-5.10.61-maxio0621.patch
打上补丁 - 显示
patching file scripts/dtc/include-prefixes/arm/stm32mp157a-fsmp1a.dts
即可(可忽略其他错误) - 查看
stm32mp157xx-fsmp1x.dtsi
,是否包含:
ðernet0 { status = "okay"; pinctrl-0 = <ðernet0_rgmii_pins_a>; pinctrl-1 = <ðernet0_rgmii_sleep_pins_a>; pinctrl-names = "default", "sleep"; phy-mode = "rgmii-id"; max-speed = <1000>; phy-handle = <&phy0>; mdio0 { #address-cells = <1>; #size-cells = <0>; compatible = "snps,dwmac-mdio"; phy0: ethernet-phy@0 { reg = <0>; }; }; }
- 通过 KConfig 来开启配置:
$ make menuconfig Device Drivers ---> [*] Network device support ---> [*] Ethernet driver support ---> <*> STMicroelectronics Multi-Gigabit Ethernet driver <*> STMMAC Platform bus support <*> Support for snps,dwc-qos-ethernet.txt DT binding. <*> Generic driver for DWMAC <*> STM32 DWMAC support
- 添加 MAE0621A 驱动( v3.1 开发板的驱动 ):
make menuconfig Device Drivers ---> [*] Network device support ---> -*- PHY Device support and infrastructure ---> < > Quality Semiconductor PHYs <*> Realtek PHYs <*> MAXIO PHYS -------> #需要按下键盘的空格键两下,一定选择为*!!!!!
3.2 重新编译并拷贝镜像和设备树文件
$ time make -j4 ARCH=arm uImage vmlinux dtbs LOADADDR=0xC2000000 $ cp arch/arm/boot/uImage ~/tftpboot/ $ cp arch/arm/boot/dts/stm32mp157a-fsmp1a.dtb ~/tftpboot/
- 此时,再次复位开发板,可以进入 Linux 的 shell,但是还有一个错误:
- 如果显示的依然是 IP 相关的错误,可以尝试
make clean
后完全重头编译
/etc/init.d/rcS: line 6: can't create /proc/sys/kernel/hotplug: nonexistent directory
- 这个需要开启内核的热加载功能:
$ make menuconfig Device Drivers ---> Generic Driver Options ---> [*] Support for uevent helper (/sbin/hotplug) path to uevent helper
- 再次通过上面的命令编译拷贝文件重启设备
- 此时可以无错误进入 Linux 的 Shell 交互界面中
4. make menuconfig 的执行过程
4.1 Makefile分析
- 打开源码目录下的
Makefile
,查找menuconfig
选项
- 没有找到
menuconfig
选项,考虑可能使用通配符,查找%
- 在 vim 中输入
/^%
查找%
开头的行,找到%config
(602行附近):
%config: outputmakefile scripts_basic FORCE $(Q)$(MAKE) $(build)=scripts/kconfig $@
- 通过
echo
去查看输出的内容
602 %config: outputmakefile scripts_basic FORCE 603 @echo $(Q) 604 @echo $(MAKE) 605 @echo $(build) 606 @echo $@ 607 $(MAKE) $(build)=scripts/kconfig $@
- 由于
$(Q)
只是@
,对输出内容进行回显,所以在在构建命令中删除方便理解构建指令
- 输出如下的结果:
@ make -f ./scripts/Makefile.build obj menuconfig make -f ./scripts/Makefile.build obj=scripts/kconfig menuconfig
- 上面最后一条是完整的构建指令,其中
f
是指定另一个Makefile
运行
- 所以需要再次进入
./scripts/Makefile.build
这个Makefile
文件进行分析
4.2 scirpts/Makefile.build分析
- 通过搜索命令行的参数
obj
,可以发现它在第 6 行被赋值给脚本中的变量src
- 同时可以看到一些
obj
对应目录下的文件的编译,也就是说kconfig
会在第一次运行的时候进行编译
6 src := $(obj) --------> 解释:src := scripts/kconfig 42 kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) 解释: kbuild-dir := $(src) = scripts/kconfig 43 kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile) 解释: kbuild-file := scripts/kconfig/Makefile 44 include $(kbuild-file) 解释:将scripts/kconfig目录下的Makefile文件,替换到当前文件的这个位置
- 因为找不到其他相关的构建指令,再从
include
相关行查看
include
会将指定位置下的Makefile
加载到当前Makefile
运行,所以需要再次追踪scripts/kconfig/Makefile
4.3 script/kconfig/Makefile分析
28 menuconfig: $(obj)/mconf 29 $< $(silent) $(Kconfig) 解释: scripts/kconfig/mconf Kconfig mconf是一个可执行文件,kconfig作为参数传递给mconf
- 这个
Makefile
有menuconfig
指令
$<
代表构建指令中,依赖列表第一个项目的名称,silent
是s
,Kconfig
变量是Kconfig
指令
- 所以最后是
script/kconfig/mconf Kconfig
mconf
是一个二进制可执行文件,Kconfig 为源码目录下的当前配置文件
5. 配置文件
5.1 三个配置文件的关系
Makefile
:管理内核源码配置和编译
Kconfig
:存放 kconfig 图形化配置信息
.config
:生成默认的配置文件,给 Makefile 使用
5.2 Kconfig 语法
- 概念:
- 主菜单:可包含子菜单和菜单选项
- 子菜单:可包含子菜单和菜单选项
- 子子菜单:可包含菜单选项
- 菜单选项:选择需要执行的内容
- 打开
Kconfig
文件: - 第一行为
mainemnu
开头项,这是菜单的名称 - 例如,
mainmenu "Linux/$(ARCH) $(KERNELVERSION) Kernel Configuration"
- 其余行为
source
开头项,这是子菜单 - 例如,
source "drivers/Kconfig"
- 打开
drivers/Kconfig
: menu
开头行,这是菜单名称source
开头行,这是子菜单- 例如
source "drivers/char/Kconfig"
endmenu
行,表示菜单结束
- 打开
drivers/char/Kconfig
: config
开头行,这是菜单选项- 菜单选项的语法:
<>
:tristate
三态<*>
表示对应内容编译到镜像文件中< >
表示对应内容不编译到镜像文件中<M>
表示对应内容通过模块化编译[]
布尔值[*]
表示编译到镜像文件中[ ]
表示不编译到镜像文件中depends on
表示依赖某一个菜单选项default
:默认的选择方式help
:选项帮助信息
6. 编译外设驱动
- 以编译开发板的 LED 灯为例
- 代码的编写需要在驱动移植中学习,这里先提供测试代码:fsmp1a-linux-led.zip
- 拷贝
fsmp1a_led.c
和fsmp1a_led.h
到drivers/char
目录下
- 编辑
driver/char/Kconfig
文件,添加如下配置:
config LED_DRIVER bool "led driver" default y help this is DC23031 led driver!!!
- 在
driver/char/Makefile
文件添加:
50 obj-$(CONFIG_LED_DRIVER) += fsmp157a_led.o
- 进入图形化配置界面进行配置
$ make menuconfig Device Drivers ---> Character devices ---> [*] led driver (NEW)
- 进行编译和拷贝
- 拷贝刚才 LED 灯驱动文件包中的测试代码
fsmp1a_led_test.c
和头文件fsmp1a_led.h
到nfs/rootfs
即挂载的目录下
- 通过主机的交叉编译工具进行编译:
gcc-linux-gnueabihf-gcc fsmp1a_led.c
- 在开发板的 Linux Shell 中运行编译后的程序
./a.out
- 此时 LED 灯依据测试文件代码进行闪烁
- 框图:
应用层:(0~3G) a.out -------> 实现的逻辑 执行应用层程序,函数入口main函数 ——————————————————————————————————————————————————————————————— 内核层:(3G~4G) fsmp157a_led.o -------> 实现的机制 执行驱动相关程序,函数入口init 函数出口exit ——————————————————————————————————————————————————————————————— 硬件层 LED 蜂鸣器 马达
7. 采用模块化的方法编译设备驱动
- 修改
driver/char/Kconfig
8 config LED_DRIVER 9 tristate "led driver" 10 default y 11 help 12 this is DC23031 led driver!!!
- 进行图形化配置
vice Drivers ---> Character devices ---> <M> led driver ---> !!!更改为M选项 !!!
- 编译和拷贝
- 此时还需要拷贝
dirver/char/fsmp157a_led.ko
到rootfs
中
- 通过开发板 Linux 运行
insmod fsmp157a_led.ko
加载驱动到内核中
- 再次运行
./a.out
,程序再次工作
- 通过
rmmod fsmp157a_led
移除驱动
- 再次运行
./a.out
,程序错误退出
8. 镜像编译过程分析
8.1 uImage镜像文件的构建
- 通过
README
参考,镜像文件的编译指令为make uImage LOADADDR=0xC20000000
- 查找
Makefile
,没有找到uImage
关键字
- 查看
include
开头行,查看是否是其他Makefile
引入
- 但是
include
的Makefile
比较多,先用grep
看看哪些Makefile
构建了uImages
: grep -r 'uImage' ./*/**/Makefile
- 结果基本上都是
./arch
开头
- 这样可以确定,应该是 596 行的
arch/$(SRCARCH)/Makefile
引入的
- 搜索
SRCARCH
它是SRCARCH := $(ARCH)
,而ARCH
我们在配置交叉编译工具链中配置为了arm
- 所以对应的是
arch/arm/Makefile
中构建uImage
相关的指令
- 进入
arch/arm/Makefile
文件,查看uImage
相关行
315 BOOT_TARGETS = zImage Image xipImage bootpImage uImage 323 $(BOOT_TARGETS): vmlinux 324 @echo $(Q) 325 @echo $(MAKE) 326 @echo $(build) 327 @echo $(boot) 328 @echo $(MACHINE) 329 @echo $(boot) 330 @echo $@ 331 $(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@ 332 @$(kecho) ' Kernel: $(boot)/$@ is ready'
- 得到打印信息
@ make -f ./scripts/Makefile.build obj arch/arm/boot arch/arm/boot uImage make -f ./scripts/Makefile.build obj=arch/arm/boot MACHINE= arch/arm/boot/uImage
- 我们在上面#scirpts/Makefile.build分析分析过
script/Makefile.build
它相当于调用obj
对应的Makefile
并在obj
第一次运行时构建对应的一些文件
- 所以进入
arch/arm/boot/Makefile
文件,查看uImage
相关构建指令
89 $(obj)/uImage: $(obj)/zImage FORCE 90 @$(check_for_multiple_loadaddr) ——————————>检查加载地址 91 $(call if_changed,uimage) ——————————>call是调用命令,调用if_changed命令
- 第 90 行为一个函数,会检查
LOADADDR
参数,如果缺少会报错终止构建
- 而第 91 行的函数则找不到出处,反过来想,因为这个 Makefile 是被
script/Makefile.build
给include
的,所以应该实在script/Makefile.build
中定义的
- 再次进入
script/Makefile.build
查看if_changed
相关行:
218 if_changed = $(if $(newer-prereqs)$(cmd-check), \\ 219 $(cmd); \\ 183 cmd = @set -e; $(echo-cmd) $($(quiet)redirect) $(cmd_$(1)) 解释: @set -e:编译时遇到任何错误就退出 $(cmd_$(1)) :调用cmd_uimage
- 而
cmd_uimage
是哪个include
中引入的,首先可以通过命名检查Makefile.lib
文件,在其中搜索cmd_uimage
:
398 cmd_uimage = $(BASH) $(MKIMAGE) -A $(UIMAGE_ARCH) -O linux \\ 399 -C $(UIMAGE_COMPRESSION) $(UIMAGE_OPTS-y) \\ 400 -T $(UIMAGE_TYPE) \\ 401 -a $(UIMAGE_LOADADDR) -e $(UIMAGE_ENTRYADDR) \\ 402 -n $(UIMAGE_NAME) -d $< $@ 解释: $(BASH) :bash MKIMAGE := 内核源码目录下/scripts/mkuboot.sh 通过分析mkuboot.sh脚本文件可知,执行mkimage命令 mkimage -A arm -O linux -C gzip -T kernel -a 0xc2000000 -e 0xc2000000 -n uImage -d zImage
- 总结
uImage
和zImage
之间的关系: - uImage是通过zImage得到,将zImage通过 mkimage这个工具添加64字节头部信息
- 每次编译成功之后,都会打印添加信息内容
Image Name: Linux-5.10.61 Created: Tue May 9 17:10:31 2023 Image Type: ARM Linux Kernel Image (uncompressed) Data Size: 7172240 Bytes = 7004.14 KiB = 6.84 MiB Load Address: c2000000 Entry Point: c2000000
8.2 zImage镜像文件的构建
1.进入linux内核源码目录下,arch/arm/boot目录下,打开Makefile文件,搜索zImage 66 $(obj)/zImage: $(obj)/compressed/vmlinux FORCE 67 $(call if_changed,objcopy) ——————————————>执行cmd_objcopy 2.进入linux内核源码目录下,scripts目录下,打开Makefile.lib文件,搜索cmd_objcopy 267 cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@ arm-linux-gnueabihf-objcopy arch/arm/boot/compressed/vmlinux arch/arm/boot/zImage 3.zImage镜像文件和arch/arm/boot/compressed/vmlinux之间关系 arch/arm/boot/compressed/vmlinux通过objcopy命令格式化转换为zImage
8.3 $(obj)/compressed/vmlinux镜像文件的构建
1.进入linux内核源码目录下,arch/arm/boot目录下,打开Makefile文件,搜索vmlinux 63 $(obj)/compressed/vmlinux: $(obj)/Image FORCE 64 $(Q)$(MAKE) $(build)=$(obj)/compressed $@ 命令:make -f ./scripts/Makefile.build obj=arch/arm/boot/compressed arch/arm/boot/compressed/vmlinux 2.进入linux内核源码目录下,arch/arm/boot/compressed目录下,打开Makefile文件,搜索vmlinux 178 $(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.o \\ 179 $(addprefix $(obj)/, $(OBJS)) $(lib1funcs) $(ashldi3) \\ 180 $(bswapsdi2) $(efi-obj-y) FORCE 182 $(call if_changed,ld) 将所有的.o文件通过ld命令,根据vmlinux.lds链接脚本文件,链接生成vmlinux 185 $(obj)/piggy_data: $(obj)/../Image FORCE 186 @echo "!!!!!!!!!$(compress-y)!!!!!!!!" 187 $(call if_changed,gzip) ——————————————>执行cmd_gzip 188 $(obj)/piggy.o: $(obj)/piggy_data 3.进入linux内核源码目录下,scripts目录下,打开Makefile.lib文件,搜索cmd_gzip 273 cmd_gzip = cat $(real-prereqs) | $(KGZIP) -n -f -9 > $@ 4.arch/arm/boot/compressed/vmlinux镜像文件和Image之间关系 将Image镜像gzip压缩方式得到arch/arm/boot/compressed/vmlinux镜像文件
8.4 Image镜像文件的构建
1.进入linux内核源码目录下,arch/arm/boot目录下,打开Makefile文件,搜索Image 60 $(obj)/Image: vmlinux FORCE 61 $(call if_changed,objcopy) ——————————————>执行cmd_objcopy 2.Image和vmlinux之间关系 vmlinux通过objcopy命令格式化转换为Image
8.5 镜像文件的构建过程
vmlinux ——>objcopy——>Image ——>gzip——>arch/arm/boot/compressed/vmlinux ——>objcopy——>zImage ——>mkimage——>uImage
8.6 vmlinux镜像文件的构建
1.进入linux内核源码目录下,打开Makefile文件,搜索:vmlinux 1182 vmlinux: scripts/link-vmlinux.sh autoksyms_recursive $(vmlinux-deps) FORCE 1183 +$(call if_changed,link-vmlinux) ——————————>调用cmd_link-vmlinux 1178 cmd_link-vmlinux = \\ 1179 $(CONFIG_SHELL) $< "$(LD)" "$(KBUILD_LDFLAGS)" "$(LDFLAGS_vmlinux)"; \\ 1180 $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true) 通过以上分析可知:运行脚本文件 scripts/link-vmlinux.sh 2.进入linux内核源码目录下,打开scripts/link-vmlinux.sh 文件, 12 # vmlinux 13 # ^ 14 # | 15 # +--< $(KBUILD_VMLINUX_OBJS) 16 # | +--< init/built-in.a drivers/built-in.a mm/built-in.a + more 17 # | 18 # +--< $(KBUILD_VMLINUX_LIBS) 19 # | +--< lib/lib.a + more 20 # | 21 # +-< ${kallsymso} (see description in KALLSYMS section) 3.进入linux内核源码目录下,打开scripts/link-vmlinux.sh 文件, 48 modpost_link() 49 { 50 local objects 51 52 objects="--whole-archive \\ 53 ${KBUILD_VMLINUX_OBJS} \\ 54 --no-whole-archive \\ 55 --start-group \\ 56 ${KBUILD_VMLINUX_LIBS} \\ 57 --end-group" 58 59 ${LD} ${KBUILD_LDFLAGS} -r -o ${1} ${objects} 60 } 4.进入linux内核源码目录下,打开Makefile文件, 1136 KBUILD_VMLINUX_OBJS := $(head-y) $(patsubst %/,%/built-in.a, $(core-y)) 1137 KBUILD_VMLINUX_OBJS += $(addsuffix built-in.a, $(filter %/, $(libs-y))) 1138 ifdef CONFIG_MODULES 1139 KBUILD_VMLINUX_OBJS += $(patsubst %/, %/lib.a, $(filter %/, $(libs-y))) 1140 KBUILD_VMLINUX_LIBS := $(filter-out %/, $(libs-y)) 1141 else 1142 KBUILD_VMLINUX_LIBS := $(patsubst %/,%/lib.a, $(libs-y)) 1143 endif 1144 KBUILD_VMLINUX_OBJS += $(patsubst %/,%/built-in.a, $(drivers-y)) 654 core-y := init/ usr/ 655 drivers-y := drivers/ sound/ 656 drivers-$(CONFIG_SAMPLES) += samples/ 657 drivers-y += net/ virt/ 658 libs-y := lib/