嵌入式gdb移植与调试

日期:2024/02/20

嵌入式程序的调试可以使用两种方案:

  1. 本地GDB:

    本地GDB是指在板端执行gdb程序,在板端查看程序信息并调试

  2. 远程GDB:

    远程GDB即gdb-server,这种方式可以在板端启动gdb服务,在pc端启动gdb连接到板端,可实现程序在板端运行,调试在PC端进行

GDB移植

依赖库:ncurses-6.4.tar.gz

gdb源码:gdb-14.1.tar.xz

ncurses编译

1
2
3
4
5
6
7
tar -xvf ncurses-6.4.tar.gz
cd ncurses-6.4
./configure --host=aarch64-openeuler-linux --prefix=`pwd`/../install/ncurses --without-ada --enable-termcap --with-shared
# 指定strip,否则执行make install时会出错,也可指定参数--disable-stripping
find . -name 'Makefile' -exec sed -i 's;INSTALL.*= /usr/bin/install -c;INSTALL = /usr/bin/install --strip-program=aarch64-openeuler-linux-strip -c;' {} +
make -j 20
make install

make install 时报错

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
** Building terminfo database, please wait...
Running tic to install /home/compile_04/test/gdb/ncurses-6.4/../install/ncurses/share/terminfo ...

You may see messages regarding extended capabilities, e.g., AX.
These are extended terminal capabilities which are compiled
using
tic -x
If you have ncurses 4.2 applications, you should read the INSTALL
document, and install the terminfo without the -x option.

ncurses 6.1.20180127
"terminfo.tmp", line 1232, col 36, terminal 'fbterm': limiting value of `pairs' from 0x10000 to 0x7fff
"terminfo.tmp", line 5326, col 36, terminal 'xterm+256color': limiting value of `pairs' from 0x10000 to 0x7fff
"terminfo.tmp", line 5358, col 36, terminal 'xterm+256setaf': limiting value of `pairs' from 0x10000 to 0x7fff
"terminfo.tmp", line 5405, col 25, terminal 'xterm+direct2': limiting value of `colors' from 0x1000000 to 0x7fff
"terminfo.tmp", line 5405, col 40, terminal 'xterm+direct2': limiting value of `pairs' from 0x10000 to 0x7fff
"terminfo.tmp", line 5420, col 25, terminal 'xterm+direct': limiting value of `colors' from 0x1000000 to 0x7fff
"terminfo.tmp", line 5420, col 40, terminal 'xterm+direct': limiting value of `pairs' from 0x10000 to 0x7fff
"terminfo.tmp", line 5442, col 25, terminal 'xterm+indirect': limiting value of `colors' from 0x1000000 to 0x7fff
"terminfo.tmp", line 5442, col 40, terminal 'xterm+indirect': limiting value of `pairs' from 0x10000 to 0x7fff
"terminfo.tmp", line 8597, col 36, terminal 'dvtm-256color': limiting value of `pairs' from 0x10000 to 0x7fff
"terminfo.tmp", line 4408, terminal 'mintty': error writing /home/compile_04/test/gdb/install/ncurses/share/terminfo/m/mintty
? tic could not build /home/compile_04/test/gdb/ncurses-6.4/../install/ncurses/share/terminfo
Makefile:109: recipe for target 'install.data' failed
make[1]: *** [install.data] Error 1
make[1]: Leaving directory '/home/compile_04/test/gdb/ncurses-6.4/misc'
Makefile:133: recipe for target 'install' failed
make: *** [install] Error 2

可在misc\terminfo.tmp文件中注释掉相关的部分,比如上述报错为mintty部分,在文件中注释掉即可,terminfo.tmp文件由terminfo.src文件生成,每次执行configure命令时重新生成。本次安装遇到以下几个终端的错误。

1
2
3
4
5
mintty
xterm-16color
xterm-direct16
xterm-direct256
hterm-256color

GDB编译

1
2
3
4
5
tar -xvf gdb-14.1.tar.xz
cd gdb-14.1
./configure --host=aarch64-openeuler-linux CC=aarch64-openeuler-linux-gcc --enable-shared --prefix=`pwd`/../install/gdb --disable-werror --without-x --disable-gdbtk --disable-tui --without-included-regex --without-included-gettext LDFLAGS="-L`pwd`/../install/ncurses/lib" CPPFLAGS="-I`pwd`/../install/ncurses/include"
make CFLAGS=-static
make install

vscode + gdb 调试代码

经过上述的移植之后已经可以在板端执行gdb命令对程序进行调试了,但是这样调试并不方便。所以此处介绍vscode+gdb进行调试的方式。

vscode + gdb调试板端代码也是有两种方式,即本地调试和gdbserver。

先放上测试用的代码,后续仅介绍两种调试方式的差异部分

  • main.c

    根据命令行参数创建一个文件,通过这个例程可以验证命令行传参、文件创建、程序工作目录、输出显示等功能是否正常。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #include <stdio.h>

    #define DEBUG_INFO(format,...) \
    do { \
    printf("[INFO]: "format,##__VA_ARGS__); \
    fflush(stdout); \
    }while(0)

    int main(int argc,char*argv[])
    {
    FILE* fp = fopen(argv[1],"w");
    fprintf(fp,"hello World\n");
    fclose(fp);
    DEBUG_INFO("hello World\n");
    return 0;
    }
  • Makefile

    使用交叉编译工具链编译main.c为hello,编译完成时通过scp拷贝文件到板端。注意添加-g编译参数,可以将源码编译到可执行文件中,否则调试时代码与源码无法对齐

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    CROSS_COMNPILE=aarch64-openeuler-linux-
    CC=$(CROSS_COMNPILE)gcc

    CFLAGS += -O0 -g
    LDFLAGS += -O0 -g

    all:hello

    hello:main.o
    $(CC) $(LDFLAGS) $< -o $@
    scp hello root@192.168.5.223:~/

    %.o:%.c
    $(CC) $(CFLAGS) -c $< -o $@

    clean:
    rm *.o
    rm hello
  • .vscode/tasks.json

    该文件是vscode的任务文件,一般为编译任务,在调试之前一般会重新编译

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    {
    "tasks": [
    {
    "type": "cppbuild",
    "label": "compile", // 任务名,在任务列表中具有唯一性,可在其它文件中引用
    "command": "make", // 任务要执行的命令
    "args": [], // 执行任务时命令要携带的参数
    "options": {
    "cwd": "${fileDirname}" // 执行任务的工作目录
    },
    "problemMatcher": [
    "$gcc"
    ],
    "group": {
    "kind": "build",
    "isDefault": true
    },
    "detail": "调试器生成的任务。"
    }
    ],
    "version": "2.0.0"
    }
  • .vscode/launch.json

    该文件时vscode调试的配置文件,后面将介绍在不同的模式下如何配置该文件。此处仅展示空文件的格式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
    // 使用 IntelliSense 了解相关属性。
    // 悬停以查看现有属性的描述。
    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [

    ]
    }

本地调试

使用本地调试时,只需要板端的gdb命令能正常执行即可。原理是通过ssh、telnet、tty等命令行终端连接到板端,并在板端执行gdb程序,vscode通过终端发送命令给gdb程序,并实时捕获gdb输出。

本文以ssh连接为例(使用ssh连接时需要通过密钥验证,因为没有可以配置密码的地方,在调试时也没有密码输入框,若验证失败则会报错)

在本地调试的模式下,配置应该选择gdb 管道启动,下面是对应的launch.json文件

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
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "gdb 管道启动", // 配置的名称,可在vscode的调试窗口看到
"type": "cppdbg",
"request": "launch",
"program": "/root/hello", // 需要调试的程序(板端路径)
"args": ["testfile"], // 待调试的程序启动时传入的命令行参数
"stopAtEntry": true, // 一定要设置为true,在程序入口处中断,若不设置为true,则后续断点不生效,直接执行到结束
"cwd": "/root/test", // 程序执行的工作目录(板端路径)
"environment": [],
"externalConsole": false, // 启动外部中断显示调试输出,亲测无效
"pipeTransport": {
"debuggerPath": "gdb ", // 调试程序(板端路径)
"pipeProgram": "ssh", // 终端连接程序(PC端路径)
"pipeArgs": ["root@192.168.5.223"], // 终端参数
"pipeCwd": ""
},
"MIMode": "gdb",
"preLaunchTask": "compile", // 在调试前执行的任务(在tasks.json文件中定义,对应lable)此处设置在开始调试前自动编译
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "将反汇编风格设置为 Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
}
]
}

通过以上设置即可进行调试。保证网络畅通即可,调试过程基本和在PC端调试相同,可以进行断点设置,单步执行等。

注意:目前scanf无法输入,printf输出时需要刷新stdout才能在终端看到

gdbserver

gdbserver调试的原理是通过gdbserver在板端执行。在PC端执行gdb并连接到远程gdb进行调试。vscode也仅从PC端的gdb获取数据。

需要注意的是在该模式下需要在板端有gdbserver程序,在PC端有对应架构的gdb工具。本文调试的平台是aarch64,对应的交叉编译工具链并未提供gdb工具,因此使用gdb-multiarch命令作为PC端的工具。

ubuntu下gdb-multiarch的安装:sudo apt install gdb-multiarch

在gdbserver的模式下使用gdb 启动即可,下面是对应的launch.json文件

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
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "gdb 启动", // 配置的名称,可在vscode的调试窗口看到
"type": "cppdbg",
"request": "launch",
"program": "${workspaceRoot}/hello", // 待调试的程序(PC端路径)
"args": ["testfile"], // 无效
"stopAtEntry": true, // 一定要设置为true,在程序入口处中断,若不设置为true,则后续断点不生效,直接执行到结束
"cwd": "${workspaceRoot}", // 程序执行的工作目录(PC端路径)
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "gdb-multiarch", // PC端gdb程序
"miDebuggerServerAddress": "192.168.5.223:9091", // 服务器地址与端口号
"miDebuggerArgs": "testfile", // 无效
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "将反汇编风格设置为 Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
}
]
}

在配置号launch.json之后,先在板端执行gdbserver

1
2
3
# 板端执行
# gdbserver <ip>:<port> <program>
gdbserver :9091 hello

板端执行后在vscode中开启调试即可。目前还遇到一些问题:命令行参数无法传入,工作目录无法指定。

两种调试方式的对比

本地调试 gdbserver
连接方式 网络、串口等任何可以连接的终端 网络
程序断点与执行 正常 正常
调试步骤 一键调试 需要在板端先执行gdbserver
参数\工作目录\环境变量 正常 暂未找到解决方案
PC端准备 有源码即可 需要源码、可执行文件、PC端调试工具

综上,暂时推荐使用本地调试的方式

参考文档

  1. arm嵌入式gdb移植和搭建远程gdb调试运行环境
  2. Bluez交叉编译(ncurses make install出错)
  3. Qemu模拟ARM64使用GDB调试linux kernel(gdb-multiarch)
0%