使用crash解析vmcore(fulldump)文件,基于qemu,arm64,linux6.6
背景
文章主要讲述了使用qemu运行linux6.6的内核,并通过telnet的方式生成vmcore(fulldump)文件以及最后使用crash工具对抓取的vmcore(fulldump)进行分析,相当于搭建一套fulldump的生成解析的环境,文章使用的所有工具以及代码均为github的开放源代码。
搭建这个环境的过程并不顺利,这篇文件也包含了自己在搭建过程中踩过的一些坑;
此文章也是为后续对块设备框架做一个整体剖析的利器;
环境搭建所需要的工具以及版本:qemu8.2,crash8.0.6,linux6.6.0,gdb10.2
host主机:ubuntu20.04 server版本 gcc9.4 g++9.4
以下是最终界面:
一、生成抓取vmcore以及解析步骤,工具环境需求说明
备注:如果你使用4.19的版本以及5.15的内核版本,同样的方式抓取vmcore文件,使用crash是可以直接解析的不会报错,以下配置仅针对于6.0+的内核版本,亲测6.6,6.9可以用。
1,编译linux并生成zImage和vmlinux
这里要注意几个点,kernel这几个配置需要与下面一致,这几个都是需要修改的地方,
CONFIG_RANDOMIZE_BASE=n //这个必须手动设置为n,6.6内核默认设置为y
CONFIG_DEBUG_INFO_REDUCED=n //必须设置为n,高版本内核默认为y
CONFIG_ARM64_VA_BITS=39 // VA=39 PA=48 这个不设置的话会解析vmcore报错,应该是因为kimage_voffset计算的有问题
CONFIG_ARM64_PA_BITS=48 // 6.6内核默认是VA=48,6.9以上内核默认VA=52
底版本内核 CONFIG_RANDOMIZE_BASE默认是关闭的,但是6.0+的内核默认开启了,应该是考虑到内核的安全性,另外打开这个选项之后,也会使得使用gdb单步调试内核的时候设置的断点不会触发,因为函数的运行地址,与vmlinux中的地址会相差一个random offset,因此简单起见,建议关闭此选项,
CONFIG_DEBUG_INFO_REDUCED=y 6.0+版本内核默认打开了这个选项,应该是考虑到内核性能,这样会导致vmlinux中缺少调试信息,crash会因为无法识别到调试信息,而加载失败,这个crash加载时会有明确的报错。
CONFIG_ARM64_VA_BITS=39,6.0+版本的内核默认将vabit设置为48,6.9版本内核默认设置为52,在这两种状态下,即便正确计算了vabits_actual和kimage_voffset的值,crash加载vmcore的时候也会报错,只能加载为minidump,因此需要吧VA_BIT设置为39位。
其他的配置保持默认(COREDUMP相关的配置默认都是打开的,不再赘述)编译即可;编译会生成vmlinux以及zImage文件
2,使用qemu运行linux,并触发panic
使用以下qemu命令,运行虚拟开发板:
qemu-system-aarch64 -cpu cortex-a72 \
-machine virt,virtualization=true,gic-version=3 \ #虚拟设备
-m 1024M \ #内存1G
-smp 2 \ #双核
-kernel kernel/out6/arch/arm64/boot/Image \ #linux6.6编译生成的镜像
-append "root=/dev/ram rdinit=/linuxrc loglevel=7 log-buf-len=1M console=ttyAMA0 nokaslr" \ # 最好带上nokaslr
-initrd ./rootfs.img \
-monitor telnet:127.0.0.1:5555,server,nowait \ #这个参数必须加,因为我们选择的是通过talnet的方式生成vmcore
-nographic
-kernel kernel/out6/arch/arm64/boot/Image
Image是我们编译linux6.6生成的文件,添加路径即可
-monitor telnet:127.0.0.1:5555,server,nowait
这个参数是需要带的,当使用echo c触发panic后,需要使用telnet 连接monitor获取vmcore
运行以上命令之后:
使用以下命令触发panic:
echo c >/proc/sysrq-trigger
触发之后如下:
3,telnet 生成panic时的vmcore文件
备注:vmcore的生成方式有很多种,可以使用onitor netdump的方式,也可以通过proc/vmcore的方式生成,也可以将其写入到指定的磁盘中,这里我们选择使用monitor netdump的方式
此时使用telnet 127.0.0.1 5555连接monitor
然后运行 dump-guest-memory ./dump/vmcorev60606 生成vmcorev60606
vmcorev60606 的大小一般与板子内存大小是一致的,我们内存是1G,因此文件大小匹配
此时我们已经编译生成了vmlinux以及panic时的vmcore文件,可以使用crash进行加载了
4,使用crash工具加载vmcore文件
48位虚拟地址,和52位虚拟地址对应的kimage_voffset=0xffff80003fe00000,可以通过gdb调试获取这个值,但是即便加上这个值,加载48位编译的内核vmcore也会报IRQ的错误,而无法加载
crash_arm64 ./kernel/out6/vmlinux ./dump/vmcorev60606 -m vabits_actual=39
直接使用上面命令加载vmcore文件即可
大功告成。
附录1:QEMU源码编译,运行
github 或者码云直接下载
GitHub - qemu/qemu at stable-8.2
编译步骤:
cd qemu-stable-8.2
mkdir build
cd build
../configure --enable-kvm --enable-slirp --enable-debug --enable-virtfs --target-list=aarch64-softmmu,x86_64-softmmu
make -j16
编译过程中没有报什么错,如果有相关库依赖的,直接安装就行,编译完成
生成上面两个文件就是ok了,
创建一个链接文件,就可以直接使用;
附录2:crash的源码编译
备注:这个最好有vpn,因为编译crash的时候会下载gdb的源码包,没有vpn会很慢,或者会超时失败;
github直接下载 内核源码
https://github.com/crash-utility/crash
1,下载源码,使用下面命令直接编译
tar -xf crash-8.0.6.tar.gz
cd crash-8.0.6
make target=ARM64 CFLAGS+=-fpie CXXFLAGS+=-fpie CFLAGS_FOR_TARGET+=-fpie CXXFLAGS_FOR_TARGET+=-fpie -j16
crash如果使用自带README的编译方法会在链接GDB的时候报以下错误:
/usr/bin/ld: …/sim/aarch64/libsim.a(callback.o
): relocation R_X86_64_32S
against symbol cb_init_syscall_map'
can not be used when making a PIE object; recompile with -fPIE
/usr/bin/ld: …/sim/aarch64/libsim.a(sim-command.o): relocation R_X86_64_32 against .rodata.str1.1
can not be used when making a PIE object; recompile with -fPIE
/usr/bin/ld: …/sim/aarch64/libsim.a(sim-core.o): relocation R_X86_64_32 against .text’ can not be used when making a PIE object; recompile with -fPIE
/usr/bin/ld: …/sim/aarch64/libsim.a(sim-endian.o): relocation R_X86_64_32
against .rodata.str1.1'
can not be used when making a PIE
object; recompile with -fPIE
执行make指令后,不出意外会生成一大堆链接阶段relocation一些符号失败的问题,
这个问题还怀疑过是链接器ld的版本太低了,但是LD是系统自带的,我host是20.01不算老,所以排除了这个问题。
根据报错信息,查明这是由于这些符号库文件在编译的时候没有使用 位置无关执行代码(-fpie)的参数,导致链接重定位失败,lib文件是需要编译为位置无关代码的,因此针对以上几行的报错信息,需要在编译:
…/sim/aarch64/libsim.a(callback.o
) ----------------------callback.c
…/sim/aarch64/libsim.a(sim-command.o
)------------------sim-command.c
…/sim/aarch64/libsim.a(sim-core.o
): ---------------------sim-core.c
…/sim/aarch64/libsim.a(sim-endian.o
) ------------------------sim-endian.c
…
文件的时候这些文件为目标文件的时候加上-fpie参数即可,在ld的时候也加上 -fpie的参数 也就是需要在makefile里面对应CCFLAG += fpie CXXFLAG +=fpie 即可 如果部分文件是clang编译的,也需要同步添加,最后在make的时候传入这个修改参数,编译ok!!!
总结
这篇文章仅仅是相当远搭建了一个fulldump的环境,也持续耗费了几周的时间,但是相信利用full dump的环境,我们在后续针对某些问题细节的分析与理解会更深刻!!!
2025/03/20 1:18