qemu 抓取linux kernel vmcore

一、背景

在qemu调试linux kernel时 有时我们会遇到dump 情况,这时可以通过gdb 方式连接分析dump, 但实际中我们用得更多的是离线dump 分析,分析的文件通常是vmcore(linux kernel panic 生成的coredump文件)或者ramdump(类似高通平台提供的抓取手机的整个内存空间);这里我将介绍如何利用qemu 抓取vmcore, 以及后续利用crash 工具离线分析异常的方法。

二、qemu monitor建立连接

1、qemu 抓取vmcore 需要建立连接,server端连接建立

qemu-system-aarch64 \-monitor telnet:127.0.0.1:5554,server,nowait \-machine virt,virtualization=true,gic-version=3 \-nographic \-m size=2048M \-cpu cortex-a72 \-smp 2 \ -kernel Image \-drive format=raw,file=rootfs.img \-append "root=/dev/vda rw "

对比前面的qemu 启动linux kernel, 这里需要增加指令:

-monitor telnet:127.0.0.1:5554,server,nowait

monitor通过telnet端口5554 建立server连接;

2、qemu monitor telnet client连接

geek@geek-virtual-machine:~/workspace/linux/qemu$ telnet 127.0.0.1 5554

 

format,png

qemu monitor中也有一些指令用来查看qemu运行的linux kernel 状态,这里不详细展开,有兴趣的可以自行搜索(比如热插拔增加一个device, 执行info roms查看 qemu运行的信息,抓取寄存器等)

 

8f03b407b0e145e1ac0ddde18d18b1b2.png

对于抓取vmcore,我们唯一需要关心的是指令dump-guest-memory

dump-guest-memory [-p] [-d] [-z|-l|-s|-w] filename [begin length] -- dump guest memory into file 'filename'.-p: do paging to get guest's memory mapping.-d: return immediately (do not wait for completion).-z: dump in kdump-compressed format, with zlib compression.-l: dump in kdump-compressed format, with lzo compression.-s: dump in kdump-compressed format, with snappy compression.-w: dump in Windows crashdump format (can be used instead of ELF-dump converting),for Windows x64 guests with vmcoreinfo driver only.begin: the starting physical address.length: the memory size, in bytes.

通常使用 dump-guest-memory filename 或 dump-guest-memory -z filename 指令会抓取qemu中linux kernel的vmcore,一个是不压缩,一个是zlib压缩格式, 后续就可以利用这个vmcore来进行kernel panic 离线分析;

三、qemu vmcore抓取

1、如何生成vmcore

先用最简单的命令行触发一个panic: echo c > /proc/sysrq-trigger

~ # echo c > /proc/sysrq-trigger 
[  142.419430] sysrq: Trigger a crash
[  142.419886] Kernel panic - not syncing: sysrq triggered crash
[  142.420293] CPU: 0 PID: 143 Comm: sh Tainted: G                 N 6.6.1-g3cba94c761ec-dirty #15
[  142.420642] Hardware name: linux,dummy-virt (DT)
[  142.420985] Call trace:
[  142.421120]  dump_backtrace+0x90/0xe8
[  142.421412]  show_stack+0x18/0x24
[  142.421673]  dump_stack_lvl+0x48/0x60
[  142.422098]  dump_stack+0x1c/0x28
[  142.422434]  panic+0x39c/0x3f0
[  142.422744]  sysrq_reset_seq_param_set+0x0/0x10c
[  142.423099]  __handle_sysrq+0x154/0x294
[  142.423427]  write_sysrq_trigger+0x80/0xcc
[  142.423731]  proc_reg_write+0x108/0x16c
[  142.423990]  vfs_write+0x158/0x45c
[  142.424218]  ksys_write+0xd0/0x180
[  142.424425]  __arm64_sys_write+0x44/0x58
[  142.424651]  invoke_syscall+0x60/0x184
[  142.424887]  el0_svc_common.constprop.0+0x78/0x13c
[  142.425132]  do_el0_svc+0x30/0x40
[  142.425351]  el0_svc+0x38/0x70
[  142.425559]  el0t_64_sync_handler+0x120/0x12c
[  142.425816]  el0t_64_sync+0x190/0x194
[  142.426441] SMP: stopping secondary CPUs
[  142.427057] Kernel Offset: disabled
[  142.427264] CPU features: 0x1,00000200,3c020000,1000421b
[  142.427700] Memory Limit: none
[  142.428385] ---[ end Kernel panic - not syncing: sysrq triggered crash ]---

然后在qemu monitor 端执行: dump-guest-memory ramdump1

或者:dump-guest-memory -z vmcore1

 

bfaf842e9ec743528b394e618b81fe0c.png

用 -z参数和不带参数抓取的vmcore只是一个压缩,一个不压缩,大小不同而已,对我们分析无影响

 

e087b61099904d08afb557f705935301.png

后面我们就用这个抓取到的ramdump1/vmcore1 文件进行分析,分析前我们还需要准备对应的kernel版本的vmlinux, 以及crash 工具(这个工具是redhat开发的分析kdump的免费开源工具);

2、crash工具交叉编译

1.下载crash tool
https://github.com/crash-utility/crash.git
2.编译crash, 我们分析的vmcore是arm64平台
make target=ARM64
3.根目录会生成crash工具,加到环境变量中使用即可
4. crash 还有一些externsion在目录extensions  ---本次分析vmcore暂时不涉及,可以忽略make extensions编译生成后的so,在crash中通过extend XXX.so方式加载a. trace.so 用来提取ramdump中的trace log, 分析一些疑难杂症是有用,本质就是根据trace buffer结构体提取里面的trace loghttps://github.com/fujitsu/crash-traceb.gcore.so 可以在kernel panic后的ramdump中提取指定进程的coredump,对应用端逻辑调用栈进行分析https://github.com/fujitsu/crash-gcore

crash 的指令学习可以参考下面两篇文章:

CRASH安装和调试_crash gcore-CSDN博客

四、crash加载vmcore

1、crash加载指令:

crash vmcore路径 vmlinux路径 -m vabits_actual=XX 指定虚拟地址长度(位长的设置后面会介绍)

crash  ../qemu/vmcore1 vmlinux -m vabits_actual=48

虚拟地址长度可以在.config中查看(64位平台通常的配置是48或者39):
//CONFIG_ARM64_VA_BITS_48=y CONFIG_ARM64_VA_BITS=48

 

95ea793c147c40dea5aa724b31bd9b35.png

2、加载遇到问题

 

69c203820561480b96838512e10db9e3.png

看来这个问题已经在crash bug上有人报过了,但是问题还是没有解决(反馈者对比发现4.X 版本的内核是正常的---我自己用4.19也是正常的, 现在我用的linux6.6.1也是有问题,这个问题应该在crash arm64上存在了很久,但是没人去解决)。

[Crash-utility] [Question] crash-arm64 cannot determine VA_BITS_ACTUAL for qemu dump-guest-memory

花了些时间分析后, 发现是自动计算kimage_voffset时遇到了问题,导致后面无法进行; 由于这个在一个编译的版本上是固定值,于是我简单通过 gdb 连接,然后在内核查看变量kimage_voffset的值,最后通过crash的参数设定传入,

(gdb) p /x kimage_voffset
$3 = 0xffff80003fe00000

上面可以看到我这个版本的kimage_voffset值是0xffff80003fe00000,不清楚怎么单步调试kernel的参考我前面的文章: 无人知晓:qemu单步调试arm64 linux kernel

3、crash增加参数 kimage_voffset=XXX

重新加载vmcore, 通过gdb获取kimage_voffset的值,在crash 加载vmcore/ramdump时,arm64平台有如下几个参数可以设置:

    ARM64: //这些都是特定平台相关参数,通过 -m option=value 指定phys_offset=<physical-address>  //指定物理地址的起始kimage_voffset=<kimage_voffset-value>   //指定kimage_voofset的值max_physmem_bits=<value>                  vabits_actual=<value>     //指定虚拟地址长度,手机通常使用39位,虚拟地址空间已经到512G,足够使用,//39位相对48位,正好少一级页表,性能上有提升,同时当前的虚拟地址空间足够手机使用了--kaslr offset //kaslr指定kaslr偏移的参数,qemu调试我们通常会关闭,否则对齐vmlinux需要花些功夫//在高通平台中ocimem.bin特定offset存放,//linux ramdump parse解析的结果也有这个offset
crash最终启动命令: 
crash  ../qemu/vmcore1 vmlinux -m vabits_actual=48 -m kimage_voffset=0xffff80003fe00000

 

a18d98878a6d4f03ad38bdb5935b89e3.png

如上,加载vmcore成功。

五、crash中如何调试一个vmcore

echo c > /proc/sysrq-trigger 方式触发的dump, 入口在drivers/tty/sysrq.c中

 

5e35637510c445df96b1bbec503d602e.png

实际我们在调试中,遇到panic都需要恢复调用栈及问题发生时的寄存器来进行分析;

1、如何恢复调用栈

crash> bt
PID: 143      TASK: ffff00000bc09f00  CPU: 0    COMMAND: "sh"
bt: WARNING: cannot determine starting stack frame for task ffff00000bc09f00

执行bt为什么无法恢复调用栈?panic时sp指针等信息并没有填入导致的,正如我们在使用T32调试通常也需要也个cmm放置 x0~x29, sp/lr 等信息才能正常恢复异常现场

2、如何获取panic时的寄存器信息?

通常内核发生异常时会打印当前CPU的寄存器信息,利用这个打印信息就可以,在遇到wdt或者tz卡死类问题时,肯定是无法打印出来,这时平台通常是触发fiq到trustzone, 然后在TZ中抓取EL1 的cpu寄存信息,我们这里是因为调用的panic, 这个默认也是不打印寄存器信息的。如果是触发data abort或者instuction abort等异常还是能正常打印,如:

 

f857c1b981234d16a854df8f051686ac.png

上面是我用4.19内核echo c 触发的,4.19的实现就是通过空指针访问制造的异常(个人觉得用空指针制造的panic更方便分析)

 

a3a84528335d4f89b28f21eb4281ae57.png

3、获取panic时的调用栈

执行bt时,提供了 pid和触发panic的进程name信息:

PID: 143 TASK: ffff00000bc09f00 CPU: 0 COMMAND: "sh"

crash> task -x -R thread.cpu_context 143
PID: 143 TASK: ffff00000bc09f00 CPU: 0 COMMAND: "sh"
thread.cpu_context = {
x19 = 0xffff80008475af40,
x20 = 0x0,
x21 = 0xffff00000bc09f00,
x22 = 0xffff7fffb13f4000,
x23 = 0xffff800082e0e748,
x24 = 0xffff00000bd1d500,
x25 = 0xffff800085fd7850,
x26 = 0xffff80008475b338,
x27 = 0xffff800082e0e750,
x28 = 0xffff000034202748,
fp = 0xffff800085fd7740,
sp = 0xffff800085fd7740, //利用sp恢复
pc = 0xffff8000817d4390
},
利用bt恢复时,需要lr指针,sp + 8就是lr, sp中存放的是上一级的sp;不清楚可以看后面参考的链接:https://student.cs.uwaterloo.ca/~cs452/docs/rpi4b/aapcs64.pdf

crash> bt -S 0xffff800085fd7748
PID: 143      TASK: ffff00000bc09f00  CPU: 0    COMMAND: "sh"
bt: WARNING: cannot determine starting stack frame for task ffff00000bc09f00#0 [ffff800085fd7750] idle_cpu at ffff80008010a9a0#1 [ffff800085fd7780] irq_exit_rcu at ffff8000800c4a68#2 [ffff800085fd7790] arm64_preempt_schedule_irq at ffff8000817d424c#3 [ffff800085fd77b0] el1_interrupt at ffff8000817caf10#4 [ffff800085fd77d0] el1h_64_irq_handler at ffff8000817cb2c0#5 [ffff800085fd7910] el1h_64_irq at ffff800080011ae4#6 [ffff800082b361e0] (null) at f420PC: 000000000044fd4c   LR: 00000000004b7734   SP: 0000ffffd8894070X29: 0000ffffd8894070  X28: 0000000000000000  X27: 0000000000000000X26: 0000000001e57970  X25: 0000000000000002  X24: 0000000000000020X23: 0000000001e5c6a0  X22: 0000000000602000  X21: 0000000000000002X20: 0000000001e5c6a0  X19: 0000000000000001  X18: 0000000000000000X17: 0000000000403140  X16: 0000000000600020  X15: 000000000360ed96X14: 0000000000000001  X13: 0000ffffd88941b0  X12: 00000000ffffffc8X11: 00000000ffffff80  X10: 0000000000000000   X9: 0000000000000020X8: 0000000000000040   X7: 7f7f7f7f7f7f7f7f   X6: 0000000000000063X5: fffffffffffffffe   X4: 0000000000000001   X3: 0000000000601ca5X2: 0000000000000002   X1: 0000000001e5c6a0   X0: 0000000000000001ORIG_X0: 0000000000000001  SYSCALLNO: 40  PSTATE: 80000000

恢复到第五级,遇到一些问题,直接查看堆栈内容,在0xffff800085fd7910处出现了栈回溯问题,这是因为中断的原因,跳过这一级继续向下就可以恢复完整异常发生的调用栈,如下标红线的就是sp回溯,sp + 8就是每一级对应的lr函数,可以通过sym XXXXX查看

 

39596476fd0a4b95ae8fa38c16a86891.png

从0xffff800085fd7920 开始恢复调用栈,此时就是真实触发异常的调用栈

crash> bt -S 0xffff800085fd7928
PID: 143      TASK: ffff00000bc09f00  CPU: 0    COMMAND: "sh"
bt: WARNING: cannot determine starting stack frame for task ffff00000bc09f00#0 [ffff800085fd7930] __delay at ffff800081789ecc#1 [ffff800085fd7960] __const_udelay at ffff800081789fb0#2 [ffff800085fd7a20] panic at ffff8000800ba5ec#3 [ffff800085fd7ab0] sysrq_handle_crash at ffff800080bc78c8#4 [ffff800085fd7ac0] __handle_sysrq at ffff800080bc8414#5 [ffff800085fd7b40] write_sysrq_trigger at ffff800080bc8eb8#6 [ffff800085fd7b70] proc_reg_write at ffff8000804b83e8#7 [ffff800085fd7ca0] vfs_write at ffff8000803f5b84#8 [ffff800085fd7d60] ksys_write at ffff8000803f6130#9 [ffff800085fd7da0] __arm64_sys_write at ffff8000803f6224
#10 [ffff800085fd7dd0] invoke_syscall at ffff80008002ee48
#11 [ffff800085fd7e20] el0_svc_common.constprop.0 at ffff80008002efe4
#12 [ffff800085fd7e60] do_el0_svc at ffff80008002f0d8
#13 [ffff800085fd7e80] el0_svc at ffff8000817cb060
#14 [ffff800085fd7ea0] el0t_64_sync_handler at ffff8000817cb45c
#15 [ffff800085fd7fe0] el0t_64_sync at ffff800080011d48PC: 000000000044fd4c   LR: 00000000004b7734   SP: 0000ffffd8894070X29: 0000ffffd8894070  X28: 0000000000000000  X27: 0000000000000000X26: 0000000001e57970  X25: 0000000000000002  X24: 0000000000000020X23: 0000000001e5c6a0  X22: 0000000000602000  X21: 0000000000000002X20: 0000000001e5c6a0  X19: 0000000000000001  X18: 0000000000000000X17: 0000000000403140  X16: 0000000000600020  X15: 000000000360ed96X14: 0000000000000001  X13: 0000ffffd88941b0  X12: 00000000ffffffc8X11: 00000000ffffff80  X10: 0000000000000000   X9: 0000000000000020X8: 0000000000000040   X7: 7f7f7f7f7f7f7f7f   X6: 0000000000000063X5: fffffffffffffffe   X4: 0000000000000001   X3: 0000000000601ca5X2: 0000000000000002   X1: 0000000001e5c6a0   X0: 0000000000000001ORIG_X0: 0000000000000001  SYSCALLNO: 40  PSTATE: 80000000

crash的使用技巧可以参考文末部分(写得都很详细)

六、总结

1、利用qemu monitor 提取vmcore

2、利用crash 工具加载分析vmcore

参考:

CRASH安装和调试_crash gcore-CSDN博客

crash实战:手把手教你使用crash分析内核dump-CSDN博客

https://student.cs.uwaterloo.ca/~cs452/docs/rpi4b/aapcs64.pdf

https://linux.web.cern.ch/centos7/docs/rhel/Red_Hat_Enterprise_Linux-7-Kernel_Crash_Dump_Guide-en-US.pdf

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/442813.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

记录 arm 开发板上 nginx 配置 http 服务注意事项

1. 自定义项目&#xff0c;需要在 conf.d 目录中增加一个 .conf 配置文件&#xff1a; server {listen 9200; # 端口号server_name localhost; # 服务名称location / {root /home/imx6q/media; # 项目根目录&#xff08;需要修改 n…

JavaScript高级:js中面向对象编程

目录 1 引言 1.1 两者的区别 1.2 优缺点 2 封装性 3 原型 3.1 原型对象 3.2 构造函数和原型中的this如何指向 3.2.1 构造函数 3.2.2 原型对象 4 数组扩展案例--> 求和 和 最大值 5 原型对象中的constructor属性 6 对象原型 7 对象原型中的constructor属性 8 上…

深入了解5G终端射频标准中的频谱发射与互调特性

前面的频谱发射我们已经学习了占用带宽、带外发射和杂散发射&#xff0c;今天是频谱发射的最后一部分内容&#xff1a;互调。在很多的标准规范中&#xff0c;都有互调测试的相关内容&#xff0c;但测试条件、测试要求和测试方法都不尽相同。我们可以不必纠结互调是否有某种固定…

欢迎报名!数境·第七届工业互联网数据创新应用大赛震撼来袭!

欢迎报名&#xff01; 由深圳市宝安区人民政府和中国信息通信研究院共同主办的数境第七届工业互联网数据创新应用大赛正式开赛啦~~~ 中国信通院“数境”品牌赛事起始于2017年工业大数据创新大赛&#xff0c;伴随着我国制造业高端化、智能化、绿色化发展不断成长&#xff0c;至…

【图文详解】阿里腾讯华为云服务器被攻击后更换服务器IP操作步骤合集

阿里云 固定公网IP地址更换 情况一&#xff1a;实例创建时间不足6小时 前提条件在尝试更换固定公网IP地址之前&#xff0c;必须确保ECS实例满足以下所有条件&#xff1a; 实例创建时间不超过6小时。已成功分配一个固定公网IP地址。如果在实例创建时未分配固定公网IP地址&am…

如何快速记忆小鹤双拼键位图?

记忆方法&#xff1a;韵母表 图形 最常用字 韵母表&#xff1a;双拼的基础 图形&#xff1a;帮助新手快速联想回忆 最常用字&#xff1a;快速打字基础 一、单韵母&#xff08;紫色方块&#xff09; 一一对应如下表&#xff1a; 单韵母aoeiu、AOEIV 二、复韵母—箭矢型&am…

30岁以下的人都去哪儿了?OpenAI费尽心机吸引年轻人

比尔盖茨的博客栏目「与比尔盖兹一起解惑」近日大火&#xff0c;OpenAI首席执行官奥特曼表示&#xff0c;OpenAI“不是由一群24岁的程序员运营的”&#xff0c;并表示这很令人担忧。 目前&#xff0c;许多热门的AI初创公司正在寻找年轻才俊&#xff0c;OpenAI当然也在此之列。…

[GWCTF 2019]我有一个数据库(特详解)

页面一开始乱码&#xff0c;先教大家应该修复的方法&#xff0c;到火狐打开&#xff0c;然后找到小工具里的修复文字编码&#xff0c;就可以了&#xff08;没有的话可以在定制工具栏里添加&#xff09; 一般页面读取乱码都可以这样解决 他说什么都没有&#xff0c;先dirsearch扫…

如何使用 XGBoost 对时序数据进行精准预测?

时间序列预测是各个领域中的关键任务&#xff0c;包括金融、销售和能源需求等。准确的预测使企业能够做出明智的决策&#xff0c;优化资源&#xff0c;并有效地规划未来。 近年来&#xff0c;XGBoost 算法因在时间序列预测任务中表现出色而备受青睐。本文探讨了 XGBoost 在时间…

h5电子画册制作技巧

​随着科技的发展&#xff0c;电子画册已经成为了越来越多企业宣传和推广产品的重要工具。而h5电子画册制作更是其中的一项关键技术。那么&#xff0c;如何制作一个优秀的h5电子画册呢&#xff1f;下面就为大家分享一些制作技巧。 在制作h5电子画册之前&#xff0c;需要先确定画…

由《幻兽帕鲁》私服漏洞引发的攻击面思考

《幻兽帕鲁》私服意外丢档 当了一天的帕鲁&#xff0c;回家开机抓帕鲁的时候发现服务器无法连接。运维工具看了下系统负载发现 CPU 已经跑满。 故障排查 登录服务器进行排查发现存在可疑的 docker 进程。 经过一番艰苦的溯源&#xff0c;终于在命令行历史中发现了端倪 攻击…

1. 两数之和(力扣LeetCode)

文章目录 1. 两数之和题目描述哈希表&#xff1a;map二分查找暴力&#xff1a;双重for循环 1. 两数之和 题目描述 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可…