如何在QEMU上使用GDB单步执行ARM汇编代码?

11

我正在尝试使用GNU汇编器学习ARM汇编语言编程。我已经在我的PC上设置了QEmu,并且有一个Debian ARM-HF chroot环境。

如果我汇编和链接我的测试程序:

.text
.global _start
_start:
        mov     r0, #6
        bx      lr

随着:

as test.s -o test.o
ld test.o -o test

然后将文件加载到gdb中,并在_start上设置断点:

root@Latitude-E6420:/root# gdb test
GNU gdb (GDB) 7.6.1 (Debian 7.6.1-1)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "arm-linux-gnueabihf".
For bug reporting instructions, please see:
...
Reading symbols from /root/test...(no debugging symbols found)...done.
(gdb) break _start
Breakpoint 1 at 0x8054
(gdb)

如何逐步执行代码,显示汇编源代码和监视寄存器? 我尝试了一些基本命令,但它们没有起作用:

(gdb) break _start
Breakpoint 1 at 0x8054
(gdb) info regi
当前程序没有寄存器。
(gdb) stepi
程序没有运行。
(gdb) disas
未选择帧。
(gdb) r
Starting program: /root/test 
qemu: Unsupported syscall: 26
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
qemu: Unsupported syscall: 26
During startup program terminated with signal SIGSEGV, Segmentation fault.
(gdb) 
4个回答

16
您的问题在于您正在尝试在QEMU用户模式仿真下运行ARM gdb。 QEMU不支持ptrace系统调用(即系统调用编号为26),因此这永远不会起作用。
您需要做的是使用启用QEMU的内置gdb存根的QEMU选项在QEMU下运行测试二进制文件,该存根将侦听TCP端口。 然后,您可以运行一种编译为在您的主机系统上运行但支持ARM目标的gdb,并告诉它连接到TCP端口。
(在QEMU内部仿真ptrace技术上非常棘手,并且它不会提供任何无法通过QEMU内置的gdbstub实现的额外功能。 几乎不可能实现。)

这个问题的讨论也在这里:https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=832710 - Serge Rogatch
相关内容:在x86-64 Linux主机上实际设置QEMU + GDB交叉调试的演示:如何运行一行汇编代码,然后查看[R1]和条件标志 - Peter Cordes

12

最简单的QEMU用户模式示例

我错过了-fno-pie -no-pie选项:

sudo apt-get install gdb-multiarch gcc-arm-linux-gnueabihf qemu-user
printf '
#include <stdio.h>
#include <stdlib.h>

int main() {
    puts("hello world");
    return EXIT_SUCCESS;
}
' >  hello_world.c
arm-linux-gnueabihf-gcc -fno-pie -ggdb3 -no-pie -o hello_world hello_world.c
qemu-arm -L /usr/arm-linux-gnueabihf -g 1234 ./hello_world

在另一个终端上:

gdb-multiarch -q --nh \
  -ex 'set architecture arm' \
  -ex 'set sysroot /usr/arm-linux-gnueabihf' \
  -ex 'file hello_world' \
  -ex 'target remote localhost:1234' \
  -ex 'break main' \
  -ex continue \
  -ex 'layout split'
;

这使我们处于main,由于layout split而处于拆分的代码/反汇编视图。您可能还感兴趣:

layout regs

展示寄存器的内容。

不过,无论如何,GDB仪表板更加灵活可靠:使用代码的gdb分割视图

-fno-pie -no-pie是必需的,因为封装的Ubuntu GCC默认使用-fpie -pie,而这些会由于QEMU的错误而失败:如何在QEMU用户模式下GDB逐步调试动态链接的可执行文件?

在QEMU 2.11上,没有类似于gdbserver --multi的功能可用于QEMU GDB存根:如何像gdbserver --multi那样从GDB存根重新启动QEMU用户模式程序?

对于那些学习ARM汇编的人,我正在开始一些带有断言和使用C标准库进行IO的可运行示例:https://github.com/cirosantilli/arm-assembly-cheat

在Ubuntu 18.04、gdb-multiarch 8.1、gcc-arm-linux-gnueabihf 7.3.0和qemu-user 2.11上测试通过。

独立运行的QEMU用户模式示例

这个类似的过程也适用于ARM独立运行(没有标准库)的示例:

printf '
.data
    msg:
        .ascii "hello world\\n"
    len = . - msg
.text
.global _start
_start:
    /* write syscall */
    mov r0, #1     /* stdout */
    ldr r1, =msg   /* buffer */
    ldr r2, =len   /* len */
    mov r7, #4     /* Syscall ID. */
    swi #0

    /* exit syscall */
    mov r0, #0 /* Status. */
    mov r7, #1 /* Syscall ID. */
    swi #0
' >  hello_world.S
arm-linux-gnueabihf-gcc -ggdb3 -nostdlib -o hello_world -static hello_world.S
qemu-arm -g 1234 ./hello_world

在另一个终端中:

gdb-multiarch -q --nh \
  -ex 'set architecture arm' \
  -ex 'file hello_world' \
  -ex 'target remote localhost:1234' \
  -ex 'layout split' \
;

我们现在停留在程序的第一条指令。

QEMU全系统示例


1
我在如何运行一行汇编代码,然后查看[R1]和条件标志的答案中,提供了qemu-user + gdb远程调试的另一种步骤。 - Peter Cordes

3

使用 stepi 命令执行汇编指令的单个步骤。使用 disas 命令在当前 PC 周围反汇编代码。使用 info regi 命令显示当前寄存器状态。我的ELLCC 交叉开发工具链项目博客中有各种处理器的示例。


谢谢您的回复,我已经尝试了这些方法但是没有成功,如果方便的话请再看一下我更新的帖子。 - fred basset
1
@fred basset:你需要在设置断点后先输入运行(r)。然后应该就可以了。 - gnometorule
我尝试运行程序,但它崩溃了。这可能是qemu的问题吗? - fred basset

-2
你应该同时添加-g选项到汇编中。否则,代码行信息将不会被包含在内。 那个崩溃可能来自在代码行之后运行了一些垃圾代码。 也许你应该添加退出系统调用:
mov eax, 1 ; exit
mov ebx, 0 ; returm value
int 0x80 ; system call

3
调试信息与汇编语言水平有何关系?你的x86汇编代码与ARM有什么关系? - Ruslan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接