核心流程
首先,本文主要講述如何編譯Linux
核心並在qemu
虛擬機器上執行。這裡針對的架構是aarch64
。
本文的實驗平臺是Ubuntu 16.04
。
為了達成目標,我們需要有qemu
、buildroot
和linux
安裝包或原始碼。
首先確保qemu-system-aarch64
命令可用,可以通過在命令列執行qemu-system-aarch64 --version
判斷。
下載buildroot
原始碼,連結見下文平臺工具。假設其絕對路徑儲存在變數BUILD_ROOT_PATH
中。執行以下命令
cd $BUILD_ROOT_PATH
make menuconfig
在彈出的配置介面中,設定Target option ---> Target Architecture
為AArch64 (little endian)
;設定Toolchain ---> Toolchain type
為External toolchain
,這時我們可以看到Toolchain ---> Toolchain
的值為linaro AArch64 xxxx.xx
;設定System configuration ---> Enable root login with password
開啟,並設定System configuration ---> Root password
為xxxx
(任意的你喜歡的密碼);設定System configuration ---> Run a getty (login prompt) after boot ---> TTY port
的值為ttyAMA0
(這一條非常重要,不然虛擬機器可能啟動不了);設定Target packages ---> Show packages that are also provided by busybox
開啟;設定Target packages ---> Debugging, profiling and benchmark ---> strace
開啟;設定Filesystem images ---> cpio the root filesystem
開啟。
在配置完成之後,執行
make
注意:這裡可能需要配置wget
代理。
生成的rootfs.cpio
在目錄$BUILD_ROOT_PATH/output/images
下面。
下載Linux
核心原始碼,連結見下文平臺工具。假設其絕對路徑儲存在變數LINUX_KERNEL_PATH
中。執行以下命令
cd $LINUX_KERNEL_PATH
ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make defconfig
vim .config
在開啟的檔案中,找到CONFIG_CMDLINE
這個配置選項,設定其值為console=ttyAMA0
;找到CONFIG_INITRAMFS_SOURCE
這個配置選項,設定其值為$BUILD_ROOT_PATH/output/images/rootfs.cpio
(注意,這裡要自己展開變數BUILD_ROOT_PATH
);設定CONFIG_DEBUG_INFO
配置項為y
。
配置結束後,執行以下命令
ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make -j16
執行結束後,我們可以看到生成了一些檔案,其中包括$LINUX_KERNEL_PATH/vmlinux
和$LINUX_KERNEL_PATH/arch/arm64/boot/Image
在當前目錄建立一個shell
指令碼,避免重複輸入指令。其檔名為start.sh
,其內容為
qemu-system-aarch64 -machine virt -cpu cortex-a57 \
-machine type=virt -nographic -smp 1 \
-m 2048 \
-kernel ./arch/arm64/boot/Image \
--apend "console=ttyAMA0" \
$1 $2
執行./startup.sh
,這是可以用qemu
啟動linux
核心。在進入命令列之前,需要輸入buildroot login:
的值,其值為root
,然後需要輸入Password:
,這是前文構建rootfs.cpio
的時候,配置項System configuration ---> Root password
的值。然後就可以進入命令列執行以下常用命令了。(注意,需要先cd /
)
如果要退出qemu
,可以先按Ctrl + A
,然後按X
。
為了在主機和qemu
虛擬機器之間共享檔案,我們可以建立一個目錄,其絕對路徑為SHARED_FILE_PATH
。然後執行以下命令
cd $BUILD_ROOT_PATH
vim .config
修改BR2_ROOTFS_OVERLAY
配置項的值為$SHARED_FILE_PATH
(注意,自行展開變數)。
儲存後執行以下命令重新建立 rootfs.cpio
。
rm $BUILD_ROOT_PATH/output/images/rootfs.*
make
然後需要重新編譯核心,即
cd $LINUX_KERNEL_PATH
ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make -j16
依然通過./start.sh
啟動虛擬機器跑linux
核心,進入命令列後,執行cd /; ls
,我們可以看到$SHARED_FILE_PATH
目錄下的檔案。
重新執行./start.sh -s -S
進入qemu
的除錯狀態,然後開一個新的shell
,輸入命令
cd $LINUX_KERNEL_PATH
aarch64-linux-gnu-gdb ./vmlinux -ex "target remote :1234"
現在,可以像以往一樣使用gdb
進行除錯了……
下面介紹如何編譯一個linux
核心模組。
cd $SHARED_FILE_PATH
vim hello.c
hello.c
的內容為
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
int helloinit(void)
{
printk(KERN_DEBUG "hello world !!\n");
return 0;
}
void helloexit(void)
{
printk(KERN_DEBUG "goodbye world !!\n");
}
module_init(helloinit);
module_exit(helloexit);
MODULE_LICENSE("GPL");
然後在當前目錄建立Makefile
內容如下
ifneq (${KERNELRELEASE},)
obj-m := hello.o
else
KERNEL_SOURCE := $LINUX_KERNEL_PATH # 注意自行展開變數LINUX_KERNEL_PATH
PWD := $(shell pwd)
default:
${MAKE} -C ${KERNEL_SOURCE} M=${PWD} modules
clean:
${MAKE} -C ${KERNEL_SOURCE} M=${PWD} clean
endif
然後執行交叉編譯命令
ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make
OK,重新編譯rootfs.cpio
和linux
核心,然後執行qemu
。在進入linux
命令列後,執行以下命令
cd /
insmod hello.ko
dmesg | tail # 可以看到列印資訊 hello world !!
rmmod hello.ko
dmesg | tail # 可以看到列印資訊 goodbye world !!
相關經驗
Linux Kernel
- 使用
qemu
並開啟gdb server
功能之後,在gdb
視窗輸入b start_kernel
,進入最初的核心初始化函式。
平臺工具
參考
- Starting Linux kernel exploration
- Debugging an ARM64 linux kernel using QEMU
- Writing kernel modules
- Debugging Kernel and Modules using GDB