天天看點

linux-編譯系統介紹之kbuild(一)

Linux編譯系統

1.概述

從Linux核心2.6開始,Linux核心的編譯采用Kbuild系統,這同過去的編譯系統有很大的不同,尤其對于Linux核心子產品的編譯。在新的系統下,Linux編譯系統會兩次掃描Linux的Makefile:首先編譯系統會讀取Linux核心頂層的 Makefile,然後根據讀到的内容第二次讀取Kbuild的Makefile來編譯Linux核心。

Makefile由五個部分組成:

  • Makefile

    :根目錄Makefile,它讀取

    .config檔案

    ,并負責建立vmlinux(核心鏡像)和modules(子產品檔案).這編譯核心或子產品是,這個檔案會被首先 讀取,并根據讀到的内容配置編譯環境變量。對于核心或驅動開發人員來說,這個檔案幾乎不用任何修改。
  • .config

    :核心配置檔案(一般由make menuconfig生成)
  • arch/$(ARCH)/Makefile

    :目标處理器的Makefile是系統對應平台的Makefile。Kernel Top Makefile會包含這個檔案來指定平台相關資訊。隻有平台開發人員會關心這個檔案。
  • scripts/Makefile.*

    :所有kbuild Makefile的規則,它們包含了定義/規則等。
  • kbuild Makefiles

    :每個子目錄都有

    kbuild Makefile

    ,它們負責生成built-in或子產品化目标。(注意:kbuild Makefile是指使用kbuild結構的Makefile,核心中的大多數Makefile都是kbuild Makefile。)Kernel Makefile被解析完成後,Kbuild會讀取相關的Kbuild Makefile進行核心或子產品的編譯。Kbuild Makefile有特定的文法指定哪些編譯進核心中、哪些編譯為子產品、及對應的源檔案是什麼等。核心及驅動開發人員需要編寫這個Kbuild Makefile檔案。

2.kbuild檔案

Kbuild Makefile的檔案名不一定是Makefile,盡管推薦使用Makefile這個名字。 大多的Kbuild檔案的名字都是Makefile。為了與其他Makefile檔案相差別,你也可以指定Kbuild Makefile的名字為

Kbuild

注意:而且如果“Makefile”和“Kbuild”檔案同時存在,則Kbuild系統會使用“Kbuild”檔案。

2.1 obj-y 和 obj-m

最簡單的kbuild Makefile可以僅包含:

obj-$(CONFIG_FOO) += foo.o
           

其中

$(CONFIG_FOO)

可以等于

y

m

,它的值由

.config

檔案給出。如果

$(CONFIG_FOO)

既不是y也不是m,那麼該檔案不會被編譯和連結。

  • $(CONFIG_FOO)

    等于y時,上面語句等價于

    obj-y += foo.o

    ,它告訴kbuild在目前目錄下,有一個叫做foo.o的目标檔案,它将從

    foo.c

    或則

    foo.S

    編譯得到。
  • $(CONFIG_FOO)

    等于m時,表示

    foo.o

    需要被編譯成子產品。

2.1.1 obj-y生成built-in.o

Kbuild編譯所有的

$(obj-y)

檔案,并調用

$(LD) -r

把所有這些檔案合并到

built-in.o

檔案。這個

built-in.o

會被上一級目錄的Makefile使用,最終連結到vmlinux中。

2.1.2 目标由多個源檔案編譯得到

如果某個目标由多個源檔案編譯得到,那麼可以通過

$(<module_name>-objs)

$(<module_name>-y)

把這些源檔案告訴kbuild。Kbuild能夠識别字尾

-objs

-y

,例如:

#drivers/isdn/i4l/Makefile
obj-$(CONFIG_ISDN) += isdn.o
isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o
           

Kbuild會編譯所有

$(isdn-objs)

中的對象,并調用

"$(LD) -r"

把它們連結成

isdn.o

檔案。

下面是一個使用字尾

-y

的例子。字尾

-y

的好處是,可以使用

CONFIG_XXX

符号來決定是否加入某些源檔案(

.o

從對應的

.c

.S

編譯得到):

#fs/ext2/Makefile
obj-$(CONFIG_EXT2_FS)        += ext2.o
ext2-y                       := balloc.o bitmap.o
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o
           

2.1.3 調用子目錄Makefile

Makefile隻負責編譯目前目錄中的對象。子目錄中的對象,由子目錄中的Makefile負責。如何讓make調用子目錄中的Makefile?答案是把子目錄包含到

obj-y

obj-m

中。例如:

#fs/Makefile
obj-$(CONFIG_EXT2_FS) += ext2/
           

2.2 lib-y和lib-m

在一個目錄下,obj-y所列出的檔案,将被編譯成

built-in.o

檔案,而lib-y或lib-m所列出的檔案,将在目前目錄下生成

lib.a

注意:一般

lib-y

lib-m

隻用在

lib/

arch/*/lib

這兩個目錄中。

2.3 編譯選項變量

  • ccflags-y

    asflags-y

    ldflags-y

    這三個變量隻在目前Makefile中有效。

    補充:

    $(KBUILD_CFLAGS)

    是定義在根目錄Makefile中的變量,它适用于整個核心數。
  • subdir-ccflags-y

    subdir-asflags-y

    這兩個變量作用于目前Makefile及其所有子目錄。

CFLAGS_$@

AFLAGS_$@

這兩個變量隻在目前Makefile中有效.

$@

可以用來指定檔案名,讓不同檔案可以使用不同的編譯選項。例如:

# drivers/scsi/Makefile
CFLAGS_aha152x.o =   -DAHA152X_STAT -DAUTOCONF
CFLAGS_gdth.o    = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ /
	DGDTH_STATISTICS
CFLAGS_seagate.o =   -DARBITRATE -DPARITY -DSEAGATE_USE_ASM
           

2.4

$(src)

$(obj)

$(kecho)

  • $(src)

    指向目前Makefile所在目錄的相對路徑。
  • $(obj)

    指向用來儲存目标檔案的相對目錄。示例如下:
#drivers/scsi/Makefile
$(obj)/53c8xx_d.h: $(src)/53c7,8xx.scr $(src)/script_asm.pl
$(CPP) -DCHIP=810 - < $< | ... $(src)/script_asm.pl
           

這裡,

$(src)

等于

drivers/scsi/

$(obj)

同樣也等于

drivers/scsi/

使用“

make -s

”指令時,隻會輸出警告和錯誤資訊。

$(kecho)

能夠将其後的内容輸出到标準輸出流(一般就是顯示器),前提是沒有使用“

make -s

”。示例如下:

#arch/blackfin/boot/Makefile
$(obj)/vmImage: $(obj)/vmlinux.gz
$(call if_changed,uimage)
@$(kecho) 'Kernel: $@ is ready'
           

2.5

$(CC)

相關功能

2.5.1

as-option

ld-option

cc-option

當編譯、連結檔案時,

xx-opiton

可以用來檢查目前使用的

$(CC)

是否支援給出的編譯選項。如前者不支援,可使用後者。例如:

#arch/sh/Makefile
cflags-y += $(call as-option,-Wa$(comma)-isa=$(isa-y),)
           

如果目前的

$(CC)

不支援

-Wa$(comma)-isa=$(isa-y)

,那麼可以使用第二個編譯選項(這裡為空)。

2.5.2

cc-option-yn

cc-option-yn

用來檢查

$(CC)

是否支援給出的編譯選項。如支援,傳回y,否則傳回n。例如:

#arch/ppc/Makefile
biarch := $(call cc-option-yn, -m32)
aflags-$(biarch) += -a32
cflags-$(biarch) += -m32
           

2.5.3

cc-option-align

gcc在3.0之後改變了指定函數、循環等對齊選項的類型。

  • gcc < 3.00

    時,

    cc-option-align = -malign;

  • gcc >= 3.00

    cc-option-align = -falign

    。使用

    $(cc-option-align)

    來選擇正确的字首。例如:
KBUILD_CFLAGS += $(cc-option-align)-functions=4
           

2.5.4

cc-version

cc-ifversion

cc-fullversion

cc-version

傳回

$(CC

)版本。如

$(CC)

為gcc 3.41,那麼

cc-version

0341

。例如:

#arch/i386/Makefile
cflags-y += $(shell /
	if [ $(call cc-version) -ge 0300 ] ; then /
	echo "-mregparm=3"; fi ;)
           

cc-ifversion

在版本符合條件的前提下傳回最後一個參數。示例如下:

#fs/reiserfs/Makefile
ccflags-y := $(call cc-ifversion, -lt, 0402, -O1)
           

如果

$(CC)

版本低于4.2,那麼

ccflags-y

将等于

-O1

cc-fullversion

給出更詳細的版本資訊,例如:

#arch/powerpc/Makefile
$(Q)if test "$(call cc-fullversion)" = 	"040200" ; then /
	echo -n '*** GCC-4.2.0 cannot compile the 64-	bit powerpc ' ; /
	false ; /
	fi
           

2.5.5

cc-cross-prefix

cc-cross-prefix

用于檢查是否存在給定字首的

$(CC)

,如存在,傳回第一個比對的字首,否則傳回空。如有多個字首需要比對,各字首之間使用單個空格隔開。例如:

#arch/m68k/Makefile
ifneq ($(SUBARCH),$(ARCH))
	ifeq ($(CROSS_COMPILE),)
		CROSS_COMPILE := $(call cc-cross-prefix, m68k-linux-gnu-)
	endif
endif
           

3.編譯外部子產品

有時候我們需要在核心源代碼數的外面編譯核心子產品,編譯的基本指令是:

make -C $(KERNEL_DIR) M=`pwd` modules
           

我們可以把這個指令內建到Makefile裡,這樣我們就可以隻輸入“make”指令就可以了。

Makefile,它把

Normal Makefile

Kbuild Makefile

內建到一個檔案中了。為了差別Kbuild Makefile 和Normal Makefile,這樣我們改寫Makefile為如下形式,并且添加

Kbuild Makefile - “Kbuild”

#Makefile

ifneq ($(KERNELRELEASE),)
include "Kbuild"
else
KERNEL_DIR = /lib/modules/`uname -r`/build
MODULEDIR := $(shell pwd)

.PHONY: modules
default: modules
modules:
        make -C $(KERNEL_DIR)  M=$(MODULEDIR) modules
clean distclean:
        rm -f *.o *.mod.c .*.*.cmd *.ko
        rm -rf .tmp_versions
endif
           
#Kbuild Makefile
MODULE_NAME = helloworld
$(MODULE_NAME)-objs := hello.o
obj-m   := $(MODULE_NAME).o
# 一般不需要在Makefile裡包含如下代碼,這樣寫完全是為了相容老版本的Kbuild系統。KERNELRELEASE變量在Kernel Makefile裡定義的,是以隻有在第二次由Kbuild讀取這個Makefile檔案時才會解析到Kbuild的内容。 
ifneq ($(KERNELRELEASE),)
include "Kbuild"
else
...
endif
           

3.3 外部頭檔案

  • EXTRA_CFLAGS :

    EXTRA_CFLAGS

    可以給Kbuild系統添加外部系統頭檔案,

    EXTRA_CFLAGS += $(ext_include_path)

  • $(src)

    /

    $(obj)

    • $(src)

      是一個相對路徑,它就是

      Makefile

      Kbuild

      檔案所在的路徑。
    • 同樣

      $(obj)

      就是編譯目标儲存的路徑,預設就是源代碼所在路徑。

繼續閱讀