2011年12月7日 星期三

我的CJSCOPE JS-131HR新NB

為什麼會選擇這個牌子ㄋ?當然是為了荷包和性能。就是C/P值啦。目前使用至今算是非常滿意,重量真的很輕(和ULTRA BOOK比也才多個300g)。加到8G的RAM後同時開了3個虛擬機還真是非常順暢,四核心加上HT技術果然和兩年前買的Core 2 Due有明顯的效能差異。編譯linux核心也不用等完喝一杯咖啡的時間。
目前VM都只安裝Oracle VirtualBox,為什麼用它呢?優點說不盡,缺點呢:None.。好比說你已在xp使用多年的軟體,偏偏安裝到64bit的win7或Vista就問題一堆。燒錄軟體、很多Embeded的軟體也都只有for 2k/Xp。這時候你就等於買了一部實體NB,但卻擁有N部虛擬computer,只要你的RAM夠大,同時開10個OS也不怕。所以我目前已經開始再觀察512*8的DRAM IC價格。當它來到了平民價,且品質也趨於穩定就是將我的寶貝增加到16GB的時候了。說真的,玩GAME也用不到那麼多記憶體;若是您有使用VM才會建議將DRAM擴充到那麼大。
總之,花了21k擁有4核的NB,真的只有這一部。若是您考慮的是這個品牌的存續或日後的維修等問題‧‧‧我想這些都是未知數。所以我願意冒這個險。總之我使用的評價就是"爽"。
為了荷包,我也沒有添購正版Win7,所以只用試用版!!!!!

2011年11月29日 星期二

x64存儲管理機制初窺

x64存儲管理機制初窺
x64存儲管理機制,經proljmik指點,去找intel手冊和AMD手冊看,看完之後把我的理解寫下,
如有什麼理解偏差之處,還望各位不要笑話。
1.長模式(long mode
   AMD64架構的長模式中包括兩個子模式:64bit模式和compatibility模式。
   64bit模式:在此模式中虛擬位址空間使用平坦(flat)模式,段寄存器包括CS,DS,ES,SS全部清零(FS,GS除外,在線性位址計算時,提供額外base寄存器定址某些OS的系統資料結構),能為64bitOS和應用程式提供64位元定址支援。
   compatiblity模式:在此模式中使用與lagecy保護模式一樣的存儲管理模式,為的是平滑運行32bit16bit的應用程式,無需重新編譯,注意:在長模式中不支援真實模式和虛擬8086模式!!











由於在64bit模式下使用flat模式,所以無lagecy保護模式下的分段管理模式,所以虛擬位址直接對應線性位址。所以x64的分頁管理模式才是64bit下的重頭戲
  
   2.x64分頁管理模式
   如下圖,是x86-64下的所有頁大小和物理位址大小的匯總.










x64下,理論上可以通過使用PAE(實體位址擴充)分頁結構支援64bit的線性位址到52bit物理位址的映射的,但在對這一架構的首次實現中,只實現了48位元的線性位址到40位元物理位址的映射。現在你可以通過CPUID指令查看自己的cpu所支援的最大物理位址(MAXPHYDDR.
   大家一定還記得在lagecy保護模式下通過PAE功能來訪問超過32bit(36bit)的物理位址,但是在x64模式下的PAE與先前的PAE是不一樣的!!
   從線性位址到物理位址的轉換中用到了四級頁資料結構,一個新的頁表資料結構---PML4被引入用來指向頁目錄指標表。PML4只能在x64模式下使用。而且頁目錄表指標從原來的4個項增加到了512個,所以要從線性位址中分配9bit用來索引PDP表(原來是2bit)。
   PDEPTE的大小保持不變。所以我們可以看到PML4+PDP+PDE+PTE+page offset=48bit,高16bit保留(其實是用以符號擴展,下面將看到符號擴展的用途)。
   CR3指向了PML4的基底位址。
   需要注意的是在使用x64模式之前,必須使CR4.PAE=1PAE用來擴展PDEPTE64bit)。如果你在未使CR4.PAE=1之前,企圖開啟x64模式的話,會引發#GP異常。
   我們從上圖還可以看到頁大小的選擇完全取決於PDE.PS(但我並沒有在文檔中看到ps位,是直接在文檔中置10),無視CR4.PSE的存在。
   2.1x64分頁管理(4kb
   上圖再說




















sign extended--符號擴展位元
   PML4 entry--在線性位址3947bit用於索引PML4 entry,指向PDP
   PDP entry--在線性位址的3038bit用來索引PDP entry,指向PDE
   PDE entry--在線性位址的2129bit用來索引PDEentry,指向PTE
   PTE entry--在線性位址的1220bit用來索引PTE entry,指向page offset
   page offset--在線性位址的011bit提供在頁中的offset
   2.2x64分頁管理(2mb




















我們可以看到與4kb的頁相比少了PTE,page offset從原來的12bit增加到了21bit下面我們來看看各分頁表項目的內部結構.
























(4kB)上圖看到,用上述方法可以得到512×512×512×512=2^36個頁面,2^36*4kb=2^48個線性位址.



















上圖看到,用上述方法可以得到512×512×512=2^27個頁面,2^27*2mb=2^48個線性位址 0bitP位,即存在位,表示由項所指的頁面或頁表當前是否載入到了物理記憶體中,如不存在,可以通過引發#PF異常從磁片中載入到物理記憶體中
   1bitR/W,即讀寫位,為頁表指定讀寫特權,當置位時可讀可寫;清零是唯讀
   2bit:使用者/系統位元,即為頁表指定系統還是普通使用者特權
   3bit:頁面通寫,指定快取記憶體寫策略是回寫還是通寫
   4bit:快取記憶體禁止位,當置位元時禁止相關的頁面和頁表進行快取記憶體,反之,可以
   5bit:訪問位,當置位時表示已經被訪問,反之,沒有
   6bit(非PTEPDE2MB)):可用位
   6bitPTEPDE2MB)):D位,dirty,即髒位,置位元時表示頁面被寫過
   78bit(非PTEPDE2MB)):0
   avail:系統程式師可用
   G:全域標誌
   PAT:頁表屬性
   各種base address是用來得到下一級表的基底位址的,最後的物理位址=page base address*2^12(2^21)+2^12(2^21),page base address的大小取決於你的cpu的最大物理位址了。
   3.最後,我來看一看sign extended會形成什麼效果。
   由於sign extended的作用會將線性位址分成相同大小的兩段,從000007FFF`FFFFFFFF,以及從 FFFF8000`00000000FFFFFFFF`FFFFFFFF總計256TB的地址範圍,這非常符合作業系統的習慣。
   這種“古怪”的規則為日後擴展到真正的64位定址保留了一個重要的特性:很多的作業系統(包括但不限於Windows NT系列)將位址空間的高半部分(被稱作內核空間)留給自己,將低半部分(使用者空間)留給應用程式碼、用戶態棧、堆和其他數據區。這種設計保證了每一個符合AMD64的實現都擁有兩個記憶體片段:低半段從00000000`00000000開始,隨著更多的虛擬位址位元變得可用而“向上生長”;高半部分被“懸掛”在位址空間的頂部而“向下生長”。同樣,將未被使用的位址位元內容固定下來防止被作業系統用作標誌位元、特權級標號等其他用途,是為了避免當架構擴展至52,56, 60 64位的時候出現問題。















參考資料:
   1.http://zh.wikipedia.org/zh-cn/X86-64
   2.intel手冊
   3AMD手冊

2011年10月6日 星期四

如何使用Bochs來Debug GRUB2 & Kernel

参考以下文章:
http://www.linuxsir.org/bbs/thread356982.html
http://man.he.net/man8/grub-setup
http://blog.csdn.net/zhouyelihua/article/details/6683437
http://www.gnu.org/software/grub/manual/grub.html#Installing-GRUB-using-grub_002dinstall
http://read.pudn.com/downloads153/ebook/675818/linux-0.11-040304/bochsrc-hd-new.bxrc__.htm

由於bochs內建調試功能, 且支援gdb, 用它調試內核會很方便.
假設你已經build好grub,這裡我舉的例子是使用grub1.99,若還沒有Build,現在正是時候:
1 ./configure CFLAGS=-g
2 make
3 sudo make install 
在ubuntu下預設是安裝到usr/local/lib/grub/i386-pc,別擔心會影響到您的開機MBR,因為make install只是將一些GRUB的各個executable Binary及mod模組存放到指定的prefix,除非你執行grub-install和update-grub 等指令才有可能去動到MBR。
以下指令請使用Root身份執行
1.1構建磁碟影像檔dd if=/dev/zero of=hd0.img count=$((63*16*100))
用這個命令可以構建一個50MB左右的磁片鏡像, 輸出結果如下:
100800+0 records in
100800+0 records out
51609600 bytes (52 MB) copied, 0.734578 s, 70.3 MB/s
注意count必須為63*16的倍數, 否則bochs識別硬碟會有問題.
1.2 掛載磁碟影像losetup /dev/loop0 hd0.img
這個命令可以將檔綁定到一個loop設備. 如果/dev/loop0不存在, 可以嘗試 modprobe loop.
然後進行設備初始化:cfdisk -s 63 -h 16 /dev/loop0
畫面如下橘色文字:
                         cfdisk (util-linux-ng 2.17.2)

                               硬碟: /dev/loop0
                      大小: 51609600 位元組(B), 51 MB
           磁頭: 16   每一磁軌的磁區數: 63   磁柱數: 100

    分割區名稱  屬性旗標   分割區型態 檔案系統型態     標記名稱       大小 (MB)
 ------------------------------------------------------------------------------
                            主要/邏輯 剩餘空間                            51.61

     [  說明  ]    [  新增  ]    [  列印  ]    [  離開  ]    [  單位  ]
     [  寫入  ]

                    由剩餘空間切一個新的分割區

只創建一個主要磁碟分割就可以,並設定 [使可開機] , 寫入後, 用命令fdisk檢查結果。
指令:fdisk -lu /dev/loop0 結果畫面如下:當然Linux版本不同,畫面難免大同小異
磁碟 /dev/loop0: 51 MB,51609600 位元組
16 磁頭,63 磁區/磁軌,100 磁柱,總計 100800 磁區
單位 = 磁區 之於 1 * 512 = 512 位元組
磁區大小 (邏輯/實體):512 位元組 / 512 位元組
I/O 大小 (最小/最佳化):512 位元組 / 512 位元組
磁碟識別碼:0x00000000

所用裝置 開機      開始         結束      區塊   識別號  系統
/dev/loop0p1   *          63      100799       50368+  83  Linux
接著將分區1掛載到/dev/loop1:losetup /dev/loop1 hd0.img -o $((63*512))
格式化/dev/loop1ext3格式.:mkfs.ext3 /dev/loop1
mnt下創建img目錄, 做以後維護用:mkdir -p /mnt/img
loop1掛載到/mnt/img:mount /dev/loop1 /mnt/img/
安裝引導程式. 因為我狂熱傾向於模組化架構, 所以選擇GRUB2.
mkdir /mnt/img/boot
cp -r /usr/lib/grub/i386-pc/ /mnt/img/boot/grub
這個/usr/lib/grub/i386-pc是你的GRUB2安裝路徑,像我的1.98及1.99版在ubuntu下是安裝在以下路徑:/usr/local/lib/grub/i386-pc
接著生成一個core.img, biosdisk模組負責讀取磁片, part_msdos模組負責處理MBR,ext2模組負責讀取ext3分區:
cd /mnt/img/boot/grub/
grub-mkimage -o core.img biosdisk part_msdos ext2   #1.98版使用這個指令
grub-mkimage -O i386-pc -o core.img biosdisk part_msdos ext2  #1.99版使用這個指令
注意,這個core.img還不是個可開機用的影像檔。它並未包含boot.img及diskboot.img。
接著安裝grub2(hd0), 根目錄在(hd0,1)
echo "(hd0)   /dev/loop0" > ./device.map
grub-setup -m ./device.map -d /mnt/img/boot/grub/ -r '(hd0,1)' '(hd0)'
其實上面這2行也可以用下面一行取代
grub-setup -d /mnt/img/boot/grub/ --force /dev/loop0
檢查一下/dev/loop0安裝成果:
hexdump -C /dev/loop0 | less
00000180  7d e8 2e 00 cd 18 eb fe  47 52 55 42 20 00 47 65  |}.......GRUB .Ge|
00000190  6f 6d 00 48 61 72 64 20  44 69 73 6b 00 52 65 61  |om.Hard Disk.Rea|
清理一下
離開終端,重新開一個終端,否則umount會失敗,若你不是以root執行的話
umount /mnt/img
losetup -d /dev/loop1
losetup -d /dev/loop0
2.1建立.bochsrc檔如下:說明一下,在linux下預設是讀取.bochsrc這個檔名,所以你若創建一個叫bochs.bxrc或其他檔名,則等一下開啟bochs時,指令如下:
bochs -q -f bochs.bxrc,否則只須下bochs -q
###############################################################
# bochsrc.bxrc file for kenos disk image.
###############################################################
megs: 32
romimage: file=/usr/share/bochs/BIOS-bochs-legacy, address=0xf0000
vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest
# hard disk
ata0-master: type=disk, mode=flat, path="/home/benson/working_benson/DBG_grub/hd0.img", cylinders=100, heads=16, spt=63
#ata0-slave: type=cdrom, path="/home/marc/setups/slax-6.0.9.iso", status=inserted
# choose the boot disk.
boot: c
# where do we send log messages?
log: bochsout.txt
# disable the mouse, since DLX is text only
mouse: enabled=0
keyboard_mapping: enabled=1, map=/usr/share/bochs/keymaps/x11-pc-us.map
#keyboard_mapping: enabled=1, map=$BXSHARE/keymaps/x11-pc-fr.map
#keyboard_mapping: enabled=1, map=$BXSHARE/keymaps/x11-pc-de.map
#keyboard_mapping: enabled=1, map=$BXSHARE/keymaps/x11-pc-es.map
#cpu: count=1, ips=10000000
# Make the PIT emulated by bochs tick at the correct interval. Note that
# enabling this feature sacrifices reproducibility, which may be a problem
# at times when tracking a specific bug.
#clock: sync=realtime, time0=utc
# For debugging with gdb.
# Keep this line when using bochs-gdb.
# Comment it out when using bochs-dbg
# See http://bochs.sourceforge.net/doc/docbook/user/bochsrc.html#BOCHSOPT-GDBSTUB
#gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0
使用bochs開啟hd0.img
bochs -f bochsrc.bxrc 雖然會顯示unknown filesystem
















解決辦法:
grub rescue>set prefix=(hd0,1)/boot/grub
grub rescue>root=(hd0,1)
grub rescue>insmod normal
grub rescue>normal
這樣就可以回到grub> 或選單狀態了。
此時也可以輸入help及其他command line指令了。
接著我們來試試如何使用gdb來debug GRUB2:
先在bochsrc.bxrc最後面加入幾行來使gdb connect to Bochs:

# Attach ne2000 to PCI bus, so that GRUB Legacy detects it automatically

i440fxsupport: enabled=1, slot1=ne2k
# Enable the ne2000 NIC and the builtin TFTP server
ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=vnet, ethdev="/"
# Enable the GDB remote stub
gdbstub: enabled=1

現在先開啟另一個終端輸入gdb,並重新啟動bochs -qf bochsrc.bxrc指令
gdb會出現如下畫面:藍色字為輸入指令
GNU gdb (Ubuntu/Linaro 7.2-1ubuntu11) 7.2
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
0x0000fff0 in ?? ()
(gdb)file kernel.exec
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from /home/benson/kernel.exec...done.

(gdb) b
No default breakpoint address now.
(gdb) b main
Breakpoint 1 at 0xc4fd: file kern/main.c, line 175.
(gdb) c
Continuing.
Program received signal SIGTRAP, Trace/breakpoint trap.
0x0000e05c in ?? ()
(gdb) c
Continuing.
Breakpoint 1, grub_main () at kern/main.c:175
warning: Source file is more recent than executable.
175
(gdb) list
170 void
171 grub_main (void)
172 {
173  /* First of all, initialize the machine.  */
174  grub_machine_init ();
175
176  /* Hello.  */
177  grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT);
178  grub_printf ("Welcome to GRUB!Benson Debug Here!\n\n");
179  grub_setcolorstate (GRUB_TERM_COLOR_STANDARD);
(gdb) 
執行過程如下圖所示:














這張圖是修改過source code,所以下一張圖會多出Benson Debug Here!字樣。















若是你的gdb無法connect到bochs,可能你須要重新build一次Bochs,動作如下:
註:--enable-gdb-stub和--enable-debugger是互斥的
1 ./configure --enable-gdb-stub
2 make
3 sudo make install
update.....

2011年9月27日 星期二

linux2.6 makefiles.txt學習及實例分析

linux2.6 makefiles.txt學習及實例分析 (2008-08-28 10:50)


本篇blog主要分為四部分,地一部分和第二部分主要是參考網上的文章,第三部分為自己在學習過程中總結的一些知識,第四部分想自己編寫一個簡單的Makefile,以鞏固學習成果!



本篇blog目的:通過對Makefile的學習,進一步理解linux內核如何通過makefile實現對make過程的自動化,掌握makefile語言編寫規則,最終實現自己能夠編寫出makefile檔。



本篇blog預計完成時間一個禮拜。



這篇文章主要是參考資料,不敢邀功,現給出原地址和參考書籍,感謝這兩位大蝦的慷慨分享。

參考文件:

http://forum.eepw.com.cn/forum/main?url=http%3A%2F%2Fbbs.edw.com.cn%2Fthread%2F128730%2F1

http://blog.chinaunix.net/u2/66601/showart_1150788.html Author:cyliu

《嵌入式linux系統開發技術詳解-基於arm》孫紀坤 張小全 人民郵電出版社

________________________________________

第一部分:對makefiles.txt的學習

內核目錄documention/kbuild/makefiles.txt中文版的翻譯

This document describes the Linux kernel Makefiles

=== 目錄

=== 1 概述

=== 2 用戶與作用

=== 3 Kbuild文件

--- 3.1 目標定義

--- 3.2 編譯進內核 - obj-y

--- 3.3 編譯可裝載模組 - obj-m

--- 3.4 輸出的符號

--- 3.5 目標庫檔 - lib-y

--- 3.6 遞迴躺下訪問目錄

--- 3.7 編輯標誌

--- 3.8 命令行的依賴關係(原文中沒有寫:-))

--- 3.9 跟蹤依賴

--- 3.10 特殊規則

--- 3.11 $(CC) 支援的函數



=== 4 本機程式支援

--- 4.1 簡單的本機程式

--- 4.2 複合的本機程式

--- 4.3 定義共用庫

--- 4.4 使用用C++編寫的本機程式

--- 4.5 控制本機程式的編譯選項

--- 4.6 編譯主機程式時

--- 4.7 使用 hostprogs-$(CONFIG_FOO)



=== 5 Kbuild清理



=== 6 架構Makefile

--- 6.1 調整針對某一具體架構生成的鏡像

--- 6.2 將所需文件加到 archprepare 中

--- 6.3 遞迴下向時要訪問的目錄列表

--- 6.4 具體架構的啟動鏡像

--- 6.5 構造非Kbuild目標

--- 6.6 構建啟動鏡像的命令

--- 6.7 Kbuild自定義命令

--- 6.8 聯接器預處理腳本



=== 7 Kbuild 變數

=== 8 Makefile語言

=== 9 關於作者

=== 10 TODO

________________________________________



=== 1 概述



Linux內核的Makefile分為5個部分:



Makefile 頂層Makefile

.config 內核配置檔

arch/$(ARCH)/Makefile 具體架構的Makefile

scripts/Makefile.* 通用的規則等。面向所有的Kbuild Makefiles。

kbuild Makefiles 內核源代碼中大約有500個這樣的檔



頂層Makefile閱讀的.config檔,而該檔是由內核配置程式生成的。



頂層Makefile負責制作:vmlinux(內核檔)與模組(任何模組檔)。製作的過程主要是通過遞迴向下訪問子目錄的形式完成。並根據內核配置檔確定訪問哪些子目錄。頂層Makefile要原封不動的包含一具體架構的Makefile(由頂層Makefile語句include $(srctree)/arch/$(ARCH)/Makefile指明),其名字類似於 arch/$(ARCH)/Makefile。該架構Makefile向頂層Makefile提供其架構的特別資訊。



每一個子目錄都有一個Kbuild Makefile檔,用來執行從其上層目錄傳遞下來的命令。Kbuild Makefile從.config檔中提取資訊,生成Kbuild完成內核編譯所需的檔列表。



scripts/Makefile.*包含了所有的定義、規則等資訊。這些檔被用來編譯基於kbuild Makefile的內核。(**有點不通**)



=== 2 用戶與作用



可以將人們與內核Makefile的關係分成4類。



*使用者* 編譯內核的人。他們只是鍵入"make menuconfig"或"make"這樣的命令。一般

情況下是不會讀或編輯任何內核Makefile(或者任何的原始檔案)。



*普通開發人員* 這是一群工作在內核某一功能上的人,比如:驅動開發,檔系統或

網路協定。他們所需要維護的只是他們所工作的子系統的Kbuild Makefile。為了提高

工作的效率,他們也需要對內核Makefile有一個全面的認識,並且要熟悉Kbuild的介面





*架構開發人員* 這是一些工作在具體架構,比如sparc 或者ia64,上面的人。架構開

發者需要在熟悉kbuild Makefile的同時,也要熟悉他所工作架構的Makefile。



*Kbuild開發者* 維護Kbuild系統的人。他們需要知曉內核Makefile的方方面面。



該文件是為普通開發人員與架構開發人員所寫。





=== 3 Kbuild文件



大部分內核中的Makefile都是使用Kbuild組織結構的Kbuild Makefile。這章介紹了

Kbuild Makefile的語法。

Kbuild檔傾向於"Makefile"這個名字,"Kbuild"也是可以用的。但如果"Makefile"

"Kbuild"同時出現的話,使用的將會是"Kbuild"檔。



3.1節 目標定義是一個快速介紹,以後的幾章會提供更詳細的內容以及實例。



--- 3.1 目標定義



目標定義是Kbuild Makefile的主要部分,也是核心部分。主要是定義了要編

譯的檔,所有的選項,以及到哪些子目錄去執行遞迴操作。



最簡單的Kbuild makefile 只包含一行:



例子:

obj-y += foo.o



該例子告訴Kbuild在這目錄裏,有一個名為foo.o的目標檔。foo.o將從foo.c

或foo.S檔編譯得到。



如果foo.o要編譯成一模組,那就要用obj-m了。所採用的形式如下:



例子:

obj-$(CONFIG_FOO) += foo.o



$(CONFIG_FOO)可以為y(編譯進內核) 或m(編譯成模組)。如果CONFIG_FOO不是y

和m,那麼該檔就不會被編譯聯接了。



--- 3.2 編譯進內核 - obj-y



Kbuild Makefile 規定所有編譯進內核的目標檔都存在$(obj-y)列表中。而

這些列表依賴內核的配置。



Kbuild編譯所有的$(obj-y)檔。然後,調用"$(LD) -r"將它們合併到一個

build-in.o文件中。稍後,該build-in.o會被其父Makefile聯接進vmlinux中。



$(obj-y)中的檔是有順序的。列表中有重複項是可以的:當第一個文件被聯

接到built-in.o中後,其餘文件就被忽略了。



聯接也是有順序的,那是因為有些函數(module_init()/__initcall)將會在啟

動時按照他們出現的順序進行調用。所以,記住改變聯接的順序可能改變你

SCSI控制器的檢測順序,從而導致你的硬碟資料損害。



例子:

#drivers/isdn/i4l/Makefile

# Makefile for the kernel ISDN subsystem and device drivers.

# Each configuration option enables a list of files.

obj-$(CONFIG_ISDN) += isdn.o

obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o



--- 3.3 編譯可裝載模組 - obj-m



$(obj-m) 列舉出了哪些檔要編譯成可裝載模組。



一個模組可以由一個檔或多個檔編譯而成。如果是一個原始檔案,Kbuild Makefile只需簡單的將其加到$(obj-m)中去就可以了。



例子:

#drivers/isdn/i4l/Makefile

obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o



注意:此例中 $(CONFIG_ISDN_PPP_BSDCOMP) 的值為'm'



如果內核模組是由多個原始檔案編譯而成,那你就要採用上面那個例子一樣的方法去聲明你所要編譯的模組。



Kbuild需要知道你所編譯的模組是基於哪些檔,所以你需要通過變數 $(-objs)來告訴它。



例子:

#drivers/isdn/i4l/Makefile

obj-$(CONFIG_ISDN) += isdn.o

isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o



在這個例子中,模組名將是isdn.o,Kbuild將編譯在$(isdn-objs)中列出的所有檔,然後使用"$(LD) -r"生成isdn.o。



Kbuild能夠識別用於組成目標檔的尾碼-objs和尾碼-y。這就讓Kbuild

Makefile可以通過使用 CONFIG_ 符號來判斷該物件是否是用來組合物件的。



例子:

#fs/ext2/Makefile

obj-$(CONFIG_EXT2_FS) += ext2.o

ext2-y := balloc.o bitmap.o

ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o



在這個例子中,如果 $(CONFIG_EXT2_FS_XATTR) 是 'y',xattr.o將是複合物件 ext2.o的一部分。



注意:當然,當你要將其編譯進內核時,上面的語法同樣適用。所以,如果你的 CONFIG_EXT2_FS=y,那Kbuild會按你所期望的那樣,生成 ext2.o文件,然後將其聯接到 built-in.o中。



--- 3.4 輸出的符號



在Makefile中,沒有對模組輸出的符號有特殊要求。



--- 3.5 目標庫檔 - lib-y



在 obj-* 中所列檔是用來編譯模組或者是聯接到特定目錄中的 built-in.o。同樣,也可以列出一些將被包含在lib.a庫中的文件。在 lib-y 中所列出的檔用來組成該目錄下的一個庫檔。



在 obj-y 與 lib-y 中同時列出的檔,因為都是可以訪問的,所以該檔是不會被包含在庫檔中的。同樣的情況,lib-m 中的檔就要包含在lib.a庫文件中。



注意,一個Kbuild makefile可以同時列出要編譯進內核的檔與要編譯成庫的檔。所以,在一個目錄裏可以同時存在 built-in.o 與 lib.a 兩個文件。



例子:

#arch/i386/lib/Makefile

lib-y := chechsum.o delay.o



這將由 checksum.o 和delay.o 兩個檔創建一個庫檔 lib.a。為了讓 Kbuild 真正認識到這裏要有一個庫檔 lib.a 要創建,其所在的目錄要加到 libs-y 列表中。 還可參考"6.3 遞迴下向時要訪問的目錄列表" lib-y 使用一般限制在 lib/ 和 arch/*/lib 中。



--- 3.6 遞迴向下訪問目錄



一個Makefile只對編譯所在目錄的物件負責。在子目錄中的檔的編譯要由其所在的子目錄的Makefile來管理。只要你讓Kbuild知道它應該遞迴操作,那麼該系統就會在其子目錄中自動的調用 make 遞迴操作。



這就是 obj-y 和 obj-m 的作用。

ext2 被放的一個單獨的目錄下,在fs目錄下的Makefile會告訴Kbuild使用下面的賦值進行向下遞迴操作。



例子:

#fs/Makefile

obj-$(CONFIG_EXT2_FS) += ext2/



如果 CONFIG_EXT2_FS 被設置為 'y'(編譯進內核)或是'm'(編譯成模組),相應的 obj- 變數就會被設置,並且Kbuild就會遞迴向下訪問 ext2 目錄。Kbuild只是用這些資訊來決定它是否需要訪問該目錄,而具體怎麼編譯由該目錄中的Makefile來決定。



將 CONFIG_ 變數 設置成目錄名是一個好的編程習慣。這讓Kbuild在完全忽略那些相應的 CONFIG_ 值不是'y'和'm'的目錄。



--- 3.7 編輯標誌



EXTRA_CFLAGS, EXTRA_AFLAGS, EXTRA_LDFLAGS, EXTRA_ARFLAGS



所有的 EXTRA_ 變數只在所定義的 Kbuild Makefile 中起作用。EXTRA_ 變數可

以在Kbuild Makefile中所有命令中使用。



$(EXTRA_CFLAGS) 是用 $(CC) 編譯C原始檔案時的選項。



例子:

# drivers/sound/emu10kl/Makefile

EXTRA_CFLAGS += -I$(obj)

ifdef DEBUG

EXTRA_CFLAGS += -DEMU10KL_DEBUG

endif





該變數是必須的,因為頂層Makefile擁有變數 $(CFLAGS) 並用來作為整個源

代碼樹的編譯選項。



$(EXTRA_AFLAGS) 也是一個針對每個目錄的選項,只不過它是用來編譯彙編

源代碼的。



例子:

#arch/x86_64/kernel/Makefile

EXTRA_AFLAGS := -traditional





$(EXTRA_LDFLAGS) 和 $(EXTRA_ARFLAGS)分別與 $(LD)和 $(AR)類似,只不

過,他們是針對每個目錄的。



例子:

#arch/m68k/fpsp040/Makefile

EXTRA_LDFLAGS := -x



CFLAGS_$@, AFLSGA_$@



CFLAGS_$@ 和 AFLAGS_$@ 只能在當前Kbuild Makefile中的命令中使用。



$(CFLAGS_$@) 是 $(CC) 針對每個檔的選項。$@ 表明了具體操作的檔。



例子:

# 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



以上三行分別設置了aha152x.o,gdth.o 和 seagate.o的編輯選項。



$(AFLAGS_$@) 也類似,只不是是針對組合語言的。



例子:

# arch/arm/kernel/Makefile

AFLAGS_head-armv.o := -DTEXTADDR=$(TEXTADDR) -traditional

AFLAGS_head-armo.o := -DTEXTADDR=$(TEXTADDR) -traditional



--- 3.9 跟蹤依賴



Kbuild 跟蹤在以下方面依賴:

1) 所有要參與編譯的檔(所有的.c 和.h文件)

2) 在參與編譯檔中所要使用的 CONFIG_ 選項

3) 用於編譯目標的命令行



因此,如果你改變了 $(CC) 的選項,所有受影響的檔都要重新編譯。



--- 3.10 特殊規則



特殊規則就是那Kbuild架構不能提供所要求的支援時,所使用的規則。一個典型的例子就是在構建過程中生成的頭檔。另一個例子就是那些需要採用特殊規則來準備啟動鏡像。



特殊規則的寫法與普通Make規則一樣。Kbuild並不在Makefile所在的目錄執行,所以所有的特殊規則都要提供參與編譯的檔和目標檔的相對路徑。



在定義特殊規則時,要使用以下兩個變數:



$(src)

$(src) 表明Makefile所在目錄的相對路徑。經常在定位源代碼樹中的檔時,使用該變數。



$(obj)

$(obj) 表明目標檔所要存儲目錄的相對路徑。經常在定位所生成的檔時,使用該變數。



例子:

#drivers/scsi/Makefile

$(obj)/53c8xx_d.h: $(src)/53c7,8xx.scr $(src)/script_asm.pl

$(CPP) -DCHIP=810 - < $<
... $(src)/script_asm.pl



這就是一個特殊規則,遵守著make所要求的普通語法。目標檔依賴於兩個原始檔案。用$(obj)來定位目標檔,用$(src)來定位原始檔案(因為它們不是我們生成的檔)。



--- 3.11 $(CC) 支援的函數



內核可能由多個不同版本的$(CC)編譯,而每個版本都支援一不同的功能集與選項集。Kbuild提供了檢查 $(CC) 可用選項的基本功能。$(CC)一般情況下是gcc編譯器,但也可以使用其他編譯器來代替gcc。



as-option

as-option,當編譯彙編文件(*.S)時,用來檢查 $(CC) 是否支援特定選項。如果第一個選項不支援的話,可選的第二個選項可以用來指定。



例子:

#arch/sh/Makefile

cflags-y += $(call as-option,-Wa$(comma)-isa=$(isa-y),)



在上面的例子裏,如果 $(CC) 支援選項 -Wa$(comma)-isa=$(isa-y),cflags-y就會被賦予該值。第二個參數是可選的,當第一個參數不支援時,就會使用該值。



ld-option

ld-option,當鏈結目標檔時,用來檢查 $(CC) 是否支援特定選項。如果第一個選項不支援的話,可選的第二個選項可以用來指定。



例子:

#arch/i386/kernel/Makefile

vsyscall-flags += $(call ld-option, -Wl$(comma)--hash-style=sysv)



在上面的例子中,如果 $(CC)支援選項 -Wl$(comma)--hash-style=sysv,ld-option就會被賦予該值。第二個參數是可選的,當第一個參數不支援時,就會使用該值。





cc-option

cc-option,用來檢查 $(CC) 是否支援特定選項,並且不支援使用可選的第二項。



例子:

#arch/i386/Makefile

cflags-y += $(call cc-option,-march=pentium-mmx,-march=i586)



在上面的例子中,如果 $(CC)支援選項 -march=pentium-mmx,cc-option就會被賦予該值,否則就賦 -march-i586。cc-option的第二個參數是可選的。如果忽略的話,當第一個選項不支援時,cflags-y 不會被賦值。



cc-option-yn

cc-option-yn,用來檢查 gcc 是否支援特定選項,返回'y'支持,否則為'n'。



例子:

#arch/ppc/Makefile

biarch := $(call cc-option-yn, -m32)

aflags-$(biarch) += -a32

cflags-$(biarch) += -m32



在上面的例子裏,當 $(CC) 支援 -m32選項時,$(biarch)設置為y。當

$(biarch) 為y時,擴展的 $(aflags-y) 和 $(cflags-y)變數就會被賦值為

-a32 和 -m32。



cc-option-align

gcc版本大於3.0時,改變了函數,迴圈等用來聲明記憶體對齊的選項。當用到對齊選項時,$(cc-option-align) 用來選擇正確的首碼:

gcc < 3.00

cc-option-align = -malign

gcc >= 3.00

cc-option-align = -falign



例子:

CFLAGS += $(cc-option-align)-functions=4



在上面的例子中,選項 -falign-funcions=4 被用在gcc >= 3.00的時候。對於小於3.00時, 使用 -malign-funcions=4 。



cc-version

cc-version以數學形式返回 $(CC) 編譯器的版本號。

其格式是:,二者都是數學。比如,gcc 3.41 會返回 0341。當某版本的$(CC) 在某方面有缺陷時,cc-version就會很有用。比如,選項-mregparm=3 雖然會被gcc接受,但其實現是有問題的。



例子:

#arch/i386/Makefile

cflags-y += $(shell \

if [ $(call cc-version) -ge 0300 ] ; then \

echo "-meregparm=3"; fi ;)



在上面的例子中,-mregparm=3只會在gcc的版本號大於等於3.0的時候使用。



cc-ifversion

cc-ifversion測試 $(CC) 的版本號,如果版本運算式為真,就賦值為最後的參數。



例子:

#fs/reiserfs/Makefile

EXTRA_CFLAGS := $(call cc-ifversion, -lt, 0402, -O1)



在這個例子中,如果 $(CC) 的版本小於4.2,EXTRA_CFLAGS就被賦值 -O1。cc-ifversion 可使用所有的shell 操作符:-eq,-ne,-lt,-le,-gt,和-ge。第三個參數可以像上面例子一樣是個文本,但也可以是個擴展的變數或巨集。



/*這段翻譯的不好*/

=== 4 本機程式支援



Kbuild 支持編譯那些將在編譯階段使用的可執行檔。為了使用該可執行檔,要將編譯分成二個階段。



第一階段是告訴Kbuild存在哪些可執行檔。這是通過變數 hostprogs-y來完成的。

第二階段是添加一個對可執行檔的顯性依賴。有兩種方法:增加依賴關係到一個規則

中,或是利用變數 $(always)。

以下是詳細敍述.



--- 4.1 簡單的本機程式



在編譯內核時,有時會需要編譯並運行一個程式。

下面這行就告訴了kbuild,程式bin2hex應該在本機上編譯。



例子:

hostprogs-y := bin2hex



在上面的例子中,Kbuild假設bin2hex是由一個與其在同一目錄下,名為

bin2hex.c 的C語言原始檔案編譯而成的。



--- 4.2 複合的本機程式



本機程式可以由多個檔編譯而成。

所使用的語法與內核的相應語法很相似。

$(-objs) 列出了聯接成最後的可執行檔所需的所有目標檔。



例子:

#scripts/lxdialog/Makefile

hostprogs-y := lxdialog

lxdialog-objs := checklist.o lxdialog.o



副檔名為.o的文件是從相應的.c檔編譯而來的。在上面的例子中,checklist.c 編譯成了checklist.o,lxdialog.c編譯成了lxdialog.o。最後,兩個.o檔聯接成了一可執行檔,lxdialog。注意:語法 -y不是只能用來生成本機程式。



--- 4.3 定義共用庫



副檔名為so的檔稱為共用庫,被編譯成位置無關物件。Kbuild也支援共用庫,但共用庫的使用很有限。在下面的例子中,libconfig.so共用庫用來聯接到可執行檔 conf中。



例子:

#scripts/kconfig/Makefile

hostprogs-y := conf

conf-objs := conf.o libkconfig.so

libkcofig-objs := expr.o type.o



共用庫檔經常要求一個相應的 -objs,在上面的例子中,共用庫libkconfig是由 expr.o 和 type.o兩個檔組成的。expr.o 和 type.o 將被編譯成位置無關碼,然後聯接成共用庫檔libkconfig.so。C++並不支援共用庫。



--- 4.4 使用用C++編寫的本機程式



kbuild也支援用C++編寫的本機程式。在此專門介紹是為了支持kconfig,並且

在一般情況下不推薦使用。



例子:

#scripts/kconfig/Makefile

hostprogs-y := qconf

qconf-cxxobjs := qconf.o



在上面的例子中,可執行檔是由C++檔 qconf.cc編譯而成的,由$(qconf-cxxobjs)來標識。



如果qconf是由.c和.cc一起編譯的,那麼就需要專門來標識這些檔了。



例子:

#scripts/kconfig/Makefile

hostprogs-y := qconf

qconf-cxxobjs := qconf.o

qconf-objs := check.o



--- 4.5 控制本機程式的編譯選項



當編譯本機程式時,有可能使用到特殊選項。程式經常是利用$(HOSTCC)編譯,其選項在 $(HOSTCFLAGS)變數中。可通過使用變數 HOST_EXTRACFLAGS,影響所有在Makefile檔中要創建的主機程式。



例子:

#scripts/lxdialog/Makefile

HOST_EXTRACFLAGS += -I/usr/include/ncurses

為一單個檔設置選項,可按形式進行:



例子:

#arch/ppc64/boot/Makefile

HOSTCFLAGS_pinggyback.o := -DKERNELBASE=$(KERNELBASE)

同樣也可以給聯接器聲明一特殊選項。



例子:

#scripts/kconfig/Makefile

HOSTLOADLIBES_qconf := -L$(QTDIR)/lib



當聯接qconf時,將會向聯接器傳遞附加選項 "-L$(QTDIR)/lib"。



--- 4.6 編譯主機程式時



Kbuild只在需要時編譯主機程式。

有兩種方法:



(1) 在一具體的規則中顯性列出所需要的檔



例子:

#drivers/pci/Makefile

hostprogs-y := gen-devlist

$(obj)/devlist.h: $(src)/pci.ids $(obj)/gen-devlist

( cd $(obj); ./gen-devlist ) < $<



目標 $(obj)/devlist.h 是不會在 $(obj)/gen-devlist 更新之前編譯的。注意

在該規則中所有有關主機程式的命令必須以$(obj)開頭。



(2) 使用 $(always)

當Makefile要編譯主機程式,但沒有適合的規則時,使用 $(always)。



例子:

#scripts/lxdialog/Makefile

hostprogs-y := lxdialog

always := $(hostprogs-y)



這就是告訴Kbuild,即使沒有在規則中聲明,也要編譯 lxdialog。



--- 4.7 使用 hostprogs-$(CONFIG_FOO)



一個典型的Kbuild模式如下:



例子:

#scripts/Makefile

hostprogs-$(CONFIG_KALLSYMS) += kallsyms



Kbuild 知道 'y' 是編譯進內核,而 'm' 是編譯成模組。所以,如果配置符號是'm',Kbuild仍然會編譯它。換句話說,Kbuild處理 hostprogs-m 與 hostprogs-y 的方式是完全一致的。只是,如果不用 CONFIG,最好用hostprogs-y。



=== 5 Kbuild清理(clean)



"make clean"刪除幾乎所有的在編譯內核時生成的檔,包括了主機程式在內。Kbuild 通過列表 $(hostprogs-y),$(hostprogs-m),$(always),$(extra-y) 和$(targets) 知道所要編譯的目標。這些目標檔都會被 "make clean" 刪除。另外,在"make clean"還會刪除匹 "*.oas]","*.ko" 的檔,以及由 Kbuild生成的輔助檔。



輔助文件由 Kbuild Makefile 中的 $(clean-files) 指明。



例子:

#drivers/pci/Makefile

clean-files := devlist.h classlist.h



當執行 "make clean" 時,"devlist.h classlist.h"這兩個檔將被刪除。如果不使用絕對路徑(路徑以'/'開頭)的話,Kbuild假設所要刪除的檔與Makefile在同一個相對路徑上。



要刪除一目錄:

例子:

#scripts/package/Makefile

clean-dirs := $(objtree)/debian/



這就會刪除目錄 debian,包括其所有的子目錄。如果不使用絕對路徑(路徑以'/'開頭)的話,Kbuild假設所要刪除的目錄與Makefile在同一個相對路徑上。



一般情況下,Kbuild會根據 "obj-* := dir/" 遞迴訪問其子目錄,但有的時候,Kbuild架構還不足以描述所有的情況時,還要顯式的指明所要訪問的子目錄。



例子:

#arch/i386/boot/Makefile

subdir- := compressed/



上面的賦值命令告訴Kbuild,當執行"make clean"時,要遞迴訪問目錄 compressed/。為了支援在最終編譯完成啟動鏡像後的架構清理工作,還有一可選的目標 archclean:



例子:

#arch/i386/Makefile

archclean:

$(Q)$(MAKE) $(clean)=arch/i386/boot



當"make clean"執行時,make會遞迴訪問並清理 arch/i386/boot。在 arch/i386/boot中的Makefile可以用來提示make進行下一步的遞迴操作。



注意1:arch/$(ARCH)/Makefile 不能使用"subdir-",因為該Makefile被包含在頂層的Makefile中,Kbuild是不會在此處進行操作的。



注意2:"make clean" 會訪問在 core-y,libs-y,drivers-y 和 net-y 列出的所有目錄。



=== 6 架構Makefile



在遞迴訪問目錄之前,頂層Makefile要完成設置環境變數以及遞迴訪問的準備工作。頂層Makefile包含的公共部分,而 arch/$(ARCH)/Makefile 包含著針對某一特定架構的配置資訊。所以,要在 arch/$(ARCH)/Makefile 中設置一部分變數,並定義一些目標。



Kbuild執行的幾個步驟(大致):

1) 根據內核配置生成檔 .config

2) 將內核的版本號存儲在 include/linux/version.h

3) 生成指向 include/asm-$(ARCH) 的符號鏈結

4) 更新所有編譯所需的檔:

-附加的檔由 arch/$(ARCH)/Makefile 指定。

5) 遞迴向下訪問所有在下列變數中列出的目錄: init-* core-* drivers-* net-* libs-*,並編譯生成目標檔。

-這些變數的值可以在 arch/$(ARCH)/Makefile 中擴充。

6) 聯接所有的目標檔,在源代碼樹頂層目錄中生成 vmlinux。最先聯接是在 head-y中列出的檔,該變數由 arch/$(ARCH)/Makefile 賦值。

7) 最後完成具體架構的特殊要求,並生成最終的啟動鏡像。

-包含生成啟動指令

-準備 initrd 鏡像或類似文件





--- 6.1 調整針對某一具體架構生成的鏡像



LDFLAGS 一般是 $(LD) 選項



該選項在每次調用聯接器時都會用到。一般情況下,只用來指明模擬器。



例子:

#arch/s390/Makefile

LDFLAGS := -m elf_s390

注意:EXTRA_LDFLAGS 和 LDFLAGS_$@ 可用來進一步自定義選項。請看第七章。



LDFLAGS_MODULE 聯接模組時的聯接器的選項



LDFLAGS_MODULE 所設置的選項將在聯接器在聯接模組檔 .ko 時使用。

預設值為 "-r",指定輸出檔是可重定位的。



LDFLAGS_vmlinux 聯接vmlinux時的選項



LDFLAGS_vmlinux用來傳遞聯接vmlinux時的聯接器的選項。

LDFLAGS_vmlinux需 LDFLAGS_$@ 支持。



例子:

#arch/i386/Makefile

LDFLAGS_vmlinux := -e stext



OBJCOPYFLAGS objcopy 選項



當用 $(call if_changed,objcopy) 來轉換(translate)一個.o檔時,該選項就會被使用。

$(call if_changed,objcopy) 經常被用來為vmlinux生成原始的二進位碼。



例子:

#arch/s390/Makefile

OBJCOPYFLAGS := -O binary



#arch/s390/boot/Makefile

$(obj)/image: vmlinux FORCE

$(call if_changed,objcopy)



在此例中,二進位檔 $(obj)/image 是 vmlinux 的一個二進位版本。

$(call if_chagned,xxx)的用法稍後描述。



AFLAGS $(AS) 彙編編譯器選項



預設值在頂層Makefile 擴充或修改在各具體架構的Makefile



例子:

#arch/sparc64/Makefile

AFLAGS += -m64 -mcpu=ultrasparc



CFLAGS $(CC) 編譯器選項



預設值在頂層Makefile 擴充或修改在各具體架構的Makefile。



一般,CFLAGS要根據內核配置設置。



例子:

#arch/i386/Makefile

cflags-$(CONFIG_M386) += -march=i386

CFLAGS += $(cflags-y)



許多架構Makefile都通過調用所要使用的C編譯器,動態的檢查其所支援的選項:



#arch/i386/Makefile



...

cflags-$(CONFIG_MPENTIUMII) += $(call cc-option,\

-march=pentium2,-march=i686)

...

# Disable unit-at-a-time mode ...

CFLAGS += $(call cc-option,-fno-unit-at-a-time)

...





第一個例子利用了一個配置選項,當其為'y'時,擴展。



CFLAGS_KERNEL :



#arch/i386/Makefile



...

cflags-$(CONFIG_MPENTIUMII) += $(call cc-option,\

-march=pentium2,-march=i686)

...

# Disable unit-at-a-time mode ...

CFLAGS += $(call cc-option,-fno-unit-at-a-time)

...





第一個例子利用了一個配置選項,當其為'y'時,擴展。



CFLAGS_KERNEL 編譯進內核時,$(CC) 所用的選項



$(CFLAGS_KERNEL) 包含了用於編譯常駐內核代碼的附加編譯器選項。



CFLAGS_MODULE 編譯成模組時,$(CC)所用的選項



$(CFLAGS_MODULE) 包含了用於編譯可裝載模組的附加編譯器選項。





--- 6.2 將所需文件加到 archprepare 中:



archprepare規則在遞迴訪問子目錄之前,列出編譯目標檔所需檔。

一般情況下,這是一個包含彙編常量的頭檔。(assembler constants)



例子:

#arch/arm/Makefile

archprepare: maketools



此例中,目標檔 maketools 將在遞迴訪問子目錄之前編譯。 在TODO一章可以看到,Kbuild是如何支援生成分支頭檔的。(offset header files)



--- 6.3 遞迴下向時要訪問的目錄列表



如何生成 vmlinux,是由架構makefile和頂層Makefile一起來定義的。注意,架構Makefile是不會定義與模組相關的內容的,所有構建模組的定義是與架構無關的。





head-y,init-y,core-y,libs-y,drivers-y,net-y



$(head-y) 列出了最先被聯接進 vmlinux 的目標檔。

$(libs-y) 列出了生成的所有 lib.a 所在的目錄。

其餘所列的目錄,是 built-in.o 所在的目錄。



$(init-y) 在 $(head-y) 之後所要使用的檔。

然後,剩下的步驟如下:

$(core-y),$(libs-y),$(drivers-y)和$(net-y)。



頂層makefile定義了通用的部分,arch/$(ARCH)/Makefile 添加了架構的特殊要求。



例子:

#arch/sparc64/Makefile

core-y += arch/sparc64/kernel/

libs-y += arch/sparc64/prom/ arch/sparc64/lib/

drivers-$(CONFIG_OPROFILE) += arch/sparc64/oprofile/





--- 6.4 具體架構的啟動鏡像



一具體架構Makefile的具體目的就是,將生成並壓縮 vmlinux 檔,寫入啟動代碼,並將其拷貝到正確的位置。這就包含了多種不同的安裝命令。該具體目的也無法在各個平臺間進行標準化。



一般,附加的處理命令入在 arch/$(ARCH)/下的boot目錄。



Kbuild並沒有為構造boot所指定的目標提供任何更好的方法。所以,arch/$(ARCH)/Makefile 將會調用 make 以手工構造 boot的目標檔。



比較好的方法是,在arch/$(ARCH)/Makefile中包含快捷方式,並在arch/$(ARCH)/boot/Makefile 中使用全部路徑。



例子:

#arch/i386/Makefile

boot := arch/i386/boot

bzImage: vmlinux

$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@



當在子目錄中調用 make 時,推薦使用 "$(Q)$(MAKE) $(build)=

" 。



並沒有對架構特殊目標的命名規則,但用命令 "make help" 可以列出所有的相關目標。

為了支持 "make help",$(archhelp) 必須被定義。



例子:

#arch/i386/Makefile

define archhelp

echo '* bzImage - Image (arch/$(ARCH)/boot/bzImage)'

endef



當make 沒帶參數執行時,所遇到的第一個目標將被執行。在頂層,第一個目標就是 all:。

每個架構Makefile都要默認構造一可啟動的鏡像檔。在 "make help"中,默認目標就是被加亮的'*'。添加一新的前提檔到 all:,就可以構造出一不同的vmlinux。



例子:

#arch/i386/Makefile

all: bzImage



當 make 沒有參數時,bzImage將被構造。



--- 6.5 構造非Kbuild目標



extra-y



extra-y 列出了在當前目錄下,所要創建的附加檔,不包含任何已包含在obj-* 中的檔。



用 extra-y 列目標,主要是兩個目的:

1) 可以使Kbuild檢查命令行是否發生變化

- 使用 $(call if_changed,xxx) 的時候

2) 讓Kbuild知道哪些檔要在 "make clean" 時刪除



例子:

#arch/i386/kernel/Makefile

extra-y := head.o init_task.o



在此例子中,extra-y用來列出所有只編譯,但不聯接到 built-in.o的目標檔。



--- 6.6 構建啟動鏡像的命令



Kbuild 提供了幾個用在構建啟動鏡像時的宏。



if_changed



if_changed 為下列命令的基礎。



使用方法:

target: source(s) FORCE

$(call if_changed,ld/objcopy/gzip)



當執行該規則時,就檢查是否有檔需要更新,或者在上次調用以後,命令行發生了改變。如果有選項發生了改變,後者會導致重新構造。只有在 $(targets)列出的的目標檔,才能使用if_changed,否則命令行的檢查會失敗,並且目標總會被重建。給 $(targets)的賦值沒有首碼 $(obj)/ 。 if_changed 可用來聯接自定義的Kbuild命令,關於Kbuild自定義命令請看 6.7節。



注意:忘記 FORCE 是一種典型的錯誤。還有一種普遍的錯誤是,空格有的時候是有意義的;比如。下面的命令就會錯誤(注意在逗號後面的那個多餘的空格):

target: source(s) FORCE

#WRONG!# $(call if_changed, ld/objcopy/gzip)



ld

聯接目標。經常是使用LDFLAGS_$@來設置ld的特殊選項。



objcopy

拷貝二進位碼。一般是在 arch/$(ARCH)/Makefile 中使用 OBJCOPYFLAGS。

OBJCOPYFLAGS_$@ 可以用來設置附加選項。



gzip

壓縮目標檔。盡可能的壓縮目標檔。



例子:

#arch/i386/boot/Makefile

LDFLAGS_bootsect := -Ttext 0x0 -s --oformat binary

LDFLAGS_setup := -Ttext 0x0 -s --oformat binary -e begtext



targets += setup setup.o bootsect bootsect.o

$(obj)/setup $(obj)/bootsect: %: %.o FORCE

$(call if_changed,ld)



在這個例子中,有兩個可能的目標檔,分別要求不同的聯接選項。定義聯接器的選項使用的是 LDFLAGS_$@ 語法,每個潛在的目標一個。$(targets) 被分配給所有的潛在目標,因此知道目標是哪些,並且還會:

1) 檢查命令行是否改變

2) 在 "make clean" 時,刪除目標檔



前提部分中的 ": %: %.o" 部分使我們不必在列出檔 setup.o 和 bootsect.o 。

注意:一個普遍的錯誤是忘記了給 "target"賦值,導致在target中的檔總是無緣無故的被重新編譯。





--- 6.7 Kbuild自定義命令



當Kbuild的變數 KBUILD_VERBOSE 為0時,只會顯示命令的簡寫。如果要為自定義命令使用這一功能,需要設置2個變數:

quiet_cmd_ - 要顯示的命令

cmd_ - 要執行的命令



例子:

#

quiet_cmd_image = BUILD $@

cmd_image = $(obj)/tools/build $(BUILDFLAGS) \

$(obj)/vmlinux.bin > $@



targets += bzImage

$(obj)/bzImage: $(obj)/vmlinux.bin $(obj)/tools/build FORCE

$(call if_changed,image)

@echo 'Kernel: $@ is ready'



當用"make KBUILD_VERBOSE=0"更新 $(obj)/bzImage 目標時顯示:



BUILD arch/i386/boot/bzImage





--- 6.8 聯接器預處理腳本



當構造 vmlinux 鏡像時,使用聯接器腳本:

arch/$(ARCH)/kernel/vmlinux.lds。

該腳本是由在同一目錄下的 vmlinux.lds.S 生成的。Kbuild認識.lds檔,並包含由*.lds.S檔生成*.lds檔的規則。



例子:

#arch/i386/kernel/Makefile

always := vmlinux.lds



#Makefile

export CPPFLAGS_vmlinux.lds += -P -C -U$(ARCH)



$(always)的值是用來告訴Kbuild,構造目標 vmlinux.lds。

$(CPPFLAGS_vmlinux.lds),Kbuild在構造目標vmlinux.lds時所用到的特殊選項。



當構造 *.lds 目標時,Kbuild要用到下列變數:

CPPFLAGS : 在頂層目錄中設置

EXTRA_CPPFLAGS : 可以在Kbuild Makefile中設置

CPPFLAGS_$(@F) : 目標特別選項

注意,此處的賦值用的完整的檔案名。



針對*.lds文件的Kbuild構架還被用在許多具體架構的文件中。(***不通***)



=== 7 Kbuild 變數



頂層Makefile輸出以下變數:



1、VERSION,PATCHLEVEL,SUBLEVEL,EXTRAVERSION



這些變數定義了當前內核的版本號。只有很少一部分Makefile會直接用到這些

變數;可使用 $(KERNELRELEASE)代替。



$(VERSION),$(PATCHLEVEL),和$(SUBLEVEL) 定義了最初使用的三個數位的版本

號,比如"2""4"和"0"。這三個值一般是數字。



$(EXTRAVERSION) 為了補丁定義了更小的版本號。一般是非數位的字串,比如

"-pre4" ,或就空著。



KERNELRELEASE



$(KERNELRELEASE) 是一個字串,類似"2.4.0-pre4",用於安裝目錄的命名或

顯示當前的版本號。一部分架構Makefile使用該變數。



2、ARCH



該變數定義了目標架構,比如"i386","arm" 或"sparc"。有些Kbuild Makefile

根據 $(ARCH) 決定編譯哪些文件。



默認情況下,頂層Makefile將其設置為本機架構。如果是跨平臺編譯,用戶可以

用下麵的命令覆蓋該值:



make ARCH=m68k ...





3、INSTALL_PATH



該變數為架構Makefile定義了安裝內核鏡像與 System.map 檔的目錄。主要用來指明架構特殊的安裝路徑。



4、INSTALL_MOD_PATH,MODLIB



$(INSTALL_MOD_PATH) 為了安裝模組,給 $(MODLIB) 聲明了首碼。該變數不能

在Makefile中定義,但可以由用戶傳給Makefile。



$(MODLIB) 具體的模組安裝的路徑。頂層Makefile將$(MODLIB)定義為$(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)。用戶可以通過命令行參數的形式將其覆蓋。



5、INSTALL_MOD_STRIP



如果該變數有定義,模組在安裝之前,會被剝出符號表。如果

INSTALL_MOD_STRIP 為 "1",就使用默認選項 --strip-debug。否則,

INSTALL_MOD_STRIP 將作為命令 strip 的選項使用。





=== 8 Makefile語言



內核的Makefile使用的是GNU Make。該Makefile只使用GNU Make已注明的功能,並使用

了許多GNU 的擴展功能。



GNU Make支援基本的顯示處理過程的函數。內核Makefile 使用了一種類似小說的方式

,顯示"if"語句的構造、處理過程。



GNU Make 有2個賦值操作符,":="和"="。":=",將對右邊的運算式求值,並將所求的值

賦給左邊。"="更像是一個公式定義,只是將右邊的值簡單的賦值給左邊,當左邊的表達

式被使用時,才求值。



有時使用"="是正確的。但是,一般情況下,推薦使用":="。



=== 9 關於作者

第一版由 Michael Elizabeth Chastain,

修改:kai Germaschewski

Sam Ravnborg



=== 10 TODO



- 描述Kbuild是如何用 _shipped 來支持 shipped 檔的。

- 生成分支頭檔

- 在第7節加入更多的變數

________________________________________

第二部分:Makefile 實例分析



VERSION = 2

PATCHLEVEL = 6

SUBLEVEL = 20

EXTRAVERSION = .7

NAME = Homicidal Dwarf Hamster



# 以上表明了內核版本。組合起來就是:2.6.20.7 ,yeah,這就是我分析的內核版本



# 注意寫makefile時不要使用makefile的內建的規則和變數



#要想不列印"Entering directory ..."字樣,請使用no-print-directory選項

MAKEFLAGS += -rR --no-print-directory



# 因為需要遞迴執行build, 所以必須注意要保證按照正確順序執行make.



# 使用 'make V=1' 可以看到完整命令



ifdef V

ifeq ("$(origin V)", "command line")

KBUILD_VERBOSE = $(V)

endif

endif

ifndef KBUILD_VERBOSE

KBUILD_VERBOSE = 0

endif



# 使用 'make C=1' 僅檢查需要重新使用c編譯器編譯的檔

# 使用 'make C=2' 檢查所有c編譯器編譯的檔



ifdef C

ifeq ("$(origin C)", "command line")

KBUILD_CHECKSRC = $(C)

endif

endif

ifndef KBUILD_CHECKSRC

KBUILD_CHECKSRC = 0

endif



# 使用 make M=dir 表明需要編譯的模組目錄

# 舊的語法make ... SUBDIRS=$PWD 依然支持

# 這裏通過環境變數 KBUILD_EXTMOD來表示

ifdef SUBDIRS

KBUILD_EXTMOD ?= $(SUBDIRS)

endif

ifdef M

ifeq ("$(origin M)", "command line")

KBUILD_EXTMOD := $(M)

endif

endif





# kbuild 可以把輸出檔放到一個分開的目錄下

# 定位輸出檔目錄有兩種語法支援:

# 兩種方法都要求工作目錄是內核原始檔案目錄的根目錄

# 1) O=

# 使用 "make O=dir/to/store/output/files/"

#

# 2) 設置 KBUILD_OUTPUT

# 設置環境變數 KBUILD_OUTPUT 來指定輸出檔存放的目錄

# export KBUILD_OUTPUT=dir/to/store/output/files/

# make

#

# 但是 O= 方式優先於KBUILD_OUTPUT環境變數方式



# KBUILD_SRC 會再obj目錄的make調用

# KBUILD_SRC 到目前還不是為了給一般用戶使用的

ifeq ($(KBUILD_SRC),)



ifdef O

ifeq ("$(origin O)", "command line")

KBUILD_OUTPUT := $(O)

endif

endif



# 缺剩目標

PHONY := _all

_all:



ifneq ($(KBUILD_OUTPUT),)

# 調用輸出目錄的第二個make, 傳遞相關變數檢查輸出目錄確實存在



saved-output := $(KBUILD_OUTPUT)

KBUILD_OUTPUT := $(shell cd $(KBUILD_OUTPUT) && /bin/pwd)

$(if $(KBUILD_OUTPUT),, \

$(error output directory "$(saved-output)" does not exist))



PHONY += $(MAKECMDGOALS)



$(filter-out _all,$(MAKECMDGOALS)) _all:

$(if $(KBUILD_VERBOSE:1=),@)$(MAKE) -C $(KBUILD_OUTPUT) \

KBUILD_SRC=$(CURDIR) \

KBUILD_EXTMOD="$(KBUILD_EXTMOD)" -f $(CURDIR)/Makefile $@



# Leave processing to above invocation of make

skip-makefile := 1

endif # ifneq ($(KBUILD_OUTPUT),)

endif # ifeq ($(KBUILD_SRC),)



ifeq ($(skip-makefile),)



PHONY += all

ifeq ($(KBUILD_EXTMOD),)

_all: all

else

_all: modules

endif



srctree := $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR))

TOPDIR := $(srctree)

# FIXME - TOPDIR is obsolete, use srctree/objtree

objtree := $(CURDIR)

src := $(srctree)

obj := $(objtree)



VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))



export srctree objtree VPATH TOPDIR





# SUBARCH 告訴 usermode build 當前的機器是什麼體系. 它是第一個設置的,並且如果

# 是usermode build ,命令行中的 "ARCH=um" 優先下面的 ARCH 設置. 如果是 native build ,

# 設置ARCH , 獲取正常的數值, 將會忽略SUBARCH .



SUBARCH := $(shell uname -m
sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \

-e s/arm.*/arm/ -e s/sa110/arm/ \

-e s/s390x/s390/ -e s/parisc64/parisc/ \

-e s/ppc.*/powerpc/ -e s/mips.*/mips/ )



# 交叉編譯和選擇不同的gcc/bin-utils

# ---------------------------------------------------------------------------

#

# 當交叉編譯其他體系的內核時,ARCH 應該設置為目標體系.

# ARCH 可以再make執行間設置:

# make ARCH=ia64

# 另外一種方法是設置 ARCH 環境變數.

# 默認 ARCH 是當前執行的宿主機.



# CROSS_COMPILE 作為編譯時所有執行需要使用的首碼

# 只有gcc 和 相關的 bin-utils 使用 $(CROSS_COMPILE)設置的首碼.

# CROSS_COMPILE 可以再命令行設置:

# make CROSS_COMPILE=ia64-linux-

# 另外 CROSS_COMPILE 也可以再環境變數中設置.

# CROSS_COMPILE 的預設值是空的

# Note: 一些體系的 CROSS_COMPILE 是再其 arch/*/Makefile中設置的



ARCH ?= $(SUBARCH)

CROSS_COMPILE ?=



# Architecture as present in compile.h

UTS_MACHINE := $(ARCH)



KCONFIG_CONFIG ?= .config



# 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)



HOSTCC = gcc

HOSTCXX = g++

HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer

HOSTCXXFLAGS = -O2



# Decide whether to build built-in, modular, or both.

# Normally, just do built-in.



KBUILD_MODULES :=

KBUILD_BUILTIN := 1



# 如果僅編譯模組 "make modules", 就不會編譯內建的 objects.

# 當使用modversions編譯 modules 時 , 就需要考慮build-in objects,

# 目的是再記錄前確認 checksums 已經更新.



ifeq ($(MAKECMDGOALS),modules)

KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1)

endif



# If we have "make modules", compile modules

# in addition to whatever we do anyway.

# Just "make" or "make all" shall build modules as well



ifneq ($(filter all _all modules,$(MAKECMDGOALS)),)

KBUILD_MODULES := 1

endif



ifeq ($(MAKECMDGOALS),)

KBUILD_MODULES := 1

endif



export KBUILD_MODULES KBUILD_BUILTIN

export KBUILD_CHECKSRC KBUILD_SRC KBUILD_EXTMOD



# Beautify output

# ---------------------------------------------------------------------------

#

# 一般,make再執行命令前會列印整個命令資訊. 現在通過$($(quiet)$(cmd))方式

# 可以設置 $(quiet) 來選擇不同的命令輸出方式等.

#

# quiet_cmd_cc_o_c = Compiling $(RELDIR)/$@

# cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<

#

# 如果 $(quiet) 為空, 整個命令行將會列印.

# 如果 $(quiet)設置為 "quiet_", 只列印簡短的版本資訊.

# 如果 $(quiet)設置為 "silent_", 將不會列印任何資訊, 因為沒有$(silent_cmd_cc_o_c) 變數存在.

#

# 一個簡單的首碼 $(Q)放到命令前面,以便再 non-verbose 模式下可以隱藏命令:

#

# $(Q)ln $@ :<

#

# 如果 KBUILD_VERBOSE 等於 0 ,那麼上面的命令將隱藏.

# 如果 KBUILD_VERBOSE 等於 1 ,那麼上面的命令將顯示.



ifeq ($(KBUILD_VERBOSE),1)

quiet =

Q =

else

quiet=quiet_

Q = @

endif



# 如果make -s (silent mode), 不會顯示命令



ifneq ($(findstring s,$(MAKEFLAGS)),)

quiet=silent_

endif



export quiet Q KBUILD_VERBOSE





# Look for make include files relative to root of kernel src

MAKEFLAGS += --include-dir=$(srctree)



# We need some generic definitions.

include $(srctree)/scripts/Kbuild.include



# Make variables (CC, etc...)



AS = $(CROSS_COMPILE)as

LD = $(CROSS_COMPILE)ld

CC = $(CROSS_COMPILE)gcc

CPP = $(CC) -E

AR = $(CROSS_COMPILE)ar

NM = $(CROSS_COMPILE)nm

STRIP = $(CROSS_COMPILE)strip

OBJCOPY = $(CROSS_COMPILE)objcopy

OBJDUMP = $(CROSS_COMPILE)objdump

AWK = awk

GENKSYMS = scripts/genksyms/genksyms

DEPMOD = /sbin/depmod

KALLSYMS = scripts/kallsyms

PERL = perl

CHECK = sparse



CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise $(CF)

MODFLAGS = -DMODULE

CFLAGS_MODULE = $(MODFLAGS)

AFLAGS_MODULE = $(MODFLAGS)

LDFLAGS_MODULE = -r

CFLAGS_KERNEL =

AFLAGS_KERNEL =





# Use LINUXINCLUDE when you must reference the include/ directory.

# Needed to be compatible with the O= option

LINUXINCLUDE := -Iinclude \

$(if $(KBUILD_SRC),-Iinclude2 -I$(srctree)/include) \

-include include/linux/autoconf.h



CPPFLAGS := -D__KERNEL__ $(LINUXINCLUDE)



CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \

-fno-strict-aliasing -fno-common

AFLAGS := -D__ASSEMBLY__



# Read KERNELRELEASE from include/config/kernel.release (if it exists)

KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null)

KERNELVERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)



export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION

export ARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC

export CPP AR NM STRIP OBJCOPY OBJDUMP MAKE AWK GENKSYMS PERL UTS_MACHINE

export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS



export CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS

export CFLAGS CFLAGS_KERNEL CFLAGS_MODULE

export AFLAGS AFLAGS_KERNEL AFLAGS_MODULE



# When compiling out-of-tree modules, put MODVERDIR in the module

# tree rather than in the kernel tree. The kernel tree might

# even be read-only.

export MODVERDIR := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/).tmp_versions



# Files to ignore in find ... statements



RCS_FIND_IGNORE := \( -name SCCS -o -name BitKeeper -o -name .svn -o -name CVS -o -name .pc -o -name .hg -o -name .git \) -prune -o

export RCS_TAR_IGNORE := --exclude SCCS --exclude BitKeeper --exclude .svn --exclude CVS --exclude .pc --exclude .hg --exclude .git

[未完,待續]

# ===========================================================================

# 下面是設置config和build 內核時共同使用的規則



# scripts裏面是最基本的幫助builds,在scripts/basic目錄下的makefile是所有build時都會用到的工具:fixdep/

PHONY += scripts_basic

scripts_basic:

$(Q)$(MAKE) $(build)=scripts/basic



# 把對scripts/basic/makefile轉化到scripts_basic處理

scripts/basic/%: scripts_basic ;



#這裏先說說script目錄裏面的makefile檔作用

#makefile:配置目標,包括編譯島內核和模組方式的target

#makefile_clean:當然是刪除了

#kbuild_include:一般的配置選項,會在makefile_build中調用

#makefile_build:build配置

#makefile_lib:module,vmlinux的配置

#makefile_host:配置binary

#makefile_modinst:安裝module

#makefile_modpost:



PHONY += outputmakefile

# outputmakefile規則目的是在輸出目錄產生一個makefile檔。這會為make提供方便。這裏是調用scripts/mkmakefile創建makfile檔

outputmakefile:

ifneq ($(KBUILD_SRC),)

$(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \

$(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)

endif



# 設置是.config,還是module,還是build命令



no-dot-config-targets := clean mrproper distclean \

cscope TAGS tags help %docs check% \

include/linux/version.h headers_% \

kernelrelease kernelversion



config-targets := 0

mixed-targets := 0

dot-config := 1



ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)

ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)

dot-config := 0

endif

endif



ifeq ($(KBUILD_EXTMOD),)

ifneq ($(filter config %config,$(MAKECMDGOALS)),)

config-targets := 1

ifneq ($(filter-out config %config,$(MAKECMDGOALS)),)

mixed-targets := 1

endif

endif

endif



ifeq ($(mixed-targets),1)

# ===========================================================================

# We're called with mixed targets (*config and build targets).

# Handle them one by one.



%:: FORCE

$(Q)$(MAKE) -C $(srctree) KBUILD_SRC= $@



else

ifeq ($(config-targets),1)

# ===========================================================================

#僅配置linux



# Read arch specific Makefile to set KBUILD_DEFCONFIG as needed.

# KBUILD_DEFCONFIG may point out an alternative default configuration

# used for 'make defconfig'

include $(srctree)/arch/$(ARCH)/Makefile

export KBUILD_DEFCONFIG



config %config: scripts_basic outputmakefile FORCE

$(Q)mkdir -p include/linux include/config

$(Q)$(MAKE) $(build)=scripts/kconfig $@



else

# ===========================================================================

# 僅編譯內核



ifeq ($(KBUILD_EXTMOD),)

# Additional helpers built in scripts/

# Carefully list dependencies so we do not try to build scripts twice

# in parallel

PHONY += scripts

scripts: scripts_basic include/config/auto.conf

$(Q)$(MAKE) $(build)=$(@)



#下麵就不用說了吧

# Objects we will link into vmlinux / subdirs we need to visit

init-y := init/

drivers-y := drivers/ sound/

net-y := net/

libs-y := lib/

core-y := usr/

endif # KBUILD_EXTMOD



ifeq ($(dot-config),1)

# Read in config

-include include/config/auto.conf



ifeq ($(KBUILD_EXTMOD),)

# Read in dependencies to all Kconfig* files, make sure to run

# oldconfig if changes are detected.

-include include/config/auto.conf.cmd



# To avoid any implicit rule to kick in, define an empty command

$(KCONFIG_CONFIG) include/config/auto.conf.cmd: ;



# If .config is newer than include/config/auto.conf, someone tinkered

# with it and forgot to run make oldconfig.

# if auto.conf.cmd is missing then we are probably in a cleaned tree so

# we execute the config step to be sure to catch updated Kconfig files

include/config/auto.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd

$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig

else

# external modules needs include/linux/autoconf.h and include/config/auto.conf

# but do not care if they are up-to-date. Use auto.conf to trigger the test

PHONY += include/config/auto.conf



include/config/auto.conf:

$(Q)test -e include/linux/autoconf.h -a -e $@

( \

echo; \

echo " ERROR: Kernel configuration is invalid."; \

echo " include/linux/autoconf.h or $@ are missing."; \

echo " Run 'make oldconfig && make prepare' on kernel src to fix it."; \

echo; \

/bin/false)



endif # KBUILD_EXTMOD



else

# Dummy target needed, because used as prerequisite

include/config/auto.conf: ;

endif # $(dot-config)





# ===========================================================================

# 僅編譯內核



ifeq ($(KBUILD_EXTMOD),)

# Additional helpers built in scripts/

# Carefully list dependencies so we do not try to build scripts twice

# in parallel

PHONY += scripts

scripts: scripts_basic include/config/auto.conf

$(Q)$(MAKE) $(build)=$(@)



#下麵就不用說了吧

# Objects we will link into vmlinux / subdirs we need to visit

init-y := init/

drivers-y := drivers/ sound/

net-y := net/

libs-y := lib/

core-y := usr/

endif # KBUILD_EXTMOD



.....



all: vmlinux



#配置gcc選項

ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE

CFLAGS += -Os

else

CFLAGS += -O2

endif

....



# 缺剩的編譯的內核鏡像

export KBUILD_IMAGE ?= vmlinux



#

# vmlinux與map安裝路徑,默認時boot目錄

export INSTALL_PATH ?= /boot



#

#module安裝目錄

#



MODLIB = $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)

export MODLIB



#

# 如果定義了INSTALL_MOD_STRIP,在安裝module後會strip這些安裝的modules



ifdef INSTALL_MOD_STRIP

ifeq ($(INSTALL_MOD_STRIP),1)

mod_strip_cmd = $(STRIP) --strip-debug

else

mod_strip_cmd = $(STRIP) $(INSTALL_MOD_STRIP)

endif # INSTALL_MOD_STRIP=1

else

mod_strip_cmd = true

endif # INSTALL_MOD_STRIP

export mod_strip_cmd



#如果不是安裝module

ifeq ($(KBUILD_EXTMOD),)

core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/



vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \

$(core-y) $(core-m) $(drivers-y) $(drivers-m) \

$(net-y) $(net-m) $(libs-y) $(libs-m)))



vmlinux-alldirs := $(sort $(vmlinux-dirs) $(patsubst %/,%,$(filter %/, \

$(init-n) $(init-) \

$(core-n) $(core-) $(drivers-n) $(drivers-) \

$(net-n) $(net-) $(libs-n) $(libs-))))



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

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

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

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

libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))

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

libs-y := $(libs-y1) $(libs-y2)



# Build vmlinux

...



#配置syms

...



#以下是內核link時的配置

....