在进行arm祼机和内核开发时,利用qemu搭建虚拟开发环境时,为了便于调试,可以利用GDB强大调试功能。
qemu和交叉编译链安装本文不再请述,后续再抽时间补上,本文重启讲述GDB的安装和调试,下面是以centos9上进行的测试。
一、GDB编译和安装
1、安装要求
为了满足在X86主机上上调试qemu虚拟机上运行的ARM代码,在GDB编译时有以下两个要求:
1、configure时参数: --enable-targets 必须指定目标平台,如果清楚最好的所有平台,采用以下参数:--enable-targets=all
2、configure 必须指定参数:--with-expat。该参数是让GDB可以进行XML解析,可以正常解析qemu的gdbserver发送的xml数据,根据xml数据自动选择目标平台,如果不支持XML解析,笔者在调试过程中出现了以下一些错误:
gdb XML support was disabled at compile time,Remote 'g' packet reply is too long(网上很多帖了解决方案,说GDB的bug,需要改原代码,其实是gdb无法解析gdbserver发送的xml数据,不知道数据长度而出现的问题。)
gdb XML support was disabled at compile time,Truncated register 16 in remote 'g' packet(这个问题是因为GDB无法解析gdbserver发送的XML数据,在set architecture 又设置不正确时会产生,尤其是调试x86 64位内核时,如果内核需要从32位模式转64位模式,用set architecture命令很难设置)
2、安装过程
本文以gdb-13.2为例说明编译过程(采用独立目录方式编译,即不直接在源代码目录下编译),下载gdb源码(gdb-13.2-src.tar.gz)后执行以下操作:
#tar -zxvf gdb-13.2-src.tar.gz
#mkdir build
#cd build
#../gdb-13.2/configure --enable-targets=all --enable-64-bit-bfd --with-expat --prefix=/usr/local/gdb-13.2
#make
#make install
执行完以上代码后,会将程序安装在/usr/local/gdb-13.2目录下。安装完成后,需要设置PATH路径,以/etc/profile.d目录下新建文件:gdb-13.2.sh ,文件内容如下:
export PATH=$PATH:/usr/local/gdb-13.2/bin
编辑完gdb-13.2.sh文件后,执行如下命令或重启系统PATH路径行效。
#source /etc/profile
执行 “gdb -v”命令,显示如下信息表示安装成功。
# gdb -v
GNU gdb (GDB) 13.2
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
3、需要安装第三方库
在编译gdb过程中,如果有些库没有安装,会报错,笔者在安装过程中遇过以下问题,需要安装第三方库文件:
1、GMP is missing or unusable
需要安装gmp库: yum install gmp-devel / apt-get install libgmp3-dev
2、 undefined reference to 'tputs'
需要安装 ncurses 开发库:yum install ncurses-devel
3、makeinfo‘ is missing on your system
需要安装 texinfo 库: apt-get install texinfo 或 yum install texinfo
4、 error: expat is missing or unusable(checking for libexpat... no)
需要安装 expat 开发库: apt-get install libexpat1-dev 或 yum install expat-devel
二、利用gdb远程调试qemu虚拟机程序
1、祼机程序调试
测试代码文件-hello.c内容如下:
#include <stdio.h>
#include <unistd.h>int main(){
while(1){
printf("Hello ARM!\n");
sleep(1);
}
return 0;
}
执行以下编译命令,生成arm架构下可运行的hello程序:
#arm-none-linux-gnueabihf-gcc -static -Wall -g -o hello hello.c
说明:-static是采用静态编译,否则在运行qemu时会寻找动态库,无法找到。编译时需要带-g参数,有利于gdb进行调试。
执行以下命令,可以不断输出Hello ARM!,表示程序是否可以正常运行:
# qemu-arm hello
Hello ARM!
Hello ARM!
Hello ARM!
Hello ARM!
Hello ARM!
Hello ARM!
Hello ARM!
Hello ARM!
Hello ARM!
执行以下命令,让qemu开启gdbserver,等待gdb连接进行调试
# qemu-arm -g 1234 hello
说明:-g表示启动gdbserver,相当于gdb tcp:1234,这时程序阻塞,等待gdb连接。
另启一个终端命令窗口(需要切换到 hello 文件所在目录,否则在gdb执行file命令时,需要使用 hello 文件的绝对路径),可以利用gdb进行调试(gdb的具体使用不在本文说明),如下所示:
# gdb
GNU gdb (GDB) 13.2
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
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 "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.For help, type "help".
Type "apropos word" to search for commands related to "word".
(gdb) file hello
Reading symbols from hello...
(gdb) list
1 #include <stdio.h>
2 #include <unistd.h>
3
4 int main(){
5 while(1){
6 printf("Hello ARM!\n");
7 sleep(1);
8 }
9 return 0;
10 }
(gdb) b hello.c:6
Breakpoint 1 at 0x103b8: file hello.c, line 6.
(gdb) target remote :1234
Remote debugging using :1234
_start () at ../sysdeps/arm/start.S:79
79 ../sysdeps/arm/start.S: 没有那个文件或目录.
(gdb) c
Continuing.Breakpoint 1, main () at hello.c:6
6 printf("Hello ARM!\n");
(gdb) n
7 sleep(1);
(gdb) n
6 printf("Hello ARM!\n");
(gdb)
2、内核程序调试
测试代码文件-test01.s内容如下:
.global _start
_start:
ldr r0, =0X020C4068 /* CCM_CCGR0 */
ldr r1, =0XFFFFFFFF
str r1, [r0]
ldr r0, =0X020C406C /* CCM_CCGR1 */
str r1, [r0]
ldr r0, =0X020C4070 /* CCM_CCGR2 */
str r1, [r0]
ldr r0, =0X020C4074 /* CCM_CCGR3 */
str r1, [r0]
ldr r0, =0X020C4078 /* CCM_CCGR4 */
str r1, [r0]
ldr r0, =0X020C407C /* CCM_CCGR5 */
str r1, [r0]
ldr r0, =0X020C4080 /* CCM_CCGR6 */
str r1, [r0]
ldr r0, =0X020E0068 /* IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 */
ldr r1, =0X5
str r1,[r0]
ldr r0, =0X020E02F4 /* IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 */
ldr r1, =0X10B0
str r1,[r0]
ldr r0, =0X0209C000 /* GPIO1_DR */
ldr r1, =0
str r1,[r0]
loop:
b loop
执行以下命令汇编程序进行编译,生成 test01.elf 文件:
[root@localhost as]# ls
test01.s
[root@localhost as]# arm-none-eabi-gcc -Wall -g -o test01.o -c test01.s
[root@localhost as]# ls
test01.o test01.s
[root@localhost as]# arm-none-eabi-ld -Ttext 0x80000000 test01.o -o test01.elf
[root@localhost as]# ls
test01.elf test01.o test01.s
[root@localhost as]#
执行以下命令,启动qemu虚拟机,等待gdb连接调试
[root@localhost as]# qemu-system-arm -machine mcimx6ul-evk -kernel test01.elf -S -s
VNC server running on ::1:5900
说明:-s表示在1234端口启动gdbserver,相当于gdb tcp:1234;-S 让qemu阻塞,等待gdb连接。
另启一个终端命令窗口(需要切换到 test01.elf 文件所在目录,否则在gdb执行file命令时,需要使用 test01.elf 文件的绝对路径),可以利用gdb进行调试,如下所示:
[root@localhost as]# gdb
GNU gdb (GDB) 13.2
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
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 "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.For help, type "help".
Type "apropos word" to search for commands related to "word".
(gdb) file test01.elf
Reading symbols from test01.elf...
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
_start () at test01.s:3
3 ldr r0, =0X020C4068 /* CCM_CCGR0 */
(gdb) list
1 .global _start
2 _start:
3 ldr r0, =0X020C4068 /* CCM_CCGR0 */
4 ldr r1, =0XFFFFFFFF
5 str r1, [r0]
6 ldr r0, =0X020C406C /* CCM_CCGR1 */
7 str r1, [r0]
8 ldr r0, =0X020C4070 /* CCM_CCGR2 */
9 str r1, [r0]
10 ldr r0, =0X020C4074 /* CCM_CCGR3 */
(gdb) b 4
Breakpoint 1 at 0x80000004: file test01.s, line 4.
(gdb) c
Continuing.Breakpoint 1, _start () at test01.s:4
4 ldr r1, =0XFFFFFFFF
(gdb) n
5 str r1, [r0]
(gdb) i r
r0 0x20c4068 34357352
r1 0xffffffff -1
r2 0x0 0
r3 0x0 0
r4 0x0 0
r5 0x0 0
r6 0x0 0
r7 0x0 0
r8 0x0 0
r9 0x0 0
r10 0x0 0
r11 0x0 0
r12 0x0 0
sp 0x0 0x0
lr 0x0 0
--Type <RET> for more, q to quit, c to continue without paging--q
Quit
(gdb)