前言
本期主要讲解如何移植,使用gdb
调试程序,及如何使用gdbserver
远程调试开发板
正文
话不多少进入正文,gdb
基础调试的文章网上有很多了,大家可以多参考几篇博客。
移植gdbserver
开发板上面空间不足以运行gdb
,所以要移植gdbserver
进行远程调试。
- 移植
gmp
GDB
是依赖于GMP
的,所以要先交叉编译GMP
。这一点是网上很多水博客的人没有提到的。不然会报错:
configure: error: GMP is missing or unusable
官网:[https://gmplib.org/](https://gmplib.org/),截至2023年8月最新版本为6.3.0
下载,解压,配置,编译,安装。
tar -xf gmp.tar.xz
cd gmp
./configure --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf --prefix=$PWD/__install
make
make install
# 此时记下目录,后面配置会用到,我的是:
# /home/ql/Desktop/config/gdb-study/gmp-6.3.0/__install/lib
# /home/ql/Desktop/config/gdb-study/gmp-6.3.0/__install/include
# # 为了方便使用,也可以将__install目录下的文件复制到交叉编译工具链下对应的include和lib目录下,后续将不用增加额外配置项
-
编译gdb
官网:https://sourceware.org/gdb/,最新版本为12.2
下载,解压,配置,编译gdb,编译gdbserver,将要用到的产出物推到板子上。
tar -xf gdb.tar.xz
cd gdb
# 记下的目录在这里用到
./configure --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf --prefix=$PWD/__install CFLAGS="-I/home/ql/Desktop/config/gdb-study/gmp-6.3.0/__install/include -L/home/ql/Desktop/config/gdb-study/gmp-6.3.0/__install/lib" CXXFLAGS="-I/home/ql/Desktop/config/gdb-study/gmp-6.3.0/__install/include -L/home/ql/Desktop/config/gdb-study/gmp-6.3.0/__install/lib"
make -j $nproc
正常用apt安装的交叉编译工具链到这里可以编译成功的,由于我用了自己厂商给的工具链,中间会遇到一些小问题,无非是缺少宏定义或者头文件没有引入,直接修改对应文件代码即可,比如:
- 缺少一些宏定义
./fibheap.c:38:24: error: 'long_min' undeclared (first use in this function) #define fibheapkey_min long_min
解决:
将以下代码复制到报错文件
#ifndef ULONG_MAX
#define ULONG_MAX ((unsigned long)(~0L)) /* 0xFFFFFFFF */
#endif
#ifndef LONG_MAX
#define LONG_MAX ((long)(ULONG_MAX >> 1)) /* 0x7FFFFFFF */
#endif
#ifndef LONG_MIN
#define LONG_MIN ((long)(~LONG_MAX)) /* 0x80000000 */
#endif
- 缺少宏定义
解决:查看宏定义属于头文件fcntl.h
,则引入头文件#include <fcntl.h>
- 编译
gdbserver
正常编译gdb
会顺便将gdbserver
编译出来的,在gdbserver
目录下,如果需要单独编译的话,直接运行以下命令即可(前提是要先编译过gdb
,不然可能会报错)。
make install # 安装到目录下的__isntall
cd gdbserver
./configure --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf
make # 同目录下生成gdbserver文件,推到板子上就可以用啦
运行如下命令验证是否成功:
root@sun8i:/mnt/UDISK# ./gdbserver --version
GNU gdbserver (GDB) 13.2
Copyright (C) 2023 Free Software Foundation, Inc.
gdbserver is free software, covered by the GNU General Public License.
This gdbserver was configured as "arm-openwrt-linux"
如图:
gdb
调试
私以为当从以下几个方面掌握:
-
断点和单步执行:学习如何在程序中设置断点,以便在特定位置暂停程序的执行。
-
变量查看和修改:学习如何在调试过程中查看和修改变量的值。
-
栈和调用链:了解函数调用的栈帧结构,学习如何查看当前的函数调用链,以便在调试过程中理解函数的嵌套关系。
-
内存和指针:学习如何查看和跟踪内存中的数据,包括指针和动态内存分配。这对于定位内存泄漏和野指针等问题非常重要。
-
多线程调试:学习如何在多线程程序中进行调试,理解线程的状态和相互作用,以及如何避免竞态条件等并发问题。
-
异常处理和信号:了解如何调试程序中的异常情况和信号处理程序。通过对程序中的异常情况进行调试,可以更好地理解代码的异常处理机制。
-
调试优化:学习如何使用 GDB 的各种功能和选项来加速调试过程,并提高调试效率。
主要调试命令有以下几类:
信息展示:
命令 | 作用 |
---|---|
info sources | 查看所有相关源文件 |
info source | 查看当前运行的源文件 |
list | 查看当前运行位置代码,简写l |
info break | 查看当前断点 |
x/<n><fmt> <address> | 以fmt格式打印address地址的n个,fmt可选以下内容:b:以字节为单位显示。h:以半个字(2 字节)为单位显示。w:以字(4 字节)为单位显示。g:以双字(8 字节)为单位显示。x/d/s:以十六进制/十进制/空字符为终止标志打印字符串。i:显示机器指令。a:以内存地址和符号名称的形式显示。 |
backtrace | 简写bt 查看调用栈信息,也可加参数 bt full/ bt 数字 |
display i | 每次都展示变量i |
info threads | 打印线程信息 |
断点相关:
命令 | 作用 |
---|---|
break main.c:10 if i=10 | 在第十行设置条件断点,只有i=10的才停 |
break add | 在add函数设置断点 |
break *address | 在address地址上设置断点 |
break main.c:add | 在main.c文件的add函数设置断点 |
info breakpoints | 展示所有断点,这里有断点的编号 |
delete/disable/enable 断点编号 | 删除,屏蔽,使能编号 |
流程相关
命令 | 作用 |
---|---|
run | 开始运行 |
continue | 继续运行 |
next | 运行下一行 |
step | 进入函数 |
finish | 运行到当前函数结束,并给出返回值 |
call add(a,b) | 强制调用add函数,入参是a,b |
call malloc_stats() | 统计内存使用 |
变量相关
命令 | 作用 |
---|---|
set var i=10 | 将变量i设置成10 |
print i=10 | 将i赋值为10 |
进程相关
- info threads:显示当前所有线程的信息,包括线程编号(Id)、线程状态(State)和当前所在位置等。
- thread <thread_id>:切换到指定线程。例如,thread 2 将切换到线程编号为 2 的线程。
信号相关
- 异常处理:
- catch : 设置一个断点来捕获指定的异常。例子:catch throw 捕获所有 throw 语句引发的异常。
- handle [command]: 对指定信号进行处理。您可以指定一个命令来在捕获该信号时执行。例子:handle SIGSEGV nostop 可以在捕获到 SIGSEGV(段错误)信号时继续执行程序而不暂停。
- 信号处理:
- info signals: 显示所有信号的信息,包括信号名称和描述。
- handle [options]: 设置对指定信号的处理方式。例如,handle SIGINT nostop 可以在捕获到 SIGINT(中断)信号时继续执行程序而不暂停。
- signal : 发送一个指定的信号给正在运行的程序。例子:signal SIGTERM 发送 SIGTERM 给程序,以请求其优雅地终止。
贴一些记录:
(gdb) l
11 printf("i=%d\n",i);
12 }
13
14 char *str = "Hello World!";
15
16 for(i=0;i<15;i++){
17 printf("%d \t: %c\n",i,str[i]);
18 }
19
20
(gdb) print str
$1 = 0x555555556004 "Hello World!"
(gdb) x/s 0x555555556004
0x555555556004: "Hello World!"
(gdb) x/w 0x555555556004
0x555555556004: U"\x6c6c6548\x6f57206f\x21646c72\x253d6900\x25000a64\x3a092064\xa632520\x253d6900\x6a202c64\xa64253d\x6c654800\x57206f6c\x646c726f\x646e4520!\x3b031b01\064\005\xffffefe0h\xfffff010\220\xfffff020¨\xfffff040P\xfffff129À"
(gdb) x/s 0x555555556004
0x555555556004: "Hello World!"
(gdb) x/xb 0x555555556004
0x555555556004: 0x48
(gdb) x/5xb 0x555555556004
0x555555556004: 0x48 0x65 0x6c 0x6c 0x6f
(gdb) x/5xh
0x555555556009: 0x5720 0x726f 0x646c 0x0021 0x3d69
(gdb) x/a 0x555555556004
0x555555556004: 0x6f57206f6c6c6548
(gdb) x/xb &i
0x7fffffffde70: 0x04
远程调试
远程调试只是换了一个启动方式,调试方法基本还是一致的。
- 在目标设备上启动
gdbserver
:在目标设备(被调试的设备)上运行gdbserver
,并指定要调试的可执行文件。例如,可以通过以下命令启动gdbserver
并监听特定端口:
gdbserver host:port <executable> # host:port替换为ip和端口号 <executable> 替换为被调试程序名
# 如下:
root@sun8i:/mnt/UDISK# ./gdbserver 10.192.136.217:8888 runner
Process /mnt/UDISK/runner created; pid = 1732
Listening on port 8888
Remote debugging from host 10.192.136.238, port 58124
- 在本机启动
gdb
gdb
target remote <target_ip>:<port>
#如下:
(gdb) target remote 10.192.136.217:8888
`target:/mnt/UDISK/runner' has disappeared; keeping its symbols.
Remote debugging using 10.192.136.217:8888
一些过程命令
./configure --host=arm-openwrt-linux --target=arm-openwrt-linux --prefix=$PWD/__install CFLAGS="-I/home/ql/Desktop/config/gdb-study/gmp-6.3.0/__install/include -L/home/ql/Desktop/config/gdb-study/gmp-6.3.0/__install/lib -I/home/ql/Downloads/R16_Tina_compiler_glibc/staging_dir/target/usr/include -L/home/ql/Downloads/R16_Tina_compiler_glibc/staging_dir/target/usr/lib" CXXFLAGS="-I/home/ql/Desktop/config/gdb-study/gmp-6.3.0/__install/include -L/home/ql/Desktop/config/gdb-study/gmp-6.3.0/__install/lib -I/home/ql/Downloads/R16_Tina_compiler_glibc/staging_dir/target/usr/include -L/home/ql/Downloads/R16_Tina_compiler_glibc/staging_dir/target/usr/lib"
PATH=$PATH:/home/ql/Downloads/R16_Tina_compiler_glibc/toolchain-sunxi-glibc/toolchain/bin
STAGING_DIR=/home/ql/Downloads/R16_Tina_compiler_glibc/staging_dir