-
-
[原创]QEMU在kvm加速下软件断点的实现分析
-
发表于:
2019-3-20 15:05
6600
-
问题源于我想在KVM的加持下来加速用QEMU调试Linux的虚拟机。刚开始我还没遇到这个问题,突然有一天我需要在start_kernel()这个函数下断点。发现断不下来。但是系统启动后,对任意函数下断点又没有问题。问题就变成了在KVM的加持下对start_kernel()下断点无效。
QEMU在使用KVM方案后,kvm_set_user_memory_region()函数通过KVM API设置虚拟机的内存和主机的内存映射关系。本次调试的虚拟机内存设置为4G,cpu核数为1。
图1
如图1所示看到一段虚拟机的内存0xfffc0000是和主机内存0x7fffe4400000做的映射。0xfffc0000位置用于放置bios代码。
下面是我找到一些后面要用的虚拟机和主机映射的地址范围。
图2
图2中的其中比较有意思的是$37这组数据。上面说过虚拟机只有4G物理内存,为什么会有4G-5G这一段物理内存那?这个问题是PCI BUS总线地址原因。PCI BUS总线地址占到物理内存的3G-4G位置上,我们的那段3G-4G内存地址就映射到了4G-5G的内存地址。在Linux的dmesg中看e820的布局也能看到这个现象。e820中没有3G-4G的物理地址,代替它的是4G-5G的物理地址。
图3
图2的内存布局是为了方便我们下一步验证的工作。QEMU加载Linux内核代码在load_linux()中实现的。部分代码如下:
图4
图4中的代码告诉QEMU把一个linux的内核加载到物理内存的位置。调试的内核是Linux-4.13-2版本,物理内存位置在897行。real_addr的部分是放置Linux内核header.S(arch/x86/boot)的代码。一个压缩的内核例如bzImage由两部分组成setup和kernel.其中kernel是压缩了,setup部分用于启动和解压。图4 real_addr用于放setup实模式的代码。prot_addr用于放置setup保护模式的代码。
图5
图6
图5是代码,图6是生成后内核。header.S很多功能例如定义了setup的大小等重要信息。header.S中有很多关键的信息用于确定后面压缩的内核在那。
知道了QEMU把Linux加载到哪,下面就是QEMU什么时候加载Linux内核了。通过图2找到虚拟机0x10200物理地址在主机内存的位置,下一个watch断点我们找到如图7所示
图7
图8
图8已经看到QEMU中运行的bootloader代码把bzImage加载到指定的内存中。下面介绍一种不是完美方法来查看虚拟机中当前CPU运行的状态。首先对kvm_getput_regs()中有对虚拟机中CPU寄存器的获取
图9
图9 为下断点获取((CPUX86State *) 0x5555566756e0)它保存,
图10
此函数是在KVM中的虚拟机执行前执行。所以获取当前CPU的CPUX86State比较好。
继续回到上面看图8中此时虚拟机中断时候的寄存器和内存状态。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!