在学习单片机的时候我们可以通过集成式 IDE 来进行调试,比如 MDKIAR 等。在嵌入式 linux 领域是否也可以进行调试呢?答案肯定是可以的,在嵌入式 linux 领域我们最常用的就是 GDB 调试工具,通过 GDB 来调试嵌入式 C 程序。本章我们首先学习如何搭建嵌入式 Linux的 GDB 调试环境,然后讲解如何使用 GDB 工具调试 C 程序。


GDB 简介

gdb 工具是 GNU 项目调试器,基于命令行。和其他的调试器一样,我们可以使用 gdb 来一行行的运行程序、单步执行、跳入/跳出函数、设置断点、查看变量等等,它是 UNIX/LINUX 操作系统下强大的程序调试工具。gdb 支持多种语言,包括 Ada、汇编、C/C++DFortranGO、Objective-C、OpenCLModula-2Pascal Rust。关于 gdb 更多详细的信息请到 gdb 官网查阅,gdb 官网地址为:www.gnu.org

一般的桌面 Linux 系统,比如 ubuntucentos 等,我们可以直接运行 gdb 来调试程序。但是嵌入式中芯片性能一般比较弱,所以直接在嵌入式系统中运行 gdb 不太现实(性能强大的嵌入式芯片可以这么做)。嵌入式系统中一般在 PC 端运行 gdb 工具,源码也是在 PC 端,源码对应的可执行文件放到开发板中运行。为此我们需要在开发板中运行 gdbserver,通过网络与 PC 端的 gdb 进行通信。因此要想在 PC 上通过 gdb 调试嵌入式程序,那么需要两个东西:gdb 和gdbserver,其中 gdb 是运行在 PC 上的,gdbserver 需要我们移植到开发板上。


GDB 移植

一般交叉编译已经自带了 gdb gdbserver,因此可以不用移植,直接使用交叉编译器自带的即可。比如本教程所使用的 arm-linux-gnueabihf-gcc 就自带了主机使用的 arm-linux-gnueabihf-gdb 和开发板所使用的 gdbserver。进入 ubuntu 中交叉编译器安装目录中的 bin 文件夹下,比如笔者的就是/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin,此目录中就包含了 arm-linux-gnueabihf-gdb gdbserver,如图 B3.2.1 所示:

如果交叉编译器自带了 gdb gdbserver 的话只需要将 gdbserver 拷贝到开发板根文件系统的/bin 目录下

如果交叉编译器没有自带 gdb gdbserver 那就需要自己手动编译了。

获取 gdb gdbserver 源码

首先到 gdb 官网上获取源码,地址为 http://www.gnu.org/software/gdb/download/,在笔者写本教程的时候,最新的 gdb 源码版本为 9.1。已经放到了开发板光盘中,路径为:1、例程源码 ->7、第三方库源码-> gdb-9.1.tar.gz。将 gdb 源码发送到 ubuntu 中中并解压,命令如下:

tar -vxzf gdb-9.1.tar.gz

解压完成以后就会得到一个名为 gdb-9.1 的文件夹,此文件夹就是 gdb gdbserver 源码,其中 gdb-9.1/gdb/gdbserver 目录就是 gdbserver 源码。

编译 gdb

1、编译 gdb

首先编译 gdbgdb 是运行在 PC 端的程序,gdb 编译的时候需要进行配置,配置项如下:

--target目标机交叉编译器前缀,也就是你所使用的交叉编译器前缀,比如在本教程中就设置为 arm-linux-gnueabihf

--host指定编译后的程序在哪里运行,编译 gdb 的时候就需用设置,因为我们是需要在 PC上运行的,编译 gdbserver 的时候就要设置为 arm-linux

--prefix指定安装目录。

创建一个名为“gdb”的文件夹,用来保存编译后的 gdb gdbserver,路径自行选择。gdb编译比较奇葩!使用如下命令配置并编译 gdb

cd gdb-9.1/   //进入 gdb 源码目录
mkdir build  //在 gdb 源码下新建 build 目录,gdb 编译比较奇葩!不能直接在 gdb 源
             //码目录下进行配置和编译,必须新建一个文件夹,然后在此文件夹下配
             //置和编译,切记!
cd build     //进入到刚刚创建的 build 目录下
../configure --target=arm-linux-gnueabihf --prefix=/home/zuozhongkai/linux/IMX6ULL/tool/gdb
             //配置 gdb。配置完成以后会在 build 目录下生成 Makefile 文件。
make         //编译
make install //安装

编译完成以后PC端运行的gdb工具就会安装到gdb/bin目录下,名字为arm-linux-gnueabihf-gdb,如图 B3.2.2.1 所示:

图 B3.2.2.1 中的 arm-linux-gnueabihf-gdb 就会我们编译得到的 gdb 工具,输入如下命令运行此 gdb

cd gdb/bin //进入到 bin 目录下
./arm-linux-gnueabihf-gdb //运行此 gdb

结果如图 B2.2.2.2 所示:

从图 B3.2.2.2 可以看出,arm-linux-gnueabihf-gdb 版本号为 9.1,说明编译安装成功,输入“q”可以退出 gdb 工具。

2、关闭交叉编译器自带的 gdb

我们安装的 arm-linux-gnueabihf 交叉编译器已经默认自带了一个名为“arm-linux-gnueabihf-gdb”的 gdb 工具!大家可以到自己的交叉编译器安装目录看一下,相对路径为:gcc-linaro-4.9.4- 2017.01-x86_64_arm-linux-gnueabihf/bin,如图 B3.2.2.3 所示:

从图 B3.2.2.3 可以看出,编译器自带的 gdb 工具和我们编译出来的 gdb 名字一样,这两个都可以使用,也就是说我们可以不用自行编译 gdb,直接使用交叉编译器里面的也是可以的!

只是这两个 gdb 的版本号可能不一样,可以查看一下交叉编译器自带的 gdb 工具版本号,我这里如图 B3.2.2.4 所示:

从图 B3.2.2.4 可以看出,交叉编译器自带的 gdb 工具版本号为 7.10。现在有个问题,一山不能容二虎, 9.1 版本和 7.10 版本只能留一个,我测试过,这两个任意哪一个都可以。这里我们使用自己编译的 9.1 版本的 gdb 工具,因此需要对交叉编译器自带的 7.10 版本的 gdb 工具重命名,这里笔者将其重命名为“arm-linux-gdb”。

最后打开 ubuntu /etc/profile 文件,修改 PATH 环境变量值,将我们编译出来的 9.1 版本的“arm-linux-gnueabihf-gdb”所在的路径添加进去,这样我们就可以直接使用此 gdb 了,根据自己的实际情况修改 PATH 值,比如我的 PATH 内容如下:

export PATH=/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin:/home/zuozhongkai/linux/IMX6ULL/tool/gdb/bin:$PATH

其中红色加粗部分就是我所编译出来的 9.1 版本 gdb 工具所存放的路径,修改完以后重启

ubuntu,这个时候我们就可以使用命令“arm-linux-gnueabihf-gdb”来打开 gdb 调试器了。

移植 gdbserver

接下来移植 gdbservergdbserver 是在开发板上运行的,因此要交叉编译。gdbserver 源码保存在 gdb-9.1/gdb/gdbserver 目录下,进入此目录,然后输入如下命令配置并编译:

cd gdb-9.1/gdb/gdbserver //进入到 gdbserver 目录
./configure --target=arm-linux-gnueabihf --host=arm-linux-gnueabihf //配置
make CC=arm-linux-gnueabihf-gcc //交叉编译 gdbserver

编译完成以后就会在目录下生成一个名为“gdbserver”的文件,这个就是我们需要放到开发板上去的 gdbserver,如图 B3.2.3.1 所示:

将图 B3.2.3.1 中交叉编译出来的 gdbserver 软件发送到开发板中的/usr/bin 目录下,完成以后在开发板中输入如下命令查看 gdbserver 版本号:

gdbserver –version //查看版本号

如果 gdbserver 移植成功的话就会打印出其版本号,如图 B3.2.3.2 所示:

从图 B3.2.3.2 可以看出,当前 gdbserver 版本号为 9.1,说明我们的移植是成功的。PC 上的gdb 和开发板上的 gdbserver 都已经准备好了,接下来就是使用这两个工具来完成调试。


使用 GDB 进行嵌入式程序调试

编写一个测试应用

首先编写一个简单的测试软件,我们一会就用 gdb 来调试这个软件,新建名为 gdbtest.c 的文件,在里面输入如下所示内容:

使用 arm-linux-gnueabihf-gcc 交叉编译 gdbtest.c 文件,要想调试程序,那么编译的时候必须加上“-g”选项,这样编译出来的可执行文件才带有调试信息,这一点一定要切记!编译命令如下所示:

arm-linux-gnueabihf-gcc gdbtest.c -o gdbtest -g //编译测试程序,注意-g 选项

编译完成以后将得到的 gdbtest 可执行文件发送到开发板中。

GDB 调试程序

一切准备就绪以后就可以使用GDB进行调试了,确保ubuntu和开发板可以进行网络通信。

在开发板中输入如下命令:

gdbserver 192.168.1.253:2001 gdbtest //启动开发板上的 gdbserver

上述命令中 192.168.1.253 为调试机的 IP 地址,也就是 ubuntu IP 地址,2001 是端口号,可以任意给一个端口号,gdbtest 是要调试的可执行文件。输入以后开发板输出信息如图 B3.3.2.1所示:

接着在 ubuntu 中输入如下命令启动 gdb 调试工具:

arm-linux-gdb gdbtest

结果如图 B3.3.2.2 所示:

图 B3.3.2.2 中最下面的(gdb)行用于输入命令,输入如下命令连接到开发板上:

target remote 192.168.1.251:2001 //连接到开发板上

上述命令表示连接到开发板上,其中 192.168.1.251 就是开发板 IP 地址,2001 就是开发板gdbserver 设置的端口号。连接成功以后开发板中的 gdbserver 就会提示连接信息,如图 B2.3.2.3 所示:

从图 B3.3.2.3 可以看出,远端调试机的 IP 地址为 192.168.1.253,也就是我们的 ubuntu 址连接成功以后就可以在 ubuntu 上进行代码调试了。gdb 工具是一个基于命令行的调试工具,我们就来学习一下常用的几个命令。

1l 命令

l 命令(list)用于列出所有程序源码,输入“l”,结果如图 B3.3.2.4 所示:

从图 B3.3.2.4 可以看出,输入“l”命令以后就打印出了调试程序的所有源码,如果源码没有打印完的话就重复按下“l”命令,或者按下回车键,gdb 调试工具中回车键表示重复上一个命令!

2b 命令

b 命令(break)用于设置断点,也可以用缩写“b”,后面可以跟具体的函数或者行号,比如“break main”表示在 main 函数处设置断点,“break 11”在第 11 行设置断点。输入如下命令,在 main 函数处设置断点:

b main break main

设置以后提示如图 B3.3.2.5 所示:

从图 B3.3.2.5 可以看出,断点 1 设置到了 gdbtest.c 的第 6 行,大家可以看一下图 B3.3.2.4,第 6 行正好是 main 函数的起始处。

3c 命令

c 命令用于运行到断点处,输入 c 命令程序就会运行,直到下一个断点处,如图 B3.3.2.6 所示:

从图 B3.3.2.6 可以看出,当前程序运行到第 6 行停止,因为我们前面在第 6 行放置了一个断点,因此程序运行停止。继续输入“c”命令,程序就会持续不断的运行,开发板中就会不断的打印出信息,如图 B3.3.2.7 所示:

4s 命令

s 命令(step)是单步运行执行,此函数会进入到函数里面。

5n 命令

n 命令(next)也是单步运行,但是 n 命令不会进入到函数里面。

6p 命令

p 命令(print)用于打印某个变量值。

7q 命令

q 命令(quit)用于退出调试,开发板上的 gdbserver 也会停止。

关于 gdb 调试更多详细的使用方法和命令大家自行上网查阅,这里就不一一介绍了。

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐