通过qemu+gdb实现跨架构调试
需求
有时候身边只有 x86 架构的硬件环境,却想学习和测试 arm、mips 等其他架构特性,此时怎么办呢?众所周知,VMware 只能模拟同架构不同操作系统,对此可以通过 qemu 实现跨架构模拟。
安装 qemu-user
qemu 是一个支持跨平台虚拟化的虚拟机,有 user mode 和 system mode 两种配置方式。其中qemu 在system mode配置下模拟出整个计算机,可以在qemu之上运行一个操作系统。qemu 的system mode与常见的VMware和Virtualbox等虚拟机比较相似,但是qemu 的优势是可以跨指令集。例如,VMware和Virtualbox之类的工具通常只能在x86计算机上虚拟出一个x86计算机,而qemu 支持在x86上虚拟出一个ARM计算机。qemu在user mode配置下,可以运行跟当前平台指令集不同的平台可执行程序。例如可以用qemu在x86上运行ARM的可执行程序,但是两个平台必须是同一种操作系统,比如Linux。
sudo apt install qemu-user
安装 gdb-multiarch
gdb-multiarch 是一个经过交叉编译后的、支持多架构版本的 gdb。
sudo apt install gdb-multiarch
安装 aarch64 编译工具链
sudo apt install gcc-aarch64-linux-gnu
交叉编译测试用例
lhx@ubuntu:~/test/qemu$ ls
hello.c
lhx@ubuntu:~/test/qemu$ cat hello.c
#include <stdio.h>
void hello()
{
printf("Hello World !\n");
}
int main()
{
hello();
return 0;
}
lhx@ubuntu:~/test/qemu$ aarch64-linux-gnu-gcc -g -static hello.c
lhx@ubuntu:~/test/qemu$ ls
a.out hello.c
lhx@ubuntu:~/test/qemu$ file a.out
a.out: ELF 64-bit LSB executable, ARM aarch64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=e1fe3c59cad06eff9cab2729a00233bc10d763ce, for GNU/Linux 3.7.0, with debug_info, not stripped
lhx@ubuntu:~/test/qemu$ qemu-aarch64 ./a.out
Hello World !
开始qemu+gdb跨架构调试
- 窗口1:启动 a.out
通过 qemu-aarch64 运行交叉编译的 a.out, 并指定 gdb 调试端口号为1234,然后等待 gdb 远程连接。
lhx@ubuntu:~/test/qemu$ qemu-aarch64 -g 1234 ./a.out
Hello World !
- 窗口2:gdb 远程调试
通过 gdb-multiarch 启动 a.out,这里 a.out 用于读取和远程端一致的调试符号信息。连接上远程端口号后,便可以进行设断点、查看寄存器、反汇编等一系列调试操作。
lhx@ubuntu:~/test/qemu$ ls
a.out hello.c
lhx@ubuntu:~/test/qemu$ gdb-multiarch -q a.out
Reading symbols from a.out...
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
0x0000000000400558 in _start ()
(gdb) b main
Breakpoint 1 at 0x4006d4: file hello.c, line 10.
(gdb) c
Continuing.
Breakpoint 1, main () at hello.c:10
10 hello();
(gdb) s
hello () at hello.c:5
5 printf("Hello World !\n");
(gdb) n
6 }
(gdb) info registers
x0 0xe 14
x1 0x1 1
x2 0x0 0
x3 0x48bf00 4767488
x4 0xfbad2a84 4222429828
x5 0x21a 538
x6 0x10 16
x7 0x7f7f7f7f7f7f7f7f 9187201950435737471
x8 0x40 64
x9 0x3fffffff 1073741823
x10 0x20000000 536870912
x11 0x10000 65536
x12 0x48b000 4763648
x13 0x410 1040
x14 0x0 0
x15 0x48c738 4769592
x16 0x40b998 4241816
x17 0x416fc0 4288448
x18 0x0 0
x19 0x400db8 4197816
x20 0x400e80 4198016
x21 0x0 0
x22 0x400280 4194944
x23 0x489030 4755504
x24 0x18 24
x25 0x48b000 4763648
x26 0x48b000 4763648
x27 0x451000 4526080
x28 0x0 0
x29 0x4000800260 274886296160
x30 0x4006c0 4196032
sp 0x4000800260 0x4000800260
pc 0x4006c0 0x4006c0 <hello+20>
cpsr 0x60000000 1610612736
fpsr 0x0 0
fpcr 0x0 0
(gdb) bt
#0 hello () at hello.c:6
#1 0x00000000004006d8 in main () at hello.c:10
(gdb) disassemble hello
Dump of assembler code for function hello:
0x00000000004006ac <+0>: stp x29, x30, [sp, #-16]!
0x00000000004006b0 <+4>: mov x29, sp
0x00000000004006b4 <+8>: adrp x0, 0x451000 <_nl_locale_subfreeres+552>
0x00000000004006b8 <+12>: add x0, x0, #0x3e8
0x00000000004006bc <+16>: bl 0x407350 <puts>
0x00000000004006c0 <+20>: nop
0x00000000004006c4 <+24>: ldp x29, x30, [sp], #16
0x00000000004006c8 <+28>: ret
End of assembler dump.
(gdb) c
Continuing.
[Inferior 1 (process 1) exited normally]
(gdb)