需求 有时候身边只有 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。
1 sudo apt install qemu-user
安装 gdb-multiarch gdb-multiarch 是一个经过交叉编译后的、支持多架构版本的 gdb。
1 sudo apt install gdb-multiarch
安装 aarch64 编译工具链 1 sudo apt install gcc-aarch64-linux-gnu
交叉编译测试用例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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 远程连接。
1 2 lhx@ubuntu:~/test/qemu$ qemu-aarch64 -g 1234 ./a.out Hello World !
-g port:该选项表示 QEMU_GDB 环境变量取值,即 等待 gdb 连接的端口号。
窗口 2:gdb 远程调试 通过 gdb-multiarch 启动 a.out,这里 a.out 用于读取和远程端一致的调试符号信息。连接上远程端口号后,便可以进行设断点、查看寄存器、反汇编等一系列调试操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 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)