实验环境:ubuntu20.04, qemu 7.0.0 , busybox 1.35.0
lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 20.04.6 LTS
Release: 20.04
Codename: focal
uname -a
Linux ostest-MS-7B09 5.15.0-58-generic #64~20.04.1-Ubuntu SMP Fri Jan 6 16:42:31 UTC 2023
x86_64 x86_64 x86_64 GNU/Linux
gcc -v
gcc version 10.3.0 (Ubuntu 10.3.0-1ubuntu1~20.04)
直接安装相关包
sudo apt install -y qemu-system-x86 qemu-system-arm
# 首先安装 ninja
apt-get install re2c
git clone https://gitee.com/gitmirror/ninja.git
cd ninja
python3 ./configure.py --bootstrap
sudo cp ./ninja /usr/bin
# 测试
ninja --version
# 安装依赖包
sudo apt-get install build-essential zlib1g-dev pkg-config libglib2.0-dev
sudo apt-get install binutils-dev libboost-all-dev autoconf libtool libssl-dev libpixman-1-dev
wget https://download.qemu.org/qemu-7.0.0.tar.xz
tar xJf qemu-7.0.0.tar.xz
cd qemu-7.0.0
# 编译配置
# --enable-trace-backends 指定追踪输出方法 simple 二进制输出
# https://qemu.readthedocs.io/en/latest/devel/tracing.html
# arm64
./configure --target-list=aarch64-softmmu,aarch64-linux-user --enable-debug --enable-debug-info --enable-trace-backends=simple
# x86架构
./configure --target-list=x86_64-softmmu --enable-debug --enable-debug-info --enable-trace-backends=simple --enable-kvm
# --enable-debug --enable-debug-info 能够调试qemu
make -j$(nproc)
The Linux Kernel Archives
参考:
linux 5.x下载地址
# wget https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.10.tar.gz
# 将压缩包放到自己指定的目录,解压
tar -zxf linux-5.10.tar.gz
# centos7 下安装依赖,遇到缺啥依赖,就安装啥
sudo yum -y install flex bison libssl-dev \
openssl-devel elfutils-libelf-devel ncurses-devel
# ubuntu20.04 下
sudo apt install -y build-essential flex bison \
libssl-dev libelf-dev libncurses-dev dwarves zstd \
gcc make openssl libc6-dev
# 编码过程中遇到,cc1: error: -Werror=date-time: no option -Wdate-time
# 需要使用高版本的gcc[1]
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
scl enable devtoolset-9 bash
# 需要注意的是scl命令启用只是临时的,退出shell或重启就会恢复原系统gcc版本。
# 如果要长期使用gcc 9.3的话[1]:
echo "source /opt/rh/devtoolset-9/enable" >>/etc/profile
# 来清除以前的配置,回到默认配置
make mrproper
# 复制内核的配置信息(复用本机的默认config文件)
cp /boot/config-`uname -r` ./.config
# 或者生成相应架构的配置信息
make x86_64_defconfig
# 根据机器默认生成
make defconfig
# 修改配置信息,修改完后 EXIT -> SAVE
make menuconfig
配置选项
Kernel hacking
-> Compile time checks and compiler options -
> Provide GDB scripts for kernel debugging
编译选项
CONFIG_GDB_SCRIPTS=y
# CONFIG_DEBUG_INFO_REDUCED is not set
CONFIG_FRAME_POINTER=y
# 查看cpu 信息
lscpu
# 查看cpu线程数
nproc
# -j1 表示使用线程编译 将配置信息输出到文件,用于sourceinsight
make -j$(nproc) > build_log.txt
# 实时查看文件内容 -f 循环读取
tail -f build_log.txt
# 构建所有目标
make all
# 构建内核映像
make bzImage
# 构建所有驱动
make modules
#ubuntu 20.04 出现一下错误
# 1) 由于是直接复制的默认配置, 可能会报没有规则可制作目标“debian/canonical-certs.pem”
vim ./.config
CONFIG_MODULE_SIG_KEY=""
CONFIG_SYSTEM_TRUSTED_KEYRING=n
CONFIG_SYSTEM_TRUSTED_KEYS=""
# 2) BTF: .tmp_vmlinux.btf: pahole (pahole) is not available
sudo apt install dwarves
# 3) /bin/sh: 1: zstd: not found
sudo apt install zstd
精确制导 — 把linux内核源码中需要的代码导入Source Insight
这种方式会直接安装该系统上,重启时需要根据grub 选择相应的内核,如果出现问题可能需要修改grub
# 安装内核
make modules_install
# 安装驱动
make install
# 重启,选择编译好的新的内核版本
reboot
linux内核禁止优化的设置
kernel hacking: GCC optimization for better debug experience (-Og)
奔跑吧Linux内核
[v5,4/4] kernel hacking: new config CC_OPTIMIZE_FOR_DEBUGGING to apply GCC -Og optimization
kernel hacking: support building kernel with -Og optimization level
vim Makefile
:735
ifdef CONFIG_CC_OPTIMIZE_FOR_DEBUGGING
KBUILD_CFLAGS += -Og $(call cc-disable-warning,maybe-uninitialized,)
KBUILD_CFLAGS += $(call -fno-inline-functions,-fno-inline-small-functions,-fno-inline-functions-called-once,)
else
ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE
KBUILD_CFLAGS += -O2
else ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3
KBUILD_CFLAGS += -O3
else ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
KBUILD_CFLAGS += -Os
endif
添加选项配置
vim init/Kconfig
# 1301
choice
prompt "Compiler optimization level"
default CC_OPTIMIZE_FOR_PERFORMANCE
config CC_OPTIMIZE_FOR_PERFORMANCE
bool "Optimize for performance (-O2)"
help
This is the default optimization level for the kernel, building
with the "-O2" compiler flag for best performance and most
helpful compile-time warnings.
config CC_OPTIMIZE_FOR_PERFORMANCE_O3
bool "Optimize more for performance (-O3)"
depends on ARC
help
Choosing this option will pass "-O3" to your compiler to optimize
the kernel yet more for performance.
config CC_OPTIMIZE_FOR_SIZE
bool "Optimize for size (-Os)"
help
Choosing this option will pass "-Os" to your compiler resulting
in a smaller kernel.
config CC_OPTIMIZE_FOR_DEBUGGING
bool "Optimize for better debugging experience (-Og)"
help
This will apply GCC '-Og' optimization level which is supported
since GCC 4.8. This optimization level offers a reasonable level
of optimization while maintaining fast compilation and a good
debugging experience. It is similar to '-O1' while preferring to
keep debug ability over runtime speed. The overall performance
will drop a bit (~6%).
Use only if you want to debug the kernel, especially if you want
to have better kernel debugging experience with gdb facilities
like kgdb or qemu. If enabling this option breaks your kernel,
you should either disable this or find a fix (mostly in the arch
code).
endchoice
面对 error: call to ‘__bad_copy_from’ declared with attribute error: copy source size is too small
报错, 还需要
vim include/linux/compiler-gcc.h
# :51
#ifndef CONFIG_CC_OPTIMIZE_FOR_DEBUGGING
#define __compiletime_warning(message) __attribute__((__warning__(message)))
#define __compiletime_error(message) __attribute__((__error__(message)))
#endif
make menuconfig
:
General setup --->
Compiler optimization level (Optimize for better debugging experience (-Og)) --->
Linux启动阶段,boot loader加载完内核文件之后,便开始挂载磁盘根文件系统。挂载操作需要磁盘驱动,所以挂载前要先加载驱动。但是驱动位于/lib/modules,不挂载磁盘就访问不到,形成了一个死循环。initramfs根文件系统就可以解决这个问题,其中包含必要的设备驱动和工具,boot loader会加载initramfs到内存中,内核将其挂载到根目录,然后运行/init初始化脚本,去挂载真正的磁盘根文件系统。
BusyBox combines tiny versions of many common UNIX utilities into a single small executable.
# 下载busybox并解压
wget https://busybox.net/downloads/busybox-1.35.0.tar.bz2
tar -xjf ./busybox-1.35.0.tar.bz2
mv busybox-1.35.0 busybox
cd busybox
# 生成默认配置
make defconfig
# 微调,选项静态库
make menuconfig
# 进入图形界面,选择
-> Settings
-> Build Options
[*] Build BusyBox as a static binary (no shared libs)
# 编译
make -j$(nproc)
# 默认路径为当期目录的_install文件夹
make install
# 运行脚本生成根文件系统镜像
chmod +x makerootfs.sh
./makerootfs.sh
makerootfs.sh 脚本内容
#!/bin/sh
# 参考 https://github.com/chyyuu/tiny_linux_ChildIsTalent
# 参考 https://blog.csdn.net/feinifi/article/details/125360159 脚本编写
# 设置rootdir 根目录,下面放置busybox 等目录
rootdir=
# 切换到编译安装成功后的目录
cd ${rootdir}/busybox/_install
# init 是程序运行的第一个程序,这里运行的就是busybox程序,采用符号链接的形式
ln -s bin/busybox init
mkdir -p dev etc home lib mnt proc root sys tmp var
mkdir -p usr/bin usr/sbin
mkdir -p etc/init.d
# 语法
# :::
# id : /dev/id
# runlevels : 忽略
# action : 何时执行,有以下选项 sysinit, respawn, askfirst, wait, once,restart, ctrlaltdel, and shutdown
# sysinit: 指定系统的启动脚本, respawn 指定打开一个登录会话, restart 指定第一个进程
# ctrlaltdel: 指定了当按下ctrl+alt+del组合键时的执行命令
# process : 应用程序或脚本
# inittab文件:系统启动后访问的第一个脚本文件,后续启动的文件都由它指定
cat>>etc/inittab<<EOF
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
::shutdown:/sbin/swapoff -a
EOF
chmod 755 etc/inittab
cat>>etc/init.d/rcS<<EOF
#!/bin/sh
mount proc
mount -o remount,rw /
mount -a
# 设置网络连接
/sbin/ifconfig lo 127.0.0.1 up
/sbin/route add 127.0.0.1 lo &
ifconfig eth0 up
ip addr add 10.0.2.15/24 dev eth0
ip route add default via 10.0.2.2
clear
echo "test ......"
EOF
chmod 755 etc/init.d/rcS
# https://blog.csdn.net/pzs0221/article/details/104567123
# 定义了文件系统的各个“挂载点”,需要与实际的系统相配合
#
cat>>etc/fstab<<EOF
# /etc/fstab
proc /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
devtmpfs /dev devtmpfs defaults 0 0
EOF
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ${rootdir}/initramfs.cpio.gz
# https://itas109.blog.csdn.net/article/details/107737843
# https://blog.csdn.net/itas109/article/details/107968533
# 上述方法挂载根文件失败: FS: Unable to mount root fs on unknown-block(0,0) | (1,0)
# 参考解决方法 https://blog.csdn.net/suiyuan19840208/article/details/7237541
# 在用busybox制作系统过程中遇到的问题 https://blog.csdn.net/weixin_30606669/article/details/99459143
# https://www.cnblogs.com/hellogc/p/7482066.html
参考Linux kernel + busybox自制Linux系统
注意在编译Linux时记得修改配置
General setup --->
# 默认选择的
[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
Device Drivers --->
[*] Block devices --->
# Default RAM disk size 默认是4086,选择后修改大小
<*> RAM block device support
(16) Default number of RAM disks
(65536) Default RAM disk size (kbytes)
原文使用的ext3系统,如果需要使用记得配置相应的文件系统支持
#!/bin/sh
# 切换到编译安装成功后的目录
cd _install
mkdir -p dev etc home lib mnt proc root sys tmp var
mkdir -p etc/init.d
# inittab文件:系统启动后访问的第一个脚本文件,后续启动的文件都由它指定
cat>>etc/inittab<<EOF
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::askfirst:-/bin/sh
::cttlaltdel:/bin/umount -a -r
EOF
chmod 755 etc/inittab
cat>>etc/init.d/rcS<<EOF
echo "----------mount all in fstab----------------"
/bin/mount -a #读取/etc/fstab,加载文件系统
mkdir -p /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
echo "****************Hello itas109******************"
echo "Kernel Version:linux-5.4.50"
echo "***********************************************"
EOF
chmod 755 etc/init.d/rcS
# https://blog.csdn.net/pzs0221/article/details/104567123
# 定义了文件系统的各个“挂载点”,需要与实际的系统相配合
#
cat>>etc/fstab<<EOF
#device mount-point type option dump fsck
proc /proc proc defaults 0 0
temps /tmp rpoc defaults 0 0
none /tmp ramfs defaults 0 0
sysfs /sys sysfs defaults 0 0
mdev /dev ramfs defaults 0 0
EOF
cd dev
sudo mknod console c 5 1
sudo mknod null c 1 3
sudo mknod tty1 c 4 1
cd ../../
sudo rm -rf rootfs.ext4
sudo rm -rf fs
dd if=/dev/zero of=./rootfs.ext4 bs=1M count=32
mkfs.ext4 rootfs.ext4
mkdir fs
sudo mount -o loop rootfs.ext4 ./fs
sudo cp -rf ./_install/* ./fs
sudo umount ./fs
gzip --best -c rootfs.ext4 > rootfs.img.gz
qemu启动
# root="" 指定根文件系统所在的设备
./build/qemu-system-x86_64 \
-kernel bzImage \
-initrd rootfs.img.gz \
-append "root=/dev/ram init=/linuxrc console=ttyS0" \
-nographic
参考使用 qemu 搭建内核开发环境
路径需要做相应的修改
# 创建一个磁盘镜像
qemu-img create -f raw disk.raw 512M
# 格式化
mkfs -t ext4 ./disk.raw
# 以 loop 方式将磁盘镜像文件挂载到一个目录上,方便操作镜像中的文件
sudo mount -o loop ./disk.raw ./img
# 在Linux源码路径 将Linux内核模块 安装到镜像中
sudo make modules_install INSTALL_MOD_PATH=./img # 指定安装路径
# 在busybox目录中,编译后将其安装到其磁盘镜像中
make CONFIG_PREFIX=./img install
# 按上面的方式添加其他启动文件,挂载文件系统
# qemu启动
./build/qemu-system-x86_64 \
-M pc-q35-2.10 \
-m 512M \
-smp 4\
--enable-kvm \
-kernel bzImage \
-drive format=raw,file=./disk.raw \
-append "init=/linuxrc root=/dev/sda console=ttyS0" \
-nographic
debootstrap是debian/ubuntu下的一个工具,用来构建一套基本的系统(根文件系统)
参考Ubuntu 20.04使用qemu搭建ARM64 Linux系统
sudo apt install -y debootstrap
# 查看支持的发行版本
ls /usr/share/debootstrap/scripts
sudo debootstrap --arch [平台] [发行版本代号] [构建目录] [镜像地址]
sudo debootstrap --arch=amd64 --foreign focal \
/media/new/linux/rootfs http://mirrors.ustc.edu.cn/ubuntu/
sudo chroot rootfs/ debootstrap/debootstrap --second-stage
# 提示
I: Base system installed successfully.
# 额外安装软件包
sudo chroot ./rootfs/
# 设置密码,方便后面登录
passwd root
# 按需要安装包,或者拷贝程序
# 制作ext4文件系统
dd if=/dev/zero of=linux_rootfs.ext4 bs=1M count=2048
mkfs.ext4 linux_rootfs.ext4
mkdir -p tmpfs
sudo mount -t ext4 linux_rootfs.ext4 tmpfs/ -o loop
sudo cp -af rootfs/* tmpfs/
sudo umount tmpfs
sudo chmod 777 linux_rootfs.ext4
运行:
${OS_TEST_DIR}/qemu7//build/qemu-system-x86_64 \
--enable-kvm \
-cpu host \
-kernel bzImage \
-hda linux_rootfs.ext4 \
-append "noinintrd root=/dev/sda rootfstype=ext4 rw console=ttyS0" \
-nographic
${OS_TEST_DIR}/qemu7//build/qemu-system-x86_64 \
--enable-kvm \
-cpu host \
-kernel bzImage \
-hda linux_rootfs.ext4 \
-append "noinintrd root=/dev/sda rootfstype=ext4 rw " \
-vnc :1\
-monitor stdio
# 先将文件拷贝出来
cp ./linux-5.10/arch/x86_64/boot/bzImage ./
# ${qemu} 是qemu-system-x86_64的执行路径,kernel,initrd修改成自己的
${qemu}/build/qemu-system-x86_64 \
--enable-kvm \
-cpu host \
-kernel bzImage \
-initrd initramfs.cpio.gz \
-append "console=ttyS0" \
-nographic
# 如果使用非图形启动,在使用ssh终端需要相应的支持,此时可以加上 --vnc:1 命令,使用vncview连接
--enable-kvm
: 如果虚拟机架构与宿主机相同,可以执行加速
-kernel
: 指定编译好的内核;
-initrd
: 指定initramfs,initrd 是一个 mini 根文件系统,提供一些必要的工具和驱动,主要起引导作用,帮助 OS 顺利过渡到真实的根文件系统。这里制作的 initrd 不仅起引导作用,同时也是最终的根文件系统
-nographic
: 取消图形输出窗口;
append "console=ttyS0"
: 将输出重定向到console,将会显示在标准输出stdio
[ 5.280033] VFS: Cannot open root device "(null)" or unknown-block(0,0): error -6
[ 5.280435] Please append a correct "root=" boot option; here are the available partitions:
[ 5.281893] 0b00 1048575 sr0
[ 5.282027] driver: sr
xffffffffbfffffff)nel Offset: 0x23600000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0
--- 5.295688] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) ]
# 挂载出错,在使用-append "init=/linuxrc" 也不行
# 解决方法是需要加上 ln -s bin/busybox init
# 关机
poweroff
参考QEMU模拟Cortex-A9运行U-boot和Linux
如果报 -sh: xxx not found
大致是编译的程序是需要动态链接的,但busybox构建的没有包含该文件需要的链接库
gcc -static
-static 选项对于自定义的链接库程序,可参考Linux下修改可执行程序或者库的动态链接库的路径
sudo apt install -y chrpath
chrpath -r ./lib xxx.so #其中./lib是需要修改的路径,xxx.so为源路径库
chrpath -r ./lib test #其中./lib是需要修改的路径,test可执行文件
通过LD_LIBRARY_PATH 环境变量
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:$(pwd)/mylib
ld: final link failed: No space left on device
, 虚拟机的磁盘不够需要扩容,参考VMware虚拟机扩展内存和磁盘参考qemu + gdb + busybox 内核调试流程
${qemu}/build/qemu-system-x86_64 \
-s -S\
-kernel bzImage \
-initrd initramfs.cpio.gz \
-append "console=ttyS0 nokaslr" \
-nographic
# 新开一个端口
gdb
# 读取内核符号表
file ./linux-5.10/vmlinux
# 或者 gdb 启动时直接指定
# gdb .linux/linux-5.10/vmlinux
# 远程连接
target remote localhost:1234
# 打断点
b start_kernel
# 开始运行
c
-S
cpu 启动时暂停。
-s
是 -gdb tcp::1234
命令简写,启动 gdbserver 并监听 1234 端口
nokaslr
关闭内核地址随机化,不添加该参数,内核地址随机化,使得 gdb 从 vmlinux 计算得到的断点位置并不是实际位置,最终会导致断点失效
参考 Debugging kernel and modules via gdb
# 在linux 目录
make scripts_gdb
CALL scripts/checksyscalls.sh
CALL scripts/atomic/check-atomics.sh
GEN scripts/gdb/linux/constants.py
# 在 ~/.gdbinit 添加
add-auto-load-safe-path /media/new/linux/linux-5.10/
linux 脚本提供额外的命令
# List of commands and functions
(gdb) apropos lx
function lx_clk_core_lookup -- Find struct clk_core by name
function lx_current -- Return current task.
function lx_device_find_by_bus_name -- Find struct device by bus and name (both strings)
function lx_device_find_by_class_name -- Find struct device by class and name (both strings)
function lx_module -- Find module by name and return the module variable.
function lx_per_cpu -- Return per-cpu variable.
function lx_rb_first -- Lookup and return a node from an RBTree
function lx_rb_last -- Lookup and return a node from an RBTree.
function lx_rb_next -- Lookup and return a node from an RBTree.
function lx_rb_prev -- Lookup and return a node from an RBTree.
function lx_task_by_pid -- Find Linux task by PID and return the task_struct variable.
function lx_thread_info -- Calculate Linux thread_info from task variable.
function lx_thread_info_by_pid -- Calculate Linux thread_info from task variable found by pid
lx-clk-summary -- Print clk tree summary
lx-cmdline -- Report the Linux Commandline used in the current kernel.
lx-configdump -- Output kernel config to the filename specified as the command
lx-cpus -- List CPU status arrays
lx-device-list-bus -- Print devices on a bus (or all buses if not specified)
lx-device-list-class -- Print devices in a class (or all classes if not specified)
lx-device-list-tree -- Print a device and its children recursively
lx-dmesg -- Print Linux kernel log buffer.
lx-fdtdump -- Output Flattened Device Tree header and dump FDT blob to the filename
lx-genpd-summary -- Print genpd summary
lx-iomem -- Identify the IO memory resource locations defined by the kernel
lx-ioports -- Identify the IO port resource locations defined by the kernel
lx-list-check -- Verify a list consistency
lx-lsmod -- List currently loaded modules.
lx-mounts -- Report the VFS mounts of the current process namespace.
lx-ps -- Dump Linux tasks.
lx-symbols -- (Re-)load symbols of Linux kernel and currently loaded modules.
lx-timerlist -- Print /proc/timer_list
lx-version -- Report the Linux Version of the current kernel.