首頁 > 軟體

根目錄vmlinux的編譯過程

2020-06-16 17:30:41

編譯過核心的話,一般都會看到在根目錄下有個檔案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


IT145.com E-mail:sddin#qq.com