如何使用 GDB 和 QEMU 调试 Linux 内核?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11408041/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 13:46:23  来源:igfitidea点击:

How to debug the Linux kernel with GDB and QEMU?

linuxlinux-kernelgdbqemu

提问by E-Kami

I'm new to kernel development and I would like to know how to run/debug the linux kernel using QEMU and gdb. I'm actually reading Robert Love's book but unfortunately it doesn't help the reader on how to install proper tools to run or debug the kernel... So what I did was to follow this tutorial http://opensourceforu.efytimes.com/2011/02/kernel-development-debugging-using-eclipse/. I'm using eclipse as an IDE to develop on the kernel but I wanted first to get it work under QEMU/gdb. So what I did so far was:

我是内核开发的新手,我想知道如何使用 QEMU 和 gdb 运行/调试 linux 内核。我实际上正在阅读 Robert Love 的书,但不幸的是它并没有帮助读者了解如何安装适当的工具来运行或调试内核......所以我所做的是遵循本教程http://opensourceforu.efytimes.com /2011/02/kernel-development-debugging-using-eclipse/。我使用 Eclipse 作为 IDE 在内核上进行开发,但我想首先让它在 QEMU/gdb 下工作。所以到目前为止我所做的是:

1) To compile the kernel with:

1)编译内核:

make defconfig (then setting the CONFIG_DEBUG_INFO=y in the .config)
make -j4

2) Once the compilation is over I run Qemu using:

2) 编译结束后,我使用以下命令运行 Qemu:

qemu-system-x86_64 -s -S /dev/zero -kernel /arch/x86/boot/bzImage

which launch the kernel in "stopped" state

以“停止”状态启动内核

3) Thus I have to use gdb, I try the following command:

3)因此我必须使用gdb,我尝试以下命令:

gdb ./vmlinux

which run it correctly but... Now I don't know what to do... I know that I have to use remote debugging on the port 1234 (default port used by Qemu), using the vmlinux as the symbol table file for debugging.

哪个运行正确但是...现在我不知道该怎么做...我知道我必须在端口 1234(Qemu 使用的默认端口)上使用远程调试,使用 vmlinux 作为符号表文件调试。

So my question is: What should I do to run the kernel on Qemu, attach my debugger to it and thus, get them work together to make my life easier with kernel development.

所以我的问题是:我应该怎么做才能在 Qemu 上运行内核,将我的调试器连接到它,从而让它们一起工作,让我的内核开发生活更轻松。

采纳答案by BjoernD

I'd try:

我会尝试:

(gdb) target remote localhost:1234
(gdb) continue

Using the '-s' option makes qemu listen on port tcp::1234, which you can connect to as localhost:1234 if you are on the same machine. Qemu's '-S' option makes Qemu stop execution until you give the continue command.

使用 '-s' 选项使 qemu 侦听端口 tcp::1234,如果您在同一台机器上,您可以作为 localhost:1234 连接到该端口。Qemu 的“-S”选项使 Qemu 停止执行,直到您发出 continue 命令。

Best thing would probably be to have a look at a decent GDB tutorial to get along with what you are doing. This onelooks quite nice.

最好的办法可能是看一个像样的 GDB 教程来了解你正在做的事情。这个看起来相当不错。

回答by Ritesh

When you try to start vmlinux exe using gdb, then first thing on gdb is to issue cmds:

当您尝试使用 gdb 启动 vmlinux exe 时,gdb 上的第一件事就是发出 cmds:

(gdb) target remote localhost:1234

(gdb) 目标远程本地主机:1234

(gdb) break start_kernel

(gdb) 中断 start_kernel

(continue)

(继续)

This will break the kernel at start_kernel.

这将在 start_kernel 处破坏内核。

回答by Lekensteyn

BjoernID's answer did not really work for me. After the first continuation, no breakpoint is reached and on interrupt, I would see lines such as:

BjoernID 的回答对我没有真正的作用。在第一次继续之后,没有达到断点,在中断时,我会看到如下几行:

0x0000000000000000 in ?? ()
(gdb) break rapl_pmu_init
Breakpoint 1 at 0xffffffff816631e7
(gdb) c
Continuing.
^CRemote 'g' packet reply is too long: 08793000000000002988d582000000002019[..]

I guess this has something to do with different CPU modes (real mode in BIOS vs. long mode when Linux has booted). Anyway, the solution is to run QEMU first without waiting (i.e. without -S):

我想这与不同的 CPU 模式(BIOS 中的实模式与 Linux 启动时的长模式)有关。无论如何,解决方案是先运行 QEMU 而无需等待(即没有-S):

qemu-system-x86_64 -enable-kvm -kernel arch/x86/boot/bzImage -cpu SandyBridge -s

In my case, I needed to break at something during boot, so after some deciseconds, I ran the gdb command. If you have more time (e.g. you need to debug a module that is loaded manually), then the timing doesn't really matter.

就我而言,我需要在启动期间中断一些东西,所以在几分秒后,我运行了 gdb 命令。如果您有更多时间(例如,您需要调试手动加载的模块),那么时间安排并不重要。

gdballows you to specify commands that should be run when started. This makes automation a bit easier. To connect to QEMU (which should now already be started), break on a function and continue execution, use:

gdb允许您指定启动时应运行的命令。这使得自动化更容易一些。要连接到 QEMU(现在应该已经启动),中断函数并继续执行,请使用:

gdb -ex 'target remote localhost:1234' -ex 'break rapl_pmu_init' -ex c ./vmlinux

回答by Alex Hoppus

As for me the best solution for debugging the kernel - is to use gdb from Eclipse environment. You should just set appropriate port for gdb (must be the same with one you specified in qemu launch string) in remote debugging section. Here is the manual: http://www.sw-at.com/blog/2011/02/11/linux-kernel-development-and-debugging-using-eclipse-cdt/

对我而言,调试内核的最佳解决方案是使用 Eclipse 环境中的 gdb。您应该在远程调试部分为 gdb 设置适当的端口(必须与您在 qemu 启动字符串中指定的端口相同)。这是手册:http: //www.sw-at.com/blog/2011/02/11/linux-kernel-development-and-debugging-using-eclipse-cdt/

回答by debug

On Linux systems, vmlinux is a statically linked executable file that contains the Linux kernel in one of the object file formats supported by Linux, which includes ELF, COFF and a.out. The vmlinux file might be required for kernel debugging, symbol table generation or other operations, but must be made bootable before being used as an operating system kernel by adding a multiboot header, bootsector and setup routines.

在 Linux 系统上,vmlinux 是一个静态链接的可执行文件,其中包含 Linux 支持的目标文件格式之一的 Linux 内核,包括 ELF、COFF 和 a.out。内核调试、符号表生成或其他操作可能需要 vmlinux 文件,但必须在用作操作系统内核之前通过添加多引导头、引导扇区和设置例程使其可引导。

An image of this initial root file system must be stored somewhere accessible by the Linux bootloader to the boot firmware of the computer. This can be the root file system itself, a boot image on an optical disc, a small partition on a local disk (a boot paratition, usually using ext4 or FAT file systems), or a TFTP server (on systems that can boot from Ethernet).

该初始根文件系统的映像必须存储在 Linux 引导加载程序可访问的计算机引导固件的某个位置。这可以是根文件系统本身、光盘上的引导映像、本地磁盘上的小分区(引导分区,通常使用 ext4 或 FAT 文件系统)或 TFTP 服务器(在可以从以太网引导的系统上) )。

  1. Compile linux kernel

    Build the kernel with this series applied, enabling CONFIG_DEBUG_INFO(but leave CONFIG_DEBUG_INFO_REDUCED off)

  2. Install GDB and Qemu

    sudo pacman -S gdb qemu
    
  3. Create initramfs

    #!/bin/bash
    
    # Os     : Arch Linux
    # Kernel : 5.0.3
    
    INIT_DIR=$(pwd)
    BBOX_URL="https://busybox.net/downloads/busybox-1.30.1.tar.bz2"
    BBOX_FILENAME=$(basename ${BBOX_URL})
    BBOX_DIRNAME=$(basename ${BBOX_FILENAME} ".tar.bz2")
    RAM_FILENAME="${INIT_DIR}/initramfs.cpio.gz"
    
    function download_busybox {
        wget -c ${BBOX_URL} 2>/dev/null
    }
    
    function compile_busybox {
        tar xvf ${BBOX_FILENAME} && cd "${INIT_DIR}/${BBOX_DIRNAME}/"
        echo "[*] Settings > Build options > Build static binary (no shared libs)"
        echo "[!] Please enter to continue"
        read tmpvar
        make menuconfig && make -j2 && make install
    }
    
    function config_busybox {
        cd "${INIT_DIR}/${BBOX_DIRNAME}/"
        rm -rf initramfs/ && cp -rf _install/ initramfs/
        rm -f initramfs/linuxrc
        mkdir -p initramfs/{dev,proc,sys}
        sudo cp -a /dev/{null,console,tty,tty1,tty2,tty3,tty4} initramfs/dev/
    
    cat > "${INIT_DIR}/${BBOX_DIRNAME}/initramfs/init" << EOF
    #!/bin/busybox sh
    mount -t proc none /proc
    mount -t sysfs none /sys
    
    exec /sbin/init
    EOF
    
        chmod a+x initramfs/init
        cd "${INIT_DIR}/${BBOX_DIRNAME}/initramfs/"
        find . -print0 | cpio --null -ov --format=newc | gzip -9 > "${RAM_FILENAME}"
        echo "[*] output: ${RAM_FILENAME}"
    
    }
    
    download_busybox
    compile_busybox
    config_busybox
    
  4. Boot Linux Kernel With Qemu

    #!/bin/bash
    
    KER_FILENAME="/home/debug/Projects/kernelbuild/linux-5.0.3/arch/x86/boot/bzImage"
    RAM_FILENAME="/home/debug/Projects/kerneldebug/initramfs.cpio.gz"
    
    qemu-system-x86_64 -s -kernel "${KER_FILENAME}" -initrd "${RAM_FILENAME}" -nographic -append "console=ttyS0"
    
    $ ./qemuboot_vmlinux.sh
    SeaBIOS (version 1.12.0-20181126_142135-anatol)
    
    
    iPXE (http://ipxe.org) 00:03.0 C980 PCI2.10 PnP PMM+07F92120+07EF2120 C980
    
    
    Booting from ROM...
    Probing EDD (edd=off to disable)... o
    [    0.019814] Spectre V2 : Spectre mitigation: LFENCE not serializing, switching to generic retpoline
    can't run '/etc/init.d/rcS': No such file or directory
    
    Please press Enter to activate this console.
    / #  uname -a
    Linux archlinux 5.0.3 #2 SMP PREEMPT Mon Mar 25 10:27:13 CST 2019 x86_64 GNU/Linux
    / #
    
  5. Debug Linux Kernel With GDB

    ~/Projects/kernelbuild/linux-5.0.3 ? gdb vmlinux
    ...
    (gdb) target remote localhost:1234
    Remote debugging using localhost:1234
    0xffffffff89a4b852 in ?? ()
    (gdb) break start_kernel
    Breakpoint 1 at 0xffffffff826ccc08
    (gdb)
    Display all 190 possibilities? (y or n)
    (gdb) info functions
    All defined functions:
    
    Non-debugging symbols:
    0xffffffff81000000  _stext
    0xffffffff81000000  _text
    0xffffffff81000000  startup_64
    0xffffffff81000030  secondary_startup_64
    0xffffffff810000e0  verify_cpu
    0xffffffff810001e0  start_cpu0
    0xffffffff810001f0  __startup_64
    0xffffffff81000410  pvh_start_xen
    0xffffffff81001000  hypercall_page
    0xffffffff81001000  xen_hypercall_set_trap_table
    0xffffffff81001020  xen_hypercall_mmu_update
    0xffffffff81001040  xen_hypercall_set_gdt
    0xffffffff81001060  xen_hypercall_stack_switch
    0xffffffff81001080  xen_hypercall_set_callbacks
    0xffffffff810010a0  xen_hypercall_fpu_taskswitch
    0xffffffff810010c0  xen_hypercall_sched_op_compat
    0xffffffff810010e0  xen_hypercall_platform_op
    
  1. 编译linux内核

    使用此系列构建内核,启用CONFIG_DEBUG_INFO(但关闭 CONFIG_DEBUG_INFO_REDUCED)

  2. 安装 GDB 和 Qemu

    sudo pacman -S gdb qemu
    
  3. 创建 initramfs

    #!/bin/bash
    
    # Os     : Arch Linux
    # Kernel : 5.0.3
    
    INIT_DIR=$(pwd)
    BBOX_URL="https://busybox.net/downloads/busybox-1.30.1.tar.bz2"
    BBOX_FILENAME=$(basename ${BBOX_URL})
    BBOX_DIRNAME=$(basename ${BBOX_FILENAME} ".tar.bz2")
    RAM_FILENAME="${INIT_DIR}/initramfs.cpio.gz"
    
    function download_busybox {
        wget -c ${BBOX_URL} 2>/dev/null
    }
    
    function compile_busybox {
        tar xvf ${BBOX_FILENAME} && cd "${INIT_DIR}/${BBOX_DIRNAME}/"
        echo "[*] Settings > Build options > Build static binary (no shared libs)"
        echo "[!] Please enter to continue"
        read tmpvar
        make menuconfig && make -j2 && make install
    }
    
    function config_busybox {
        cd "${INIT_DIR}/${BBOX_DIRNAME}/"
        rm -rf initramfs/ && cp -rf _install/ initramfs/
        rm -f initramfs/linuxrc
        mkdir -p initramfs/{dev,proc,sys}
        sudo cp -a /dev/{null,console,tty,tty1,tty2,tty3,tty4} initramfs/dev/
    
    cat > "${INIT_DIR}/${BBOX_DIRNAME}/initramfs/init" << EOF
    #!/bin/busybox sh
    mount -t proc none /proc
    mount -t sysfs none /sys
    
    exec /sbin/init
    EOF
    
        chmod a+x initramfs/init
        cd "${INIT_DIR}/${BBOX_DIRNAME}/initramfs/"
        find . -print0 | cpio --null -ov --format=newc | gzip -9 > "${RAM_FILENAME}"
        echo "[*] output: ${RAM_FILENAME}"
    
    }
    
    download_busybox
    compile_busybox
    config_busybox
    
  4. 使用 Qemu 启动 Linux 内核

    #!/bin/bash
    
    KER_FILENAME="/home/debug/Projects/kernelbuild/linux-5.0.3/arch/x86/boot/bzImage"
    RAM_FILENAME="/home/debug/Projects/kerneldebug/initramfs.cpio.gz"
    
    qemu-system-x86_64 -s -kernel "${KER_FILENAME}" -initrd "${RAM_FILENAME}" -nographic -append "console=ttyS0"
    
    $ ./qemuboot_vmlinux.sh
    SeaBIOS (version 1.12.0-20181126_142135-anatol)
    
    
    iPXE (http://ipxe.org) 00:03.0 C980 PCI2.10 PnP PMM+07F92120+07EF2120 C980
    
    
    Booting from ROM...
    Probing EDD (edd=off to disable)... o
    [    0.019814] Spectre V2 : Spectre mitigation: LFENCE not serializing, switching to generic retpoline
    can't run '/etc/init.d/rcS': No such file or directory
    
    Please press Enter to activate this console.
    / #  uname -a
    Linux archlinux 5.0.3 #2 SMP PREEMPT Mon Mar 25 10:27:13 CST 2019 x86_64 GNU/Linux
    / #
    
  5. 使用 GDB 调试 Linux 内核

    ~/Projects/kernelbuild/linux-5.0.3 ? gdb vmlinux
    ...
    (gdb) target remote localhost:1234
    Remote debugging using localhost:1234
    0xffffffff89a4b852 in ?? ()
    (gdb) break start_kernel
    Breakpoint 1 at 0xffffffff826ccc08
    (gdb)
    Display all 190 possibilities? (y or n)
    (gdb) info functions
    All defined functions:
    
    Non-debugging symbols:
    0xffffffff81000000  _stext
    0xffffffff81000000  _text
    0xffffffff81000000  startup_64
    0xffffffff81000030  secondary_startup_64
    0xffffffff810000e0  verify_cpu
    0xffffffff810001e0  start_cpu0
    0xffffffff810001f0  __startup_64
    0xffffffff81000410  pvh_start_xen
    0xffffffff81001000  hypercall_page
    0xffffffff81001000  xen_hypercall_set_trap_table
    0xffffffff81001020  xen_hypercall_mmu_update
    0xffffffff81001040  xen_hypercall_set_gdt
    0xffffffff81001060  xen_hypercall_stack_switch
    0xffffffff81001080  xen_hypercall_set_callbacks
    0xffffffff810010a0  xen_hypercall_fpu_taskswitch
    0xffffffff810010c0  xen_hypercall_sched_op_compat
    0xffffffff810010e0  xen_hypercall_platform_op