当前位置: 首页 > news >正文

在Ubuntu上使用QEMU学习RISC-V程序(2)gdb调试

文章目录

      • 一、准备工作
      • 二、基本调试流程
        • 1. 设置断点
        • 2. 执行程序
        • 3. 查看源代码/汇编
      • 三、查看寄存器
        • 1. 查看通用寄存器
        • 2. 查看特殊寄存器
      • 四、查看内存
        • 1. 内存查看命令
        • 2. 内存修改命令
      • 五、调试实战示例
      • 六、高级调试技巧
        • 1. 条件断点
        • 2. 自动显示
        • 3. 内存断点(观察点)
        • 4. 回溯调用栈
      • 七、退出调试
    • 八、退出QEMU
      • 总结

在嵌入式开发中,GDB是调试RISC-V程序的核心工具。以下是使用 gdb-multiarch调试 add.elf程序的详细步骤,包括断点设置、寄存器查看、内存分析等关键操作:

一、准备工作

  1. 给elf文件增加调试信息
# 编译汇编代码(添加-g选项)
riscv64-unknown-elf-as -g -march=rv64g -mabi=lp64 add.s -o add.o# 链接(保持-g选项)
riscv64-unknown-elf-ld -g -T link.ld add.o -o add.elf# 检查文件是否包含调试信息
riscv64-unknown-elf-readelf -S add.elf | grep debug# 输出示例(应包含多个debug_*节)
.debug_info     PROGBITS        0000000000000000  000002e0
.debug_abbrev   PROGBITS        0000000000000000  000006e8
.debug_line     PROGBITS        0000000000000000  000007e0
  1. 启动QEMU并暂停执行
qemu-system-riscv64 \-machine virt \-cpu rv64 \-m 128M \-nographic \-bios none \-kernel add.elf \-s -S  # -s开启GDB服务器,-S暂停执行直到GDB连接
  1. 启动GDB并连接到QEMU
    如果没有安装gdb-multiarch,先用sudo apt install gdb-multiarch进行安装。
gdb-multiarch add.elf  # 加载带有调试信息的ELF文件# 在GDB交互式界面中执行以下命令
(gdb) set architecture riscv:rv64  # 指定目标架构
(gdb) target remote :1234         # 连接到QEMU的GDB服务器

二、基本调试流程

1. 设置断点
(gdb) break _start        # 在_start标签处设置断点
(gdb) break add.s:20      # 在add.s文件第20行设置断点
(gdb) break print_string  # 在print_string函数入口设置断点
(gdb) info breakpoints    # 查看已设置的断点
2. 执行程序
(gdb) continue            # 继续执行到下一个断点
(gdb) next                # 单步执行(不进入函数)
(gdb) step                # 单步执行(进入函数)
(gdb) finish              # 执行完当前函数并返回
(gdb) until 25            # 执行到当前文件第25行
3. 查看源代码/汇编
(gdb) list                # 显示当前位置附近的源代码
(gdb) list 10,20          # 显示第10-20行代码
(gdb) disassemble         # 反汇编当前函数
(gdb) x/i $pc             # 显示当前执行的指令
(gdb) layout asm          # 切换到汇编视图
(gdb) layout regs         # 显示寄存器窗口

三、查看寄存器

1. 查看通用寄存器
(gdb) info registers      # 显示所有寄存器
(gdb) print $a0           # 查看a0寄存器的值
(gdb) p/x $sp             # 以十六进制形式查看sp寄存器
(gdb) p/a $ra             # 以地址形式查看ra寄存器
(gdb) display $t0         # 每次停止时自动显示t0寄存器
2. 查看特殊寄存器
(gdb) p $mstatus          # 查看机器状态寄存器
(gdb) p $mcause           # 查看异常原因寄存器
(gdb) p $mtvec            # 查看异常向量表基址
(gdb) set $pc = 0x80000010  # 修改程序计数器

四、查看内存

1. 内存查看命令
(gdb) x/10xw 0x80000000   # 从0x80000000开始,以16进制显示10个32位字
(gdb) x/20b $sp            # 从sp开始,以字节形式显示20个值
(gdb) x/10i 0x80000000    # 从0x80000000开始,显示10条指令
(gdb) examine/i $pc        # 查看当前执行的指令
2. 内存修改命令
(gdb) set {int}0x80000010 = 42  # 将0x80000010处的32位整数设置为42
(gdb) set *0x80000014 = 0x1234  # 将0x80000014处的16位值设置为0x1234

五、调试实战示例

假设我们要调试add.s程序中的加法操作:

  1. 设置断点并开始执行
(gdb) break _start
(gdb) continue
  1. 单步执行到加法操作
(gdb) step  # 执行初始化栈指针
(gdb) step  # 加载a0 = 10
(gdb) step  # 加载a1 = 20
(gdb) step  # 执行add a2, a0, a1
  1. 验证加法结果
(gdb) p/x $a0    # 应该显示0xa (10)
(gdb) p/x $a1    # 应该显示0x14 (20)
(gdb) p/x $a2    # 应该显示0x1e (30)
  1. 查看内存中的变量
(gdb) x/i $pc    # 显示当前指令
(gdb) x/10xw 0x80000000  # 查看.text段内容
(gdb) x/10xw $sp         # 查看栈内容
  1. 调试UART输出
(gdb) break print_string
(gdb) continue
(gdb) step  # 进入print_string函数
(gdb) p $a0  # 查看要打印的字符串地址
(gdb) x/s $a0  # 以字符串形式显示内存内容

六、高级调试技巧

1. 条件断点
(gdb) break add.s:25 if $a0 > 10  # 当a0>10时触发断点
2. 自动显示
(gdb) display/i $pc        # 每次停止时显示当前指令
(gdb) display/x $a0        # 每次停止时显示a0寄存器
3. 内存断点(观察点)
(gdb) watch *0x80000020    # 当地址0x80000020被修改时触发
4. 回溯调用栈
(gdb) backtrace           # 显示调用栈
(gdb) frame 1             # 切换到第1层栈帧

七、退出调试

(gdb) disconnect          # 断开与QEMU的连接
(gdb) quit                # 退出GDB

八、退出QEMU

退出qemu-system-riscv64通常可以使用快捷键或通过监视器界面来操作,具体方法如下:

  • 使用快捷键:按下Ctrl + a,然后松开这两个键,再按下x,即可直接终止QEMU进程,回到shell界面。
  • 通过监视器界面:首先按下Ctrl + a,然后松开,再按下c,这将退出当前操作系统的shell界面,进入QEMU的监视器界面。接着在监视器界面中,输入q并按回车键,即可完全退出QEMU。

总结

通过上述命令,你可以全面掌控程序的执行流程,监控寄存器和内存的变化,从而快速定位问题。GDB的强大功能不仅适用于调试简单程序,也是开发复杂嵌入式系统的必备工具。建议结合具体程序多练习,熟练掌握这些命令将显著提高开发效率。

http://www.lryc.cn/news/597955.html

相关文章:

  • 【OpenCV篇】OpenCV——03day.图像预处理(2)
  • 征服 Linux 网络:核心服务与实战解析
  • 《从点击到共鸣:论坛前端如何用交互细节编织用户体验》
  • GISBox实操指南:如何将IFC文件高效转换为3DTiles格式‌‌
  • JVM 核心内容
  • Java并发编程第六篇(AQS设计理念与源码解析)
  • Linux724 逻辑卷挂载;挂载点扩容;逻辑卷开机自启
  • 快速启用 JMeter(macOS Automator 创建 JMeter 脚本)
  • VUE2 学习笔记5 动态绑定class、条件渲染、列表过滤与排序
  • 【AJAX】XMLHttpRequest、Promise 与 axios的关系
  • 最新免费使用Claude Code指南(Windows macOS/Linux)
  • web前端调试
  • 前端如何做安全策略
  • easyexcel流式导出
  • Windows计算器项目全流程案例:从需求到架构到实现
  • 4.5 优化器中常见的梯度下降算法
  • 绿色转向的时代红利:创新新材如何以技术与标准主导全球铝业低碳重构
  • 从手动操作到自动化:火语言 RPA 在多系统协作中的实践
  • 飞腾D3000麒麟信安系统下配置intel I210 MAC
  • 基础入门 [CMD] Windows SSH 连接服务器教程(系统自带方式)
  • Linux和Windows基于V4L2和TCP的QT监控
  • 【MAC电脑系统变量管理】
  • 进程调度的艺术:从概念本质到 Linux 内核实现
  • n8n AI资讯聚合与分发自动化教程:从数据获取到微信与Notion集成
  • RabbitMQ--消息顺序性
  • 【华为】笔试真题训练_20250611
  • go下载包
  • 数据库常用DDL语言
  • 文件管理困境如何破?ZFile+cpolar打造随身云盘新体验
  • M²IV:面向大型视觉-语言模型中高效且细粒度的多模态上下文学习