1. 使用GDB进行断点调试

任务目标

在嵌入式Linux目标板上,使用GDB对一段C程序进行断点调试,掌握设置断点、单步执行、查看变量值三项核心操作。本案例以ARM Cortex-A系列开发板(或QEMU模拟的ARM环境)为运行平台,通过远程GDB调试方式完成一次完整的调试闭环。

环境与依赖

  • 目标板:ARM Linux开发板(或QEMU + arm-linux-gnueabihf环境)
  • 交叉编译器:arm-linux-gnueabihf-gcc
  • 调试器:gdb-multiarch(主机侧),gdbserver(目标板侧)
  • 网络:目标板与主机可互相访问(本例使用TCP :2345)

被测程序(target.c)

#include <stdio.h>

int calculate(int a, int b) {
    int sum = a + b;
    int product = a * b;
    return product - sum;
}

int main() {
    int x = 7;
    int y = 3;
    int result = 0;

    for (int i = 0; i < 3; i++) {
        result += calculate(x + i, y);
    }

    printf("Final result: %d\n", result);
    return 0;
}

编译与部署

# 目标板侧:交叉编译(含调试信息)
arm-linux-gnueabihf-gcc -g -O0 -o target target.c

# 将target复制到目标板,目标板上启动gdbserver
# 假设目标板IP为192.168.1.100
gdbserver 0.0.0.0:2345 ./target

# 主机侧启动gdb-multiarch并连接
gdb-multiarch ./target
(gdb) target remote 192.168.1.100:2345

GDB调试操作

# 在calculate函数入口设置断点
(gdb) break calculate
Breakpoint 1 at 0x1040c: file target.c, line 4.

# 在main函数第12行(for循环内)设置第二个断点
(gdb) break target.c:12
Breakpoint 2 at 0x10454: file target.c, line 12.

# 查看当前断点列表
(gdb) info breakpoints

# 继续执行,应停在第一个断点(calculate入口)
(gdb) continue
Breakpoint 1, calculate (a=7, b=3) at target.c:4
4	    int sum = a + b;

# 单步执行(进入函数内部逐行)
(gdb) step
5	    int product = a * b;

# 查看当前变量值
(gdb) print a
$1 = 7
(gdb) print b
$2 = 3
(gdb) print sum
$3 = 10

# 继续单步
(gdb) step
6	    return product - sum;

# 查看product值
(gdb) print product
$4 = 21

# 执行当前行并停在return处
(gdb) step
7	}

# 查看返回值表达式
(gdb) print product - sum
$5 = 11

# 继续运行,应停在第二个断点(for循环内)
(gdb) continue
Breakpoint 2, main () at target.c:12
12	        result += calculate(x + i, y);

# 查看循环变量
(gdb) print i
$6 = 0
(gdb) print x
$7 = 7
(gdb) print y
$8 = 3

# 单步跳过函数调用(不进入calculate)
(gdb) next
13	    }

# 查看result累加结果
(gdb) print result
$9 = 11

# 继续执行至程序结束
(gdb) continue
[Inferior 1 (process 1234) exited normally]

验证方法与通过标准

  • 程序最终输出 Final result: 33(由代码逻辑可唯一推出:i=0时calculate(7,3)=11,i=1时calculate(8,3)=13,i=2时calculate(9,3)=15,累加得33)
  • 调试过程中,断点均能准确命中,print显示的变量值与代码逻辑一致
  • step进入函数内部逐行执行,next跳过函数调用,两者行为差异可明确观察
  • 退出GDB后目标板程序正常结束,无段错误或异常退出

常见问题

  • 连接失败:确认目标板gdbserver已启动且端口未被占用,主机与目标板网络互通(可ping测试)
  • 断点无法命中:检查编译时是否带-g选项,且未使用-O2以上优化(优化会改变代码行与指令映射)
  • print显示"optimized out":使用-O0重新编译
GDB远程调试流程与断点命中状态 主机 (Host) 目标板 (Target) gdb-multiarch ./target 加载符号表与调试信息 break calculate 设置断点 #1 (函数入口) break target.c:12 设置断点 #2 (for循环内) continue / step / next 发送调试命令并等待目标响应 gdbserver 0.0.0.0:2345 ./target 等待主机连接,加载程序 程序停在 calculate() 入口 断点#1命中,等待单步指令 程序停在 target.c:12 断点#2命中,循环变量可查 执行状态实时同步 寄存器/内存/变量值回传主机 TCP :2345 调试数据 断点命中时程序暂停 → 主机发送调试命令 → 目标板执行并返回状态 → 主机更新显示

关键点说明

  • step会进入被调函数内部逐行执行;next将函数调用视为单条语句直接跳过,两者是嵌入式调试中最常用的单步方式
  • 断点命中后程序完全暂停,此时可安全查看全局变量、局部变量、寄存器值,不会影响目标板外设状态
  • 嵌入式远程调试中,print操作通过RSP协议从目标板读取实时数据,与本地调试行为一致