# Linux kernel 編譯裝初嘗試 ###### tags: `OS` `kernel` 本次作HW1先用hyper-v虛擬機安裝工作環境,再將工作環境中的kernel版本更新,預期是更新到2.6版本,但後來嘗試了幾個版本,直到試到4.4.1才成功,如下是記錄我自己的編譯安裝過程。 1. 安裝開發環境 2. Kernel安裝 3. make menuconfig設置 4. 編譯 5. 執行測試 - step.2~5總共嘗試了五次才成功 ## 安裝開發環境 原本要使用container類型的docker搭配VScode,後來重溫了一下虛擬機技術後發現虛擬技術的架構不同 而後決定選用windows專業版內建的hyper-v,較為方便的同時也是怕在安裝過程中一不小心把本機端的kernel修改掉,所以專門選擇一個VM去編譯安裝。 ![](https://hackmd.io/_uploads/rJbNg9r16.png) Hyper-V屬於type-1的虛擬機技術 ![](https://hackmd.io/_uploads/Skg3gqSk6.png) 而屬於container類型的docker的架構可以看到與VM有所區別(如上圖) container本質上做為一個application layer,其kernel是共用的,所以不能隨意修改 選用VM架構是比較好的。 ![](https://hackmd.io/_uploads/Sy3_Zcr1p.png) 控制台$\rightarrow$程式集$\rightarrow$程式與功能$\rightarrow$開啟或關閉windows功能$\rightarrow$勾選Hyper-V ![](https://hackmd.io/_uploads/SyOzfcHJT.png) 搜尋列"Hyper-V"$\rightarrow$開啟"Hyper-V管理員" ![](https://hackmd.io/_uploads/ry85McSya.png) 右側動作列點選"快速建立"(預設為DRAM 1GB, Disk 12GB) ![](https://hackmd.io/_uploads/Sy8Rz9Sya.png) 我這裡是選擇"Ubuntu18.04LTS"$\rightarrow$建立虛擬機器 ![](https://hackmd.io/_uploads/S11H75r16.png) (上圖是22.04,後來我改裝18.04) - 如果Ubuntu有提示要更新記得更新,以避免會有些環境小問題。 (不是升到20.04那個提示,純粹update那個才是) ![](https://hackmd.io/_uploads/BJjA-nHJa.png) ![](https://hackmd.io/_uploads/HyhbLaSka.png) 成功啟動 右鍵"設定"記憶體8G, DISK 50G (因為執行時因為記憶體太小而導致VScode執行會Crash 編譯時DISK太小不夠用,無法繼續編譯) 然後擴充後的50G是未使用,需要在去分割硬碟讓它擴充使用 先查看硬碟使用狀況再用gparted調整 ``` $lsblk #查看硬碟使用情形 $sudo apt install gparted $sudo gparted //進入UI調整 ``` gparted最後記得按綠色勾勾套用變動。 ![](https://hackmd.io/_uploads/ByNH99h1p.png) --- - step1.遇到的問題 虛擬機載入時進去xorg畫面後卡住,沒有繼續顯示畫面 https://askubuntu.com/questions/1296796/cant-connect-to-hyper-v-ubuntu-20-04-vm-after-system-program-problem-detected --- ## 第一次嘗試(v2.6.8) ### Kernel安裝 ``` $cd /usr/src $wget https://mirrors.edge.kernel.org/pub/linux/kernel/v2.6/linux-2.6.8.tar.gz $tar -xvzf linux.2.6.8 $cd linux-2.6.8 $make mrproper $make clean ``` 2.6版本下隨便選了個2.6.8 ![](https://hackmd.io/_uploads/BJwslpB1a.png) ![](https://hackmd.io/_uploads/ByF5WaH1a.png) ``` $make menuconfig ``` ![](https://hackmd.io/_uploads/SkdILTH1T.png) 主要error如下圖 ![](https://hackmd.io/_uploads/ryAvu6HJ6.png) ``` $sudo apt install linux-headers-(uname -r) $make upgrade ``` 參考reference[3]覺得可能是相關套件沒安裝好,後來發現還是一樣的error ![](https://hackmd.io/_uploads/B12T8aS1a.png) 先嘗試暴力解,把error這行註釋掉試試看後再次make menuconfig 可以了,但遇到另一個問題 ![](https://hackmd.io/_uploads/rJjsppSkT.png) 安裝一下缺少的套件 https://stackoverflow.com/questions/14416487/gcc-usr-bin-ld-error-cannot-find-lncurses ``` $sudo apt install libncurses5-dev ``` 終於成功進入menuconfig介面了 ![](https://hackmd.io/_uploads/ByKSxRryp.png) --- ### menuconfig設置 設置的地方主要看預設時沒有被選擇的 (以下圖示不是最終設定,依文字敘述為主) #### 1.Code maturity level options ![](https://hackmd.io/_uploads/BJBrPCrJa.png) 對開發中或未完成的code和driver進行提示,將這個選項打開,我覺得若是之後遇到錯誤可能有版本相關提示,比較方便。 #### 2 General setup ![](https://hackmd.io/_uploads/B1RNLRSJa.png) 這裡有打開熱插拔 我覺得不需要設定特殊的kernel feature,而其他都預設的,沒變動 #### 3 Loadable module support ![](https://hackmd.io/_uploads/HkZs8CBJ6.png) 這裡有打開*automatic kernel module loading*,好像可以在啟動時預載module,雖然不知道影響多大,不過因為好奇所以先開啟。 #### 4 Processor type and features ![](https://hackmd.io/_uploads/S1GcDRry6.png) preemptive kernel應該是可以稍微增加一點效能,我選擇打開 不過這裡還不能選擇有哪幾種preemptive mode。 NUMA應該是不需要,而其他都預設值。 - Processor family ![](https://hackmd.io/_uploads/Sytov0HJa.png) 我自己的處理器是AMD Zen3 5600x所以選擇*Generic-x86-64* #### 5 Power mangment options ![](https://hackmd.io/_uploads/BJ9F5RSy6.png) power這塊看了一下感覺不需要動,skip。 #### 6 BUS options ![](https://hackmd.io/_uploads/SyHsqRrJT.png) 不確定之後的HW會不會動到PCIE,後兩個選項好像是支持lspic這個指令的[4],這裡先將它們都打開。 #### 7 Executable file formats / Emulations ![](https://hackmd.io/_uploads/Hytp9RHyT.png) 參考[1]和[5],決定打開*IA32 a.out support*以支援老舊的軟體,其他選項沒變動。 #### 8 Device Drivers ![](https://hackmd.io/_uploads/SkBin0ry6.png) - ATA/ATAPI/MFM/RLL support ![](https://hackmd.io/_uploads/BkT8p18ya.png) 這裡稍微觀察了一下我自己是SSD+SATA硬碟所以選了一些相關的配置 不過還不是很懂每一個選項,就大概選了一下。 #### 9 Firmware Drivers 不知道為什麼進不去這個選項中。 #### 10 File systems ![](https://hackmd.io/_uploads/HktNZeIJ6.png) 我把ext2和ext3都選了,想說支援得廣泛一點吧。 #### 11 Kernel hacking ![](https://hackmd.io/_uploads/rynggeUka.png) 同之前所說,不確定之後的Homework會動到哪部分,所以關於debugging這部分我全開了 #### 12 Security options ![](https://hackmd.io/_uploads/rJyhbg8yT.png) 沒動,關於安全的不隨便亂動。 #### 13 Cryptographic options ![](https://hackmd.io/_uploads/SJETbeL16.png) 關於加密支援也維持原樣。 #### 14 Library routines ![](https://hackmd.io/_uploads/r1F0bgIyT.png) 好像是支援某些第三方的,所以沒動。 之後在最外層的大選單選擇Save configuration to alternate file保存一下,因為整體menuconfig設定有點麻煩。 --- ### 編譯 - 以下圖示是terminal跟vscode界面交錯使用,所以背景色不統一 - 用gcc-4.8編譯的會沒有syntax highlight ``` $sudo make all ``` ![](https://hackmd.io/_uploads/rkrH2e8k6.png) 在CFLAGS增加-fno-pie參數 禁止position-independent code編譯方式 ``` CFLAGS=... \ -fno-pie ``` ![](https://hackmd.io/_uploads/HJ0KcGUJa.png) 因為tss_struct的定義在這段使用的後面,所以改用指標可以避免error的問題 ``` //extern struct tss_struct[NR_CPUS]; extern struct *tss_struct; ``` ![](https://hackmd.io/_uploads/BJIw6f8y6.png) 有多重定義的都是extern inline宣告,把extern都改成static ``` //extern inline ... static inline ... ``` ![](https://hackmd.io/_uploads/BkdkQQIya.png) 這裡同上,不過要找一下verify_area的原始定義在哪裡,一樣也是將extern改成static,如下圖 ![](https://hackmd.io/_uploads/HJHfs78y6.png) /usr/src/linux-*/include/asm/uaccess.h ``` //extern inline int verify_area(... static inline int verify_area(... ``` ![](https://hackmd.io/_uploads/HJOb3Q8JT.png) 多重定義同上更改方式,如下圖 ![](https://hackmd.io/_uploads/ryIiTQLy6.png) 這裡要看define定義,報錯訊息的是的是*IN* *IN*是由*IN1*和*INT2*組成,修改*IN1*中的extern宣告改成static,*_OUT*同理。* ![](https://hackmd.io/_uploads/SkEmJ4Lyp.png) 出錯在我們是64-bit下去編譯32-bit需求的程式,所以會出現無支援的指令 所以我們在Makefile中所有CFLAGS後面都加上-m32,LD相關的都加上--32。 ``` CFLAG= ... \ -m32 ... LDFLAG= ... \ --32 ``` ![](https://hackmd.io/_uploads/Bk90rS8ya.png) 這裡像是轉成x86架構下的assembly code的時後出現問題 但我一直解決不了,所以最後放棄了2.6.8版 同時也覺得bug真多,所以試試看編譯其他版本。 --- ## 第二次嘗試(v2.6.30) 前面重複的步驟沒有問題就不再贅述 但因為版本不同,所以make menuconfig選項也稍微有點變化,設定時多注意。 ### 編譯 ``` $sudo make all ``` ![](https://hackmd.io/_uploads/rkrH2e8k6.png) 在CFLAGS增加-fno-pie參數 *CFLAGS=... \ -fno-pie* ![](https://hackmd.io/_uploads/Bk_KJvU1T.png) 因為編譯時要求gcc版本,所以改裝4.8版本繼續編譯 ``` $sudo apt install gcc-4.8 g++-4.8 $sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 10 $sudo update-alternatives --config gcc $gcc --version ``` ![](https://hackmd.io/_uploads/H1F4Jw8ka.png) 按照提示去修改linux-2.6.8/arch/x86/vdso/Makefile ``` //VDSO_LDFLAGS_vdso.lds = -m elf_x86_64 的 "-m elf_x86_64" 改成 "-m64" VDSO_LDFLAGS_vdso.lds = ... -m64" ... //VDSO_LDFLAGS_vdso32.lds = -m elf_i386 的 "-m elf_i386" 改成 "-m32" VDSO_LDFLAGS_vdso32.lds = ..."-m32"... ``` ![](https://hackmd.io/_uploads/rJJdWwU1T.png) kernel/timeconst.pl中*if(!defined(@val))*判斷式改成*!@val* ``` if(!@val) { ... ``` ![](https://hackmd.io/_uploads/S1cM4PUJ6.png) 這個kvm.h檔是關於x86的虛擬機技術,設定menuconfig的時候忘記關掉了,這裡回去make menuconfig把它關掉就可以了。 (先前編譯2.6.8版時沒有支援虛擬化這個選項) ![](https://hackmd.io/_uploads/B1FZ3wIJT.png) drivers/net/igbvf/igbvf.h 將error那行註解掉。 ``` //struct page *page ``` ![](https://hackmd.io/_uploads/r1saUxDJa.png) 同上 ``` //struct snd_soc_codec *codec ``` ### 編譯結果 ![](https://hackmd.io/_uploads/H1uYSmPkp.png) 之後終於勉強算是第一次成功編譯,查看剛剛*make bzImage*(含在*make all*指令)安裝的地方以確保成功 接著,編譯完後安裝編譯好的modules ``` $sudo make modules_install ``` ![](https://hackmd.io/_uploads/BkXF3SDk6.png) 正常來說*modules.builtin*應該要包含在目錄下才對 看起來檢查編譯過程應該沒有設定錯誤 在*make modules_install*安裝過程有訊息提示,如下圖 ![](https://hackmd.io/_uploads/rycEjD_kp.png) 不過我們有顯示depmod的指令提示原本應該是3.2版本 所以有可能版本過低? 嘗試更新看看module_init_tools ``` $sudo wget https://mirrors.edge.kernel.org/pub/linux/utils/kernel/module-init-tools/module-init-tools-3.10.tar.gz $tar -xvzf module_init-tools-3.10.tar.gz $cd module_init_tools-3.10 $vi README //查看一下安裝步驟,如下圖 ``` ![](https://hackmd.io/_uploads/Bkzehw_1a.png) 照著步驟安裝,其中將depmod設置成編譯目標的2.6.30版本 ``` $./configure $make $make install $depmod 2.6.30 ``` 然後查看版本檢查安裝是否成功,確保移除原本的depmod並且加入新的環境變數 ``` $/usr/local/sbin/depmod --version ``` ![](https://hackmd.io/_uploads/H1hihwdyp.png) 之後再次嘗試後依然還是跑出一樣的Warning: you may need to install module-init-tools 後來想了一下,我把編譯指令稍微換個嘗試一下 ``` $make clean $make -j6 $make bzImage $make modules_install ``` ![](https://hackmd.io/_uploads/H1Bs3SPyp.png) 看到**done**,很意外的安裝成功了 但剛剛關於"modules.buildin"的錯誤還是有跑出來(不會解,暫且無視) ![](https://hackmd.io/_uploads/HkHiADOk6.png) 再從這裡確認一下,有看到我們安裝的2.6.30 緊接著修改grub,如下圖 ``` $sudo vi /etc/defalut/grub ###### GRUB_TIMEOUT_STYLE=menu //開機時開啟選單,以方便選擇kernel版本 GRUB_TIMEOUT=-1 //時間-1代表直到使用者選擇為止 不會動 ... ``` ![](https://hackmd.io/_uploads/rkUa6vuy6.png) 修改完後記得更新才會生效 ``` $sudo update-grub ``` 然後重啟 ``` $sudo reboot ``` ![](https://hackmd.io/_uploads/HJifJ_d1T.png) 選擇*Advanced options for Ubuntu* ![](https://hackmd.io/_uploads/SJHLyOuJp.png) 選擇我們千辛萬苦安裝好的2.6.30 ![](https://hackmd.io/_uploads/HyGegddkp.png) 版本太老了...開始找原因... ![](https://hackmd.io/_uploads/H1Z5XUDya.png) 利用*file*指令的技巧可以得知glibc支援的kernel最低版本是3.2.0[8][11]。 ## 第三次嘗試(v3.2.4) 以下重複的就不贅述,僅附上command過程 ``` $cd /usr/src $sudo wget https://mirrors.edge.kernel.org/pub/linux/kernel/v3.x/linux-3.2.20.tar.gz $sudo tar -xvzf linux-3.2.20\ $cd linux-3.2.20 $sudo make mrproper $sudo make clean $sudo make menuconfig $sudo make bzImage -j6 $sudo make modules -j6 $sudo make modules_install $sudo make install $sudo update-grub $sudo reboot ``` ![](https://hackmd.io/_uploads/HyGegddkp.png) 還是一樣too old 而後再回去看載點時,看似好像有patch,抓補丁來看看 ``` $sudo wget http://ftp.ntu.edu.tw/linux/kernel/v3.x/patch-3.2.20.gz $sudo gunzip -c patch-3.2.20.gz | sudo patch -p1 ``` 再重新經歷一次編譯過程 ![](https://hackmd.io/_uploads/rkq7T2_ya.png) ![](https://hackmd.io/_uploads/HyUnTn_JT.png) ![](https://hackmd.io/_uploads/r1N70n_J6.png) ![](https://hackmd.io/_uploads/HytJb6uJ6.png) 以上問題都是程式碼重複了,把重複的function整段都註釋掉就可以了。 ![](https://hackmd.io/_uploads/HyGegddkp.png) 還是一樣too old,有點不明所以然,明明glibc最低支援到3.2.0,但我選用3.2.8還被報錯。 ## 第四次嘗試(v3.2.20) ![](https://hackmd.io/_uploads/HyGegddkp.png) too old ## 第五次嘗試(v4.4.1) 一樣直接進到make環節 ![](https://hackmd.io/_uploads/Hy0Al1YyT.png) 基本包沒安裝好 ``` $sudo apt-get install libssl-dev ``` ![](https://hackmd.io/_uploads/H1hizJK1p.png) ./.config 修改CONFIG_SYSTEM_TRUSTED_KEYS,使其賦值是空值。[10] ``` CONFIG_SYSTEM_TRUSTED_KEYS="" ``` ### 成功執行 ![](https://hackmd.io/_uploads/BJ3E_kY16.png) ![](https://hackmd.io/_uploads/Sy48_yFy6.png) ### 心得   這次的編譯安裝過程真的很麻煩,都在想為什麼source code可以有這麼多問題,而且很多還不會解,如果是因為環境或gcc版本問題造成的bug等等我可以理解,但很多 source code 是重複的程式碼有很多,要手動註釋掉,很不能理解。   最具挑戰的是查明glibc所適配的最低版本kernel,查閱了 glibc 的README 和 announcement email 都沒有找到正式公告,但以Ubuntu18.04內的glibc顯示支援最低是3.2.0,其中嘗試了兩個3.2以上的版本都無效,直到嘗試4.4.1,到現在都還搞不懂為什麼,這是這次最可惜的部分。   從各 kernel 版本 make menuconfig 的演變來看,其實安裝更新 kernel已經越來越簡單的。找相關資料時,鳥哥[1]的網站果然還是經典,雖然很多是過時的,卻還有很很多核心觀念。總體來說手動更新 kernel 是有一點點有趣的,可惜因為報告長度沒辦法紀錄到全部的過程,至於完整過程我更新在文首markdown的連結版 我覺得如果改用 qemu 這種可以調適硬體環境的應該早就好了,但莫名很堅持想手動 debug 編譯問題,如果有時間的話下次會嘗試直接用qemu可能更好,可以選擇與 kernel 適配的硬體結構 --- ## Reference 1. https://linux.vbird.org/linux_basic/centos7/0540kernel.php 2. https://mirrors.edge.kernel.org/pub/linux/kernel/v2.6/ 3. https://hackmd.io/@sysprog/linux-kernel-module 4. https://www.vinchin.com/blog/vinchin-technique-share-details.html?id=10446 5. https://unix.stackexchange.com/questions/38385/what-does-config-ia32-aout-do-actually 6. https://blog.csdn.net/xiaoyu1233/article/details/79850289 7. https://hackmd.io/@qqgnoe466263/SkN1lw0QB 8. https://stackoverflow.com/questions/6941332/anticipate-kernel-too-old-errors-between-2-6-16-and-2-6-26-kernel-versions 9. https://hackmd.io/@qqgnoe466263/SkN1lw0QB 10. https://blog.csdn.net/qq_36393978/article/details/118157426 11. https://unix.stackexchange.com/questions/9705/can-new-glibc-versions-be-used-with-an-old-kernel