前言
最近在讀《Linux內(nèi)核設(shè)計(jì)與實(shí)現(xiàn)》炉菲,在書的開始就要先搭建一個(gè)Linux2.6的環(huán)境。
為了把環(huán)境搭好坤溃,折騰了好幾天拍霜。所以來分享一下搭建流程以及可能遇到的坑。
基礎(chǔ)環(huán)境說明
操作系統(tǒng)(虛擬機(jī)): ubuntu-14.04 64bit (ubuntu-14.04.6-server-amd64)
Linux內(nèi)核: 2.6.26
qemu: 2.0.0
busybox: 1.20.1
gcc: 4.8.4
這里是用虛擬機(jī)啟動(dòng)ubuntu進(jìn)行編譯薪介,然后使用busybox制作根文件系統(tǒng)祠饺,最后用qemu模擬器來啟動(dòng)編譯好的Linux。
強(qiáng)烈建議使用上述相同的環(huán)境進(jìn)行編譯汁政,不然會(huì)遇到很多坑道偷。個(gè)人覺得入門的時(shí)候沒必要把時(shí)間浪費(fèi)在這上面。
這里編譯的Linux內(nèi)核是32位的记劈,原因是在使用這個(gè)版本的qemu和gdb在調(diào)試64位系統(tǒng)的時(shí)候會(huì)遇到bug勺鸦。
當(dāng)然直接選擇32位的ubuntu也是可以的,編譯的時(shí)候會(huì)更簡單目木,不過我用的vscode remote不支持連接32位系統(tǒng)换途。
有興趣可以了解下什么是根文件系統(tǒng)
下文主要介紹Mac下的安裝流程,Windows/Linux環(huán)境下也大致一樣刽射。
搭建流程在Windows和Mac環(huán)境下都完成過怀跛。
系統(tǒng)環(huán)境安裝
- 使用vagrant創(chuàng)建虛擬機(jī)
vagrant 是一個(gè)虛擬機(jī)管理軟件,可以簡化虛擬機(jī)的創(chuàng)建和銷毀流程柄冲。它實(shí)際上還是使用virtualbox創(chuàng)建虛擬機(jī)吻谋。如果不想使用vagrant,可以直接使用virtualbox/vmware现横。
$ echo """Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/trusty64"
config.vm.box_check_update = false
config.vm.network "private_network", ip: "192.168.33.100"
config.vm.provider "virtualbox" do |vb|
vb.cpus = 2
vb.memory = "1024"
end
end
""" > Vagrantfile
$ vagrant up
$ vagrant ssh
$ sudo su
下面默認(rèn)使用root權(quán)限進(jìn)行所有操作
- 替換國內(nèi)軟件源(阿里源)
$ cd /etc/apt
$ cp sources.list sources.backup
$ echo """
deb http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse
""" > sources.list
$ apt-get update
- 安裝編譯環(huán)境
$ apt-get install -y libncurses5-dev build-essential
$ apt-get install -y lib32readline-gplv2-dev # 編譯32位系統(tǒng)
- 安裝調(diào)試環(huán)境
apt-get install -y qemu-system-x86 gdb
- 下載linux和busybox
$ mkdir linux
$ cd linux
$ wget https://www.busybox.net/downloads/busybox-1.20.1.tar.bz2
$ wget https://mirrors.edge.kernel.org/pub/linux/kernel/v2.6/linux-2.6.26.tar.bz2
$ tar -xf busybox-1.20.1.tar.bz2
$ tar -xf linux-2.6.26.tar.bz2
Linux編譯
- 打patch
在linux-2.6.26文件夾下創(chuàng)建文件fix.patch漓拾,復(fù)制以下內(nèi)容
diff -Naur linux-2.6.26/arch/x86/lib/copy_user_64.S linux-2.6.26-2/arch/x86/lib/copy_user_64.S
--- linux-2.6.26/arch/x86/lib/copy_user_64.S 2008-07-13 21:51:29.000000000 +0000
+++ linux-2.6.26-2/arch/x86/lib/copy_user_64.S 2021-04-22 07:04:49.894796787 +0000
@@ -341,7 +341,7 @@
11: pop %rax
7: ret
CFI_ENDPROC
-END(copy_user_generic_c)
+END(copy_user_generic_string)
.section __ex_table,"a"
.quad 1b,3b
diff -Naur linux-2.6.26/arch/x86/vdso/Makefile linux-2.6.26-2/arch/x86/vdso/Makefile
--- linux-2.6.26/arch/x86/vdso/Makefile 2008-07-13 21:51:29.000000000 +0000
+++ linux-2.6.26-2/arch/x86/vdso/Makefile 2021-04-22 07:05:29.090798510 +0000
@@ -25,7 +25,7 @@
export CPPFLAGS_vdso.lds += -P -C
-VDSO_LDFLAGS_vdso.lds = -m elf_x86_64 -Wl,-soname=linux-vdso.so.1 \
+VDSO_LDFLAGS_vdso.lds = -m64 -Wl,-soname=linux-vdso.so.1 \
-Wl,-z,max-page-size=4096 -Wl,-z,common-page-size=4096
$(obj)/vdso.o: $(src)/vdso.S $(obj)/vdso.so
@@ -69,7 +69,7 @@
vdso32-images = $(vdso32.so-y:%=vdso32-%.so)
CPPFLAGS_vdso32.lds = $(CPPFLAGS_vdso.lds)
-VDSO_LDFLAGS_vdso32.lds = -m elf_i386 -Wl,-soname=linux-gate.so.1
+VDSO_LDFLAGS_vdso32.lds = -m32 -Wl,-soname=linux-gate.so.1
# This makes sure the $(obj) subdirectory exists even though vdso32/
# is not a kbuild sub-make subdirectory.
diff -Naur linux-2.6.26/kernel/mutex.c linux-2.6.26-2/kernel/mutex.c
--- linux-2.6.26/kernel/mutex.c 2008-07-13 21:51:29.000000000 +0000
+++ linux-2.6.26-2/kernel/mutex.c 2021-04-22 07:06:51.646802139 +0000
@@ -58,7 +58,7 @@
* We also put the fastpath first in the kernel image, to make sure the
* branch is predicted by the CPU as default-untaken.
*/
-static void noinline __sched
+static __used void noinline __sched
__mutex_lock_slowpath(atomic_t *lock_count);
/***
@@ -95,7 +95,7 @@
EXPORT_SYMBOL(mutex_lock);
#endif
-static noinline void __sched __mutex_unlock_slowpath(atomic_t *lock_count);
+static __used noinline void __sched __mutex_unlock_slowpath(atomic_t *lock_count);
/***
* mutex_unlock - release the mutex
@@ -270,7 +270,7 @@
/*
* Release the lock, slowpath:
*/
-static noinline void
+static __used noinline void
__mutex_unlock_slowpath(atomic_t *lock_count)
{
__mutex_unlock_common_slowpath(lock_count, 1);
@@ -315,7 +315,7 @@
}
EXPORT_SYMBOL(mutex_lock_killable);
-static noinline void __sched
+static __used noinline void __sched
__mutex_lock_slowpath(atomic_t *lock_count)
{
struct mutex *lock = container_of(lock_count, struct mutex, count);
diff -Naur linux-2.6.26/Makefile linux-2.6.26-2/Makefile
--- linux-2.6.26/Makefile 2008-07-13 21:51:29.000000000 +0000
+++ linux-2.6.26-2/Makefile 2021-04-22 07:03:43.150793853 +0000
@@ -214,8 +214,8 @@
HOSTCC = gcc
HOSTCXX = g++
-HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
-HOSTCXXFLAGS = -O2
+HOSTCFLAGS = -Wall -Wstrict-prototypes -O1 -fomit-frame-pointer
+HOSTCXXFLAGS = -O1
# Decide whether to build built-in, modular, or both.
# Normally, just do built-in.
@@ -502,9 +502,9 @@
all: vmlinux
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
-KBUILD_CFLAGS += -Os
+KBUILD_CFLAGS += -O1
else
-KBUILD_CFLAGS += -O2
+KBUILD_CFLAGS += -O1
endif
ifneq (CONFIG_FRAME_WARN,0)
$ patch -p1 < fix.patch
如果patch提示失敗,有可能是因?yàn)榘裦ix.patch里面的tab轉(zhuǎn)化成空格了戒祠,這時(shí)候你可以按照上面的內(nèi)容直接修改源文件骇两。
- 設(shè)置編譯配置
$ make ARCH=i386 defconfig
$ make ARCH=i386 menuconfig
如果提示
Your display is too small to run Menuconfig! It must be at least 19 lines by 80 columns.
那就說明你的命令行窗口太小,最好切到全屏姜盈,再重新配置
開啟debug信息選項(xiàng)
選擇界面上下進(jìn)行移動(dòng)低千,tab切換下方選項(xiàng)
- 開啟編譯
$ make ARCH=i386 -j2
-jN 代表多任務(wù)并行化,數(shù)字一般為cpu核數(shù)*2
編譯成功后顯示
Root device is (252, 0)
Setup is 12288 bytes (padded to 12288 bytes).
System is 2844 kB
CRC 31a57b1f
Kernel: arch/x86/boot/bzImage is ready (#1)
編譯時(shí)長跟機(jī)器配置有關(guān),一般幾分鐘到十幾分鐘示血。
我用的是18款的mbp 2.2 GHz 六核I7棋傍,創(chuàng)建的虛擬機(jī)是4核,編譯用時(shí)5m10s难审。
如果覺得編譯速度太慢瘫拣,有以下幾種方法可以加快速度:
- 改變虛擬機(jī)的核數(shù),加大-jN的數(shù)目告喊;
- 使用tmpfs文件系統(tǒng)麸拄,將代碼直接放到內(nèi)存中;
- 使用ccache緩存每次編譯的結(jié)果黔姜。
創(chuàng)建根文件系統(tǒng)
- 制作鏡像
$ cd ..
$ dd if=/dev/zero of=initrd.img count=1024 bs=4096
$ mkfs.ext2 initrd.img
$ mkdir rootfs
$ mount -o loop initrd.img rootfs/
- 創(chuàng)建字符設(shè)備
$ mkdir rootfs/dev
$ mknod rootfs/dev/console c 5 1
$ mknod rootfs/dev/ram b 1 0
- 打patch
$ cd busybox-1.20.1
$ echo """diff -Naur busybox-1.20.1/include/libbb.h busybox-1.20.1-2/include/libbb.h
--- busybox-1.20.1/include/libbb.h 2012-05-28 00:46:41.000000000 +0000
+++ busybox-1.20.1-2/include/libbb.h 2021-04-21 07:55:27.526183582 +0000
@@ -12,6 +12,8 @@
#include "platform.h"
+
+#include <sys/resource.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
""" > fix.patch
$ patch -p1 < fix.patch
- 修改Makefile
vi中輸入292G 跳到292行拢切,在最后加上-m32
$ vi Makefilie
CC = $(CROSS_COMPILE)gcc -m32
- 設(shè)置編譯選項(xiàng)
$ make defconfig
$ make menuconfig
選擇靜態(tài)鏈接
關(guān)閉shell → job contorl選項(xiàng)
- 編譯安裝busybox
$ make -j2
$ make CONFIG_PREFIX=../rootfs install
安裝完成后,可以在rootfs目錄看到
$ cd ..
$ ls -lah rootfs/
total 20
drwxr-xr-x 5 root root 4096 Apr 21 08:05 ./
drwxr-xr-x 7 root root 4096 Apr 21 08:05 ../
drwxr-xr-x 2 root root 4096 Apr 21 08:05 bin/
lrwxrwxrwx 1 root root 11 Apr 21 08:05 linuxrc -> bin/busybox*
drwxr-xr-x 2 root root 4096 Apr 21 08:05 sbin/
drwxr-xr-x 4 root root 4096 Apr 21 08:05 usr/
- 最后卸載rootfs
$ umount rootfs
啟動(dòng)系統(tǒng)
$ qemu-system-x86_64 \
-nographic \
-kernel ./linux-2.6.26/arch/x86/boot/bzImage \
-initrd ./initrd.img \
-append "root=/dev/ram init=/bin/sh console=ttyS0"
使用Ctrl a+x來退出qemu
input: ImExPS/2 Generic Explorer Mouse as /class/input/input2
RAMDISK: ext2 filesystem found at block 0
RAMDISK: Loading 4096KiB [1 disk] into ram disk... done.
VFS: Mounted root (ext2 filesystem) readonly.
Freeing unused kernel memory: 448k freed
/ #
VFS: Mounted root (ext2 filesystem) readonly.
Freeing unused kernel memory: 448k freed
Warning: unable to open an initial console.
Failed to execute /bin/ash. Attempting defaults...
Kernel panic - not syncing: No init found. Try passing init= option to kernel.
如果遇到這個(gè)錯(cuò)誤秆吵,一般是根文件系統(tǒng)沒創(chuàng)建對(duì)失球,請(qǐng)根據(jù)上面的步驟重新創(chuàng)建一遍。
VFS: Mounted root (ext2 filesystem) readonly.
Freeing unused kernel memory: 448k freed
如果提示VFS錯(cuò)誤帮毁,則是Linux內(nèi)核編譯的時(shí)候沒有開啟ext2的配置,可以回到make menuconfig中開啟這個(gè)選項(xiàng)豺撑。
/bin/ash: can't access tty; job control turned off
如果提示job control烈疚,則需要回到busygox編譯那里關(guān)閉這個(gè)選項(xiàng)。
GDB Debug
- 開啟debug選項(xiàng)
$ qemu-system-x86_64 \
-nographic \
-kernel ./linux-2.6.26/arch/x86/boot/bzImage \
-initrd ./initrd.img \
-append "root=/dev/ram init=/bin/ash console=ttyS0" -s -S
- 開啟gdb
在另外一個(gè)終端開啟
$ cd linux
$ gdb --dir=./linux-2.6.26
(gdb) file linux-2.6.26/vmlinux
(gdb) target remote :1234
(gdb) hb start_kernel
(gdb) c
gdb -tui 可以開啟窗口實(shí)時(shí)查看代碼
只有使用hb才能打斷點(diǎn)聪轿,這是gdb的bug導(dǎo)致的gdbserver inside qemu does not stop on breakpoints
覺得每次輸入file和targer麻煩的爷肝,可以創(chuàng)建gdb的默認(rèn)啟動(dòng)命令
$ echo """ file linux-2.6.26/vmlinux target remote :1234 """ > /root/.gdbinit $ echo """ set auto-load safe-path / """ > .gdbinit
Vscode
如果你是使用vscode瀏覽源碼,可以參考這個(gè)文章vscode在linux下搭建內(nèi)核驅(qū)動(dòng)開發(fā)環(huán)境
參考鏈接:
學(xué)習(xí)ulk3,搭建linux2.6內(nèi)核的調(diào)試環(huán)境
elf_i386或elf_x86_64:沒有那個(gè)文件或目錄 解決方法
內(nèi)核2.6.22.6編譯出現(xiàn) undefined reference to __mutex_unlock_slowpath
linux 內(nèi)核編譯錯(cuò)誤 .size expression for copy_user_generic_c does not evaluate to a constant