23个版本
| 新版本 0.7.2 | 2024年8月17日 |
|---|---|
| 0.7.1 | 2024年1月6日 |
| 0.7.0 | 2023年11月24日 |
| 0.6.6 | 2023年4月13日 |
| 0.1.1 | 2020年3月29日 |
#52 in 嵌入式开发
46,746 每月下载量
在 17 个crate中使用了(直接使用13个)
430KB
6.5K SLoC
gdbstub
一个舒适、功能丰富且易于集成的Rust中的GDB远程串行协议实现,提供无妥协的#![no_std]支持。
gdbstub可以轻松地将强大的客户机调试支持集成到您的仿真器/虚拟机管理程序/调试器/嵌入式项目中。通过仅实现gdbstub::Target特质的一小部分基本方法,您可以在极短的时间内启动并运行丰富的GDB调试会话!
gdbstub的API广泛使用了称为可内联的动态扩展特质 (IDETs) 的技术,以暴露对启用GDB协议功能的精细粒度、零成本控制,而无需依赖于编译时功能标志。除了使切换启用协议功能变得轻而易举外,IDETs还确保任何未实现的功能都将被保证在发布构建中删除!
如果您想查看一个示例代码片段,以了解功能丰富的gdbstub集成可能是什么样子,请查看examples/armv4t/gdb/mod.rs
为什么使用gdbstub?
- 出色的易用性
- 不是简单地暴露底层的GDB协议“瑕瑜互见”,
gdbstub试图尽可能多地抽象原始GDB协议细节,从用户那里分离出来。- 不再需要深入GDB代码库深处的晦涩XML文件,只需从CPU/架构寄存器读取/写入,
gdbstub就自带了一组社区维护的、针对大多数流行平台的内置架构定义! - 将GDB无数的可选协议扩展组织成一致、可理解和类型安全的特质层次结构。
- 自动处理客户端/服务器协议功能协商,无需微观管理特定的
qSupported数据包响应。
- 不再需要深入GDB代码库深处的晦涩XML文件,只需从CPU/架构寄存器读取/写入,
gdbstub充分利用Rust强大的类型系统和泛型,在编译时强制执行协议不变性,最小化最终用户需要担心复杂协议细节的数量。- 使用一种名为可内联动态扩展特质 (IDETs) 的创新技术,
gdbstub允许对活动协议扩展进行细粒度控制,而不依赖于笨拙的cargo功能或使用unsafe代码!
- 不是简单地暴露底层的GDB协议“瑕瑜互见”,
- 易于集成
gdbstub的API设计为“即插即用”解决方案,当您想在项目中添加调试支持时,并无需进行任何大规模重构工作即可集成到现有项目中。
#![no_std]准备就绪 & 尺寸优化gdbstub是一个**首选**无标准库的库,其中所有协议功能都必须与no_std兼容。gdbstub不要求任何动态内存分配,并可以配置为使用固定大小的预分配缓冲区。这使得gdbstub可以在甚至资源最受限的无alloc平台上使用。gdbstub在大多数最小配置中完全**无panic**,结果生成更小、更健壮的代码。- *有关更多详细信息,请参阅下面的编写无panic代码部分。
gdbstub与传输层无关,并使用一个基本的Connection接口与GDB服务器通信。只要目标有一些执行有序、串行、字节级I/O的方法(例如:通过UART的putchar/getchar),就可以在目标上运行gdbstub!- "你不为不用之物付费":与解析/处理协议扩展相关的所有代码,如果未实现,则保证从优化后的二进制文件中删除死代码。有关更多详细信息,请参阅下面的零开销协议扩展部分。
gdbstub的最小配置具有极低的二进制大小和RAM开销,使其可以在资源最受限的微控制器上使用。- 在发布模式下编译,使用
min-sized-rust中概述的所有技巧,一个基线gdbstub实现可以小到**小于10kb的.text+.rodata**! * - 确切数字因目标平台、编译器版本和
gdbstub修订而异。在混合语言项目中,可能需要跨语言LTO(#101)。数据使用包含的example_no_std项目编译在 x86_64 上收集。
- 在发布模式下编译,使用
我能否在生产环境中使用 gdbstub?
是的,只要你不介意在 1.0.0 发布之前API有所变动。
由于 gdbstub 在编译时通过Rust的类型系统强制执行GDB协议不变性,因此实现新的GDB协议功能通常需要做出一些破坏性的API更改。虽然这些更改通常很小,但它们确实是semver-breaking的,并且在版本之间移动时可能需要代码更改。任何特别复杂的变化通常都会在专门的 迁移指南 文档中记录。
尽管如此,gdbstub 自从最初的 0.1 发布以来,已经集成到许多实际项目中,实证研究表明,它似乎正在很好地完成其工作!迄今为止,大多数报告的问题都是由不正确实现的 Target 和/或 Arch 实现引起的,而核心 gdbstub 库本身已被证明相对无错误。
有关 gdbstub 在版本 1.0.0 稳定API之前还需要实现哪些功能的更多信息,请参阅 未来计划 + 路线图。
调试功能
尽管GDB远程串行协议非常复杂,支持诸如远程文件I/O、启动新进程、"回滚"程序执行等高级功能,但幸运的是,大多数这些功能都是完全可选的。仅通过实现一些基本方法,就可以开始一个基本的调试会话。
- 基本GDB协议
- 读取/写入内存
- 读取/写入寄存器
- 枚举线程
没错!这就足够了,可以让 gdb 连接上了!
当然,大多数用例还希望支持额外的调试功能。目前,gdbstub 实现了以下GDB协议扩展:
- 自动目标架构 + 功能配置
- 恢复
- 继续
- 单步执行
- 范围步进
- 反向 步进/继续
- 断点
- 软件断点
- 硬件断点
- 读取/写入/访问观察点(即:值断点)
- 扩展模式
- 启动新进程
- 附加到现有进程
- 终止现有进程
- 向启动的进程传递环境变量 + 参数
- 更改工作目录
- 启用/禁用ASLR
- 读取内存映射(
info mem) - 读取段/段重定位偏移量
- 处理自定义
monitor命令- 使用GDB的
monitor命令扩展GDB协议以自定义调试命令!
- 使用GDB的
- 主机I/O
- 访问远程目标文件的系统以读取/写入文件
- 可用于在附加时自动读取远程可执行文件(使用
ExecFile)
- 读取辅助向量(
info auxv) - 额外线程信息(
info threads) - 额外库信息(
info sharedlibraries)
注意: GDB 功能由 gdbstub 的贡献者根据需要实现。如果您希望 gdbstub 实现缺失的 GDB 功能,请提交问题并/或打开一个 PR!
有关 GDB 远程功能的完整列表,请查看 GDB 远程配置文档,其中包含 GDB 命令及其对应的远程串行协议数据包的表格。
零开销协议扩展
通过使用称为 可内联动态扩展特性(IDETs) 的技术,gdbstub 能够利用 Rust 编译器的强大优化过程,确保在发布版本中任何未使用的功能都会被死代码消除,而无需依赖于编译时功能标志!
例如,如果您的目标没有实现自定义 GDB monitor 命令处理程序,生成的二进制文件将不包括与解析/处理底层 qRcmd 数据包相关的任何代码!
如果您对 IDETs 的工作原理感兴趣,我在文档中包含了一个简要的说明 这里。
功能标志
默认情况下,启用了 std 和 alloc 功能。
在 #![no_std] 环境中使用 gdbstub 时,请确保设置 default-features = false。
alloc- 为
Box<dyn Connection>实现Connection。 - 通过
log::trace!记录外出数据包(使用堆分配的输出缓冲区)。 - 为某些协议功能提供内置实现
- 在
GdbStub中使用堆分配的数据包缓冲区(如果没有通过GdbStubBuilder::with_packet_buffer提供)。 - (监视命令)在
ConsoleOutput中使用堆分配的输出缓冲区。
- 在
- 为
std(隐含alloc)- 为
TcpStream和UnixStream实现Connection。 - 为
std::error::Error实现gdbstub::Error。 - 为简化从 Target 方法中处理
std::io::Error,添加TargetError::Io变体。
- 为
paranoid_unsafe- 有关更多详细信息,请参阅下面的
unsafe部分。
- 有关更多详细信息,请参阅下面的
示例
现实世界示例
虽然这些项目可能使用 gdbstub 的旧版本,但它们仍然可以作为典型的 gdbstub 集成示例。
如果您在项目中使用了 gdbstub,请考虑提交一个PR并将其添加到这个列表中!
- 虚拟机监控器 (VMMs)
- crosvm - Chrome OS VMM
- cloud-hypervisor - 用于现代云工作的VMM
- uhyve - RustyHermit的轻量级虚拟机(RustyHermit)
- 操作系统内核(在
no_std上使用gdbstub)betrusted-io/xous-core- Xous微内核操作系统vmware-labs/node-replicated-kernel- x86-64(amd64)机器的一个(实验性)研究操作系统内核
- 仿真器
- solana_rbpf - eBPF程序的虚拟机和JIT编译器
- rustyboyadvance-ng - 任天堂Gameboy Advance仿真器和调试器(ARMv4T)
- gamegirl - Gameboy(Color/Advance)仿真器
- bevy-atari - Atari XL/XE仿真器(MOS 6502)
- rmips - MIPS R3000虚拟机模拟器
- clicky - 经典clickwheel iPod仿真器(双核ARMv4T)
- ts7200 - TS-7200 SoC仿真器(ARMv4T)
- vaporstation - PlayStation One仿真器(MIPS)
- microcorruption-emu - microcorruption.com ctf的仿真器(MSP430)
- 其他
- probe-rs - 一个现代化的嵌入式调试工具包
- udbserver - 为Unicorn Engine提供的插件GDB调试(多架构)
- enarx - 在可信执行环境中运行应用程序的开源框架
- icicle-emu - 一个实验性的、多架构的模糊测试专用仿真框架
树内“玩具”示例
这些示例是作为CI的一部分构建的,并保证与gdbstub的最新版本API保持更新。
armv4t-./examples/armv4t/- 一个基于ARMv4T的简单系统仿真器,具有
gdbstub支持。 - 实现了(几乎)所有可用的
target::ext功能。这使得它在首次实现新的协议扩展时成为一个极好的资源!
- 一个基于ARMv4T的简单系统仿真器,具有
armv4t_multicore-./examples/armv4t_multicore/- 是
armv4t示例的双核变体。 - 实现了
gdbstub多线程扩展API的核心,但除此之外没有太多。
- 是
example_no_std-./example_no_std- 这是一个极简示例,展示了如何在
#![no_std]项目中使用gdbstub。 - 与
armv4t/armv4t_multicore示例不同,本项目不包括可工作的模拟器,仅简单地模拟所有gdbstub函数。 - 充当
gdbstub的测试平台,以跟踪其大约的二进制占用空间(通过check_size.sh脚本),以及验证某些死代码消除优化。
- 这是一个极简示例,展示了如何在
unsafe 在 gdbstub 中的使用
gdbstub 将对 unsafe 的使用限制在最低限度,所有使用 unsafe 的地方都需要有相应的 // SAFETY 注释作为理由。
对于那些对信任第三方 unsafe 代码感到担忧的人,gdbstub 提供了一个可选的 paranoid_unsafe 功能,该功能在 gdbstub crate 的整个范围内启用 #![forbid(unsafe_code)],将所有 unsafe 代码实例替换为等效的(尽管性能较低)替代方案。
以下列表详尽地记录了 gdbstub 中所有 unsafe 的使用情况
-
使用
default功能- 不发出可证明无法到达的恐慌
src/protocol/packet.rs:在PacketBuf中使用存储的子Range<usize>到缓冲区的索引的方法src/protocol/common/hex.rs:decode_hex_buf
- 不发出可证明无法到达的恐慌
-
当启用
std功能时src/connection/impls/unixstream.rs:一个实现UnixStream::peek的方法,它使用libc::recv。一旦 rust-lang/rust#76923 在 stdlib 中稳定了这个功能,它将被删除。
编写无恐慌代码
理想情况下,Rust 编译器应该有一些方法可以启用严格的“无恐慌”模式。不幸的是,在撰写本文时(2022/04/24),还没有这样的模式。因此,避免 Rust 编译器和 stdlib 的隐式恐慌的唯一方法是编写代码时要非常小心,并 手动检查 那些恐慌路径是否被优化掉了!
当我说“手动检查”时,我的意思是 检查生成的汇编输出。
为什么要付出这么大的努力?
- 恐慌基础设施可能非常 昂贵,当你在针对嵌入式系统优化时,panic 基础设施会将数百个额外的字节带到最终的二进制文件中。
gdbstub可以用于实现低级调试器,如果调试器本身出现恐慌,那么...你不太可能轻易地调试它!
因此,gdbstub 承诺在以下条件下将零额外的恐慌引入现有项目
- 二进制文件以发布模式编译
- *受使用的特定
rustc版本的影响(代码生成和优化在不同版本之间有所不同) - *不同的硬件架构可能受到不同的编译器优化的影响
- 例如:目前只有
x86被积极测试以确保无panic
- 例如:目前只有
- *受使用的特定
gdbstub的paranoid_unsafecargo 特性已被 禁用- LLVM 无法在不包含一些
unsafe代码的情况下省略某些panic检查 - 有关
gdbstub中的 unsafe 的更多信息,请参阅unsafe部分
- LLVM 无法在不包含一些
- 正在使用的
Arch实现不包括 panic 代码- 注意: 在
gdbstub_arch下的架构实现并不保证一定是无panic的! - 如果您在
gdbstub_arch中发现了一个 panic 架构,请考虑提交一个 PR 来修复它
- 注意: 在
如果您在无 panic 项目的环境中使用 gdbstub,并确定 gdbstub 是引入 panic 代码路径的罪魁祸首,请提交一个问题!
未来计划 + 路线图至 1.0.0
虽然 GDB 协议的绝大多数功能(例如:远程文件系统支持、tracepoint 数据包、大多数查询数据包等)不应需要破坏性 API 变更,但以下功能很可能需要至少一些破坏性 API 变更,因此应该在 1.0.0 之前实现。
请注意,这并不是一个详尽的列表,并且可能会发生变化。
- 通过
Arch特性允许对目标特性进行细粒度控制(#12) - 实现 GDB 的各种高级操作模式
- 有一个
gdbstub在“裸机”环境中运行的示例(#![no_std])
此外,虽然这些特性不是 严格 的阻止因素,但探索这些特性也是好的
- 是否应该
gdbstub承诺一个 MSRV? - 从
gdbstub_arch中移除RawRegId的遗留实例(#29) - 暴露
async/await接口(特别是关于处理 GDB 客户端中断)(#36) - 如何/是否支持 LLDB 扩展(#99)
- 通过单个目标支持多架构调试
- 例如:在 macOS 上调试 x86 和 ARM 进程
- 正确处理“nack”数据包(用于不稳定的连接)(#137)
许可证
gdbstub 是免费的、开源的!本仓库中所有代码都采用了双重许可,可选择以下其中之一:
- MIT 许可证 (LICENSE-MIT 或 https://open-source.org.cn/licenses/MIT)
- Apache 许可证 2.0 版 (LICENSE-APACHE 或 https://apache.ac.cn/licenses/LICENSE-2.0)
您可以根据自己的选择。这意味着您可以选择您喜欢的许可证!这种双重许可的方法是 Rust 生态系统中的事实标准,并且存在非常好的理由来包含两者。
除非您明确表示,否则,根据 Apache-2.0 许可证的定义,您有意提交的任何贡献,均应如上所述进行双重许可,不附加任何额外的条款或条件。
依赖项
~290–420KB