Quantcast
Channel: CodeSection,代码区,Linux操作系统:Ubuntu_Centos_Debian - CodeSec
Viewing all articles
Browse latest Browse all 11063

根目录vmlinux的编译过程

$
0
0

编译过内核的话,一般都会看到在根目录下有个文件vmlinux,这个就是通常所说的内核了。但是用了这么久,倒是从来没看过是怎么编译出来的。那今天我们就来探索一下。

那些七大姑八大姨们

一切的一切都是make读取makefile编译链接的,就好像孙悟空逃不出如来佛祖的手掌,vmlinux的出世也是在makefile的安排之下。那就现在看看makefile

# SHELL used by kbuild
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
else if [ -x /bin/bash ]; then echo /bin/bash; \
else echo sh; fi ; fi)
# Final link of vmlinux
cmd_link-vmlinux = $(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux)
quiet_cmd_link-vmlinux = LINK $@
vmlinux: scripts/link-vmlinux.sh vmlinux_prereq $(vmlinux-deps) FORCE
+$(call if_changed,link-vmlinux)

看完这一段差点一口老血吐出来,我真是没有想到他们尽然可以这么玩。。。

注:对if_changed函数的分析可以看我的这篇文章 if_changed

这个cmd_link-vmlinux就是把第一个依赖作为脚本传给了系统使用的shell,由系统shell执行。好吧,我也是醉了,道行就这么又深了一点点。

回到正题,我们先看看vmlinux相关的都是谁。

vmlinux一共依赖两个 一个是vmlinux_prereq,另一个是vmlinux-deps。那分头分析。

vmlinux_prereq # Include targets which we want to execute sequentially if the rest of the
# kernel build went well. If CONFIG_TRIM_UNUSED_KSYMS is set, this might be
# evaluated more than once.
PHONY += vmlinux_prereq
vmlinux_prereq: $(vmlinux-deps) FORCE
ifdef CONFIG_HEADERS_CHECK
$(Q)$(MAKE) -f $(srctree)/Makefile headers_check
endif
ifdef CONFIG_BUILD_DOCSRC
$(Q)$(MAKE) $(build)=Documentation
endif
ifdef CONFIG_GDB_SCRIPTS
$(Q)ln -fsn `cd $(srctree) && /bin/pwd`/scripts/gdb/vmlinux-gdb.py
endif
ifdef CONFIG_TRIM_UNUSED_KSYMS
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/adjust_autoksyms.sh \
"$(MAKE) KBUILD_MODULES=1 -f $(srctree)/Makefile vmlinux_prereq"
endif

这个看觉和最后的vmlinux关系不大, 咱暂时就不看了。

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

vmlinux依赖这几个变量,我们一个个来看~

KBUILD_LDS, 链接文件 export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds

这个好说,就是链接文件~

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

这个head-y定义根据架构不同而不同,比如在x86架构下,该定义为:

head-y := arch/x86/kernel/head_$(BITS).o
head-y += arch/x86/kernel/head$(BITS).o
head-y += arch/x86/kernel/head.o

恩,就一个架构,都有三个文件,好多~

在搜索的时候又发现这么一句在文档中:

$(head-y) lists objects to be linked first in vmlinux.

这个提醒了我一下,在链接的时候,文件出现的顺序是有影响的。

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

这个init-y原来就是init/built-in.o一个文件。

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

这个包含的内容比较多了啊。

#core-y
core-y := usr/
core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/
core-y := $(patsubst %/, %/built-in.o, $(core-y))
#libs-y
libs-y := lib/
libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))
libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y))
libs-y := $(libs-y1) $(libs-y2)
#drivers-y
drivers-y := drivers/ sound/ firmware/
drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))
#net-y
net-y := net/
net-y := $(patsubst %/, %/built-in.o, $(net-y))
#virt-y
virt-y := virt/
virt-y := $(patsubst %/, %/built-in.o, $(virt-y))

这下,内核根目录下所有的子目录就都包含了~

除了lib目录下有两个文件,built-in.o 和 lib.a,其余下都只有一个文件built-in.o。

谁是你们的粘合剂

房子是由转头垒起来的,同样的转头不同的垒法造出来的房子会不一样。我们看完了构造vmlinux的原料,现在来看看他们是如何构造vmlinux的吧。

将跟目录中Makefile文件vmlinux目标的规则在x86平台展开就是:

cmd_vmlinux := /bin/bash scripts/link-vmlinux.sh ld -m elf_x86_64 --emit-relocs --build-id

这里面调用了scripts/link-vmlinux.sh,看来秘密都在这个文件中~

vmlinux.o # Link of vmlinux.o used for section mismatch analysis
# ${1} output file
modpost_link()
{
${LD} ${LDFLAGS} -r -o ${1} ${KBUILD_VMLINUX_INIT} \
--start-group ${KBUILD_VMLINUX_MAIN} --end-group
}
info LD vmlinux.o
modpost_link vmlinux.o

有没有感到很熟悉?又看到KBUILD_VMLINUX_INIT和KBUILD_VMLINUX_MAIN了吧。

关于 start-group, end-group我查了一下ld的man,说是这个可以循环去寻找出现在对象文件中的symbol,直到找到。

以为vmlinux.o就这么完事儿了吧,结果又出现一行命令。

# modpost vmlinux.o to check for section mismatches
${MAKE} -f "${srctree}/scripts/Makefile.modpost" vmlinux.o

这个最后用的是编译出来的scripts/mod/modpost来做section的检测的。

我把x86平台上这条语句执行的情况展开了,留着以后研究。

scripts/mod/modpost -m -a -o ./Module.symvers -S vmlinux.o vmlinux info LD vmlinux
vmlinux_link "${kallsymso}" vmlinux

这下是链接vmlinux了

# Link of vmlinux
# ${1} - optional extra .o files
# ${2} - output file
vmlinux_link()
{
local lds="${objtree}/${KBUILD_LDS}"
if [ "${SRCARCH}" != "um" ]; then
${LD} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2} \
-T ${lds} ${KBUILD_VMLINUX_INIT} \
--start-group ${KBUILD_VMLINUX_MAIN} --end-group ${1}
else
${CC} ${CFLAGS_vmlinux} -o ${2} \
-Wl,-T,${lds} ${KBUILD_VMLINUX_INIT} \
-Wl,--start-group \
${KBUILD_VMLINUX_MAIN} \
-Wl,--end-group \
-lutil -lrt -lpthread ${1}
rm -f linux
fi
}

第一眼看到有-lpthread的时候惊呆了,我大kernel还要链接pthread?后来一看明白了,原来还有用户态的内核???好高级阿~

然后仔细一看,这个和vmlinux.o的链接有啥区别???

这篇文章 讲了一点,说vmlinux是给gdb用的,vmlinux.o是用来启动的?

我们拭目以待~

本文永久更新链接地址 : http://www.linuxidc.com/Linux/2016-10/135904.htm


Viewing all articles
Browse latest Browse all 11063

Trending Articles