ESXi自带一个子linux系统, 虽然能实现一部分功能, 但是目前为止, 依然没有公开的教程说过如何调试vmx进程, 本篇教程将分享我的研究成果, 帮助大家轻松调试虚拟机进程.

最简单的方法

首先, ESXi 自带一个gdbserver, 所以最简单的方法就是直接跑起程序, 再用gdbserver attach它. (最好用老一点的gdb, 比如gdb 7.8)

  1. 跑起虚拟机, 查看vmx对应的进程id

    1
    2
    3
    4
    5
    6
    7
    [root@192:~] ps|grep vmx
    70639 70639 vmx
    70643 70639 vmx-vthread-706
    70644 70639 vmx-filtPoll:c7
    70645 70639 vmx-mks:c7
    70646 70639 vmx-svga:c7
    70647 70639 vmx-vcpu-0:c7
  2. gdbserver attach 上去

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    [root@192:~] gdbserver --attach :8808 70639
    Attached; pid = 70639
    !gdb_connected()
    !gdb_connected()
    !gdb_connected()
    !gdb_connected()

    [root@192:~] gdbserver --attach :8808 70639
    Attached; pid = 70639
    !gdb_connected()
    Listening on port 8808

    一定要保证出现Listening on port, 不然 ctrl+c 中断, 再重试就好, 目标进程不受影响.

  3. 找一台可以使用gdb的机器, 把ESXi中的vmx文件拷贝到机器中, 然后使用如下命令连接

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    [root@192 vv]# gdb -q
    gdb$ file Desktop/vmx-7.0-15843807.elf
    Reading symbols from /home/vv/Desktop/vmx-7.0-15843807.elf...Missing separate debuginfo for /home/vv/Desktop/vmx-7.0-15843807.elf
    (no debugging symbols found)...done.
    gdb$ target remote 192.168.170.240:8808
    Remote debugging using 192.168.170.240:8808
    => 0x8ce2771248 <__libc_ifunc_impl_list+3304>: cmp rax,0xfffffffffffff000
    0x8ce277124e <__libc_ifunc_impl_list+3310>: ja 0x8ce2771275 <__libc_ifunc_impl_list+3349>

    gdb$

    这样就可以正常调试了

另一种方法-使用自己编译好的gdb

如果自带的gdbserver 不太好用, 那也可以自己源码静态编译一个gdb版本拷贝过去, 然后直接用.

  1. 自己源码编译一个gdb, 或者用我编译好的gdb7.8 no python 拷贝到esxi上

    如果要自己编译支持带python的gdb, 先下载python源码. 使用./configure --prefix=/home/vv/python; make; make install 编译安装python到/home/vv/python.

    然后下载gdb源码, 安装lzma开发组件yum xz-devel texinfo, 使用./configure --prefix=/home/vv/gdb --enable-static --with-python=/home/vv/Desktop/python --with-lzma;make 编译, 成功后make install 安装. 其它操作参考gdb10.2里给出的脚本.

    如果想编译11.0之后的gdb, 需要做一下修改, 修改如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    enum target_xfer_status
    linux_nat_target::xfer_partial (enum target_object object,
    const char *annex, gdb_byte *readbuf,
    const gdb_byte *writebuf,
    ULONGEST offset, ULONGEST len, ULONGEST *xfered_len)
    {
    ...
    if (object == TARGET_OBJECT_MEMORY)
    {
    ...
    - return linux_proc_xfer_memory_partial (readbuf, writebuf,
    - offset, len, xfered_len);
    + enum target_xfer_status ret = linux_proc_xfer_memory_partial (readbuf, writebuf,
    + offset, len, xfered_len);
    + if(ret == TARGET_XFER_OK){return ret;}
    }

    return inf_ptrace_target::xfer_partial (object, annex, readbuf, writebuf,
    offset, len, xfered_len);
    }

    在11.0之后的版本读取目标进程内存时, 默认从/proc/pid/mem读取, 而esxi上是没有这个文件的, 所以, 除非修改它的实现, 否则之后的版本不适合在esxi上用.

    最好还是用7.8的版本, 否则容易有奇奇怪怪的问题

  2. 运行起虚拟机

  3. 运行gdb, 一般会告诉你缺失libtinfo.so.5文件或者libncurses.so.5, 最简单的方法就是创建一个软链接, 把它自带的一个文件软链接成缺失的文件.

    1
    [root@192:~] ln -s /lib64/libncurses.so.5 /lib64/libtinfo.so.5

    或者从centos 7 3.10内核的系统中拷贝一个过去放在lib64目录下.

  4. 使用gdb调试

    1
    2
    3
    gdb$ file /bin/vmx
    gdb$ set architecture i386:x86-64:intel
    gdb$ attach xxxxx

以下是编译的带python 2.7 的gdb7.8的分块包(因为github最大支持25M), 可以通过cat gdb78_p.tar.xz.* > gdb10.2.tar.xz 合并他们

gdb78_p.tar.xz.s0

gdb78_p.tar.xz.s1

gdb78_p.tar.xz.s2

注意事项

如果发现ctrl+c不好使, 那么就把虚拟机的cpu个数设置为1. 如果还是不好使, 建议重新选个guest安装测试.

其他

以下命令发表调试vmx

1
apid=`ps|grep -m 1 "vmx"|cut -d " " -f 1`;gdb -ex "file /bin/vmx" -ex "handle SIGPIPE nostop noprint pass" -ex "attach $apid" -ex "shell echo \"set \\\$vmx=0x\$(cat /proc/\$(ps|grep -m 1 vmx|cut -d ' ' -f 1)/maps|grep -m 1 vmx|cut -d '-' -f 1)\">/tmp/cmd" -ex "shell echo \"set \\\$stk=0x\$(cat /proc/\$(ps|grep -m 1 vmx|cut -d ' ' -f 1)/maps|grep -m 1 stack|cut -d '-' -f 1)\">>/tmp/cmd" -ex "source /tmp/cmd" -ex "shell rm  /tmp/cmd"