写点什么

qemu 单步调试 arm64 linux kernel

作者:无人知晓
  • 2023-12-09
    广东
  • 本文字数:2045 字

    阅读完需:约 7 分钟

一、背景和目的

https://xie.infoq.cn/article/3415445f1c8831423a94c4bc2 《qemu 搭建 arm64 linux kernel 调试环境》

之前介绍了 qemu 启动 kernel 的配置步骤和方法,现在开始我们的调试,这篇文章主要讲解如何单步调试内核,所有的实验还是基于 ARM64;

二、环境准备

需要准备 host=x86 target = arm64 的 gdb, 有三种方式:一种是 sudo apt install gdb-multiarch;

另外一种是用 ARM 官网下载交叉编译工具链,其中自带 gdb(目前我使用的方式Arm GNU Toolchain Downloads – Arm Developer); 还有一种是下载 gdb 源码并编译;

不同的方式有些差异,用 apt 安装的如果 ubuntu 比较老,可能存在部分特性不支持(比如 ARMv8.5 的 PAC,BTI 等,之前用 ubuntu18.04 就遇到了);

注意:使用 ARM 官网 gdb 的伙伴启动时可能会遇到缺少库和 python3.8 的报错(依赖 libncurses5 ,libncursesw5 及 python3.8),可以参考下面解决(偷懒可以直接安装 gdb-multiarch)

三、kernel debug

单步调试 kernel 只需要三步:

第一步:qemu 启动内核并暂停等待(暂停是可选的,如果不调启动,可以去掉),同时需要建立网络端口等待 gdb attach;

第二步:启动 gdb(target=arm64)加载对应 kernel Image 的 vmlinux, attach 到指定端口即可;

第三步:如果是启动是挂起,直接设置断点即可调试,如果未选择启动暂停,ctrl + c 会触发挂起,然后就可以和前面一样,正常设置断点。

qemu 启动调试脚本(注意这里有个小坑,直接调试的伙伴直接跳转到最后拷贝即可

qemu-system-aarch64 \    -machine virt,virtualization=true,gic-version=3 \    -nographic \    -m size=1024M \    -cpu cortex-a72 \    -smp 2 \     -kernel Image \    -drive format=raw,file=rootfs.img \    -append "root=/dev/vda rw" \    -s \    -S
可以看到对比之前的启动参数也就是增加了-s 和-S,具体含义如下:# -s 是-gdb tcp::1234 的简写,如果需要换端口可以用-gdb tcp::1234替换-s参数# -S 是freeze cpu at startup的指令,也就是kernel 启动时就挂起,等待调试连接,如果不需要调试内核启动,这个参数也 可以去掉
复制代码


gdb 启动找到 vmlinux 所在目录(最好在 linux 编译的根目录,不要拷贝出来,这样调试源码可以直接显示,不然还要在 gdb 中设置 src path),geek@geek-virtual-machine:~/workspace/linux/linux-6.6.1$ aarch64-none-linux-gnu-gdb vmlinux

(gdb) target remote :1234Remote debugging using :12340x0000000040000000 in ?? ()(gdb) b start_kerne
然后continue即可停在指定断点,后面就可以step 单步调试了,实际调试时会发现设置的断点start_kernel停不住,ctrl+c 触发挂起时会出现bt无法显示相关符号等问题
(gdb) bt#0 0xffffd43266a17f6c in ?? ()#1 0xffffd43265ad7ad0 in ?? ()#2 0x0000000000000002 in ?? ()Backtrace stopped: previous frame identical to this frame (corrupt stack?)
复制代码

这里的原因也是经常会遇到和遗忘的,查看 kernel log 可以看到 KASLR 字样

[    0.000000] KASLR enabled[    0.000000] CPU features: kernel page table isolation forced ON by KASLR
复制代码

KASLR 是内核启动添加随机地址保护,启动后实际运行地址和 vmlinux 有一个随机偏移值,在 gdb 中设置断点是基于 vmlinux 的(这个是不带偏移值的),实际 qemu 中运行的内核是在这个地址 + 随机偏移值,所以断点无法触发,出现上面的问题;

上面的问题有两种解决方法:

1、是重新编译内核,在 arch/arm64/configs/defconfig 中将 CONFIG_RANDOMIZE_BASE=y 修改成 CONFIG_RANDOMIZE_BASE=n

2、是在 qemu 启动的 cmdline 中增加 nokaslr 参数,通过参数方式关闭

      -append "root=/dev/vda rw nokaslr" \
复制代码

修改正常后,断点能正确停止,bt 调用栈显示正常

(gdb) bt#0  cpu_do_idle () at arch/arm64/kernel/idle.c:32#1  0xffff800081017f80 in arch_cpu_idle () at arch/arm64/kernel/idle.c:44#2  0xffff800081018bcc in default_idle_call () at kernel/sched/idle.c:97#3  0xffff8000800d7ad0 in cpuidle_idle_call () at kernel/sched/idle.c:170#4  do_idle () at kernel/sched/idle.c:282#5  0xffff8000800d7d48 in cpu_startup_entry (state=CPUHP_ONLINE) at kernel/sched/idle.c:380#6  0xffff800081018eac in rest_init () at init/main.c:726#7  0xffff800081aa08bc in arch_call_rest_init () at init/main.c:823Backtrace stopped: previous frame identical to this frame (corrupt stack?)
复制代码

四、总结

qemu 内核调试时需要注意关闭 kaslr,更新正确的脚本,也不依赖 kenerl config 是否开启 kaslr

qemu 启动脚本(最终版本):

qemu-system-aarch64 \    -machine virt,virtualization=true,gic-version=3 \    -nographic \    -m size=1024M \    -cpu cortex-a72 \    -smp 2 \     -kernel Image \    -drive format=raw,file=rootfs.img \    -append "root=/dev/vda rw nokaslr" \    -s \    -S
复制代码

关于 kaslr 原理相关的知识,有兴趣的伙伴参考 文章 kaslr原理分析

发布于: 刚刚阅读数: 5
用户头像

无人知晓

关注

还未添加个人签名 2023-07-31 加入

还未添加个人简介

评论

发布
暂无评论
qemu单步调试arm64 linux kernel_qemu_无人知晓_InfoQ写作社区