0. kbuild的组成

0.1 构成文件

顶层Makefile
.config
arch/$(ARCH)/Makefile
各个目录下的Makefile
scripts/Makefile.*

0.2 预定义的目标和变量

obj-m
obj-y
xxx-objs
zImage
menuconfig

0.2 工作流程

include arch/$(ARCH)/Makefile
读取.config,读取用户的各种配置变量,
解析预定义目标,构建依赖关系
编译各个模块或组件

  • 将每个目录下的源文件编译为对应的.o
  • 将.o归档为built-in.a

将built-in.a链接为vmlinux

1. kbuild的使用

make ARCH=arm vexpress_defconfig
make menuconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- uImage LOADADDR=0x60003000

注意 LOADADDR 没有硬性要求,只要在 RAM 地址范围 几乎都可以,比如可以设置为 0x60000000

kbuild给开发者提供了如下 变量
obj-m : 内容为所有需要以模块形式生产的组件
obj-y : 内容为所有需要编译进内核的组件
xxx-objs : 代表一个组件,依赖的目标文件

开发者通常只需要通过 menuconfig 设置上诉变量,原理是
若有一个 hello 模块,则 Kconfig 文件有如下定义

config HELLO
  tristate "hello"
  default n
  help 
    for test

开发者运行完 menuconfig 后,主目录会生成 .config,其中有

CONFIG_HELLO=m
或
CONFIG_HELLO=y

在进行编译时,hello模块目录下有Makefile文件,内容为

obj-$(CONFIG_HELLO) += hello.o

如果 CONFIG_HELLO=m,则hello.o 会生成 hello-objs 目标,hello-objs目标的默认依赖是 hello.o。
所以开发者可以容易的编译内核和模块。

2. .config

2.1 生成.config的原理

执行 xxx_defconfig,或 menuconfig 都是如下目标

%config: outputmakefile scripts_basic FORCE
    $(Q)$(MAKE) $(build)=scripts/kconfig $@

MAKE 变量 和 build 变量

root@ubuntu:~/wlt/build/linux-5.16.2# grep "^build\s*[: ]=" -rn ./
$(srctree)/scripts/Makefile.build obj
root@ubuntu:~/wlt/build/linux-5.16.2# grep "^MAKE\s*[: ]=" -rn ./
./tools/bpf/Makefile:8:MAKE = make

MAKE : make,
build : -f $(srctree)/scripts/Makefile.build obj
所以上面的make的执行是

make -f $(srctree)/scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
make -f $(srctree)/scripts/Makefile.build obj=scripts/kconfig menuconfig

scripts/Makefile.build

src := $(obj)

# The filename Kbuild has precedence over Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file)

kbuild-dir, 如果 src是绝对路径则使用src,否则改为绝对路径.
kbuild-file,如果 $(kbuild-dir)/Kbuild 文件存在,则用$(kbuild-dir)/Kbuild,否则用$(kbuild-dir)/Makefile
这里,kbuild-file为scripts/kconfig/Makefile

scripts/kconfig/Makefile

menuconfig-prog := mconf

$(foreach c, config menuconfig nconfig gconfig xconfig, $(eval $(call config_rule,$(c))))

%_defconfig: $(obj)/conf
    $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)

当使用 xxx_defconfig,执行
$(obj)/conf $(silent) –defconfig=arch/$(SRCARCH)/configs/xxx_defconfig $(Kconfig)
可见是 使用命令 conf ,并且参数为 arch/$(SRCARCH)/configs/xxx_defconfig

当使用 menuconfig,则调用函数 config_rule

 39 define config_rule
 40 PHONY += $(1)
 41 $(1): $(obj)/$($(1)-prog)
 42     $(Q)$$< $(silent) $(Kconfig)

也就是规则

 41 meunconfig: $(obj)/$(menuconfig-prog)
 42     $(Q)$$< $(silent) $(Kconfig)

可见是运行命令 mconf
所以 mconf 最终生成根目录的 .config

2.2 构建时.config如何指导构建

2.2.1 syncconfig

kbuild根据 .config 生成
include/config/auto.conf 用来配置Makefile
include/generated/autoconf.h 给C程序使用
include/config/*.h 本质是空文件,用于构建依赖关系

 733 %/config/auto.conf %/config/auto.conf.cmd %/generated/autoconf.h: $(KCONFIG_CONFIG)
 734     $(Q)$(kecho) "  SYNC    $@"
 735     $(Q)$(MAKE) -f $(srctree)/Makefile syncconfig

和上面的分析一样,最终到
scripts/kconfig/Makefile

 70 simple-targets := oldconfig allnoconfig allyesconfig allmodconfig \
 71     alldefconfig randconfig listnewconfig olddefconfig syncconfig \
 72     helpnewconfig yes2modconfig mod2yesconfig

 76 $(simple-targets): $(obj)/conf
 77     $(Q)$< $(silent) --$@ $(Kconfig)

所以执行的命令是
$(obj)/conf $(silent) –syncconfig $(Kconfig)

2.2 include/config/auto.conf

主Makefile,默认会加载 auto.conf 文件

 284 need-config := 1

 663 ifdef need-config
 664 include include/config/auto.conf
 665 endif

auto.conf内容是用户配置

  9 CONFIG_KERNEL_GZIP=y
 10 CONFIG_DEFAULT_INIT=""

在make构建时,会根据auto.conf的输入,设置变量 obj-y obj-m。

 21 obj-$(CONFIG_PARISC)        += parisc/
 22 obj-$(CONFIG_RAPIDIO)       += rapidio/

2.3 include/generated/autoconf.h

include/generated/autoconf.h的内容是宏定义

  9 #define CONFIG_KERNEL_GZIP 1
 10 #define CONFIG_DEFAULT_INIT ""

源文件可以使用这些宏,已实现功能配置

  536 #ifdef CONFIG_USB_OHCI_BIG_ENDIAN_DESC
  537 #ifdef CONFIG_USB_OHCI_LITTLE_ENDIAN
  538 #define big_endian_desc(ohci)   (ohci->flags & OHCI_QUIRK_BE_DESC)
  539 #else
  540 #define big_endian_desc(ohci)   1       /* only big endian */
  541 #endif
  542 #else
  543 #define big_endian_desc(ohci)   0       /* only little endian */
  544 #endif

2.3 include/config下的空文件

Kbuild需要追踪的

  • 编译所需所有源文件和头文件
    kbuild使用 gcc -MD a.c 获得 a.c 依赖的 *.h 文件,生成 a.d 文件,对 a.d 文件进行整理后生成文件 .a.c.cmd,内容为 a.c 的依赖关系供make使用。

  • 编译配置选项
    kbuild 将根据使用的配置变量(CONFIG_XXX),在include/config/下生成对应名称的.h空文件,并根据使用此配置变量的.c文件,为.c文件添加依赖关系,记录到 .xx.c.cmd 中

3. vmlinux

3.1 vmlinux的构建

core-y      := init/ usr/ arch/$(SRCARCH)/
drivers-y   := drivers/ sound/
drivers-$(CONFIG_SAMPLES) += samples/
drivers-$(CONFIG_NET) += net/
drivers-y   += virt/
libs-y      := lib/

KBUILD_VMLINUX_OBJS := $(head-y) $(patsubst %/,%/built-in.a, $(core-y))
KBUILD_VMLINUX_OBJS += $(addsuffix built-in.a, $(filter %/, $(libs-y)))
ifdef CONFIG_MODULES
KBUILD_VMLINUX_OBJS += $(patsubst %/, %/lib.a, $(filter %/, $(libs-y)))
KBUILD_VMLINUX_LIBS := $(filter-out %/, $(libs-y))
else
KBUILD_VMLINUX_LIBS := $(patsubst %/,%/lib.a, $(libs-y))
endif
KBUILD_VMLINUX_OBJS += $(patsubst %/,%/built-in.a, $(drivers-y))

export KBUILD_VMLINUX_OBJS KBUILD_VMLINUX_LIBS
export KBUILD_LDS          := arch/$(SRCARCH)/kernel/vmlinux.lds
# used by scripts/Makefile.package
export KBUILD_ALLDIRS := $(sort $(filter-out arch/%,$(vmlinux-alldirs)) LICENSES arch include scripts tools)

vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_OBJS) $(KBUILD_VMLINUX_LIBS)

vmlinux: scripts/link-vmlinux.sh autoksyms_recursive $(vmlinux-deps) FORCE
    +$(call if_changed_dep,link-vmlinux)

可以看出vmlinux-deps 就是各个目录的 built-in.a

cmd_link-vmlinux =                                                 \
    $(CONFIG_SHELL) $< "$(LD)" "$(KBUILD_LDFLAGS)" "$(LDFLAGS_vmlinux)";    \
    $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)

CONFIG_SHELL就是 sh,$< 就是 scripts/link-vmlinux.sh,这个脚本的注释说明该脚本完成:

  1. 将 built-in.a lib.a 链接成vmlinux
  2. 生成 System.map,内容是所以内核符号表

3.2 built-in.a

# The actual objects are generated when descending,
# make sure no implicit rule kicks in
$(sort $(vmlinux-deps) $(subdir-modorder)): descend ;

PHONY += descend $(build-dirs)
descend: $(build-dirs)
$(build-dirs): prepare
    $(Q)$(MAKE) $(build)=$@ \
    single-build=$(if $(filter-out $@/, $(filter $@/%, $(KBUILD_SINGLE_TARGETS))),1) \
    need-builtin=1 need-modorder=1

可见编译vmlinux时,首先生成目标 descend,而descend依赖为 $(build-dirs)、
而 $(build-dirs) 根据编译内核或编译模块而不同,以编译内核为例

core-y          += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/
core-$(CONFIG_BLOCK)    += block/

# 先找出形如 %/的,再去掉 /,得到%
vmlinux-dirs    := $(patsubst %/,%,$(filter %/, \
             $(core-y) $(core-m) $(drivers-y) $(drivers-m) \
             $(libs-y) $(libs-m)))

build-dirs  := $(vmlinux-dirs)

所以 build-dirs 为 kernel certs mm fs ipc security …

$(build-dirs): prepare
    $(Q)$(MAKEjjjj) $(build)=$@ \
    single-build=$(if $(filter-out $@/, $(filter $@/%, $(KBUILD_SINGLE_TARGETS))),1) \
    need-builtin=1 need-modorder=1

./scripts/Kbuild.include:75:build := -f $(srctree)/scripts/Makefile.build obj
展开后为

kernel certs mm fs ipc security block crypto:prepare
$(Q)make -f $(srctree)/scripts/Makefile.build obj=$@ \
    single-build=$(if $(filter-out $@/, $(filter $@/%, $(KBUILD_SINGLE_TARGETS))),1) \
    need-builtin=1 need-modorder=1

$(srctree)/scripts/Makefile.build

src := $(obj)

# 定义默认目标为 __build
PHONY := __build
__build:

# 先清空 obj-m obj-y
obj-y :=
obj-m :=

# 根据传入的obj,加载不同Makefile,以设置obj-y obj-m
# The filename Kbuild has precedence over Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file)

可见 include $(kbuild-file) 根据 obj 的不同而不同,如obj为kernel,则 include kernel/Makefile。
子目录下的Makefile最关键定义了 obj-y 和 obj-m ,然后构建 __build

ifdef single-build

KBUILD_SINGLE_TARGETS := $(filter $(obj)/%, $(KBUILD_SINGLE_TARGETS))

curdir-single := $(sort $(foreach x, $(KBUILD_SINGLE_TARGETS), \
            $(if $(filter $(x) $(basename $(x)).o, $(targets)), $(x))))

# Handle single targets without any rule: show "Nothing to be done for ..." or
# "No rule to make target ..." depending on whether the target exists.
unknown-single := $(filter-out $(addsuffix /%, $(subdir-ym)), \
            $(filter-out $(curdir-single), $(KBUILD_SINGLE_TARGETS)))

single-subdirs := $(foreach d, $(subdir-ym), \
            $(if $(filter $(d)/%, $(KBUILD_SINGLE_TARGETS)), $(d)))

__build: $(curdir-single) $(single-subdirs)
ifneq ($(unknown-single),)
    $(Q)$(MAKE) -f /dev/null $(unknown-single)
endif
    @:

ifeq ($(curdir-single),)
# Nothing to do in this directory. Do not include any .*.cmd file for speed-up
targets :=
else
targets += $(curdir-single)
endif

else

__build: $(if $(KBUILD_BUILTIN), $(targets-for-builtin)) \
     $(if $(KBUILD_MODULES), $(targets-for-modules)) \
     $(subdir-ym) $(always-y)
    @:

endif

构建vmlinux时,则 signal-build为 0,所以执行

__build: $(if $(KBUILD_BUILTIN), $(targets-for-builtin)) \
     $(if $(KBUILD_MODULES), $(targets-for-modules)) \
     $(subdir-ym) $(always-y)
    @:

构建vmlinux时,__build的依赖为 $(targets-for-builtin)$(subdir-ym) $(always-y)

targets-for-builtin := $(extra-y)

ifneq ($(strip $(lib-y) $(lib-m) $(lib-)),)
targets-for-builtin += $(obj)/lib.a
endif

ifdef need-builtin
targets-for-builtin += $(obj)/built-in.a
endif

这里只分析 targets-for-builtin += $(obj)/built-in.a 情况。

# combine symversions for later processing
ifeq ($(CONFIG_LTO_CLANG) $(CONFIG_MODVERSIONS),y y)
      cmd_update_lto_symversions =                  \
    rm -f $@.symversions                        \
    $(foreach n, $(filter-out FORCE,$^),                \
        $(if $(shell test -s $(n).symversions && echo y),   \
            ; cat $(n).symversions >> $@.symversions))
else
      cmd_update_lto_symversions = echo >/dev/null
endif

quiet_cmd_ar_builtin = AR      $@
      cmd_ar_builtin = rm -f $@; $(AR) cDPrST $@ $(real-prereqs)

quiet_cmd_ar_and_symver = AR      $@
      cmd_ar_and_symver = $(cmd_update_lto_symversions); $(cmd_ar_builtin)

$(obj)/built-in.a: $(real-obj-y) FORCE
    $(call if_changed,ar_and_symver)

可见 built-in.a 的规则最关键的是 rm -f $@; $(AR) cDPrST $@ $(real-prereqs)
而依赖是 $(real-obj-y)

suffix-search = $(strip $(foreach s, $3, $($(1:%$(strip $2)=%$s))))
real-search = $(foreach m, $1, $(if $(call suffix-search, $m, $2, $3 -), $(call suffix-search, $m, $2, $3), $m))

real-obj-y := $(call real-search, $(obj-y), .o, -objs -y)
real-obj-m := $(call real-search, $(obj-m), .o, -objs -y -m)

以上实现找到真正的 .o文件,比如 obj-y 是 hello,且 hello-objs 是 a.o b.o c.o ,则找到真正依赖的.o文件是 a.o b.o c.o
所以下面构建.o文件

$(obj)/%.o: $(src)/%.S FORCE
    $(call if_changed_rule,as_o_S)

$(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE
    $(call if_changed_rule,cc_o_c)
    $(call cmd,force_checksrc)

if_changed_rule是

if_changed_rule = $(if $(if-changed-cond),$(rule_$(1)),@:)

所以 cc_o_c 为 rule_cc_o_c, as_o_S 为 rule_as_o_S

define rule_cc_o_c
    $(call cmd_and_fixdep,cc_o_c)
    $(call cmd,gen_ksymdeps)
    $(call cmd,checksrc)
    $(call cmd,checkdoc)
    $(call cmd,gen_objtooldep)
    $(call cmd,modversions_c)
    $(call cmd,record_mcount)
endef

define rule_as_o_S
    $(call cmd_and_fixdep,as_o_S)
    $(call cmd,gen_ksymdeps)
    $(call cmd,gen_objtooldep)
    $(call cmd,modversions_S)
endef

说明 rule_cc_o_c 不仅进行编译,还进行检查,和生成其他辅助文件,其中编译部分为

quiet_cmd_cc_o_c = CC $(quiet_modtag)  $@
      cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< $(cmd_objtool)

quiet_cmd_as_o_S = AS $(quiet_modtag)  $@
      cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $< $(cmd_objtool)

所以%.o的构建依赖 .S .c 文件,规则是编译。

4. zImage 的构建

arch/$(ARCH)/Makefile

boot := arch/arm/boot
ifeq ($(CONFIG_XIP_KERNEL),y)
KBUILD_IMAGE := $(boot)/xipImage
else
KBUILD_IMAGE := $(boot)/zImage
endif

BOOT_TARGETS    = zImage Image xipImage bootpImage uImage

$(BOOT_TARGETS): vmlinux
    $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
    @$(kecho) '  Kernel: $(boot)/$@ is ready'

zImage: Image

当构建 Image时,
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@ 展开
make -f script/Makefile.build obj=arch/arm/boot MACHINE=$(MACHINE) arch/arm/boot/Image
所以 使用 script/Makefile.build 进行构建,目标为 arch/arm/boot/Image
script/Makefile.build 先 include arch/arm/boot/Makefile
且 $(obj)/Image就是 arch/arm/boot/Image,所以规则为

$(obj)/xipImage: FORCE
    @echo 'Kernel not configured for XIP (CONFIG_XIP_KERNEL!=y)'
    @false

$(obj)/Image: vmlinux FORCE
    $(call if_changed,objcopy)

$(obj)/compressed/vmlinux: $(obj)/Image FORCE
    $(Q)$(MAKE) $(build)=$(obj)/compressed $@

$(obj)/zImage:  $(obj)/compressed/vmlinux FORCE
    $(call if_changed,objcopy)

if_changed

if-changed-cond = $(newer-prereqs)$(cmd-check)$(check-FORCE)

cmd = @set -e; $(echo-cmd) $($(quiet)redirect) $(cmd_$(1))

if_changed = $(if $(if-changed-cond),                                        \
    $(cmd);                                                              \
    printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)

所以最终执行的规则是

cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@

所以 Image的生成命令为
$(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) vmlinux Image

然后生成$(obj)/compressed/vmlinux

HEAD    = head.o
OBJS    += misc.o decompress.o

$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.o \
        $(addprefix $(obj)/, $(OBJS)) $(lib1funcs) $(ashldi3) \
        $(bswapsdi2) $(efi-obj-y) FORCE
    @$(check_for_multiple_zreladdr)
    $(call if_changed,ld)
    @$(check_for_bad_syms)

$(obj)/piggy_data: $(obj)/../Image FORCE
    $(call if_changed,$(compress-y))

$(obj)/piggy.o: $(obj)/piggy_data

可见 $(obj)/compressed/vmlinux 是 将 Image 进行压缩,并链接上 head.o misc.o decompress.o 。

然后构建 $(obj)/zImage

$(obj)/zImage:  $(obj)/compressed/vmlinux FORCE
    $(call if_changed,objcopy)

可见 zImage 就是 $(obj)/compressed/vmlinux 进行 objcopy

5. uImage

BOOT_TARGETS    = zImage Image xipImage bootpImage uImage

bootpImage uImage: zImage

$(BOOT_TARGETS): vmlinux
    $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
    @$(kecho) '  Kernel: $(boot)/$@ is ready'

找到

$(obj)/uImage:  $(obj)/zImage FORCE
    @$(check_for_multiple_loadaddr)
    $(call if_changed,uimage)
quiet_cmd_uimage = UIMAGE  $@
      cmd_uimage = $(BASH) $(MKIMAGE) -A $(UIMAGE_ARCH) -O linux \
            -C $(UIMAGE_COMPRESSION) $(UIMAGE_OPTS-y) \
            -T $(UIMAGE_TYPE) \
            -a $(UIMAGE_LOADADDR) -e $(UIMAGE_ENTRYADDR) \
            -n $(UIMAGE_NAME) -d $< $@

所以最终命令为
mkimage -A arm -O linux -T kernel -C none -a 0x60003000 -e 0x60003000 -d zImage uImage
-T image类型
-C 压缩方式,none gzip gzip2 等
-a 内核加载地址
-e 内核入口地址

uImage 和 zImage 的差别,uImage 多了 0x40 长度的头部。
uImage 的相对zImage 的优点,uboot也可以加载运行zImage,只是zImage的加载地址就是运行地址,而uImage可以加载到任何地址,uboot会保证运行地址。

7. modules

# 这里三个依赖都是准备工作,用于生成 module.mod文件 module.order 文件等
modules: $(if $(KBUILD_BUILTIN),vmlinux) modules_check modules_prepare

# 关键从这里的规则开始
modules: modules_check
    $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost

PHONY += modules_check
modules_check: $(MODORDER)
    $(Q)$(CONFIG_SHELL) $(srctree)/scripts/modules-check.sh $<

quiet_cmd_depmod = DEPMOD  $(MODLIB)
      cmd_depmod = $(CONFIG_SHELL) $(srctree)/scripts/depmod.sh $(DEPMOD) \
                   $(KERNELRELEASE)

modules_install:
    $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modinst
    $(call cmd,depmod)

modules_prepare: prepare
    $(Q)$(MAKE) $(build)=scripts scripts/module.lds

$(srctree)/scripts/Makefile.modpost 的默认目标是 __modpost

__modpost: $(output-symdump)
ifneq ($(KBUILD_MODPOST_NOFINAL),1)
    $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modfinal
endif

$(srctree)/scripts/Makefile.modfinal的默认目标是 __modfinal

# find all modules listed in modules.order
modules := $(sort $(shell cat $(MODORDER)))

__modfinal: $(modules)
    @:

其中 modules.order 的内容 为
a.ko
b.ko

quiet_cmd_ld_ko_o = LD [M]  $@
      cmd_ld_ko_o +=                            \
    $(LD) -r $(KBUILD_LDFLAGS)                  \
        $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE)      \
        -T scripts/module.lds -o $@ $(filter %.o, $^);      \
    $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)

# Re-generate module BTFs if either module's .ko or vmlinux changed
$(modules): %.ko: %$(mod-prelink-ext).o %.mod.o scripts/module.lds $(if $(KBUILD_BUILTIN),vmlinux) FORCE
    +$(call if_changed_except,ld_ko_o,vmlinux)
ifdef CONFIG_DEBUG_INFO_BTF_MODULES
    +$(if $(newer-prereqs),$(call cmd,btf_ko))
endif

其中 mod-prelink-ext 通常为空

ifeq ($(CONFIG_LTO_CLANG),y)
# With CONFIG_LTO_CLANG, .o files in modules might be LLVM bitcode, so we
# need to run LTO to compile them into native code (.lto.o) before further
# processing.
mod-prelink-ext := .lto
endif

将目标进行替换,找到依赖,如 a.ko
a.ko: a.o a.mod.o scripts/module.lds $(if $(KBUILD_BUILTIN),vmlinux)

原文地址:http://www.cnblogs.com/yangxinrui/p/16612270.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性