编译调试内核方法
编译内核
一、从官网上下载源码,解压。
建议下载的内核与本机内核版本一致,这样可以直接使用本机的配置文件/boot/config-{version_name}
进行编译。{version_name}
字符串与本机的linux内核版本相关,如/boot/config-5.4.0
。
二、编译之前,首先安装编译需要的依赖包
ubuntu下:
1 | sudo apt install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc flex libelf-dev bison pkg-config dwarves |
三、进入下载的源码根目录,并将本机的配置信息拷贝到此处,命名为.config
1 | cp -v /boot/config-`uname -r` .config |
使用这种方式可以沿用本机很多适合的配置,不必在接下来的配置过程中去处理过多不熟悉的配置项。
四、额外配置
配置文件中的额外配置
1 | make menuconfig |
该命令会开启一个用户界面,允许重新设定配置项并写入.config文件中。
- 如果需要编译出的内核能够调试,一般需要设置这些配置项(如果是从本机复制的.config,这些项默认都是配置好的,不用再手动设置,可以在编译前确认一遍):
- CONFIG_KGDB
- CONFIG_KGDB_SERIAL_CONSOLE:使KGDB通过串口与主机通信(打开这个选项,默认会打开CONFIG_CONSOLE_POLL和CONFIG_MAGIC_SYSRQ)
- CONFIG_KGDB_KDB
- CONFIG_DEBUG_KERNEL
- CONFIG_DEBUG_INFO
- CONFIG_FRAME_POINTER
- 注释CONFIG_DEBUG_RODATA:使某些架构下,只读区域可增加断点
- 注释CONFIG_CC_OPTIMIZE_FOR_SIZE,否则使用-Os
- CONFIG_KALLSYMS
- CONFIG_DEBUG_SECTION_MISMATCH:去掉inline优化(此条存疑,不确定功能是否为去掉inline,且设置该项为y后编译不通过)
-
一般会默认打开的一个配置项 CONFIG_RANDOMIZE_BASE,此处手动把它注释,禁止地址随机化。
-
如果有网口连接KGDB的需求,打开这些配置项
-
CONFIG_NETCONSOLE (Networking support -> Network console logging support)
-
CONFIG_KGDB_ETH (Kernel hacking -> KGDB -> Method of KGDB communication -> Ethernet)
Makefile的额外配置
修改源码根目录的Makefile文件,将其中的-O2全部改为-O1,尽可能方便调试。(不能改成-O0,否则编译过不了)
五、编译与安装
- 在源码根目录下运行
make
命令进行编译。 - 编译完成后,运行
sudo make modules_install
安装以模块方式编译的内核。 - 运行
sudo make install
安装内核镜像。该命令会自动生成新内核的initramfs/initrd,并更新grub中的多内核引导。 - 修改
/etc/default/grub
文件,将其中的GRUB_TIMEOUT_STYLE=hidden
注释掉,并修改GRUB_TIMEOUT = 10
,以流出10秒时间让用户选择进入哪个内核。完成后,执行update-grub
命令并重启系统。 - 确认新内核安装成功后,可以进入源码目录执行
make clean
,删除编译时产生的中间文件。
六、可能遇到的其它问题
-
编译时报错:debian/certs/debian-uefi-certs.pem,由certs/x509_certificate_list需求停止。或:No rule to make target 'debian/canonical-certs.pem
在.config中,找到CONFIG_SYSTEM_TRUSTED_KEYS,将其设置为空,即CONFIG_SYSTEM_TRUSTED_KEYS=""
;或在源码根目录运行下面两条命令:1
2scripts/config --disable SYSTEM_TRUSTED_KEYS
scripts/config --disable SYSTEM_REVOCATION_KEYS然后重新编译。
-
编译时报错:failed: load btf from vmlinux: invalid argument.可能是pahole版本过高,BTF格式不兼容导致的,将该软件包降级即可。
1
2$ sudo apt-cache policy pahole
$ sudo apt install pahole=1.22-8
调试内核
准备步骤
-
进行编译的机器为目标机,还需要再开一台虚拟机做开发机,两台机器通过串口连接,才能调试内核。
-
开发机只需要解压的源码(无需编译),以及目标机上编译出的
vmlinux
文件(将其放到开发机源码目录)。 -
配置两台虚拟机之间的串口。
-
开发机
-
目标机
与开发机相同,创建串口,把命名管道下的“客户端”改成“服务器端”即可。
-
进行串口测试:目标机执行
#cat /dev/ttyS1
,开发机执行#echo "test" > /dev/ttyS1
。如果目标机上能看到test
输出,则串口配置OK。 -
配置目标机的
grub.cfg
:
修改/etc/default/grub文件,增加一行:
GRUB_CMDLINE_LINUX="nokaslr rootdelay=90quiet splash text kgdboc=ttyS1,115200"
其中,115200 为串口波特率,nokaslr禁止了内核地址随机化(如果配置时已经设置了CONFIG_RANDOMIZE_BASE,则这里可以不用添加nokaslr)
调试方法
-
目标机
执行
echo ttyS0 > /sys/module/kgdboc/parameters/kgdboc
和echo g > /proc/sysrq-trigger
,控制交给kgdb,目标机进入假死状态,等待开发机的gdb连接。 -
开发机
进入内核源码目录,
gdb ./vmlinux
启动gdb。gdb配置项:
- 设置串口波特率,此处设置与目标机grub中相同的值。
set serial baud 115200
- 通过串口连接kgdb
target remote /dev/ttyS1
可以在源码目录下新建一个
.gdbinit
文件,文件中写入以上两项。gdb启动时,会读取工作目录下的.gdbinit
文件并运行其中的命令,这样,就不必在每次启动gdb时都进行配置。
开发机中运行continue,使目标机得以继续运行,直到命中断点,或显式地执行命令将控制交给kgdb,然后开发机能够继续调试。