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_unsafe
cargo 特性已被 禁用- 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 或 http://opensource.org/licenses/MIT)
- Apache 许可证 2.0 版 (LICENSE-APACHE 或 http://www.apache.org/licenses/LICENSE-2.0)
您可以根据自己的选择。这意味着您可以选择您喜欢的许可证!这种双重许可的方法是 Rust 生态系统中的事实标准,并且存在非常好的理由来包含两者。
除非您明确表示,否则,根据 Apache-2.0 许可证的定义,您有意提交的任何贡献,均应如上所述进行双重许可,不附加任何额外的条款或条件。
依赖项
~290–420KB