官方教程:sel4 debug

Compiler Settings

原文

请确保您正在构建的构建目录不是发布构建。为此,请检查构建目录CMakeCache.txt文件:

 * `CMAKE_BUILD_TYPE:STRING=Debug`
 * `KernelDebugBuild:BOOL=ON`

调试构建启用了一些有用的调试特性,包括:

  • printf + 串行驱动程序
  • -g 被传递给编译器
  • 编译和运行时断言
  • 当调用失败时,内核将打印出错误消息。

Qemu

Qemu 是一个提供硬件平台软件模拟的模拟器。 当您无法访问目标平台时,它对于开发和调试嵌入式软件很有用。 即使您确实拥有目标硬件,Qemu 也可以缩短您的编辑-编译-调试周期。 请注意,它不是周期精确的,并且无法模拟每个设备,因此有时您别无选择,只能在真实硬件上进行调试。

对于 seL4 教程,始终会生成一个模拟脚本以用于项目的构建目录。 在大多数其他项目中,可以通过将标志 -DSIMULATION=1 传递给 …/init-build.sh 调用来生成此脚本。

    mkdir build_dir
    cd build_dir
    ../init-build.sh -DSIMULATION=1
    ./simulate

如果该平台不支持qemu,则该脚本将被输出

 Unsupported platform or architecture for simulation

当在Qemu中模拟一个seL4系统时,您应该会看到指向您的终端上(模拟的)UART设备的输出:

ELF-loader started on CPU: ARM Ltd. ARMv6 Part: 0xb36 r1p3
  paddr=[82000000..8225001f]
ELF-loading image 'kernel'
  paddr=[80000000..80033fff]
  vaddr=[f0000000..f0033fff]
  virt_entry=f0000000
ELF-loading image 'sel4test-driver'
  paddr=[80034000..8036efff]
  vaddr=[10000..34afff]
  virt_entry=1c880
Enabling MMU and paging
Jumping to kernel-image entry point...

Bootstrapping kernel
Switching to a safer, bigger stack...
seL4 Test
=========

> 这里是引用

...

要退出 Qemu,请键入序列 Ctrl + a ,然后是 x 。 请注意,您可以随时退出; 您无需等待系统完成或达到某种稳定状态。 如果您在系统运行时退出 Qemu,它只会被终止。

Qemu 内置了一些强大的调试功能。您可以按 Ctrl + a ,然后按 c 切换到 Qemu 监视器。 从这里您可以检查 CPU 或设备状态、读取和写入内存以及单步执行。 Qemu 文档中提供了有关此功能的更多信息。

在调试 seL4 项目时,Qemu 调试器具有先天的局限性。 它不了解您的源代码,因此很难将您所看到的与您编译的 C 代码联系起来。 通过将 GDB 连接到 Qemu 可以获得更丰富的调试环境。

Using GDB with Qemu

GDB 是 C 应用程序开发中常用的调试器。 虽然不像调试本地 Linux 应用程序那样无缝,但可以使用 GDB 在 Qemu 的模拟环境中进行调试。

使用额外选项“-S”(启动时暂停执行)和“-s”(在 TCP 端口 1234 上启动 GDB 服务器)启动 Qemu:

./simulate --extra-qemu-args="-S -s"

在单独的终端窗口中,启动目标平台版本的 GDB。 如果您打算调试 seL4 本身,您应该传递 seL4 内核的二进制文件;如果您打算调试 seL4 上的应用程序,则应该传递用户空间应用程序。

在本例子中,我们将调试在调试模式下构建的seL4内核:

${CROSS_COMPILER_PREFIX}gdb kernel/kernel.elf

其中 CROSS_COMPILER_PREFIX 是用于您正在调试的体系结构的工具链的前缀。 对于 x86,这是空白的,对于其他架构,您可以检查构建目录中的 CMakeCache.txt 文件以查找变量 CROSS_COMPILER_PREFIX 。 在以下示例中,前缀是 arm-none-eabi-

CROSS_COMPILER_PREFIX:UNINITIALIZED=arm-none-eabi-

在GDB提示符下,输入“target remote :1234”以连接到Qemu所托管的服务器:

Reading symbols from kernel/kernel.elf...done.
(gdb) target remote :1234
Remote debugging using :1234
0x82000000 in ?? ()
(gdb)

假设我们想在调用kprintf时停止。在GDB提示符下输入“break kprintf”

(gdb) break kprintf
Breakpoint 1 at 0xf0011248: file kernel/src/machine/io.c, line 269.

我们现在可以开始Qemu运行,并等待,直到我们到达断点。为此,请在GDB提示符下键入“cont”:

(gdb) cont
Continuing.

Breakpoint 1, kprintf (format=0xf0428000 "") at kernel/src/machine/io.c:269
269     {

注意,一些输出出现在运行Qemu的终端窗口中,因为它已经部分执行。也许令人惊讶的是,有些打印发生了,而我们的断点没有触发。其原因是,我们看到的输出来自于在内核之前运行的ELF加载器。GDB不知道它的打印函数的地址(因为我们只给了GDB内核的符号表),而且它也不希望破坏它的地址。我们刚刚遇到的断点是“内核”第一次尝试打印。类似地,我们已经配置的断点对用户空间的 printf 调用没有影响。

现在我们在断点处停止,所有标准的GDB操作都是可能的(检查寄存器或堆栈,单步,继续直到函数退出,……)。更多信息请参见GDB手册。

要注意,如果你调试内核的早期引导步骤,可能不是显而易见的是,调试跨上下文中页面映射变化(例如切换页面目录或打开/关闭MMU)会混淆GDB,你可能会发现断点触发意外或被错过。 我们所描述的过程与x86类似,但如果您是在x86或x86_64主机上,您可以简单地使用您的平台的本机GDB,gdb。

Userspace debugging

在seL4上调试用户空间应用程序的步骤与我们刚才看到的步骤相同,只是我们传递了GDB一个针对用户空间的符号表,而不是内核。例如,使用相同的sel4测试环境,我们以同样的方式启动Qemu,但是使用sel4测试的二进制文件启动GDB:

${CROSS_COMPILER_PREFIX}gdb projects/sel4test/apps/sel4test-driver/sel4test-driver

连接到Qemu后,我们可以指示GDB打破用户空间 printf 功能:

Reading symbols from projects/sel4test/apps/sel4test-driver/sel4test-driver.bin...done.
(gdb) target remote :1234
Remote debugging using :1234 0x82000000 in ?? ()
(gdb) break printf
Breakpoint 1 at 0x30870: file libs/libmuslc/src/stdio/printf.c, line 9.
(gdb)

请注意,GDB已经正确地识别了Musl C中的printf函数。我们现在将像以前一样继续:

(gdb) cont
Continuing.

Breakpoint 1, printf (fmt=0x363e8 "%s") at libs/libmuslc/src/stdio/printf.c:9
9               ret = vfprintf(stdout, fmt, ap);
(gdb)

如果您此时检查运行Qemu的终端窗口,您会注意到我们看到内核的额外输出。内核的打印功能不受影响,但是GDB在第一次使用用户空间名为 printf 时就停止了执行。

从这里开始,体验与调试内核基本相同。 需要注意的一个小问题是,跨上下文切换进行调试可能会造成混淆。 如果您在 GDB 中单步执行并发现执行突然转移到一个代码未知的地址,请检查该地址本身。 当在内核代码或异常向量表中执行时,通常仅从地址很容易识别。 不幸的是,在您返回用户空间之前,没有简单的方法可以继续,特别是如果内核安排的线程与您正在调试的线程不同。 根据您正在调试的场景,修改系统设置以确保只有一个线程在运行可能会更简单。

Objdump

Objdump 可用于反汇编 ELF 文件,无论是内核还是应用程序。 这可用于查找发生故障的指令。 确保将 -g 传递给编译器,以便调试信息包含在映像中。

与 gdb 一样,必须使用 CROSS_COMPILER_PREFIX 调用工具链中适当的 objdump。

对于 ARM,假设使用 arm-none-eabi- 作为交叉编译器前缀。

arm-none-eabi-objdump -D binary_file_name > dump.s

For x86

objdump -D binary_file_name > dump.s

文件 dump.s 具有人类可读的汇编指令。

如果您有符号并且想要 © 反汇编中的源信息,那么请使用 -S 标志。 例如:

${CROSS_COMPILER_PREFIX}objdump -DS binary_file_name
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值