From 4d46537554c908e8e66b98b33b738b756b804dbd Mon Sep 17 00:00:00 2001 From: Ludovic Barman Date: Sat, 16 Dec 2023 11:20:29 +0000 Subject: Upgrade gdbstub to 0.7.0 This project was upgraded with external_updater. Usage: tools/external_updater/updater.sh update rust/crates/gdbstub For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md Test: TreeHugger Change-Id: I297d69c62c0a4e130d9525ec5d8b773f9cfd18ca --- .cargo/config.toml | 1 + .cargo_vcs_info.json | 2 +- .git-blame-ignore-revs | 2 + Android.bp | 4 +- CHANGELOG.md | 39 + Cargo.toml | 4 +- Cargo.toml.orig | 4 +- METADATA | 21 +- README.md | 14 +- cargo_embargo.json | 3 - docs/transition_guide.md | 70 ++ examples/armv4t/emu.rs | 15 +- examples/armv4t/gdb/auxv.rs | 5 +- examples/armv4t/gdb/breakpoints.rs | 3 +- examples/armv4t/gdb/catch_syscalls.rs | 3 +- examples/armv4t/gdb/exec_file.rs | 5 +- examples/armv4t/gdb/extended_mode.rs | 27 +- examples/armv4t/gdb/host_io.rs | 21 +- examples/armv4t/gdb/lldb_register_info_override.rs | 13 +- examples/armv4t/gdb/memory_map.rs | 5 +- examples/armv4t/gdb/mod.rs | 52 +- examples/armv4t/gdb/monitor_cmd.rs | 6 +- examples/armv4t/gdb/section_offsets.rs | 3 +- .../armv4t/gdb/target_description_xml_override.rs | 5 +- examples/armv4t/main.rs | 33 +- examples/armv4t_multicore/emu.rs | 13 +- examples/armv4t_multicore/gdb.rs | 23 +- examples/armv4t_multicore/main.rs | 34 +- rustfmt.toml | 3 +- src/arch.rs | 105 +-- src/common/signal.rs | 790 ++++++++------------- src/conn/impls/boxed.rs | 1 - src/conn/impls/tcpstream.rs | 3 +- src/conn/impls/unixstream.rs | 5 +- src/lib.rs | 31 +- src/protocol/commands.rs | 31 +- src/protocol/commands/_QCatchSyscalls.rs | 1 - src/protocol/commands/_d_upcase.rs | 1 - src/protocol/commands/_h_upcase.rs | 1 - src/protocol/commands/_qAttached.rs | 1 - src/protocol/commands/_qC.rs | 14 + src/protocol/commands/_qThreadExtraInfo.rs | 1 - src/protocol/commands/_qXfer_auxv_read.rs | 4 +- src/protocol/commands/_qXfer_exec_file.rs | 4 +- src/protocol/commands/_qXfer_features_read.rs | 4 +- src/protocol/commands/_qXfer_memory_map.rs | 4 +- src/protocol/commands/_t_upcase.rs | 1 - src/protocol/commands/_vAttach.rs | 1 - src/protocol/commands/_vCont.rs | 8 +- src/protocol/commands/_vFile_open.rs | 4 +- src/protocol/commands/_vFile_pwrite.rs | 1 - src/protocol/commands/_vFile_setfs.rs | 1 - src/protocol/commands/_vKill.rs | 1 - src/protocol/commands/_vRun.rs | 1 - src/protocol/commands/_x_upcase.rs | 1 - src/protocol/commands/breakpoint.rs | 3 +- src/protocol/common/hex.rs | 5 +- src/protocol/common/lists.rs | 3 +- src/protocol/common/thread_id.rs | 6 +- src/protocol/console_output.rs | 3 +- src/protocol/mod.rs | 16 +- src/protocol/recv_packet.rs | 5 +- src/protocol/response_writer.rs | 12 +- src/stub/builder.rs | 14 +- src/stub/core_impl.rs | 47 +- src/stub/core_impl/base.rs | 138 ++-- src/stub/core_impl/breakpoints.rs | 4 +- src/stub/core_impl/catch_syscalls.rs | 3 +- src/stub/core_impl/extended_mode.rs | 59 +- src/stub/core_impl/host_io.rs | 6 +- src/stub/core_impl/lldb_register_info.rs | 14 +- src/stub/core_impl/monitor_cmd.rs | 1 - src/stub/core_impl/no_ack_mode.rs | 25 + src/stub/core_impl/resume.rs | 46 +- src/stub/core_impl/reverse_exec.rs | 9 +- src/stub/core_impl/single_register_access.rs | 4 +- src/stub/core_impl/target_xml.rs | 3 +- src/stub/core_impl/x_upcase_packet.rs | 3 +- src/stub/error.rs | 244 ++++--- src/stub/mod.rs | 73 +- src/stub/state_machine.rs | 36 +- src/target/ext/auxv.rs | 3 +- src/target/ext/base/multithread.rs | 21 +- src/target/ext/base/single_register_access.rs | 3 +- src/target/ext/base/singlethread.rs | 21 +- src/target/ext/breakpoints.rs | 3 +- src/target/ext/catch_syscalls.rs | 3 +- src/target/ext/exec_file.rs | 4 +- src/target/ext/extended_mode.rs | 51 +- src/target/ext/host_io.rs | 37 +- src/target/ext/lldb_register_info_override.rs | 7 +- src/target/ext/memory_map.rs | 3 +- src/target/ext/monitor_cmd.rs | 7 +- src/target/ext/target_description_xml_override.rs | 3 +- src/target/mod.rs | 79 +-- 95 files changed, 1313 insertions(+), 1167 deletions(-) create mode 100644 .git-blame-ignore-revs create mode 100644 src/protocol/commands/_qC.rs create mode 100644 src/stub/core_impl/no_ack_mode.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index 86e7347..f74f939 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -56,4 +56,5 @@ rustflags = [ # type StopReason = MultiThreadStopReason<::Usize>>; # Result, Self::Error> "-Aclippy::type_complexity", + "-Aclippy::manual_range_patterns", ] diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index a57d223..8350bf5 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,6 +1,6 @@ { "git": { - "sha1": "85ff370e018c031bc88438123bf2766c2c806d63" + "sha1": "c489dcdf5992d6e7ebfcf37775ce81dc8cf7861d" }, "path_in_vcs": "" } \ No newline at end of file diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..47e59e3 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# fmt everything using imports_granularity = "Item" +e2329b80c2d51ee0ab4678365a35b27b51f32235 diff --git a/Android.bp b/Android.bp index df5246e..654cb99 100644 --- a/Android.bp +++ b/Android.bp @@ -23,7 +23,7 @@ rust_library { host_supported: true, crate_name: "gdbstub", cargo_env_compat: true, - cargo_pkg_version: "0.6.4", + cargo_pkg_version: "0.7.0", srcs: ["src/lib.rs"], edition: "2018", features: [ @@ -33,7 +33,7 @@ rust_library { "trace-pkt", ], rustlibs: [ - "libbitflags-1.3.2", + "libbitflags", "libcfg_if", "liblog_rust", "libmanaged", diff --git a/CHANGELOG.md b/CHANGELOG.md index 435154f..97a4ae4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,45 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +# 0.7.0 + +#### Breaking API Changes + +- `stub::GdbStubError` is now an opaque `struct` with a handful of methods to extract user-defined context (as opposed to being an `enum` that directly exposed all error internals to the user). + - _This change will enable future versions of `gdbstub` to fearlessly improve error messages and infrastructure without making semver breaking changes. See [\#112](https://github.com/daniel5151/gdbstub/pull/132) for more._ +- `common::Signal` is not longer an `enum`, and is instead a `struct` with a single `pub u8` field + a collection of associated constants. + - _As a result, yet another instance of `unsafe` could be removed from the codebase!_ +- `Arch` API: + - Entirely removed `single_step_behavior`. See [\#132](https://github.com/daniel5151/gdbstub/pull/132) for details and rationale +- `Target` APIs: + - `SingleThreadBase`/`MultiThreadBase` + - `read_addrs` now returns a `usize` instead of a `()`, allowing implementations to report cases where only a subset of memory could be read. + - `HostIo` + - `bitflags` has been updated from `1.x` to `2.x`, affecting the type of `HostIoOpenFlags` and `HostIoOpenMode` + +#### Internal Improvements + +- Reformatted codebase with nightly rustfmt using `imports_granularity = "Item"` + +# 0.6.6 + +#### New Features + +- `Target::use_no_ack_mode` - toggle support for for activating "no ack mode" [\#135](https://github.com/daniel5151/gdbstub/pull/135) ([bet4it](https://github.com/bet4it)) + +# 0.6.5 + +#### New Protocol Extensions + +- `ExtendedMode > CurrentActivePid` - Support reporting a non-default active PID [\#133](https://github.com/daniel5151/gdbstub/pull/129) + - Required to fix `vAttach` behavior (see Bugfixes section below) + +#### Bugfixes + +- Fix for targets with no active threads [\#127](https://github.com/daniel5151/gdbstub/pull/127) ([xobs](https://github.com/xobs)) +- Fix `vAttach` behavior when switching between multiple processes [\#129](https://github.com/daniel5151/gdbstub/pull/129) ([xobs](https://github.com/xobs)), and [\#133](https://github.com/daniel5151/gdbstub/pull/129) +- Minor doc fixes + # 0.6.4 #### Bugfixes diff --git a/Cargo.toml b/Cargo.toml index 3dcdd43..0eb81ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ [package] edition = "2018" name = "gdbstub" -version = "0.6.4" +version = "0.7.0" authors = ["Daniel Prilik "] exclude = [ "examples/**/*.elf", @@ -47,7 +47,7 @@ name = "armv4t_multicore" required-features = ["std"] [dependencies.bitflags] -version = "1.3" +version = "2.3.1" [dependencies.cfg-if] version = "1.0" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index f448235..445e67b 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -2,7 +2,7 @@ name = "gdbstub" description = "An implementation of the GDB Remote Serial Protocol in Rust" authors = ["Daniel Prilik "] -version = "0.6.4" +version = "0.7.0" license = "MIT OR Apache-2.0" edition = "2018" readme = "README.md" @@ -14,7 +14,7 @@ categories = ["development-tools::debugging", "embedded", "emulators", "network- exclude = ["examples/**/*.elf", "examples/**/*.o"] [dependencies] -bitflags = "1.3" +bitflags = "2.3.1" cfg-if = "1.0" log = "0.4" managed = { version = "0.8", default-features = false } diff --git a/METADATA b/METADATA index 4b50ec0..146415c 100644 --- a/METADATA +++ b/METADATA @@ -1,23 +1,20 @@ # This project was upgraded with external_updater. # Usage: tools/external_updater/updater.sh update rust/crates/gdbstub -# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md +# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md name: "gdbstub" description: "An implementation of the GDB Remote Serial Protocol in Rust" third_party { - url { - type: HOMEPAGE - value: "https://crates.io/crates/gdbstub" - } - url { - type: ARCHIVE - value: "https://static.crates.io/crates/gdbstub/gdbstub-0.6.4.crate" - } - version: "0.6.4" license_type: NOTICE last_upgrade_date { year: 2023 - month: 3 - day: 6 + month: 12 + day: 16 + } + homepage: "https://crates.io/crates/gdbstub" + identifier { + type: "Archive" + value: "https://static.crates.io/crates/gdbstub/gdbstub-0.7.0.crate" + version: "0.7.0" } } diff --git a/README.md b/README.md index c58e4f3..4ce94e4 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,13 @@ [![](https://docs.rs/gdbstub/badge.svg)](https://docs.rs/gdbstub) [![](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](./LICENSE) -An ergonomic and easy-to-integrate implementation of the [GDB Remote Serial Protocol](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html#Remote-Protocol) in Rust, with full `#![no_std]` support. +An ergonomic, featureful, and easy-to-integrate implementation of the [GDB Remote Serial Protocol](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html#Remote-Protocol) in Rust, with _no-compromises_ `#![no_std]` support. `gdbstub` makes it easy to integrate powerful guest debugging support to your emulator / hypervisor / debugger / embedded project. By implementing just a few basic methods of the [`gdbstub::Target`](https://docs.rs/gdbstub/latest/gdbstub/target/ext/base/singlethread/trait.SingleThreadBase.html) trait, you can have a rich GDB debugging session up and running in no time! `gdbstub`'s API makes extensive use of a technique called [**Inlineable Dyn Extension Traits**](#zero-overhead-protocol-extensions) (IDETs) to expose fine-grained, zero-cost control over enabled GDB protocol features _without_ relying on compile-time features flags. Aside from making it effortless to toggle enabled protocol features, IDETs also ensure that any unimplemented features are guaranteed to be dead-code-eliminated in release builds! -**If you're looking for a quick snippet of example code to see what a typical `gdbstub` integration might look like, check out [examples/armv4t/gdb/mod.rs](https://github.com/daniel5151/gdbstub/blob/master/examples/armv4t/gdb/mod.rs)** +**If you're looking for a quick snippet of example code to see what a featureful `gdbstub` integration might look like, check out [examples/armv4t/gdb/mod.rs](https://github.com/daniel5151/gdbstub/blob/master/examples/armv4t/gdb/mod.rs)** - [Documentation (gdbstub)](https://docs.rs/gdbstub) - [Documentation (gdbstub_arch)](https://docs.rs/gdbstub_arch) @@ -134,8 +134,8 @@ If you end up using `gdbstub` in your project, consider opening a PR and adding - [Firecracker](https://firecracker-microvm.github.io/) - A lightweight VMM developed by AWS (feature is in [PR](https://github.com/firecracker-microvm/firecracker/pull/2333)) - [uhyve](https://github.com/hermitcore/uhyve) - A minimal hypervisor for [RustyHermit](https://github.com/hermitcore/rusty-hermit) - OS Kernels (using `gdbstub` on `no_std`) + - [`betrusted-io/xous-core`](https://github.com/betrusted-io/xous-core/blob/b471b604/kernel/src/debug/gdb.rs) - The Xous microkernel operating system - [`vmware-labs/node-replicated-kernel`](https://github.com/vmware-labs/node-replicated-kernel/tree/4326704/kernel/src/arch/x86_64/gdb) - An (experimental) research OS kernel for x86-64 (amd64) machines - - [`betrusted-io/xous-core`](https://github.com/betrusted-io/xous-core/blob/7d3d710/kernel/src/debug/gdb_server.rs) - The Xous microkernel operating system - Emulators - [solana_rbpf](https://github.com/solana-labs/rbpf) - VM and JIT compiler for eBPF programs - [rustyboyadvance-ng](https://github.com/michelhe/rustboyadvance-ng/) - Nintendo Gameboy Advance emulator and debugger (ARMv4T) @@ -178,8 +178,6 @@ The following list exhaustively documents all uses of `unsafe` in `gdbstub`: - Don't emit provably unreachable panics - `src/protocol/packet.rs`: Method in `PacketBuf` that use index using stored sub-`Range` into the buffer - `src/protocol/common/hex.rs`: `decode_hex_buf` - - Don't emit large `match`-arm LUT - - `src/common.rs`: Checked transmute of `u8` to `Signal` - When the `std` feature is enabled: - `src/connection/impls/unixstream.rs`: An implementation of `UnixStream::peek` which uses `libc::recv`. Will be removed once [rust-lang/rust#76923](https://github.com/rust-lang/rust/issues/76923) stabilizes this feature in the stdlib. @@ -220,7 +218,7 @@ Not that this is _not_ an exhaustive list, and is subject to change. - [ ] Allow fine-grained control over target features via the `Arch` trait ([\#12](https://github.com/daniel5151/gdbstub/issues/12)) - [ ] Implement GDB's various high-level operating modes: - [x] Single/Multi Thread debugging - - [ ] Multiprocess Debugging + - [ ] Multiprocess Debugging ([\#124](https://github.com/daniel5151/gdbstub/issues/124) - [ ] Requires adding a new `target::ext::base::multiprocess` API. - _Note:_ `gdbstub` already implements multiprocess extensions "under-the-hood", and just hard-codes a fake PID, so this is mostly a matter of "putting in the work". - [x] [Extended Mode](https://sourceware.org/gdb/current/onlinedocs/gdb/Connecting.html) (`target extended-remote`) @@ -235,9 +233,7 @@ Additionally, while not _strict_ blockers to `1.0.0`, it would be good to explor - [ ] How/if to support [LLDB extensions](https://raw.githubusercontent.com/llvm-mirror/lldb/master/docs/lldb-gdb-remote.txt) ([\#99](https://github.com/daniel5151/gdbstub/issues/99)) - [ ] Supporting multi-arch debugging via a single target - e.g: debugging x86 and ARM processes on macOS -- [ ] Proper handling of "nack" packets (for spotty connections) - - Responding with "nack" is easy - the client has to re-transmit the command - - Re-transmitting after receiving a "nack" might be a bit harder... +- [ ] Proper handling of "nack" packets (for spotty connections) ([\#137](https://github.com/daniel5151/gdbstub/issues/137)) ## License diff --git a/cargo_embargo.json b/cargo_embargo.json index 99df43f..cb908d7 100644 --- a/cargo_embargo.json +++ b/cargo_embargo.json @@ -1,6 +1,3 @@ { - "module_name_overrides": { - "libbitflags": "libbitflags-1.3.2" - }, "run_cargo": false } diff --git a/docs/transition_guide.md b/docs/transition_guide.md index 6197171..3ae106c 100644 --- a/docs/transition_guide.md +++ b/docs/transition_guide.md @@ -6,6 +6,76 @@ This document does _not_ discuss any new features that might have been added bet > _Note:_ after reading through this doc, you may also find it helpful to refer to the in-tree `armv4t` and `armv4t_multicore` examples when transitioning between versions. +## `0.6` -> `0.7` + +`0.7` is a fairly minimal "cleanup" release, landing a collection of small breaking changes that collectively improve various ergonomic issues in `gdbstub`'s API. + +The breaking changes introduced in `0.7` are generally trivial to fix, and porting from `0.6` to `0.7` shouldn't take more than ~10 minutes, at most. + +##### `stub::GdbStubError` Changes + +`stub::GdbStubError` is now an opaque `struct` with a handful of methods to extract user-defined context. + +**Please file an issue if your code required matching on concrete error variants aside from `TargetError` and `ConnectionError`!**. + +In contrast with the old version - which was an `enum` that directly exposed all error internals to the user - this new type will enable future versions of `gdbstub` to fearlessly improve error infrastructure without requiring semver breaking changes. See [\#112](https://github.com/daniel5151/gdbstub/pull/132) for more. + +Assuming you stuck to the example error handling described in the `gdbstub` getting started guide, adapting to the new type should be quite straightforward. + + +```rust +// ==== 0.6.x ==== // + +match gdb.run_blocking::(&mut emu) { + Ok(disconnect_reason) => { ... }, + Err(GdbStubError::TargetError(e)) => { + println!("target encountered a fatal error: {}", e) + } + Err(e) => { + println!("gdbstub encountered a fatal error: {}", e) + } +} + +// ==== 0.7.0 ==== // + +match gdb.run_blocking::(&mut emu) { + Ok(disconnect_reason) => { ... }, + Err(e) => { + if e.is_target_error() { + println!( + "target encountered a fatal error: {}", + e.into_target_error().unwrap() + ) + } else if e.is_connection_error() { + let (e, kind) = e.into_connection_error().unwrap(); + println!("connection error: {:?} - {}", kind, e,) + } else { + println!("gdbstub encountered a fatal error: {}", e) + } + } +} +``` + + +##### `{Single, Multi}ThreadBase::read_addrs` return value + +`read_addrs` now returns a `usize` instead of a `()`, allowing implementations to report cases where only a subset of memory could be read. + +In the past, the only way to handle these cases was by returning a `TargetError`. This provides an alternative mechanism, which may or may not be more appropriate for your particular use-case. + +When upgrading, the Rust compiler will emit a clear error message pointing out the updated function signature. The fix should be trivial. + +##### Removal of `Arch::single_step_behavior` + +See [\#132](https://github.com/daniel5151/gdbstub/pull/132) for more discussion on why this API was removed. + +This change only affects you if you're maintaining a custom `Arch` implementation (vs. using a community-maintained one via `gdbstub_arch`). + +The fix here is to simply remove the `Arch::single_step_behavior` impl. + +That's it! It's that easy. + + ## `0.5` -> `0.6` `0.6` introduces a large number of breaking changes to the public APIs, and will require quite a bit more more "hands on" porting than previous `gdbstub` upgrades. diff --git a/examples/armv4t/emu.rs b/examples/armv4t/emu.rs index e8f3305..e94131b 100644 --- a/examples/armv4t/emu.rs +++ b/examples/armv4t/emu.rs @@ -1,7 +1,12 @@ -use armv4t_emu::{reg, Cpu, ExampleMem, Memory, Mode}; - -use crate::mem_sniffer::{AccessKind, MemSniffer}; +use crate::mem_sniffer::AccessKind; +use crate::mem_sniffer::MemSniffer; use crate::DynResult; +use armv4t_emu::reg; +use armv4t_emu::Cpu; +use armv4t_emu::ExampleMem; +use armv4t_emu::Memory; +use armv4t_emu::Mode; +use gdbstub::common::Pid; const HLE_RETURN_ADDR: u32 = 0x12345678; @@ -35,6 +40,8 @@ pub struct Emu { pub(crate) watchpoints: Vec, pub(crate) breakpoints: Vec, pub(crate) files: Vec>, + + pub(crate) reported_pid: Pid, } impl Emu { @@ -85,6 +92,8 @@ impl Emu { watchpoints: Vec::new(), breakpoints: Vec::new(), files: Vec::new(), + + reported_pid: Pid::new(1).unwrap(), }) } diff --git a/examples/armv4t/gdb/auxv.rs b/examples/armv4t/gdb/auxv.rs index a0f163b..3be0549 100644 --- a/examples/armv4t/gdb/auxv.rs +++ b/examples/armv4t/gdb/auxv.rs @@ -1,8 +1,7 @@ -use gdbstub::target; -use gdbstub::target::TargetResult; - use super::copy_range_to_buf; use crate::emu::Emu; +use gdbstub::target; +use gdbstub::target::TargetResult; impl target::ext::auxv::Auxv for Emu { fn get_auxv(&self, offset: u64, length: usize, buf: &mut [u8]) -> TargetResult { diff --git a/examples/armv4t/gdb/breakpoints.rs b/examples/armv4t/gdb/breakpoints.rs index 6fa3038..9d6d90d 100644 --- a/examples/armv4t/gdb/breakpoints.rs +++ b/examples/armv4t/gdb/breakpoints.rs @@ -1,9 +1,8 @@ +use crate::emu::Emu; use gdbstub::target; use gdbstub::target::ext::breakpoints::WatchKind; use gdbstub::target::TargetResult; -use crate::emu::Emu; - impl target::ext::breakpoints::Breakpoints for Emu { #[inline(always)] fn support_sw_breakpoint( diff --git a/examples/armv4t/gdb/catch_syscalls.rs b/examples/armv4t/gdb/catch_syscalls.rs index 63686f1..d58de98 100644 --- a/examples/armv4t/gdb/catch_syscalls.rs +++ b/examples/armv4t/gdb/catch_syscalls.rs @@ -1,8 +1,7 @@ +use crate::gdb::Emu; use gdbstub::target; use gdbstub::target::ext::catch_syscalls::SyscallNumbers; -use crate::gdb::Emu; - // This implementation is for illustrative purposes only. If the target doesn't // support syscalls then there is no need to implement this extension diff --git a/examples/armv4t/gdb/exec_file.rs b/examples/armv4t/gdb/exec_file.rs index e70a022..c2009a8 100644 --- a/examples/armv4t/gdb/exec_file.rs +++ b/examples/armv4t/gdb/exec_file.rs @@ -1,10 +1,9 @@ +use super::copy_range_to_buf; +use crate::emu::Emu; use gdbstub::common::Pid; use gdbstub::target; use gdbstub::target::TargetResult; -use super::copy_range_to_buf; -use crate::emu::Emu; - impl target::ext::exec_file::ExecFile for Emu { fn get_exec_file( &self, diff --git a/examples/armv4t/gdb/extended_mode.rs b/examples/armv4t/gdb/extended_mode.rs index 4b30657..31116f9 100644 --- a/examples/armv4t/gdb/extended_mode.rs +++ b/examples/armv4t/gdb/extended_mode.rs @@ -1,10 +1,11 @@ +use crate::emu::Emu; use gdbstub::common::Pid; use gdbstub::target; -use gdbstub::target::ext::extended_mode::{Args, AttachKind, ShouldTerminate}; +use gdbstub::target::ext::extended_mode::Args; +use gdbstub::target::ext::extended_mode::AttachKind; +use gdbstub::target::ext::extended_mode::ShouldTerminate; use gdbstub::target::TargetResult; -use crate::emu::Emu; - /*===================================== = Extended Mode = =====================================*/ @@ -31,8 +32,11 @@ impl target::ext::extended_mode::ExtendedMode for Emu { } fn attach(&mut self, pid: Pid) -> TargetResult<(), Self> { - eprintln!("GDB tried to attach to a process with PID {}", pid); - Err(().into()) // non-specific failure + eprintln!("GDB attached to a process with PID {}", pid); + // stub implementation: just report the same code, but running under a + // different pid. + self.reported_pid = pid; + Ok(()) } fn run(&mut self, filename: Option<&[u8]>, args: Args<'_, '_>) -> TargetResult { @@ -96,6 +100,13 @@ impl target::ext::extended_mode::ExtendedMode for Emu { ) -> Option> { Some(self) } + + #[inline(always)] + fn support_current_active_pid( + &mut self, + ) -> Option> { + Some(self) + } } impl target::ext::extended_mode::ConfigureAslr for Emu { @@ -158,3 +169,9 @@ impl target::ext::extended_mode::ConfigureWorkingDir for Emu { Ok(()) } } + +impl target::ext::extended_mode::CurrentActivePid for Emu { + fn current_active_pid(&mut self) -> Result { + Ok(self.reported_pid) + } +} diff --git a/examples/armv4t/gdb/host_io.rs b/examples/armv4t/gdb/host_io.rs index 40d6304..675b14e 100644 --- a/examples/armv4t/gdb/host_io.rs +++ b/examples/armv4t/gdb/host_io.rs @@ -1,13 +1,18 @@ -use std::io::{Read, Seek, Write}; - -use gdbstub::target; -use gdbstub::target::ext::host_io::{ - FsKind, HostIoErrno, HostIoError, HostIoOpenFlags, HostIoOpenMode, HostIoResult, HostIoStat, -}; - -use super::{copy_range_to_buf, copy_to_buf}; +use super::copy_range_to_buf; +use super::copy_to_buf; use crate::emu::Emu; use crate::TEST_PROGRAM_ELF; +use gdbstub::target; +use gdbstub::target::ext::host_io::FsKind; +use gdbstub::target::ext::host_io::HostIoErrno; +use gdbstub::target::ext::host_io::HostIoError; +use gdbstub::target::ext::host_io::HostIoOpenFlags; +use gdbstub::target::ext::host_io::HostIoOpenMode; +use gdbstub::target::ext::host_io::HostIoResult; +use gdbstub::target::ext::host_io::HostIoStat; +use std::io::Read; +use std::io::Seek; +use std::io::Write; const FD_RESERVED: u32 = 1; diff --git a/examples/armv4t/gdb/lldb_register_info_override.rs b/examples/armv4t/gdb/lldb_register_info_override.rs index 664cb45..14a1279 100644 --- a/examples/armv4t/gdb/lldb_register_info_override.rs +++ b/examples/armv4t/gdb/lldb_register_info_override.rs @@ -1,12 +1,15 @@ -use gdbstub::arch::lldb::{Encoding, Format, Generic, Register}; +use crate::gdb::custom_arch::ArmCoreRegIdCustom; +use crate::gdb::Emu; +use gdbstub::arch::lldb::Encoding; +use gdbstub::arch::lldb::Format; +use gdbstub::arch::lldb::Generic; +use gdbstub::arch::lldb::Register; use gdbstub::arch::RegId; use gdbstub::target; -use gdbstub::target::ext::lldb_register_info_override::{Callback, CallbackToken}; +use gdbstub::target::ext::lldb_register_info_override::Callback; +use gdbstub::target::ext::lldb_register_info_override::CallbackToken; use gdbstub_arch::arm::reg::id::ArmCoreRegId; -use crate::gdb::custom_arch::ArmCoreRegIdCustom; -use crate::gdb::Emu; - // (LLDB extension) This implementation is for illustrative purposes only. // // Note: In this implementation, we have r0-pc from 0-16 but cpsr is at offset diff --git a/examples/armv4t/gdb/memory_map.rs b/examples/armv4t/gdb/memory_map.rs index ddddb65..0aa5c08 100644 --- a/examples/armv4t/gdb/memory_map.rs +++ b/examples/armv4t/gdb/memory_map.rs @@ -1,8 +1,7 @@ -use gdbstub::target; -use gdbstub::target::TargetResult; - use super::copy_range_to_buf; use crate::emu::Emu; +use gdbstub::target; +use gdbstub::target::TargetResult; impl target::ext::memory_map::MemoryMap for Emu { fn memory_map_xml( diff --git a/examples/armv4t/gdb/mod.rs b/examples/armv4t/gdb/mod.rs index 97f7b2d..5837ddd 100644 --- a/examples/armv4t/gdb/mod.rs +++ b/examples/armv4t/gdb/mod.rs @@ -1,14 +1,17 @@ +use crate::emu::Emu; +use crate::emu::ExecMode; +use armv4t_emu::reg; +use armv4t_emu::Memory; use core::convert::TryInto; - -use armv4t_emu::{reg, Memory}; use gdbstub::common::Signal; use gdbstub::target; -use gdbstub::target::ext::base::singlethread::{SingleThreadBase, SingleThreadResume}; -use gdbstub::target::{Target, TargetError, TargetResult}; +use gdbstub::target::ext::base::singlethread::SingleThreadBase; +use gdbstub::target::ext::base::singlethread::SingleThreadResume; +use gdbstub::target::Target; +use gdbstub::target::TargetError; +use gdbstub::target::TargetResult; use gdbstub_arch::arm::reg::id::ArmCoreRegId; -use crate::emu::{Emu, ExecMode}; - // Additional GDB extensions mod auxv; @@ -196,11 +199,22 @@ impl SingleThreadBase for Emu { Some(self) } - fn read_addrs(&mut self, start_addr: u32, data: &mut [u8]) -> TargetResult<(), Self> { - for (addr, val) in (start_addr..).zip(data.iter_mut()) { + fn read_addrs(&mut self, start_addr: u32, data: &mut [u8]) -> TargetResult { + // These values are taken from the link script. + const MEMORY_ORIGIN: u32 = 0x5555_0000; + const MEMORY_LENGTH: u32 = 0x1000_0000; + const MEMORY_END: u32 = MEMORY_ORIGIN + MEMORY_LENGTH; + + if !(MEMORY_ORIGIN..MEMORY_END).contains(&start_addr) { + return Err(TargetError::NonFatal); + } + + let end_addr = std::cmp::min(start_addr + data.len() as u32, MEMORY_END); + + for (addr, val) in (start_addr..end_addr).zip(data.iter_mut()) { *val = self.mem.r8(addr) } - Ok(()) + Ok((end_addr - start_addr) as usize) } fn write_addrs(&mut self, start_addr: u32, data: &[u8]) -> TargetResult<(), Self> { @@ -379,10 +393,14 @@ impl target::ext::base::singlethread::SingleThreadRangeStepping for Emu { mod custom_arch { use core::num::NonZeroUsize; - - use gdbstub::arch::lldb::{Encoding, Format, Generic, Register, RegisterInfo}; - use gdbstub::arch::{Arch, RegId, Registers, SingleStepGdbBehavior}; - + use gdbstub::arch::lldb::Encoding; + use gdbstub::arch::lldb::Format; + use gdbstub::arch::lldb::Generic; + use gdbstub::arch::lldb::Register; + use gdbstub::arch::lldb::RegisterInfo; + use gdbstub::arch::Arch; + use gdbstub::arch::RegId; + use gdbstub::arch::Registers; use gdbstub_arch::arm::reg::id::ArmCoreRegId; use gdbstub_arch::arm::reg::ArmCoreRegs; use gdbstub_arch::arm::ArmBreakpointKind; @@ -590,13 +608,5 @@ mod custom_arch { } } } - // armv4t supports optional single stepping. - // - // notably, x86 is an example of an arch that does _not_ support - // optional single stepping. - #[inline(always)] - fn single_step_gdb_behavior() -> SingleStepGdbBehavior { - SingleStepGdbBehavior::Optional - } } } diff --git a/examples/armv4t/gdb/monitor_cmd.rs b/examples/armv4t/gdb/monitor_cmd.rs index 4dbdffc..4e91b28 100644 --- a/examples/armv4t/gdb/monitor_cmd.rs +++ b/examples/armv4t/gdb/monitor_cmd.rs @@ -1,7 +1,7 @@ -use gdbstub::target; -use gdbstub::target::ext::monitor_cmd::{outputln, ConsoleOutput}; - use crate::gdb::Emu; +use gdbstub::target; +use gdbstub::target::ext::monitor_cmd::outputln; +use gdbstub::target::ext::monitor_cmd::ConsoleOutput; impl target::ext::monitor_cmd::MonitorCmd for Emu { fn handle_monitor_cmd( diff --git a/examples/armv4t/gdb/section_offsets.rs b/examples/armv4t/gdb/section_offsets.rs index d04aab7..facebc3 100644 --- a/examples/armv4t/gdb/section_offsets.rs +++ b/examples/armv4t/gdb/section_offsets.rs @@ -1,8 +1,7 @@ +use crate::gdb::Emu; use gdbstub::target; use gdbstub::target::ext::section_offsets::Offsets; -use crate::gdb::Emu; - // This implementation is for illustrative purposes only. If the offsets are // guaranteed to be zero, this extension does not need to be implemented. diff --git a/examples/armv4t/gdb/target_description_xml_override.rs b/examples/armv4t/gdb/target_description_xml_override.rs index 2c5348c..681a9d5 100644 --- a/examples/armv4t/gdb/target_description_xml_override.rs +++ b/examples/armv4t/gdb/target_description_xml_override.rs @@ -1,10 +1,9 @@ +use super::copy_range_to_buf; +use crate::emu::Emu; use gdbstub::target; use gdbstub::target::TargetError; use gdbstub::target::TargetResult; -use super::copy_range_to_buf; -use crate::emu::Emu; - impl target::ext::target_description_xml_override::TargetDescriptionXmlOverride for Emu { fn target_description_xml( &self, diff --git a/examples/armv4t/main.rs b/examples/armv4t/main.rs index 96edaef..ab15f9b 100644 --- a/examples/armv4t/main.rs +++ b/examples/armv4t/main.rs @@ -2,16 +2,20 @@ //! `arm-none-eabi-cc -march=armv4t`. It's not modeled after any real-world //! system. -use std::net::{TcpListener, TcpStream}; - -#[cfg(unix)] -use std::os::unix::net::{UnixListener, UnixStream}; - use gdbstub::common::Signal; -use gdbstub::conn::{Connection, ConnectionExt}; +use gdbstub::conn::Connection; +use gdbstub::conn::ConnectionExt; +use gdbstub::stub::run_blocking; +use gdbstub::stub::DisconnectReason; +use gdbstub::stub::GdbStub; use gdbstub::stub::SingleThreadStopReason; -use gdbstub::stub::{run_blocking, DisconnectReason, GdbStub, GdbStubError}; use gdbstub::target::Target; +use std::net::TcpListener; +use std::net::TcpStream; +#[cfg(unix)] +use std::os::unix::net::UnixListener; +#[cfg(unix)] +use std::os::unix::net::UnixStream; type DynResult = Result>; @@ -177,11 +181,18 @@ fn main() -> DynResult<()> { } DisconnectReason::Kill => println!("GDB sent a kill command!"), }, - Err(GdbStubError::TargetError(e)) => { - println!("target encountered a fatal error: {}", e) - } Err(e) => { - println!("gdbstub encountered a fatal error: {}", e) + if e.is_target_error() { + println!( + "target encountered a fatal error: {}", + e.into_target_error().unwrap() + ) + } else if e.is_connection_error() { + let (e, kind) = e.into_connection_error().unwrap(); + println!("connection error: {:?} - {}", kind, e,) + } else { + println!("gdbstub encountered a fatal error: {}", e) + } } } diff --git a/examples/armv4t_multicore/emu.rs b/examples/armv4t_multicore/emu.rs index 1eed74c..1b9dac4 100644 --- a/examples/armv4t_multicore/emu.rs +++ b/examples/armv4t_multicore/emu.rs @@ -9,12 +9,15 @@ //! system that can be debugged, it would really merit a re-write, since it's //! not a good example of "proper Rust coding practices" -use std::collections::HashMap; - -use armv4t_emu::{reg, Cpu, ExampleMem, Memory, Mode}; - -use crate::mem_sniffer::{AccessKind, MemSniffer}; +use crate::mem_sniffer::AccessKind; +use crate::mem_sniffer::MemSniffer; use crate::DynResult; +use armv4t_emu::reg; +use armv4t_emu::Cpu; +use armv4t_emu::ExampleMem; +use armv4t_emu::Memory; +use armv4t_emu::Mode; +use std::collections::HashMap; const HLE_RETURN_ADDR: u32 = 0x12345678; diff --git a/examples/armv4t_multicore/gdb.rs b/examples/armv4t_multicore/gdb.rs index 3c8cd75..11ed1e3 100644 --- a/examples/armv4t_multicore/gdb.rs +++ b/examples/armv4t_multicore/gdb.rs @@ -1,12 +1,17 @@ -use armv4t_emu::{reg, Memory}; - -use gdbstub::common::{Signal, Tid}; +use crate::emu::CpuId; +use crate::emu::Emu; +use crate::emu::ExecMode; +use armv4t_emu::reg; +use armv4t_emu::Memory; +use gdbstub::common::Signal; +use gdbstub::common::Tid; use gdbstub::target; -use gdbstub::target::ext::base::multithread::{MultiThreadBase, MultiThreadResume}; +use gdbstub::target::ext::base::multithread::MultiThreadBase; +use gdbstub::target::ext::base::multithread::MultiThreadResume; use gdbstub::target::ext::breakpoints::WatchKind; -use gdbstub::target::{Target, TargetError, TargetResult}; - -use crate::emu::{CpuId, Emu, ExecMode}; +use gdbstub::target::Target; +use gdbstub::target::TargetError; +use gdbstub::target::TargetResult; pub fn cpuid_to_tid(id: CpuId) -> Tid { match id { @@ -92,11 +97,11 @@ impl MultiThreadBase for Emu { start_addr: u32, data: &mut [u8], _tid: Tid, // same address space for each core - ) -> TargetResult<(), Self> { + ) -> TargetResult { for (addr, val) in (start_addr..).zip(data.iter_mut()) { *val = self.mem.r8(addr) } - Ok(()) + Ok(data.len()) } fn write_addrs( diff --git a/examples/armv4t_multicore/main.rs b/examples/armv4t_multicore/main.rs index 4f799a7..c24f795 100644 --- a/examples/armv4t_multicore/main.rs +++ b/examples/armv4t_multicore/main.rs @@ -3,15 +3,20 @@ //! `gdbstub`'s multi-process support. It's not modeled after any real-world //! system. -use std::net::{TcpListener, TcpStream}; - -#[cfg(unix)] -use std::os::unix::net::{UnixListener, UnixStream}; - use gdbstub::common::Signal; -use gdbstub::conn::{Connection, ConnectionExt}; -use gdbstub::stub::{run_blocking, DisconnectReason, GdbStub, GdbStubError, MultiThreadStopReason}; +use gdbstub::conn::Connection; +use gdbstub::conn::ConnectionExt; +use gdbstub::stub::run_blocking; +use gdbstub::stub::DisconnectReason; +use gdbstub::stub::GdbStub; +use gdbstub::stub::MultiThreadStopReason; use gdbstub::target::Target; +use std::net::TcpListener; +use std::net::TcpStream; +#[cfg(unix)] +use std::os::unix::net::UnixListener; +#[cfg(unix)] +use std::os::unix::net::UnixStream; type DynResult = Result>; @@ -178,11 +183,18 @@ fn main() -> DynResult<()> { } DisconnectReason::Kill => println!("GDB sent a kill command!"), }, - Err(GdbStubError::TargetError(e)) => { - println!("target encountered a fatal error: {}", e) - } Err(e) => { - println!("gdbstub encountered a fatal error: {}", e) + if e.is_target_error() { + println!( + "target encountered a fatal error: {}", + e.into_target_error().unwrap() + ) + } else if e.is_connection_error() { + let (e, kind) = e.into_connection_error().unwrap(); + println!("connection error: {:?} - {}", kind, e,) + } else { + println!("gdbstub encountered a fatal error: {}", e) + } } } diff --git a/rustfmt.toml b/rustfmt.toml index 606e292..6e14a7c 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1 +1,2 @@ -wrap_comments = true \ No newline at end of file +imports_granularity = "Item" +wrap_comments = true diff --git a/src/arch.rs b/src/arch.rs index f13adb0..00fbba2 100644 --- a/src/arch.rs +++ b/src/arch.rs @@ -15,12 +15,13 @@ //! > Having community-created `Arch` implementations distributed in a separate //! crate helps minimize any unnecessary "version churn" in `gdbstub` core. +use crate::internal::BeBytes; +use crate::internal::LeBytes; use core::fmt::Debug; use core::num::NonZeroUsize; - -use num_traits::{FromPrimitive, PrimInt, Unsigned}; - -use crate::internal::{BeBytes, LeBytes}; +use num_traits::FromPrimitive; +use num_traits::PrimInt; +use num_traits::Unsigned; /// Register identifier for target registers. /// @@ -187,102 +188,6 @@ pub trait Arch { let _ = reg_id; None } - - /// Encode how the mainline GDB client handles target support for - /// single-step on this particular architecture. - /// - /// # Context - /// - /// According to the spec, supporting single step _should_ be quite - /// straightforward: - /// - /// - The GDB client sends a `vCont?` packet to enumerate supported - /// resumption modes - /// - If the target supports single-step, it responds with the `s;S` - /// capability as part of the response, omitting it if it is not - /// supported. - /// - Later, when the user attempts to `stepi`, the GDB client sends a `s` - /// resumption reason if it is supported, falling back to setting a - /// temporary breakpoint + continue to "emulate" the single step. - /// - /// Unfortunately, the reality is that the mainline GDB client does _not_ do - /// this on all architectures... - /// - /// - On certain architectures (e.g: x86), GDB will _unconditionally_ assume - /// single-step support, regardless whether or not the target reports - /// supports it. - /// - On certain architectures (e.g: MIPS), GDB will _never_ use single-step - /// support, even in the target has explicitly reported support for it. - /// - /// This is a bug, and has been reported at - /// . - /// - /// For a easy repro of this behavior, also see - /// . - /// - /// # Implications - /// - /// Unfortunately, even if these idiosyncratic behaviors get fixed in the - /// mainline GDB client, it will be quite a while until the typical - /// user's distro-provided GDB client includes this bugfix. - /// - /// As such, `gdbstub` has opted to include this method as a "guard rail" to - /// preemptively detect cases of this idiosyncratic behavior, and throw a - /// pre-init error that informs the user of the potential issues they may - /// run into. - /// - /// # Writing a proper implementation - /// - /// To check whether or not a particular architecture exhibits this - /// behavior, an implementation should temporarily override this method to - /// return [`SingleStepGdbBehavior::Optional`], toggle target support for - /// single-step on/off, and observe the behavior of the GDB client after - /// invoking `stepi`. - /// - /// If single-stepping was **disabled**, yet the client nonetheless sent a - /// `vCont` packet with a `s` resume action, then this architecture - /// _does not_ support optional single stepping, and this method should - /// return [`SingleStepGdbBehavior::Required`]. - /// - /// If single-stepping was **disabled**, and the client attempted to set a - /// temporary breakpoint (using the `z` packet), and then sent a `vCont` - /// packet with a `c` resume action, then this architecture _does_ - /// support optional single stepping, and this method should return - /// [`SingleStepGdbBehavior::Optional`]. - /// - /// If single-stepping was **enabled**, yet the client did _not_ send a - /// `vCont` packet with a `s` resume action, then this architecture - /// _ignores_ single stepping entirely, and this method should return - /// [`SingleStepGdbBehavior::Ignored`]. - fn single_step_gdb_behavior() -> SingleStepGdbBehavior; -} - -/// Encodes how the mainline GDB client handles target support for single-step -/// on a particular architecture. -/// -/// See [Arch::single_step_gdb_behavior] for details. -#[non_exhaustive] -#[derive(Debug, Clone, Copy)] -pub enum SingleStepGdbBehavior { - /// GDB will use single-stepping if available, falling back to using - /// a temporary breakpoint + continue if unsupported. - /// - /// e.g: ARM - Optional, - /// GDB will unconditionally send single-step packets, _requiring_ the - /// target to handle these requests. - /// - /// e.g: x86/x64 - Required, - /// GDB will never use single-stepping, regardless if it's supported by the - /// stub. It will always use a temporary breakpoint + continue. - /// - /// e.g: MIPS - Ignored, - /// Unknown behavior - no one has tested this platform yet. If possible, - /// please conduct a test + upstream your findings to `gdbstub_arch`. - #[doc(hidden)] - Unknown, } /// LLDB-specific types supporting [`Arch::lldb_register_info`] and diff --git a/src/common/signal.rs b/src/common/signal.rs index 652752a..4ee9273 100644 --- a/src/common/signal.rs +++ b/src/common/signal.rs @@ -1,516 +1,348 @@ /// Cross-platform signal numbers defined by the GDB Remote Serial Protocol. /// /// Transcribed from -#[repr(u8)] +#[repr(transparent)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Signal(pub u8); + #[allow(clippy::upper_case_acronyms)] #[allow(non_camel_case_types)] #[rustfmt::skip] -#[non_exhaustive] -pub enum Signal { - #[doc = "Signal 0 (shouldn't be used)"] SIGZERO = 0, - #[doc = "Hangup"] SIGHUP = 1, - #[doc = "Interrupt"] SIGINT = 2, - #[doc = "Quit"] SIGQUIT = 3, - #[doc = "Illegal instruction"] SIGILL = 4, - #[doc = "Trace/breakpoint trap"] SIGTRAP = 5, - #[doc = "Aborted"] SIGABRT = 6, - #[doc = "Emulation trap"] SIGEMT = 7, - #[doc = "Arithmetic exception"] SIGFPE = 8, - #[doc = "Killed"] SIGKILL = 9, - #[doc = "Bus error"] SIGBUS = 10, - #[doc = "Segmentation fault"] SIGSEGV = 11, - #[doc = "Bad system call"] SIGSYS = 12, - #[doc = "Broken pipe"] SIGPIPE = 13, - #[doc = "Alarm clock"] SIGALRM = 14, - #[doc = "Terminated"] SIGTERM = 15, - #[doc = "Urgent I/O condition"] SIGURG = 16, - #[doc = "Stopped (signal)"] SIGSTOP = 17, - #[doc = "Stopped (user)"] SIGTSTP = 18, - #[doc = "Continued"] SIGCONT = 19, - #[doc = "Child status changed"] SIGCHLD = 20, - #[doc = "Stopped (tty input)"] SIGTTIN = 21, - #[doc = "Stopped (tty output)"] SIGTTOU = 22, - #[doc = "I/O possible"] SIGIO = 23, - #[doc = "CPU time limit exceeded"] SIGXCPU = 24, - #[doc = "File size limit exceeded"] SIGXFSZ = 25, - #[doc = "Virtual timer expired"] SIGVTALRM = 26, - #[doc = "Profiling timer expired"] SIGPROF = 27, - #[doc = "Window size changed"] SIGWINCH = 28, - #[doc = "Resource lost"] SIGLOST = 29, - #[doc = "User defined signal 1"] SIGUSR1 = 30, - #[doc = "User defined signal 2"] SIGUSR2 = 31, - #[doc = "Power fail/restart"] SIGPWR = 32, +impl Signal { + #[doc = "Signal 0 (shouldn't be used)"] pub const SIGZERO: Self = Self(0); + #[doc = "Hangup"] pub const SIGHUP: Self = Self(1); + #[doc = "Interrupt"] pub const SIGINT: Self = Self(2); + #[doc = "Quit"] pub const SIGQUIT: Self = Self(3); + #[doc = "Illegal instruction"] pub const SIGILL: Self = Self(4); + #[doc = "Trace/breakpoint trap"] pub const SIGTRAP: Self = Self(5); + #[doc = "Aborted"] pub const SIGABRT: Self = Self(6); + #[doc = "Emulation trap"] pub const SIGEMT: Self = Self(7); + #[doc = "Arithmetic exception"] pub const SIGFPE: Self = Self(8); + #[doc = "Killed"] pub const SIGKILL: Self = Self(9); + #[doc = "Bus error"] pub const SIGBUS: Self = Self(10); + #[doc = "Segmentation fault"] pub const SIGSEGV: Self = Self(11); + #[doc = "Bad system call"] pub const SIGSYS: Self = Self(12); + #[doc = "Broken pipe"] pub const SIGPIPE: Self = Self(13); + #[doc = "Alarm clock"] pub const SIGALRM: Self = Self(14); + #[doc = "Terminated"] pub const SIGTERM: Self = Self(15); + #[doc = "Urgent I/O condition"] pub const SIGURG: Self = Self(16); + #[doc = "Stopped (signal)"] pub const SIGSTOP: Self = Self(17); + #[doc = "Stopped (user)"] pub const SIGTSTP: Self = Self(18); + #[doc = "Continued"] pub const SIGCONT: Self = Self(19); + #[doc = "Child status changed"] pub const SIGCHLD: Self = Self(20); + #[doc = "Stopped (tty input)"] pub const SIGTTIN: Self = Self(21); + #[doc = "Stopped (tty output)"] pub const SIGTTOU: Self = Self(22); + #[doc = "I/O possible"] pub const SIGIO: Self = Self(23); + #[doc = "CPU time limit exceeded"] pub const SIGXCPU: Self = Self(24); + #[doc = "File size limit exceeded"] pub const SIGXFSZ: Self = Self(25); + #[doc = "Virtual timer expired"] pub const SIGVTALRM: Self = Self(26); + #[doc = "Profiling timer expired"] pub const SIGPROF: Self = Self(27); + #[doc = "Window size changed"] pub const SIGWINCH: Self = Self(28); + #[doc = "Resource lost"] pub const SIGLOST: Self = Self(29); + #[doc = "User defined signal 1"] pub const SIGUSR1: Self = Self(30); + #[doc = "User defined signal 2"] pub const SIGUSR2: Self = Self(31); + #[doc = "Power fail/restart"] pub const SIGPWR: Self = Self(32); /* Similar to SIGIO. Perhaps they should have the same number. */ - #[doc = "Pollable event occurred"] SIGPOLL = 33, - #[doc = "SIGWIND"] SIGWIND = 34, - #[doc = "SIGPHONE"] SIGPHONE = 35, - #[doc = "Process's LWPs are blocked"] SIGWAITING = 36, - #[doc = "Signal LWP"] SIGLWP = 37, - #[doc = "Swap space dangerously low"] SIGDANGER = 38, - #[doc = "Monitor mode granted"] SIGGRANT = 39, - #[doc = "Need to relinquish monitor mode"] SIGRETRACT = 40, - #[doc = "Monitor mode data available"] SIGMSG = 41, - #[doc = "Sound completed"] SIGSOUND = 42, - #[doc = "Secure attention"] SIGSAK = 43, - #[doc = "SIGPRIO"] SIGPRIO = 44, - #[doc = "Real-time event 33"] SIG33 = 45, - #[doc = "Real-time event 34"] SIG34 = 46, - #[doc = "Real-time event 35"] SIG35 = 47, - #[doc = "Real-time event 36"] SIG36 = 48, - #[doc = "Real-time event 37"] SIG37 = 49, - #[doc = "Real-time event 38"] SIG38 = 50, - #[doc = "Real-time event 39"] SIG39 = 51, - #[doc = "Real-time event 40"] SIG40 = 52, - #[doc = "Real-time event 41"] SIG41 = 53, - #[doc = "Real-time event 42"] SIG42 = 54, - #[doc = "Real-time event 43"] SIG43 = 55, - #[doc = "Real-time event 44"] SIG44 = 56, - #[doc = "Real-time event 45"] SIG45 = 57, - #[doc = "Real-time event 46"] SIG46 = 58, - #[doc = "Real-time event 47"] SIG47 = 59, - #[doc = "Real-time event 48"] SIG48 = 60, - #[doc = "Real-time event 49"] SIG49 = 61, - #[doc = "Real-time event 50"] SIG50 = 62, - #[doc = "Real-time event 51"] SIG51 = 63, - #[doc = "Real-time event 52"] SIG52 = 64, - #[doc = "Real-time event 53"] SIG53 = 65, - #[doc = "Real-time event 54"] SIG54 = 66, - #[doc = "Real-time event 55"] SIG55 = 67, - #[doc = "Real-time event 56"] SIG56 = 68, - #[doc = "Real-time event 57"] SIG57 = 69, - #[doc = "Real-time event 58"] SIG58 = 70, - #[doc = "Real-time event 59"] SIG59 = 71, - #[doc = "Real-time event 60"] SIG60 = 72, - #[doc = "Real-time event 61"] SIG61 = 73, - #[doc = "Real-time event 62"] SIG62 = 74, - #[doc = "Real-time event 63"] SIG63 = 75, + #[doc = "Pollable event occurred"] pub const SIGPOLL: Self = Self(33); + #[doc = "SIGWIND"] pub const SIGWIND: Self = Self(34); + #[doc = "SIGPHONE"] pub const SIGPHONE: Self = Self(35); + #[doc = "Process's LWPs are blocked"] pub const SIGWAITING: Self = Self(36); + #[doc = "Signal LWP"] pub const SIGLWP: Self = Self(37); + #[doc = "Swap space dangerously low"] pub const SIGDANGER: Self = Self(38); + #[doc = "Monitor mode granted"] pub const SIGGRANT: Self = Self(39); + #[doc = "Need to relinquish monitor mode"] pub const SIGRETRACT: Self = Self(40); + #[doc = "Monitor mode data available"] pub const SIGMSG: Self = Self(41); + #[doc = "Sound completed"] pub const SIGSOUND: Self = Self(42); + #[doc = "Secure attention"] pub const SIGSAK: Self = Self(43); + #[doc = "SIGPRIO"] pub const SIGPRIO: Self = Self(44); + #[doc = "Real-time event 33"] pub const SIG33: Self = Self(45); + #[doc = "Real-time event 34"] pub const SIG34: Self = Self(46); + #[doc = "Real-time event 35"] pub const SIG35: Self = Self(47); + #[doc = "Real-time event 36"] pub const SIG36: Self = Self(48); + #[doc = "Real-time event 37"] pub const SIG37: Self = Self(49); + #[doc = "Real-time event 38"] pub const SIG38: Self = Self(50); + #[doc = "Real-time event 39"] pub const SIG39: Self = Self(51); + #[doc = "Real-time event 40"] pub const SIG40: Self = Self(52); + #[doc = "Real-time event 41"] pub const SIG41: Self = Self(53); + #[doc = "Real-time event 42"] pub const SIG42: Self = Self(54); + #[doc = "Real-time event 43"] pub const SIG43: Self = Self(55); + #[doc = "Real-time event 44"] pub const SIG44: Self = Self(56); + #[doc = "Real-time event 45"] pub const SIG45: Self = Self(57); + #[doc = "Real-time event 46"] pub const SIG46: Self = Self(58); + #[doc = "Real-time event 47"] pub const SIG47: Self = Self(59); + #[doc = "Real-time event 48"] pub const SIG48: Self = Self(60); + #[doc = "Real-time event 49"] pub const SIG49: Self = Self(61); + #[doc = "Real-time event 50"] pub const SIG50: Self = Self(62); + #[doc = "Real-time event 51"] pub const SIG51: Self = Self(63); + #[doc = "Real-time event 52"] pub const SIG52: Self = Self(64); + #[doc = "Real-time event 53"] pub const SIG53: Self = Self(65); + #[doc = "Real-time event 54"] pub const SIG54: Self = Self(66); + #[doc = "Real-time event 55"] pub const SIG55: Self = Self(67); + #[doc = "Real-time event 56"] pub const SIG56: Self = Self(68); + #[doc = "Real-time event 57"] pub const SIG57: Self = Self(69); + #[doc = "Real-time event 58"] pub const SIG58: Self = Self(70); + #[doc = "Real-time event 59"] pub const SIG59: Self = Self(71); + #[doc = "Real-time event 60"] pub const SIG60: Self = Self(72); + #[doc = "Real-time event 61"] pub const SIG61: Self = Self(73); + #[doc = "Real-time event 62"] pub const SIG62: Self = Self(74); + #[doc = "Real-time event 63"] pub const SIG63: Self = Self(75); /* Used internally by Solaris threads. See signal(5) on Solaris. */ - #[doc = "LWP internal signal"] SIGCANCEL = 76, + #[doc = "LWP internal signal"] pub const SIGCANCEL: Self = Self(76); /* Yes, this pains me, too. But LynxOS didn't have SIG32, and now GNU/Linux does, and we can't disturb the numbering, since it's part of the remote protocol. Note that in some GDB's GDB_SIGNAL_REALTIME_32 is number 76. */ - #[doc = "Real-time event 32"] SIG32 = 77, + #[doc = "Real-time event 32"] pub const SIG32: Self = Self(77); /* Yet another pain, IRIX 6 has SIG64. */ - #[doc = "Real-time event 64"] SIG64 = 78, + #[doc = "Real-time event 64"] pub const SIG64: Self = Self(78); /* Yet another pain, GNU/Linux MIPS might go up to 128. */ - #[doc = "Real-time event 65"] SIG65 = 79, - #[doc = "Real-time event 66"] SIG66 = 80, - #[doc = "Real-time event 67"] SIG67 = 81, - #[doc = "Real-time event 68"] SIG68 = 82, - #[doc = "Real-time event 69"] SIG69 = 83, - #[doc = "Real-time event 70"] SIG70 = 84, - #[doc = "Real-time event 71"] SIG71 = 85, - #[doc = "Real-time event 72"] SIG72 = 86, - #[doc = "Real-time event 73"] SIG73 = 87, - #[doc = "Real-time event 74"] SIG74 = 88, - #[doc = "Real-time event 75"] SIG75 = 89, - #[doc = "Real-time event 76"] SIG76 = 90, - #[doc = "Real-time event 77"] SIG77 = 91, - #[doc = "Real-time event 78"] SIG78 = 92, - #[doc = "Real-time event 79"] SIG79 = 93, - #[doc = "Real-time event 80"] SIG80 = 94, - #[doc = "Real-time event 81"] SIG81 = 95, - #[doc = "Real-time event 82"] SIG82 = 96, - #[doc = "Real-time event 83"] SIG83 = 97, - #[doc = "Real-time event 84"] SIG84 = 98, - #[doc = "Real-time event 85"] SIG85 = 99, - #[doc = "Real-time event 86"] SIG86 = 100, - #[doc = "Real-time event 87"] SIG87 = 101, - #[doc = "Real-time event 88"] SIG88 = 102, - #[doc = "Real-time event 89"] SIG89 = 103, - #[doc = "Real-time event 90"] SIG90 = 104, - #[doc = "Real-time event 91"] SIG91 = 105, - #[doc = "Real-time event 92"] SIG92 = 106, - #[doc = "Real-time event 93"] SIG93 = 107, - #[doc = "Real-time event 94"] SIG94 = 108, - #[doc = "Real-time event 95"] SIG95 = 109, - #[doc = "Real-time event 96"] SIG96 = 110, - #[doc = "Real-time event 97"] SIG97 = 111, - #[doc = "Real-time event 98"] SIG98 = 112, - #[doc = "Real-time event 99"] SIG99 = 113, - #[doc = "Real-time event 100"] SIG100 = 114, - #[doc = "Real-time event 101"] SIG101 = 115, - #[doc = "Real-time event 102"] SIG102 = 116, - #[doc = "Real-time event 103"] SIG103 = 117, - #[doc = "Real-time event 104"] SIG104 = 118, - #[doc = "Real-time event 105"] SIG105 = 119, - #[doc = "Real-time event 106"] SIG106 = 120, - #[doc = "Real-time event 107"] SIG107 = 121, - #[doc = "Real-time event 108"] SIG108 = 122, - #[doc = "Real-time event 109"] SIG109 = 123, - #[doc = "Real-time event 110"] SIG110 = 124, - #[doc = "Real-time event 111"] SIG111 = 125, - #[doc = "Real-time event 112"] SIG112 = 126, - #[doc = "Real-time event 113"] SIG113 = 127, - #[doc = "Real-time event 114"] SIG114 = 128, - #[doc = "Real-time event 115"] SIG115 = 129, - #[doc = "Real-time event 116"] SIG116 = 130, - #[doc = "Real-time event 117"] SIG117 = 131, - #[doc = "Real-time event 118"] SIG118 = 132, - #[doc = "Real-time event 119"] SIG119 = 133, - #[doc = "Real-time event 120"] SIG120 = 134, - #[doc = "Real-time event 121"] SIG121 = 135, - #[doc = "Real-time event 122"] SIG122 = 136, - #[doc = "Real-time event 123"] SIG123 = 137, - #[doc = "Real-time event 124"] SIG124 = 138, - #[doc = "Real-time event 125"] SIG125 = 139, - #[doc = "Real-time event 126"] SIG126 = 140, - #[doc = "Real-time event 127"] SIG127 = 141, + #[doc = "Real-time event 65"] pub const SIG65: Self = Self(79); + #[doc = "Real-time event 66"] pub const SIG66: Self = Self(80); + #[doc = "Real-time event 67"] pub const SIG67: Self = Self(81); + #[doc = "Real-time event 68"] pub const SIG68: Self = Self(82); + #[doc = "Real-time event 69"] pub const SIG69: Self = Self(83); + #[doc = "Real-time event 70"] pub const SIG70: Self = Self(84); + #[doc = "Real-time event 71"] pub const SIG71: Self = Self(85); + #[doc = "Real-time event 72"] pub const SIG72: Self = Self(86); + #[doc = "Real-time event 73"] pub const SIG73: Self = Self(87); + #[doc = "Real-time event 74"] pub const SIG74: Self = Self(88); + #[doc = "Real-time event 75"] pub const SIG75: Self = Self(89); + #[doc = "Real-time event 76"] pub const SIG76: Self = Self(90); + #[doc = "Real-time event 77"] pub const SIG77: Self = Self(91); + #[doc = "Real-time event 78"] pub const SIG78: Self = Self(92); + #[doc = "Real-time event 79"] pub const SIG79: Self = Self(93); + #[doc = "Real-time event 80"] pub const SIG80: Self = Self(94); + #[doc = "Real-time event 81"] pub const SIG81: Self = Self(95); + #[doc = "Real-time event 82"] pub const SIG82: Self = Self(96); + #[doc = "Real-time event 83"] pub const SIG83: Self = Self(97); + #[doc = "Real-time event 84"] pub const SIG84: Self = Self(98); + #[doc = "Real-time event 85"] pub const SIG85: Self = Self(99); + #[doc = "Real-time event 86"] pub const SIG86: Self = Self(100); + #[doc = "Real-time event 87"] pub const SIG87: Self = Self(101); + #[doc = "Real-time event 88"] pub const SIG88: Self = Self(102); + #[doc = "Real-time event 89"] pub const SIG89: Self = Self(103); + #[doc = "Real-time event 90"] pub const SIG90: Self = Self(104); + #[doc = "Real-time event 91"] pub const SIG91: Self = Self(105); + #[doc = "Real-time event 92"] pub const SIG92: Self = Self(106); + #[doc = "Real-time event 93"] pub const SIG93: Self = Self(107); + #[doc = "Real-time event 94"] pub const SIG94: Self = Self(108); + #[doc = "Real-time event 95"] pub const SIG95: Self = Self(109); + #[doc = "Real-time event 96"] pub const SIG96: Self = Self(110); + #[doc = "Real-time event 97"] pub const SIG97: Self = Self(111); + #[doc = "Real-time event 98"] pub const SIG98: Self = Self(112); + #[doc = "Real-time event 99"] pub const SIG99: Self = Self(113); + #[doc = "Real-time event 100"] pub const SIG100: Self = Self(114); + #[doc = "Real-time event 101"] pub const SIG101: Self = Self(115); + #[doc = "Real-time event 102"] pub const SIG102: Self = Self(116); + #[doc = "Real-time event 103"] pub const SIG103: Self = Self(117); + #[doc = "Real-time event 104"] pub const SIG104: Self = Self(118); + #[doc = "Real-time event 105"] pub const SIG105: Self = Self(119); + #[doc = "Real-time event 106"] pub const SIG106: Self = Self(120); + #[doc = "Real-time event 107"] pub const SIG107: Self = Self(121); + #[doc = "Real-time event 108"] pub const SIG108: Self = Self(122); + #[doc = "Real-time event 109"] pub const SIG109: Self = Self(123); + #[doc = "Real-time event 110"] pub const SIG110: Self = Self(124); + #[doc = "Real-time event 111"] pub const SIG111: Self = Self(125); + #[doc = "Real-time event 112"] pub const SIG112: Self = Self(126); + #[doc = "Real-time event 113"] pub const SIG113: Self = Self(127); + #[doc = "Real-time event 114"] pub const SIG114: Self = Self(128); + #[doc = "Real-time event 115"] pub const SIG115: Self = Self(129); + #[doc = "Real-time event 116"] pub const SIG116: Self = Self(130); + #[doc = "Real-time event 117"] pub const SIG117: Self = Self(131); + #[doc = "Real-time event 118"] pub const SIG118: Self = Self(132); + #[doc = "Real-time event 119"] pub const SIG119: Self = Self(133); + #[doc = "Real-time event 120"] pub const SIG120: Self = Self(134); + #[doc = "Real-time event 121"] pub const SIG121: Self = Self(135); + #[doc = "Real-time event 122"] pub const SIG122: Self = Self(136); + #[doc = "Real-time event 123"] pub const SIG123: Self = Self(137); + #[doc = "Real-time event 124"] pub const SIG124: Self = Self(138); + #[doc = "Real-time event 125"] pub const SIG125: Self = Self(139); + #[doc = "Real-time event 126"] pub const SIG126: Self = Self(140); + #[doc = "Real-time event 127"] pub const SIG127: Self = Self(141); - #[doc = "Information request"] SIGINFO = 142, + #[doc = "Information request"] pub const SIGINFO: Self = Self(142); /* Some signal we don't know about. */ - #[doc = "Unknown signal"] UNKNOWN = 143, + #[doc = "Unknown signal"] pub const UNKNOWN: Self = Self(143); /* Use whatever signal we use when one is not specifically specified (for passing to proceed and so on). */ - #[doc = "Internal error: printing GDB_SIGNAL_DEFAULT"] INTERNAL_DEFAULT = 144, + #[doc = "Internal error: printing GDB_SIGNAL_DEFAULT"] + pub const INTERNAL_DEFAULT: Self = Self(144); /* Mach exceptions. In versions of GDB before 5.2, these were just before GDB_SIGNAL_INFO if you were compiling on a Mach host (and missing otherwise). */ - #[doc = "Could not access memory"] EXC_BAD_ACCESS = 145, - #[doc = "Illegal instruction/operand"] EXC_BAD_INSTRUCTION = 146, - #[doc = "Arithmetic exception"] EXC_ARITHMETIC = 147, - #[doc = "Emulation instruction"] EXC_EMULATION = 148, - #[doc = "Software generated exception"] EXC_SOFTWARE = 149, - #[doc = "Breakpoint"] EXC_BREAKPOINT = 150, + #[doc = "Could not access memory"] pub const EXC_BAD_ACCESS: Self = Self(145); + #[doc = "Illegal instruction/operand"] pub const EXC_BAD_INSTRUCTION: Self = Self(146); + #[doc = "Arithmetic exception"] pub const EXC_ARITHMETIC: Self = Self(147); + #[doc = "Emulation instruction"] pub const EXC_EMULATION: Self = Self(148); + #[doc = "Software generated exception"] pub const EXC_SOFTWARE: Self = Self(149); + #[doc = "Breakpoint"] pub const EXC_BREAKPOINT: Self = Self(150); - #[doc = "librt internal signal"] SIGLIBRT = 151, + #[doc = "librt internal signal"] pub const SIGLIBRT: Self = Self(151); } impl core::fmt::Display for Signal { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { #[rustfmt::skip] - let s = match self { - Signal::SIGZERO => "SIGZERO - Signal 0", - Signal::SIGHUP => "SIGHUP - Hangup", - Signal::SIGINT => "SIGINT - Interrupt", - Signal::SIGQUIT => "SIGQUIT - Quit", - Signal::SIGILL => "SIGILL - Illegal instruction", - Signal::SIGTRAP => "SIGTRAP - Trace/breakpoint trap", - Signal::SIGABRT => "SIGABRT - Aborted", - Signal::SIGEMT => "SIGEMT - Emulation trap", - Signal::SIGFPE => "SIGFPE - Arithmetic exception", - Signal::SIGKILL => "SIGKILL - Killed", - Signal::SIGBUS => "SIGBUS - Bus error", - Signal::SIGSEGV => "SIGSEGV - Segmentation fault", - Signal::SIGSYS => "SIGSYS - Bad system call", - Signal::SIGPIPE => "SIGPIPE - Broken pipe", - Signal::SIGALRM => "SIGALRM - Alarm clock", - Signal::SIGTERM => "SIGTERM - Terminated", - Signal::SIGURG => "SIGURG - Urgent I/O condition", - Signal::SIGSTOP => "SIGSTOP - Stopped (signal)", - Signal::SIGTSTP => "SIGTSTP - Stopped (user)", - Signal::SIGCONT => "SIGCONT - Continued", - Signal::SIGCHLD => "SIGCHLD - Child status changed", - Signal::SIGTTIN => "SIGTTIN - Stopped (tty input)", - Signal::SIGTTOU => "SIGTTOU - Stopped (tty output)", - Signal::SIGIO => "SIGIO - I/O possible", - Signal::SIGXCPU => "SIGXCPU - CPU time limit exceeded", - Signal::SIGXFSZ => "SIGXFSZ - File size limit exceeded", - Signal::SIGVTALRM => "SIGVTALRM - Virtual timer expired", - Signal::SIGPROF => "SIGPROF - Profiling timer expired", - Signal::SIGWINCH => "SIGWINCH - Window size changed", - Signal::SIGLOST => "SIGLOST - Resource lost", - Signal::SIGUSR1 => "SIGUSR1 - User defined signal 1", - Signal::SIGUSR2 => "SIGUSR2 - User defined signal 2", - Signal::SIGPWR => "SIGPWR - Power fail/restart", - Signal::SIGPOLL => "SIGPOLL - Pollable event occurred", - Signal::SIGWIND => "SIGWIND - SIGWIND", - Signal::SIGPHONE => "SIGPHONE - SIGPHONE", - Signal::SIGWAITING => "SIGWAITING - Process's LWPs are blocked", - Signal::SIGLWP => "SIGLWP - Signal LWP", - Signal::SIGDANGER => "SIGDANGER - Swap space dangerously low", - Signal::SIGGRANT => "SIGGRANT - Monitor mode granted", - Signal::SIGRETRACT => "SIGRETRACT - Need to relinquish monitor mode", - Signal::SIGMSG => "SIGMSG - Monitor mode data available", - Signal::SIGSOUND => "SIGSOUND - Sound completed", - Signal::SIGSAK => "SIGSAK - Secure attention", - Signal::SIGPRIO => "SIGPRIO - SIGPRIO", - Signal::SIG33 => "SIG33 - Real-time event 33", - Signal::SIG34 => "SIG34 - Real-time event 34", - Signal::SIG35 => "SIG35 - Real-time event 35", - Signal::SIG36 => "SIG36 - Real-time event 36", - Signal::SIG37 => "SIG37 - Real-time event 37", - Signal::SIG38 => "SIG38 - Real-time event 38", - Signal::SIG39 => "SIG39 - Real-time event 39", - Signal::SIG40 => "SIG40 - Real-time event 40", - Signal::SIG41 => "SIG41 - Real-time event 41", - Signal::SIG42 => "SIG42 - Real-time event 42", - Signal::SIG43 => "SIG43 - Real-time event 43", - Signal::SIG44 => "SIG44 - Real-time event 44", - Signal::SIG45 => "SIG45 - Real-time event 45", - Signal::SIG46 => "SIG46 - Real-time event 46", - Signal::SIG47 => "SIG47 - Real-time event 47", - Signal::SIG48 => "SIG48 - Real-time event 48", - Signal::SIG49 => "SIG49 - Real-time event 49", - Signal::SIG50 => "SIG50 - Real-time event 50", - Signal::SIG51 => "SIG51 - Real-time event 51", - Signal::SIG52 => "SIG52 - Real-time event 52", - Signal::SIG53 => "SIG53 - Real-time event 53", - Signal::SIG54 => "SIG54 - Real-time event 54", - Signal::SIG55 => "SIG55 - Real-time event 55", - Signal::SIG56 => "SIG56 - Real-time event 56", - Signal::SIG57 => "SIG57 - Real-time event 57", - Signal::SIG58 => "SIG58 - Real-time event 58", - Signal::SIG59 => "SIG59 - Real-time event 59", - Signal::SIG60 => "SIG60 - Real-time event 60", - Signal::SIG61 => "SIG61 - Real-time event 61", - Signal::SIG62 => "SIG62 - Real-time event 62", - Signal::SIG63 => "SIG63 - Real-time event 63", - Signal::SIGCANCEL => "SIGCANCEL - LWP internal signal", - Signal::SIG32 => "SIG32 - Real-time event 32", - Signal::SIG64 => "SIG64 - Real-time event 64", - Signal::SIG65 => "SIG65 - Real-time event 65", - Signal::SIG66 => "SIG66 - Real-time event 66", - Signal::SIG67 => "SIG67 - Real-time event 67", - Signal::SIG68 => "SIG68 - Real-time event 68", - Signal::SIG69 => "SIG69 - Real-time event 69", - Signal::SIG70 => "SIG70 - Real-time event 70", - Signal::SIG71 => "SIG71 - Real-time event 71", - Signal::SIG72 => "SIG72 - Real-time event 72", - Signal::SIG73 => "SIG73 - Real-time event 73", - Signal::SIG74 => "SIG74 - Real-time event 74", - Signal::SIG75 => "SIG75 - Real-time event 75", - Signal::SIG76 => "SIG76 - Real-time event 76", - Signal::SIG77 => "SIG77 - Real-time event 77", - Signal::SIG78 => "SIG78 - Real-time event 78", - Signal::SIG79 => "SIG79 - Real-time event 79", - Signal::SIG80 => "SIG80 - Real-time event 80", - Signal::SIG81 => "SIG81 - Real-time event 81", - Signal::SIG82 => "SIG82 - Real-time event 82", - Signal::SIG83 => "SIG83 - Real-time event 83", - Signal::SIG84 => "SIG84 - Real-time event 84", - Signal::SIG85 => "SIG85 - Real-time event 85", - Signal::SIG86 => "SIG86 - Real-time event 86", - Signal::SIG87 => "SIG87 - Real-time event 87", - Signal::SIG88 => "SIG88 - Real-time event 88", - Signal::SIG89 => "SIG89 - Real-time event 89", - Signal::SIG90 => "SIG90 - Real-time event 90", - Signal::SIG91 => "SIG91 - Real-time event 91", - Signal::SIG92 => "SIG92 - Real-time event 92", - Signal::SIG93 => "SIG93 - Real-time event 93", - Signal::SIG94 => "SIG94 - Real-time event 94", - Signal::SIG95 => "SIG95 - Real-time event 95", - Signal::SIG96 => "SIG96 - Real-time event 96", - Signal::SIG97 => "SIG97 - Real-time event 97", - Signal::SIG98 => "SIG98 - Real-time event 98", - Signal::SIG99 => "SIG99 - Real-time event 99", - Signal::SIG100 => "SIG100 - Real-time event 100", - Signal::SIG101 => "SIG101 - Real-time event 101", - Signal::SIG102 => "SIG102 - Real-time event 102", - Signal::SIG103 => "SIG103 - Real-time event 103", - Signal::SIG104 => "SIG104 - Real-time event 104", - Signal::SIG105 => "SIG105 - Real-time event 105", - Signal::SIG106 => "SIG106 - Real-time event 106", - Signal::SIG107 => "SIG107 - Real-time event 107", - Signal::SIG108 => "SIG108 - Real-time event 108", - Signal::SIG109 => "SIG109 - Real-time event 109", - Signal::SIG110 => "SIG110 - Real-time event 110", - Signal::SIG111 => "SIG111 - Real-time event 111", - Signal::SIG112 => "SIG112 - Real-time event 112", - Signal::SIG113 => "SIG113 - Real-time event 113", - Signal::SIG114 => "SIG114 - Real-time event 114", - Signal::SIG115 => "SIG115 - Real-time event 115", - Signal::SIG116 => "SIG116 - Real-time event 116", - Signal::SIG117 => "SIG117 - Real-time event 117", - Signal::SIG118 => "SIG118 - Real-time event 118", - Signal::SIG119 => "SIG119 - Real-time event 119", - Signal::SIG120 => "SIG120 - Real-time event 120", - Signal::SIG121 => "SIG121 - Real-time event 121", - Signal::SIG122 => "SIG122 - Real-time event 122", - Signal::SIG123 => "SIG123 - Real-time event 123", - Signal::SIG124 => "SIG124 - Real-time event 124", - Signal::SIG125 => "SIG125 - Real-time event 125", - Signal::SIG126 => "SIG126 - Real-time event 126", - Signal::SIG127 => "SIG127 - Real-time event 127", - Signal::SIGINFO => "SIGINFO - Information request", - Signal::UNKNOWN => "UNKNOWN - Unknown signal", - Signal::INTERNAL_DEFAULT => "INTERNAL_DEFAULT - Internal error: printing GDB_SIGNAL_DEFAULT", - Signal::EXC_BAD_ACCESS => "EXC_BAD_ACCESS - Could not access memory", + let s = match *self { + Signal::SIGZERO => "SIGZERO - Signal 0", + Signal::SIGHUP => "SIGHUP - Hangup", + Signal::SIGINT => "SIGINT - Interrupt", + Signal::SIGQUIT => "SIGQUIT - Quit", + Signal::SIGILL => "SIGILL - Illegal instruction", + Signal::SIGTRAP => "SIGTRAP - Trace/breakpoint trap", + Signal::SIGABRT => "SIGABRT - Aborted", + Signal::SIGEMT => "SIGEMT - Emulation trap", + Signal::SIGFPE => "SIGFPE - Arithmetic exception", + Signal::SIGKILL => "SIGKILL - Killed", + Signal::SIGBUS => "SIGBUS - Bus error", + Signal::SIGSEGV => "SIGSEGV - Segmentation fault", + Signal::SIGSYS => "SIGSYS - Bad system call", + Signal::SIGPIPE => "SIGPIPE - Broken pipe", + Signal::SIGALRM => "SIGALRM - Alarm clock", + Signal::SIGTERM => "SIGTERM - Terminated", + Signal::SIGURG => "SIGURG - Urgent I/O condition", + Signal::SIGSTOP => "SIGSTOP - Stopped (signal)", + Signal::SIGTSTP => "SIGTSTP - Stopped (user)", + Signal::SIGCONT => "SIGCONT - Continued", + Signal::SIGCHLD => "SIGCHLD - Child status changed", + Signal::SIGTTIN => "SIGTTIN - Stopped (tty input)", + Signal::SIGTTOU => "SIGTTOU - Stopped (tty output)", + Signal::SIGIO => "SIGIO - I/O possible", + Signal::SIGXCPU => "SIGXCPU - CPU time limit exceeded", + Signal::SIGXFSZ => "SIGXFSZ - File size limit exceeded", + Signal::SIGVTALRM => "SIGVTALRM - Virtual timer expired", + Signal::SIGPROF => "SIGPROF - Profiling timer expired", + Signal::SIGWINCH => "SIGWINCH - Window size changed", + Signal::SIGLOST => "SIGLOST - Resource lost", + Signal::SIGUSR1 => "SIGUSR1 - User defined signal 1", + Signal::SIGUSR2 => "SIGUSR2 - User defined signal 2", + Signal::SIGPWR => "SIGPWR - Power fail/restart", + Signal::SIGPOLL => "SIGPOLL - Pollable event occurred", + Signal::SIGWIND => "SIGWIND - SIGWIND", + Signal::SIGPHONE => "SIGPHONE - SIGPHONE", + Signal::SIGWAITING => "SIGWAITING - Process's LWPs are blocked", + Signal::SIGLWP => "SIGLWP - Signal LWP", + Signal::SIGDANGER => "SIGDANGER - Swap space dangerously low", + Signal::SIGGRANT => "SIGGRANT - Monitor mode granted", + Signal::SIGRETRACT => "SIGRETRACT - Need to relinquish monitor mode", + Signal::SIGMSG => "SIGMSG - Monitor mode data available", + Signal::SIGSOUND => "SIGSOUND - Sound completed", + Signal::SIGSAK => "SIGSAK - Secure attention", + Signal::SIGPRIO => "SIGPRIO - SIGPRIO", + Signal::SIG33 => "SIG33 - Real-time event 33", + Signal::SIG34 => "SIG34 - Real-time event 34", + Signal::SIG35 => "SIG35 - Real-time event 35", + Signal::SIG36 => "SIG36 - Real-time event 36", + Signal::SIG37 => "SIG37 - Real-time event 37", + Signal::SIG38 => "SIG38 - Real-time event 38", + Signal::SIG39 => "SIG39 - Real-time event 39", + Signal::SIG40 => "SIG40 - Real-time event 40", + Signal::SIG41 => "SIG41 - Real-time event 41", + Signal::SIG42 => "SIG42 - Real-time event 42", + Signal::SIG43 => "SIG43 - Real-time event 43", + Signal::SIG44 => "SIG44 - Real-time event 44", + Signal::SIG45 => "SIG45 - Real-time event 45", + Signal::SIG46 => "SIG46 - Real-time event 46", + Signal::SIG47 => "SIG47 - Real-time event 47", + Signal::SIG48 => "SIG48 - Real-time event 48", + Signal::SIG49 => "SIG49 - Real-time event 49", + Signal::SIG50 => "SIG50 - Real-time event 50", + Signal::SIG51 => "SIG51 - Real-time event 51", + Signal::SIG52 => "SIG52 - Real-time event 52", + Signal::SIG53 => "SIG53 - Real-time event 53", + Signal::SIG54 => "SIG54 - Real-time event 54", + Signal::SIG55 => "SIG55 - Real-time event 55", + Signal::SIG56 => "SIG56 - Real-time event 56", + Signal::SIG57 => "SIG57 - Real-time event 57", + Signal::SIG58 => "SIG58 - Real-time event 58", + Signal::SIG59 => "SIG59 - Real-time event 59", + Signal::SIG60 => "SIG60 - Real-time event 60", + Signal::SIG61 => "SIG61 - Real-time event 61", + Signal::SIG62 => "SIG62 - Real-time event 62", + Signal::SIG63 => "SIG63 - Real-time event 63", + Signal::SIGCANCEL => "SIGCANCEL - LWP internal signal", + Signal::SIG32 => "SIG32 - Real-time event 32", + Signal::SIG64 => "SIG64 - Real-time event 64", + Signal::SIG65 => "SIG65 - Real-time event 65", + Signal::SIG66 => "SIG66 - Real-time event 66", + Signal::SIG67 => "SIG67 - Real-time event 67", + Signal::SIG68 => "SIG68 - Real-time event 68", + Signal::SIG69 => "SIG69 - Real-time event 69", + Signal::SIG70 => "SIG70 - Real-time event 70", + Signal::SIG71 => "SIG71 - Real-time event 71", + Signal::SIG72 => "SIG72 - Real-time event 72", + Signal::SIG73 => "SIG73 - Real-time event 73", + Signal::SIG74 => "SIG74 - Real-time event 74", + Signal::SIG75 => "SIG75 - Real-time event 75", + Signal::SIG76 => "SIG76 - Real-time event 76", + Signal::SIG77 => "SIG77 - Real-time event 77", + Signal::SIG78 => "SIG78 - Real-time event 78", + Signal::SIG79 => "SIG79 - Real-time event 79", + Signal::SIG80 => "SIG80 - Real-time event 80", + Signal::SIG81 => "SIG81 - Real-time event 81", + Signal::SIG82 => "SIG82 - Real-time event 82", + Signal::SIG83 => "SIG83 - Real-time event 83", + Signal::SIG84 => "SIG84 - Real-time event 84", + Signal::SIG85 => "SIG85 - Real-time event 85", + Signal::SIG86 => "SIG86 - Real-time event 86", + Signal::SIG87 => "SIG87 - Real-time event 87", + Signal::SIG88 => "SIG88 - Real-time event 88", + Signal::SIG89 => "SIG89 - Real-time event 89", + Signal::SIG90 => "SIG90 - Real-time event 90", + Signal::SIG91 => "SIG91 - Real-time event 91", + Signal::SIG92 => "SIG92 - Real-time event 92", + Signal::SIG93 => "SIG93 - Real-time event 93", + Signal::SIG94 => "SIG94 - Real-time event 94", + Signal::SIG95 => "SIG95 - Real-time event 95", + Signal::SIG96 => "SIG96 - Real-time event 96", + Signal::SIG97 => "SIG97 - Real-time event 97", + Signal::SIG98 => "SIG98 - Real-time event 98", + Signal::SIG99 => "SIG99 - Real-time event 99", + Signal::SIG100 => "SIG100 - Real-time event 100", + Signal::SIG101 => "SIG101 - Real-time event 101", + Signal::SIG102 => "SIG102 - Real-time event 102", + Signal::SIG103 => "SIG103 - Real-time event 103", + Signal::SIG104 => "SIG104 - Real-time event 104", + Signal::SIG105 => "SIG105 - Real-time event 105", + Signal::SIG106 => "SIG106 - Real-time event 106", + Signal::SIG107 => "SIG107 - Real-time event 107", + Signal::SIG108 => "SIG108 - Real-time event 108", + Signal::SIG109 => "SIG109 - Real-time event 109", + Signal::SIG110 => "SIG110 - Real-time event 110", + Signal::SIG111 => "SIG111 - Real-time event 111", + Signal::SIG112 => "SIG112 - Real-time event 112", + Signal::SIG113 => "SIG113 - Real-time event 113", + Signal::SIG114 => "SIG114 - Real-time event 114", + Signal::SIG115 => "SIG115 - Real-time event 115", + Signal::SIG116 => "SIG116 - Real-time event 116", + Signal::SIG117 => "SIG117 - Real-time event 117", + Signal::SIG118 => "SIG118 - Real-time event 118", + Signal::SIG119 => "SIG119 - Real-time event 119", + Signal::SIG120 => "SIG120 - Real-time event 120", + Signal::SIG121 => "SIG121 - Real-time event 121", + Signal::SIG122 => "SIG122 - Real-time event 122", + Signal::SIG123 => "SIG123 - Real-time event 123", + Signal::SIG124 => "SIG124 - Real-time event 124", + Signal::SIG125 => "SIG125 - Real-time event 125", + Signal::SIG126 => "SIG126 - Real-time event 126", + Signal::SIG127 => "SIG127 - Real-time event 127", + Signal::SIGINFO => "SIGINFO - Information request", + Signal::UNKNOWN => "UNKNOWN - Unknown signal", + Signal::INTERNAL_DEFAULT => "INTERNAL_DEFAULT - Internal error: printing GDB_SIGNAL_DEFAULT", + Signal::EXC_BAD_ACCESS => "EXC_BAD_ACCESS - Could not access memory", Signal::EXC_BAD_INSTRUCTION => "EXC_BAD_INSTRUCTION - Illegal instruction/operand", - Signal::EXC_ARITHMETIC => "EXC_ARITHMETIC - Arithmetic exception", - Signal::EXC_EMULATION => "EXC_EMULATION - Emulation instruction", - Signal::EXC_SOFTWARE => "EXC_SOFTWARE - Software generated exception", - Signal::EXC_BREAKPOINT => "EXC_BREAKPOINT - Breakpoint", - Signal::SIGLIBRT => "SIGLIBRT - librt internal signal", + Signal::EXC_ARITHMETIC => "EXC_ARITHMETIC - Arithmetic exception", + Signal::EXC_EMULATION => "EXC_EMULATION - Emulation instruction", + Signal::EXC_SOFTWARE => "EXC_SOFTWARE - Software generated exception", + Signal::EXC_BREAKPOINT => "EXC_BREAKPOINT - Breakpoint", + Signal::SIGLIBRT => "SIGLIBRT - librt internal signal", + + _ => "custom signal (not defined in GDB's signals.def file)" }; write!(f, "{}", s) } } - -impl Signal { - #[cfg(not(feature = "paranoid_unsafe"))] - pub(crate) fn from_protocol_u8(val: u8) -> Signal { - if val <= 151 { - // SAFETY: Signal is repr(u8), and `val` was confirmed to fall in valid range - unsafe { core::mem::transmute(val) } - } else { - Signal::UNKNOWN - } - } - - #[cfg(feature = "paranoid_unsafe")] - pub(crate) fn from_protocol_u8(val: u8) -> Signal { - match val { - 0 => Signal::SIGZERO, - 1 => Signal::SIGHUP, - 2 => Signal::SIGINT, - 3 => Signal::SIGQUIT, - 4 => Signal::SIGILL, - 5 => Signal::SIGTRAP, - 6 => Signal::SIGABRT, - 7 => Signal::SIGEMT, - 8 => Signal::SIGFPE, - 9 => Signal::SIGKILL, - 10 => Signal::SIGBUS, - 11 => Signal::SIGSEGV, - 12 => Signal::SIGSYS, - 13 => Signal::SIGPIPE, - 14 => Signal::SIGALRM, - 15 => Signal::SIGTERM, - 16 => Signal::SIGURG, - 17 => Signal::SIGSTOP, - 18 => Signal::SIGTSTP, - 19 => Signal::SIGCONT, - 20 => Signal::SIGCHLD, - 21 => Signal::SIGTTIN, - 22 => Signal::SIGTTOU, - 23 => Signal::SIGIO, - 24 => Signal::SIGXCPU, - 25 => Signal::SIGXFSZ, - 26 => Signal::SIGVTALRM, - 27 => Signal::SIGPROF, - 28 => Signal::SIGWINCH, - 29 => Signal::SIGLOST, - 30 => Signal::SIGUSR1, - 31 => Signal::SIGUSR2, - 32 => Signal::SIGPWR, - 33 => Signal::SIGPOLL, - 34 => Signal::SIGWIND, - 35 => Signal::SIGPHONE, - 36 => Signal::SIGWAITING, - 37 => Signal::SIGLWP, - 38 => Signal::SIGDANGER, - 39 => Signal::SIGGRANT, - 40 => Signal::SIGRETRACT, - 41 => Signal::SIGMSG, - 42 => Signal::SIGSOUND, - 43 => Signal::SIGSAK, - 44 => Signal::SIGPRIO, - 45 => Signal::SIG33, - 46 => Signal::SIG34, - 47 => Signal::SIG35, - 48 => Signal::SIG36, - 49 => Signal::SIG37, - 50 => Signal::SIG38, - 51 => Signal::SIG39, - 52 => Signal::SIG40, - 53 => Signal::SIG41, - 54 => Signal::SIG42, - 55 => Signal::SIG43, - 56 => Signal::SIG44, - 57 => Signal::SIG45, - 58 => Signal::SIG46, - 59 => Signal::SIG47, - 60 => Signal::SIG48, - 61 => Signal::SIG49, - 62 => Signal::SIG50, - 63 => Signal::SIG51, - 64 => Signal::SIG52, - 65 => Signal::SIG53, - 66 => Signal::SIG54, - 67 => Signal::SIG55, - 68 => Signal::SIG56, - 69 => Signal::SIG57, - 70 => Signal::SIG58, - 71 => Signal::SIG59, - 72 => Signal::SIG60, - 73 => Signal::SIG61, - 74 => Signal::SIG62, - 75 => Signal::SIG63, - 76 => Signal::SIGCANCEL, - 77 => Signal::SIG32, - 78 => Signal::SIG64, - 79 => Signal::SIG65, - 80 => Signal::SIG66, - 81 => Signal::SIG67, - 82 => Signal::SIG68, - 83 => Signal::SIG69, - 84 => Signal::SIG70, - 85 => Signal::SIG71, - 86 => Signal::SIG72, - 87 => Signal::SIG73, - 88 => Signal::SIG74, - 89 => Signal::SIG75, - 90 => Signal::SIG76, - 91 => Signal::SIG77, - 92 => Signal::SIG78, - 93 => Signal::SIG79, - 94 => Signal::SIG80, - 95 => Signal::SIG81, - 96 => Signal::SIG82, - 97 => Signal::SIG83, - 98 => Signal::SIG84, - 99 => Signal::SIG85, - 100 => Signal::SIG86, - 101 => Signal::SIG87, - 102 => Signal::SIG88, - 103 => Signal::SIG89, - 104 => Signal::SIG90, - 105 => Signal::SIG91, - 106 => Signal::SIG92, - 107 => Signal::SIG93, - 108 => Signal::SIG94, - 109 => Signal::SIG95, - 110 => Signal::SIG96, - 111 => Signal::SIG97, - 112 => Signal::SIG98, - 113 => Signal::SIG99, - 114 => Signal::SIG100, - 115 => Signal::SIG101, - 116 => Signal::SIG102, - 117 => Signal::SIG103, - 118 => Signal::SIG104, - 119 => Signal::SIG105, - 120 => Signal::SIG106, - 121 => Signal::SIG107, - 122 => Signal::SIG108, - 123 => Signal::SIG109, - 124 => Signal::SIG110, - 125 => Signal::SIG111, - 126 => Signal::SIG112, - 127 => Signal::SIG113, - 128 => Signal::SIG114, - 129 => Signal::SIG115, - 130 => Signal::SIG116, - 131 => Signal::SIG117, - 132 => Signal::SIG118, - 133 => Signal::SIG119, - 134 => Signal::SIG120, - 135 => Signal::SIG121, - 136 => Signal::SIG122, - 137 => Signal::SIG123, - 138 => Signal::SIG124, - 139 => Signal::SIG125, - 140 => Signal::SIG126, - 141 => Signal::SIG127, - 142 => Signal::SIGINFO, - 143 => Signal::UNKNOWN, - 144 => Signal::INTERNAL_DEFAULT, - 145 => Signal::EXC_BAD_ACCESS, - 146 => Signal::EXC_BAD_INSTRUCTION, - 147 => Signal::EXC_ARITHMETIC, - 148 => Signal::EXC_EMULATION, - 149 => Signal::EXC_SOFTWARE, - 150 => Signal::EXC_BREAKPOINT, - 151 => Signal::SIGLIBRT, - - _ => Signal::UNKNOWN, - } - } -} diff --git a/src/conn/impls/boxed.rs b/src/conn/impls/boxed.rs index 2de92c0..100d83e 100644 --- a/src/conn/impls/boxed.rs +++ b/src/conn/impls/boxed.rs @@ -1,6 +1,5 @@ use crate::conn::Connection; use crate::conn::ConnectionExt; - use alloc::boxed::Box; impl Connection for Box> { diff --git a/src/conn/impls/tcpstream.rs b/src/conn/impls/tcpstream.rs index 969b7ff..512a15d 100644 --- a/src/conn/impls/tcpstream.rs +++ b/src/conn/impls/tcpstream.rs @@ -1,7 +1,6 @@ -use std::net::TcpStream; - use crate::conn::Connection; use crate::conn::ConnectionExt; +use std::net::TcpStream; impl Connection for TcpStream { type Error = std::io::Error; diff --git a/src/conn/impls/unixstream.rs b/src/conn/impls/unixstream.rs index a810021..575cc4e 100644 --- a/src/conn/impls/unixstream.rs +++ b/src/conn/impls/unixstream.rs @@ -1,8 +1,7 @@ -use std::io; -use std::os::unix::net::UnixStream; - use crate::conn::Connection; use crate::conn::ConnectionExt; +use std::io; +use std::os::unix::net::UnixStream; // TODO: Remove PeekExt once rust-lang/rust#73761 is stabilized trait PeekExt { diff --git a/src/lib.rs b/src/lib.rs index d71756b..3d99165 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ -//! An ergonomic and easy-to-integrate implementation of the -//! [GDB Remote Serial Protocol](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html#Remote-Protocol) -//! in Rust, with full `#![no_std]` support. +//! An ergonomic, featureful, and easy-to-integrate implementation of the [GDB +//! Remote Serial Protocol](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html#Remote-Protocol) +//! in Rust, with no-compromises `#![no_std]` support. //! //! ## Feature flags //! @@ -86,8 +86,8 @@ //! //! ### The `Target` Trait //! -//! The [`Target`](target::Target) trait describes how to control and modify -//! a system's execution state during a GDB debugging session, and serves as the +//! The [`Target`](target::Target) trait describes how to control and modify a +//! system's execution state during a GDB debugging session, and serves as the //! primary bridge between `gdbstub`'s generic GDB protocol implementation and a //! specific target's project/platform-specific code. //! @@ -169,7 +169,7 @@ //! # //! use gdbstub::common::Signal; //! use gdbstub::conn::{Connection, ConnectionExt}; // note the use of `ConnectionExt` -//! use gdbstub::stub::{run_blocking, DisconnectReason, GdbStub, GdbStubError}; +//! use gdbstub::stub::{run_blocking, DisconnectReason, GdbStub}; //! use gdbstub::stub::SingleThreadStopReason; //! use gdbstub::target::Target; //! @@ -251,11 +251,18 @@ //! } //! DisconnectReason::Kill => println!("GDB sent a kill command"), //! }, -//! Err(GdbStubError::TargetError(e)) => { -//! println!("target encountered a fatal error: {}", e) -//! } //! Err(e) => { -//! println!("gdbstub encountered a fatal error: {}", e) +//! if e.is_target_error() { +//! println!( +//! "target encountered a fatal error: {}", +//! e.into_target_error().unwrap() +//! ) +//! } else if e.is_connection_error() { +//! let (e, kind) = e.into_connection_error().unwrap(); +//! println!("connection error: {:?} - {}", kind, e,) +//! } else { +//! println!("gdbstub encountered a fatal error: {}", e) +//! } //! } //! } //! } @@ -304,6 +311,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "paranoid_unsafe", forbid(unsafe_code))] +#![warn(missing_docs)] #[cfg(feature = "alloc")] extern crate alloc; @@ -341,7 +349,8 @@ macro_rules! unwrap { /// (Internal) The fake Tid that's used when running in single-threaded mode. const SINGLE_THREAD_TID: common::Tid = unwrap!(common::Tid::new(1)); -/// (Internal) The fake Pid reported to GDB when running in multi-threaded mode. +/// (Internal) The fake Pid reported to GDB when the target hasn't opted into +/// reporting a custom Pid itself. const FAKE_PID: common::Pid = unwrap!(common::Pid::new(1)); pub(crate) mod is_valid_tid { diff --git a/src/protocol/commands.rs b/src/protocol/commands.rs index c42dccd..1229899 100644 --- a/src/protocol/commands.rs +++ b/src/protocol/commands.rs @@ -1,17 +1,17 @@ -use paste::paste; - use crate::protocol::packet::PacketBuf; use crate::target::Target; +use paste::paste; /// Common imports used by >50% of all packet parsers. /// /// Do not clutter this prelude with types only used by a few packets. -pub(self) mod prelude { - pub use core::convert::{TryFrom, TryInto}; - +pub mod prelude { pub use crate::protocol::commands::ParseCommand; - pub use crate::protocol::common::hex::{decode_hex, decode_hex_buf}; + pub use crate::protocol::common::hex::decode_hex; + pub use crate::protocol::common::hex::decode_hex_buf; pub use crate::protocol::packet::PacketBuf; + pub use core::convert::TryFrom; + pub use core::convert::TryInto; } pub trait ParseCommand<'a>: Sized { @@ -91,6 +91,7 @@ macro_rules! commands { fn support_single_register_access(&mut self) -> Option<()>; fn support_reverse_step(&mut self) -> Option<()>; fn support_reverse_cont(&mut self) -> Option<()>; + fn support_no_ack_mode(&mut self) -> Option<()>; fn support_x_upcase_packet(&mut self) -> Option<()>; fn support_thread_extra_info(&mut self) -> Option<()>; } @@ -114,7 +115,7 @@ macro_rules! commands { fn support_lldb_register_info(&mut self) -> Option<()> { use crate::arch::Arch; - if self.use_lldb_register_info() + if self.use_lldb_register_info() && (T::Arch::lldb_register_info(usize::max_value()).is_some() || self.support_lldb_register_info_override().is_some()) { @@ -122,7 +123,7 @@ macro_rules! commands { } else { None } - } + } fn support_resume(&mut self) -> Option<()> { self.base_ops().resume_ops().map(drop) @@ -160,6 +161,14 @@ macro_rules! commands { } } + fn support_no_ack_mode(&mut self) -> Option<()> { + if self.use_no_ack_mode() { + Some(()) + } else { + None + } + } + fn support_thread_extra_info(&mut self) -> Option<()> { use crate::target::ext::base::BaseOps; match self.base_ops() { @@ -228,7 +237,6 @@ commands! { "M" => _m_upcase::M<'a>, "qAttached" => _qAttached::qAttached, "qfThreadInfo" => _qfThreadInfo::qfThreadInfo, - "QStartNoAckMode" => _QStartNoAckMode::QStartNoAckMode, "qsThreadInfo" => _qsThreadInfo::qsThreadInfo, "qSupported" => _qSupported::qSupported<'a>, "T" => _t_upcase::T, @@ -249,6 +257,10 @@ commands! { "X" => _x_upcase::X<'a>, } + no_ack_mode { + "QStartNoAckMode" => _QStartNoAckMode::QStartNoAckMode, + } + single_register_access use 'a { "p" => _p::p<'a>, "P" => _p_upcase::P<'a>, @@ -256,6 +268,7 @@ commands! { extended_mode use 'a { "!" => exclamation_mark::ExclamationMark, + "qC" => _qC::qC, "QDisableRandomization" => _QDisableRandomization::QDisableRandomization, "QEnvironmentHexEncoded" => _QEnvironmentHexEncoded::QEnvironmentHexEncoded<'a>, "QEnvironmentReset" => _QEnvironmentReset::QEnvironmentReset, diff --git a/src/protocol/commands/_QCatchSyscalls.rs b/src/protocol/commands/_QCatchSyscalls.rs index 26a27a1..0991117 100644 --- a/src/protocol/commands/_QCatchSyscalls.rs +++ b/src/protocol/commands/_QCatchSyscalls.rs @@ -1,5 +1,4 @@ use super::prelude::*; - use crate::protocol::common::lists::ArgListHex; #[derive(Debug)] diff --git a/src/protocol/commands/_d_upcase.rs b/src/protocol/commands/_d_upcase.rs index c532532..f33ae62 100644 --- a/src/protocol/commands/_d_upcase.rs +++ b/src/protocol/commands/_d_upcase.rs @@ -1,5 +1,4 @@ use super::prelude::*; - use crate::common::Pid; #[derive(Debug)] diff --git a/src/protocol/commands/_h_upcase.rs b/src/protocol/commands/_h_upcase.rs index 3e23ced..f7bb9ed 100644 --- a/src/protocol/commands/_h_upcase.rs +++ b/src/protocol/commands/_h_upcase.rs @@ -1,5 +1,4 @@ use super::prelude::*; - use crate::protocol::common::thread_id::ThreadId; #[derive(Debug)] diff --git a/src/protocol/commands/_qAttached.rs b/src/protocol/commands/_qAttached.rs index 3655559..6323e87 100644 --- a/src/protocol/commands/_qAttached.rs +++ b/src/protocol/commands/_qAttached.rs @@ -1,5 +1,4 @@ use super::prelude::*; - use crate::common::Pid; #[derive(Debug)] diff --git a/src/protocol/commands/_qC.rs b/src/protocol/commands/_qC.rs new file mode 100644 index 0000000..decba12 --- /dev/null +++ b/src/protocol/commands/_qC.rs @@ -0,0 +1,14 @@ +use super::prelude::*; + +#[derive(Debug)] +pub struct qC; + +impl<'a> ParseCommand<'a> for qC { + #[inline(always)] + fn from_packet(buf: PacketBuf<'a>) -> Option { + if !buf.into_body().is_empty() { + return None; + } + Some(qC) + } +} diff --git a/src/protocol/commands/_qThreadExtraInfo.rs b/src/protocol/commands/_qThreadExtraInfo.rs index 9fe6200..d89fbcd 100644 --- a/src/protocol/commands/_qThreadExtraInfo.rs +++ b/src/protocol/commands/_qThreadExtraInfo.rs @@ -1,5 +1,4 @@ use super::prelude::*; - use crate::protocol::common::thread_id::ThreadId; use crate::protocol::ConcreteThreadId; diff --git a/src/protocol/commands/_qXfer_auxv_read.rs b/src/protocol/commands/_qXfer_auxv_read.rs index 15a8c17..11a2c9b 100644 --- a/src/protocol/commands/_qXfer_auxv_read.rs +++ b/src/protocol/commands/_qXfer_auxv_read.rs @@ -1,6 +1,6 @@ // use super::prelude::*; // unused - -use crate::protocol::common::qxfer::{ParseAnnex, QXferReadBase}; +use crate::protocol::common::qxfer::ParseAnnex; +use crate::protocol::common::qxfer::QXferReadBase; pub type qXferAuxvRead<'a> = QXferReadBase<'a, AuxvAnnex>; diff --git a/src/protocol/commands/_qXfer_exec_file.rs b/src/protocol/commands/_qXfer_exec_file.rs index 8280bda..efcb8bc 100644 --- a/src/protocol/commands/_qXfer_exec_file.rs +++ b/src/protocol/commands/_qXfer_exec_file.rs @@ -1,7 +1,7 @@ use super::prelude::*; - use crate::common::Pid; -use crate::protocol::common::qxfer::{ParseAnnex, QXferReadBase}; +use crate::protocol::common::qxfer::ParseAnnex; +use crate::protocol::common::qxfer::QXferReadBase; pub type qXferExecFileRead<'a> = QXferReadBase<'a, ExecFileAnnex>; diff --git a/src/protocol/commands/_qXfer_features_read.rs b/src/protocol/commands/_qXfer_features_read.rs index 73f96e5..6da9a72 100644 --- a/src/protocol/commands/_qXfer_features_read.rs +++ b/src/protocol/commands/_qXfer_features_read.rs @@ -1,6 +1,6 @@ // use super::prelude::*; // unused - -use crate::protocol::common::qxfer::{ParseAnnex, QXferReadBase}; +use crate::protocol::common::qxfer::ParseAnnex; +use crate::protocol::common::qxfer::QXferReadBase; pub type qXferFeaturesRead<'a> = QXferReadBase<'a, FeaturesAnnex<'a>>; diff --git a/src/protocol/commands/_qXfer_memory_map.rs b/src/protocol/commands/_qXfer_memory_map.rs index 169d1b4..6496cdc 100644 --- a/src/protocol/commands/_qXfer_memory_map.rs +++ b/src/protocol/commands/_qXfer_memory_map.rs @@ -1,6 +1,6 @@ // use super::prelude::*; // unused - -use crate::protocol::common::qxfer::{ParseAnnex, QXferReadBase}; +use crate::protocol::common::qxfer::ParseAnnex; +use crate::protocol::common::qxfer::QXferReadBase; pub type qXferMemoryMapRead<'a> = QXferReadBase<'a, MemoryMapAnnex>; diff --git a/src/protocol/commands/_t_upcase.rs b/src/protocol/commands/_t_upcase.rs index e7c4636..e638330 100644 --- a/src/protocol/commands/_t_upcase.rs +++ b/src/protocol/commands/_t_upcase.rs @@ -1,5 +1,4 @@ use super::prelude::*; - use crate::protocol::common::thread_id::ThreadId; #[derive(Debug)] diff --git a/src/protocol/commands/_vAttach.rs b/src/protocol/commands/_vAttach.rs index 252db54..cfe026f 100644 --- a/src/protocol/commands/_vAttach.rs +++ b/src/protocol/commands/_vAttach.rs @@ -1,5 +1,4 @@ use super::prelude::*; - use crate::common::Pid; #[derive(Debug)] diff --git a/src/protocol/commands/_vCont.rs b/src/protocol/commands/_vCont.rs index b90c9e6..437fa4c 100644 --- a/src/protocol/commands/_vCont.rs +++ b/src/protocol/commands/_vCont.rs @@ -1,8 +1,8 @@ use super::prelude::*; - use crate::common::Signal; use crate::protocol::common::hex::HexString; -use crate::protocol::common::thread_id::{SpecificThreadId, ThreadId}; +use crate::protocol::common::thread_id::SpecificThreadId; +use crate::protocol::common::thread_id::ThreadId; // TODO?: instead of lazily parsing data, parse the strings into a compressed // binary representations that can be stuffed back into the packet buffer and @@ -112,8 +112,8 @@ impl<'a> VContKind<'a> { [b'c'] => Continue, [b's'] => Step, [b't'] => Stop, - [b'C', sig @ ..] => ContinueWithSig(Signal::from_protocol_u8(decode_hex(sig).ok()?)), - [b'S', sig @ ..] => StepWithSig(Signal::from_protocol_u8(decode_hex(sig).ok()?)), + [b'C', sig @ ..] => ContinueWithSig(Signal(decode_hex(sig).ok()?)), + [b'S', sig @ ..] => StepWithSig(Signal(decode_hex(sig).ok()?)), [b'r', range @ ..] => { let mut range = range.split(|b| *b == b','); RangeStep(HexString(range.next()?), HexString(range.next()?)) diff --git a/src/protocol/commands/_vFile_open.rs b/src/protocol/commands/_vFile_open.rs index 7497912..04a1828 100644 --- a/src/protocol/commands/_vFile_open.rs +++ b/src/protocol/commands/_vFile_open.rs @@ -1,6 +1,6 @@ use super::prelude::*; - -use crate::target::ext::host_io::{HostIoOpenFlags, HostIoOpenMode}; +use crate::target::ext::host_io::HostIoOpenFlags; +use crate::target::ext::host_io::HostIoOpenMode; #[derive(Debug)] pub struct vFileOpen<'a> { diff --git a/src/protocol/commands/_vFile_pwrite.rs b/src/protocol/commands/_vFile_pwrite.rs index 94cd34a..32f45f1 100644 --- a/src/protocol/commands/_vFile_pwrite.rs +++ b/src/protocol/commands/_vFile_pwrite.rs @@ -1,5 +1,4 @@ use super::prelude::*; - use crate::protocol::common::hex::decode_bin_buf; #[derive(Debug)] diff --git a/src/protocol/commands/_vFile_setfs.rs b/src/protocol/commands/_vFile_setfs.rs index 41a3100..aacbc69 100644 --- a/src/protocol/commands/_vFile_setfs.rs +++ b/src/protocol/commands/_vFile_setfs.rs @@ -1,5 +1,4 @@ use super::prelude::*; - use crate::target::ext::host_io::FsKind; #[derive(Debug)] diff --git a/src/protocol/commands/_vKill.rs b/src/protocol/commands/_vKill.rs index a1d687b..c06818b 100644 --- a/src/protocol/commands/_vKill.rs +++ b/src/protocol/commands/_vKill.rs @@ -1,5 +1,4 @@ use super::prelude::*; - use crate::common::Pid; #[derive(Debug)] diff --git a/src/protocol/commands/_vRun.rs b/src/protocol/commands/_vRun.rs index 849c795..a4b619d 100644 --- a/src/protocol/commands/_vRun.rs +++ b/src/protocol/commands/_vRun.rs @@ -1,5 +1,4 @@ use super::prelude::*; - use crate::protocol::common::lists::ArgListHex; #[derive(Debug)] diff --git a/src/protocol/commands/_x_upcase.rs b/src/protocol/commands/_x_upcase.rs index 218c16c..9467e5f 100644 --- a/src/protocol/commands/_x_upcase.rs +++ b/src/protocol/commands/_x_upcase.rs @@ -1,5 +1,4 @@ use super::prelude::*; - use crate::protocol::common::hex::decode_bin_buf; #[derive(Debug)] diff --git a/src/protocol/commands/breakpoint.rs b/src/protocol/commands/breakpoint.rs index 38ad046..b0a0e9d 100644 --- a/src/protocol/commands/breakpoint.rs +++ b/src/protocol/commands/breakpoint.rs @@ -1,4 +1,5 @@ -use crate::protocol::common::hex::{decode_hex, decode_hex_buf}; +use crate::protocol::common::hex::decode_hex; +use crate::protocol::common::hex::decode_hex_buf; // Breakpoint packets are split up like this: // diff --git a/src/protocol/common/hex.rs b/src/protocol/common/hex.rs index ade9032..6eb35e0 100644 --- a/src/protocol/common/hex.rs +++ b/src/protocol/common/hex.rs @@ -1,4 +1,7 @@ -use num_traits::{CheckedAdd, CheckedMul, FromPrimitive, Zero}; +use num_traits::CheckedAdd; +use num_traits::CheckedMul; +use num_traits::FromPrimitive; +use num_traits::Zero; #[derive(Debug)] pub enum DecodeHexError { diff --git a/src/protocol/common/lists.rs b/src/protocol/common/lists.rs index 5dfdab7..1f2bc88 100644 --- a/src/protocol/common/lists.rs +++ b/src/protocol/common/lists.rs @@ -1,4 +1,5 @@ -use crate::protocol::common::hex::{decode_hex_buf, is_hex}; +use crate::protocol::common::hex::decode_hex_buf; +use crate::protocol::common::hex::is_hex; /// A wrapper type around a list of hex encoded arguments separated by `;`. #[derive(Debug)] diff --git a/src/protocol/common/thread_id.rs b/src/protocol/common/thread_id.rs index 36a3ea9..e1104d3 100644 --- a/src/protocol/common/thread_id.rs +++ b/src/protocol/common/thread_id.rs @@ -1,7 +1,7 @@ -use core::convert::{TryFrom, TryInto}; -use core::num::NonZeroUsize; - use crate::protocol::common::hex::decode_hex; +use core::convert::TryFrom; +use core::convert::TryInto; +use core::num::NonZeroUsize; /// Tid/Pid Selector. #[derive(PartialEq, Eq, Debug, Clone, Copy)] diff --git a/src/protocol/console_output.rs b/src/protocol/console_output.rs index 16b30aa..54774be 100644 --- a/src/protocol/console_output.rs +++ b/src/protocol/console_output.rs @@ -1,7 +1,6 @@ -use core::fmt; - #[cfg(feature = "alloc")] use alloc::vec::Vec; +use core::fmt; /// Helper struct to send console output to GDB. /// diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 6dd8166..3ea5a03 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -3,6 +3,9 @@ //! These types should _not_ leak into the public interface (with a few //! exceptions, as listed below). +pub use console_output::ConsoleOutput; +pub use packet::PacketParseError; + mod common; mod console_output; mod packet; @@ -10,11 +13,10 @@ mod response_writer; pub(crate) mod commands; pub(crate) mod recv_packet; - -pub(crate) use common::thread_id::{ConcreteThreadId, IdKind, SpecificIdKind, SpecificThreadId}; +pub(crate) use common::thread_id::ConcreteThreadId; +pub(crate) use common::thread_id::IdKind; +pub(crate) use common::thread_id::SpecificIdKind; +pub(crate) use common::thread_id::SpecificThreadId; pub(crate) use packet::Packet; -pub(crate) use response_writer::{Error as ResponseWriterError, ResponseWriter}; - -// These types end up a part of the public interface. -pub use console_output::ConsoleOutput; -pub use packet::PacketParseError; +pub(crate) use response_writer::Error as ResponseWriterError; +pub(crate) use response_writer::ResponseWriter; diff --git a/src/protocol/recv_packet.rs b/src/protocol/recv_packet.rs index 9f3b567..f6e7003 100644 --- a/src/protocol/recv_packet.rs +++ b/src/protocol/recv_packet.rs @@ -1,10 +1,9 @@ +use crate::util::managed_vec::CapacityError; +use crate::util::managed_vec::ManagedVec; #[cfg(feature = "trace-pkt")] use alloc::string::String; - use managed::ManagedSlice; -use crate::util::managed_vec::{CapacityError, ManagedVec}; - enum State { Ready, Body, diff --git a/src/protocol/response_writer.rs b/src/protocol/response_writer.rs index 3dea22a..42c2a20 100644 --- a/src/protocol/response_writer.rs +++ b/src/protocol/response_writer.rs @@ -1,14 +1,14 @@ +use crate::conn::Connection; +use crate::internal::BeBytes; +use crate::protocol::SpecificIdKind; +use crate::protocol::SpecificThreadId; #[cfg(feature = "trace-pkt")] use alloc::string::String; #[cfg(feature = "trace-pkt")] use alloc::vec::Vec; - use num_traits::identities::one; -use num_traits::{CheckedRem, PrimInt}; - -use crate::conn::Connection; -use crate::internal::BeBytes; -use crate::protocol::{SpecificIdKind, SpecificThreadId}; +use num_traits::CheckedRem; +use num_traits::PrimInt; /// Newtype around a Connection error. Having a newtype allows implementing a /// `From> for crate::Error`, which greatly diff --git a/src/stub/builder.rs b/src/stub/builder.rs index bb1ceba..fa9e5a2 100644 --- a/src/stub/builder.rs +++ b/src/stub/builder.rs @@ -1,13 +1,11 @@ -use core::fmt::{self, Display}; -use core::marker::PhantomData; - -use managed::ManagedSlice; - -use crate::conn::Connection; -use crate::target::Target; - use super::core_impl::GdbStubImpl; use super::GdbStub; +use crate::conn::Connection; +use crate::target::Target; +use core::fmt::Display; +use core::fmt::{self}; +use core::marker::PhantomData; +use managed::ManagedSlice; /// An error which may occur when building a [`GdbStub`]. #[derive(Debug)] diff --git a/src/stub/core_impl.rs b/src/stub/core_impl.rs index 31ab884..07774d4 100644 --- a/src/stub/core_impl.rs +++ b/src/stub/core_impl.rs @@ -1,12 +1,14 @@ -use core::marker::PhantomData; - -use crate::common::{Signal, Tid}; +use crate::common::Signal; +use crate::common::Tid; use crate::conn::Connection; use crate::protocol::commands::Command; -use crate::protocol::{Packet, ResponseWriter, SpecificIdKind}; -use crate::stub::GdbStubError as Error; +use crate::protocol::Packet; +use crate::protocol::ResponseWriter; +use crate::protocol::SpecificIdKind; +use crate::stub::error::InternalError; use crate::target::Target; use crate::SINGLE_THREAD_TID; +use core::marker::PhantomData; /// Common imports used by >50% of all extensions. /// @@ -16,8 +18,9 @@ mod prelude { pub(super) use crate::internal::BeBytes; pub(super) use crate::protocol::ResponseWriter; pub(super) use crate::stub::core_impl::target_result_ext::TargetResultExt; - pub(super) use crate::stub::core_impl::{GdbStubImpl, HandlerStatus}; - pub(super) use crate::stub::error::GdbStubError as Error; + pub(super) use crate::stub::core_impl::GdbStubImpl; + pub(super) use crate::stub::core_impl::HandlerStatus; + pub(super) use crate::stub::error::InternalError as Error; pub(super) use crate::target::Target; } @@ -31,6 +34,7 @@ mod host_io; mod lldb_register_info; mod memory_map; mod monitor_cmd; +mod no_ack_mode; mod resume; mod reverse_exec; mod section_offsets; @@ -42,7 +46,7 @@ mod x_upcase_packet; pub(crate) use resume::FinishExecStatus; pub(crate) mod target_result_ext { - use crate::stub::GdbStubError; + use crate::stub::error::InternalError; use crate::target::TargetError; /// Extension trait to ease working with `TargetResult` in the GdbStub @@ -51,14 +55,14 @@ pub(crate) mod target_result_ext { /// Encapsulates the boilerplate associated with handling /// `TargetError`s, such as bailing-out on Fatal errors, or /// returning response codes. - fn handle_error(self) -> Result>; + fn handle_error(self) -> Result>; } impl TargetResultExt for Result> { - fn handle_error(self) -> Result> { + fn handle_error(self) -> Result> { let code = match self { Ok(v) => return Ok(v), - Err(TargetError::Fatal(e)) => return Err(GdbStubError::TargetError(e)), + Err(TargetError::Fatal(e)) => return Err(InternalError::TargetError(e)), // Recoverable errors: // Error code 121 corresponds to `EREMOTEIO` lol Err(TargetError::NonFatal) => 121, @@ -67,7 +71,7 @@ pub(crate) mod target_result_ext { Err(TargetError::Io(e)) => e.raw_os_error().unwrap_or(121) as u8, }; - Err(GdbStubError::NonFatalError(code)) + Err(InternalError::NonFatalError(code)) } } } @@ -92,7 +96,7 @@ pub enum State { Disconnect(DisconnectReason), } -pub struct GdbStubImpl { +pub(crate) struct GdbStubImpl { _target: PhantomData, _connection: PhantomData, @@ -133,10 +137,10 @@ impl GdbStubImpl { target: &mut T, conn: &mut C, packet: Packet<'_>, - ) -> Result> { + ) -> Result> { match packet { Packet::Ack => Ok(State::Pump), - Packet::Nack => Err(Error::ClientSentNack), + Packet::Nack => Err(InternalError::ClientSentNack), Packet::Interrupt => { debug!("<-- interrupt packet"); Ok(State::CtrlCInterrupt) @@ -144,7 +148,7 @@ impl GdbStubImpl { Packet::Command(command) => { // Acknowledge the command if !self.features.no_ack_mode() { - conn.write(b'+').map_err(Error::ConnectionWrite)?; + conn.write(b'+').map_err(InternalError::conn_write)?; } let mut res = ResponseWriter::new(conn, target.use_rle()); @@ -158,7 +162,7 @@ impl GdbStubImpl { Ok(HandlerStatus::Disconnect(reason)) => Some(reason), // HACK: handling this "dummy" error is required as part of the // `TargetResultExt::handle_error()` machinery. - Err(Error::NonFatalError(code)) => { + Err(InternalError::NonFatalError(code)) => { res.write_str("E")?; res.write_num(code)?; None @@ -188,12 +192,13 @@ impl GdbStubImpl { res: &mut ResponseWriter<'_, C>, target: &mut T, cmd: Command<'_>, - ) -> Result> { + ) -> Result> { match cmd { // `handle_X` methods are defined in the `ext` module Command::Base(cmd) => self.handle_base(res, target, cmd), Command::TargetXml(cmd) => self.handle_target_xml(res, target, cmd), Command::Resume(cmd) => self.handle_stop_resume(res, target, cmd), + Command::NoAckMode(cmd) => self.handle_no_ack_mode(res, target, cmd), Command::XUpcasePacket(cmd) => self.handle_x_upcase_packet(res, target, cmd), Command::SingleRegisterAccess(cmd) => { self.handle_single_register_access(res, target, cmd) @@ -244,11 +249,15 @@ impl GdbStubImpl { } } +#[derive(Copy, Clone)] +#[repr(transparent)] +struct ProtocolFeatures(u8); + // This bitflag is not part of the protocol - it is an internal implementation // detail. The alternative would be to use multiple `bool` fields, which wastes // space in minimal `gdbstub` configurations. bitflags::bitflags! { - struct ProtocolFeatures: u8 { + impl ProtocolFeatures: u8 { const NO_ACK_MODE = 1 << 0; const MULTIPROCESS = 1 << 1; } diff --git a/src/stub/core_impl/base.rs b/src/stub/core_impl/base.rs index e7d77dd..9110297 100644 --- a/src/stub/core_impl/base.rs +++ b/src/stub/core_impl/base.rs @@ -1,19 +1,26 @@ use super::prelude::*; -use crate::protocol::commands::ext::Base; - -use crate::arch::{Arch, Registers}; -use crate::common::Tid; -use crate::protocol::{IdKind, SpecificIdKind, SpecificThreadId}; -use crate::target::ext::base::{BaseOps, ResumeOps}; -use crate::{FAKE_PID, SINGLE_THREAD_TID}; - use super::DisconnectReason; +use crate::arch::Arch; +use crate::arch::Registers; +use crate::common::Pid; +use crate::common::Tid; +use crate::protocol::commands::ext::Base; +use crate::protocol::IdKind; +use crate::protocol::SpecificIdKind; +use crate::protocol::SpecificThreadId; +use crate::target::ext::base::BaseOps; +use crate::target::ext::base::ResumeOps; +use crate::FAKE_PID; +use crate::SINGLE_THREAD_TID; impl GdbStubImpl { #[inline(always)] - fn get_sane_any_tid(&mut self, target: &mut T) -> Result> { + fn get_sane_any_tid( + &mut self, + target: &mut T, + ) -> Result, Error> { let tid = match target.base_ops() { - BaseOps::SingleThread(_) => SINGLE_THREAD_TID, + BaseOps::SingleThread(_) => Some(SINGLE_THREAD_TID), BaseOps::MultiThread(ops) => { let mut first_tid = None; ops.list_active_threads(&mut |tid| { @@ -22,18 +29,58 @@ impl GdbStubImpl { } }) .map_err(Error::TargetError)?; - // Note that `Error::NoActiveThreads` shouldn't ever occur, since this method is - // called from the `H` packet handler, which AFAIK is only sent after the GDB - // client has confirmed that a thread / process exists. - // - // If it does, that really sucks, and will require rethinking how to handle "any - // thread" messages. - first_tid.ok_or(Error::NoActiveThreads)? + // It is possible for this to be `None` in the case where the target has + // not yet called `register_thread()`. This can happen, for example, if + // there are no active threads in the current target process. + first_tid } }; Ok(tid) } + pub(crate) fn get_current_pid( + &mut self, + target: &mut T, + ) -> Result> { + if let Some(ops) = target + .support_extended_mode() + .and_then(|ops| ops.support_current_active_pid()) + { + ops.current_active_pid().map_err(Error::TargetError) + } else { + Ok(FAKE_PID) + } + } + + // Used by `?` and `vAttach` to return a "reasonable" stop reason. + // + // This is a bit of an implementation wart, since this is really something + // the user ought to be able to customize. + // + // Works fine for now though... + pub(crate) fn report_reasonable_stop_reason( + &mut self, + res: &mut ResponseWriter<'_, C>, + target: &mut T, + ) -> Result> { + // Reply with a valid thread-id or GDB issues a warning when more + // than one thread is active + if let Some(tid) = self.get_sane_any_tid(target)? { + res.write_str("T05thread:")?; + res.write_specific_thread_id(SpecificThreadId { + pid: self + .features + .multiprocess() + .then_some(SpecificIdKind::WithId(self.get_current_pid(target)?)), + tid: SpecificIdKind::WithId(tid), + })?; + } else { + res.write_str("W00")?; + } + res.write_str(";")?; + Ok(HandlerStatus::Handled) + } + pub(crate) fn handle_base( &mut self, res: &mut ResponseWriter<'_, C>, @@ -66,11 +113,11 @@ impl GdbStubImpl { res.write_num(cmd.packet_buffer_len)?; // these are the few features that gdbstub unconditionally supports - res.write_str(concat!( - ";vContSupported+", - ";multiprocess+", - ";QStartNoAckMode+", - ))?; + res.write_str(concat!(";vContSupported+", ";multiprocess+",))?; + + if target.use_no_ack_mode() { + res.write_str(";QStartNoAckMode+")?; + } if let Some(resume_ops) = target.base_ops().resume_ops() { let (reverse_cont, reverse_step) = match resume_ops { @@ -150,27 +197,12 @@ impl GdbStubImpl { HandlerStatus::Handled } - Base::QStartNoAckMode(_) => { - self.features.set_no_ack_mode(true); - HandlerStatus::NeedsOk - } // -------------------- "Core" Functionality -------------------- // - // TODO: Improve the '?' response based on last-sent stop reason. - // this will be particularly relevant when working on non-stop mode. Base::QuestionMark(_) => { - // Reply with a valid thread-id or GDB issues a warning when more - // than one thread is active - res.write_str("T05thread:")?; - res.write_specific_thread_id(SpecificThreadId { - pid: self - .features - .multiprocess() - .then_some(SpecificIdKind::WithId(FAKE_PID)), - tid: SpecificIdKind::WithId(self.get_sane_any_tid(target)?), - })?; - res.write_str(";")?; - HandlerStatus::Handled + // TODO: Improve the '?' response. + // this will be particularly relevant when working on non-stop mode. + self.report_reasonable_stop_reason(res, target)? } Base::qAttached(cmd) => { let is_attached = match target.support_extended_mode() { @@ -238,7 +270,7 @@ impl GdbStubImpl { let addr = addr + NumCast::from(i).ok_or(Error::TargetMismatch)?; let data = &mut buf[..chunk_size]; - match target.base_ops() { + let data_len = match target.base_ops() { BaseOps::SingleThread(ops) => ops.read_addrs(addr, data), BaseOps::MultiThread(ops) => { ops.read_addrs(addr, data, self.current_mem_tid) @@ -249,6 +281,8 @@ impl GdbStubImpl { n -= chunk_size; i += chunk_size; + // TODO: add more specific error variant? + let data = data.get(..data_len).ok_or(Error::PacketBufferOverflow)?; res.write_hex_buf(data)?; } HandlerStatus::Handled @@ -302,17 +336,24 @@ impl GdbStubImpl { use crate::protocol::commands::_h_upcase::Op; match cmd.kind { Op::Other => match cmd.thread.tid { - IdKind::Any => self.current_mem_tid = self.get_sane_any_tid(target)?, + IdKind::Any => match self.get_sane_any_tid(target)? { + Some(tid) => self.current_mem_tid = tid, + None => { + return Err(Error::NonFatalError(1)); + } + }, // "All" threads doesn't make sense for memory accesses IdKind::All => return Err(Error::PacketUnexpected), IdKind::WithId(tid) => self.current_mem_tid = tid, }, // technically, this variant is deprecated in favor of vCont... Op::StepContinue => match cmd.thread.tid { - IdKind::Any => { - self.current_resume_tid = - SpecificIdKind::WithId(self.get_sane_any_tid(target)?) - } + IdKind::Any => match self.get_sane_any_tid(target)? { + Some(tid) => self.current_resume_tid = SpecificIdKind::WithId(tid), + None => { + return Err(Error::NonFatalError(1)); + } + }, IdKind::All => self.current_resume_tid = SpecificIdKind::All, IdKind::WithId(tid) => { self.current_resume_tid = SpecificIdKind::WithId(tid) @@ -323,13 +364,14 @@ impl GdbStubImpl { } Base::qfThreadInfo(_) => { res.write_str("m")?; + let pid = self.get_current_pid(target)?; match target.base_ops() { BaseOps::SingleThread(_) => res.write_specific_thread_id(SpecificThreadId { pid: self .features .multiprocess() - .then_some(SpecificIdKind::WithId(FAKE_PID)), + .then_some(SpecificIdKind::WithId(pid)), tid: SpecificIdKind::WithId(SINGLE_THREAD_TID), })?, BaseOps::MultiThread(ops) => { @@ -346,7 +388,7 @@ impl GdbStubImpl { pid: self .features .multiprocess() - .then_some(SpecificIdKind::WithId(FAKE_PID)), + .then_some(SpecificIdKind::WithId(pid)), tid: SpecificIdKind::WithId(tid), })?; Ok(()) diff --git a/src/stub/core_impl/breakpoints.rs b/src/stub/core_impl/breakpoints.rs index a54da71..3b45eff 100644 --- a/src/stub/core_impl/breakpoints.rs +++ b/src/stub/core_impl/breakpoints.rs @@ -1,8 +1,8 @@ use super::prelude::*; +use crate::arch::Arch; +use crate::arch::BreakpointKind; use crate::protocol::commands::ext::Breakpoints; -use crate::arch::{Arch, BreakpointKind}; - enum CmdKind { Add, Remove, diff --git a/src/stub/core_impl/catch_syscalls.rs b/src/stub/core_impl/catch_syscalls.rs index b7dde2e..7f38222 100644 --- a/src/stub/core_impl/catch_syscalls.rs +++ b/src/stub/core_impl/catch_syscalls.rs @@ -1,8 +1,7 @@ use super::prelude::*; -use crate::protocol::commands::ext::CatchSyscalls; - use crate::arch::Arch; use crate::protocol::commands::_QCatchSyscalls::QCatchSyscalls; +use crate::protocol::commands::ext::CatchSyscalls; use crate::target::ext::catch_syscalls::SyscallNumbers; impl GdbStubImpl { diff --git a/src/stub/core_impl/extended_mode.rs b/src/stub/core_impl/extended_mode.rs index dbe3913..703b3f8 100644 --- a/src/stub/core_impl/extended_mode.rs +++ b/src/stub/core_impl/extended_mode.rs @@ -1,5 +1,9 @@ use super::prelude::*; use crate::protocol::commands::ext::ExtendedMode; +use crate::protocol::SpecificIdKind; +use crate::protocol::SpecificThreadId; +use crate::target::ext::base::BaseOps; +use crate::SINGLE_THREAD_TID; impl GdbStubImpl { pub(crate) fn handle_extended_mode( @@ -25,9 +29,56 @@ impl GdbStubImpl { HandlerStatus::Handled } ExtendedMode::vAttach(cmd) => { + if ops.support_current_active_pid().is_none() { + return Err(Error::MissingCurrentActivePidImpl); + } + ops.attach(cmd.pid).handle_error()?; + self.report_reasonable_stop_reason(res, target)? + } + ExtendedMode::qC(_cmd) if ops.support_current_active_pid().is_some() => { + let ops = ops.support_current_active_pid().unwrap(); + + res.write_str("QC")?; + let pid = ops.current_active_pid().map_err(Error::TargetError)?; + let tid = match target.base_ops() { + BaseOps::SingleThread(_) => SINGLE_THREAD_TID, + BaseOps::MultiThread(ops) => { + // HACK: gdbstub should avoid using a sentinel value here... + if self.current_mem_tid == SINGLE_THREAD_TID { + let mut err: Result<_, Error> = Ok(()); + let mut first_tid = None; + ops.list_active_threads(&mut |tid| { + // TODO: replace this with a try block (once stabilized) + let e = (|| { + if first_tid.is_some() { + return Ok(()); + } + first_tid = Some(tid); + Ok(()) + })(); + + if let Err(e) = e { + err = Err(e) + } + }) + .map_err(Error::TargetError)?; + err?; + first_tid.unwrap_or(SINGLE_THREAD_TID) + } else { + self.current_mem_tid + } + } + }; + + res.write_specific_thread_id(SpecificThreadId { + pid: self + .features + .multiprocess() + .then_some(SpecificIdKind::WithId(pid)), + tid: SpecificIdKind::WithId(tid), + })?; - // TODO: sends OK when running in Non-Stop mode HandlerStatus::Handled } ExtendedMode::vRun(cmd) => { @@ -37,10 +88,7 @@ impl GdbStubImpl { .run(cmd.filename, Args::new(&mut cmd.args.into_iter())) .handle_error()?; - // This is a reasonable response, as the `run` handler must - // spawn the process in a stopped state. - res.write_str("S05")?; - HandlerStatus::Handled + self.report_reasonable_stop_reason(res, target)? } // --------- ASLR --------- // ExtendedMode::QDisableRandomization(cmd) if ops.support_configure_aslr().is_some() => { @@ -78,6 +126,7 @@ impl GdbStubImpl { ops.cfg_startup_with_shell(cmd.value).handle_error()?; HandlerStatus::NeedsOk } + _ => HandlerStatus::Handled, }; diff --git a/src/stub/core_impl/host_io.rs b/src/stub/core_impl/host_io.rs index 4ea6b1a..f62bf66 100644 --- a/src/stub/core_impl/host_io.rs +++ b/src/stub/core_impl/host_io.rs @@ -1,8 +1,8 @@ use super::prelude::*; -use crate::protocol::commands::ext::HostIo; - use crate::arch::Arch; -use crate::target::ext::host_io::{HostIoError, HostIoStat}; +use crate::protocol::commands::ext::HostIo; +use crate::target::ext::host_io::HostIoError; +use crate::target::ext::host_io::HostIoStat; impl GdbStubImpl { pub(crate) fn handle_host_io( diff --git a/src/stub/core_impl/lldb_register_info.rs b/src/stub/core_impl/lldb_register_info.rs index 2b27f7c..676991d 100644 --- a/src/stub/core_impl/lldb_register_info.rs +++ b/src/stub/core_impl/lldb_register_info.rs @@ -1,8 +1,11 @@ use super::prelude::*; -use crate::protocol::commands::ext::LldbRegisterInfo; - -use crate::arch::lldb::{Encoding, Format, Generic, Register, RegisterInfo as LLDBRegisterInfo}; +use crate::arch::lldb::Encoding; +use crate::arch::lldb::Format; +use crate::arch::lldb::Generic; +use crate::arch::lldb::Register; +use crate::arch::lldb::RegisterInfo as LLDBRegisterInfo; use crate::arch::Arch; +use crate::protocol::commands::ext::LldbRegisterInfo; impl GdbStubImpl { pub(crate) fn handle_lldb_register_info( @@ -112,9 +115,8 @@ impl GdbStubImpl { } }; if let Some(ops) = target.support_lldb_register_info_override() { - use crate::target::ext::lldb_register_info_override::{ - Callback, CallbackToken, - }; + use crate::target::ext::lldb_register_info_override::Callback; + use crate::target::ext::lldb_register_info_override::CallbackToken; ops.lldb_register_info( cmd.reg_id, diff --git a/src/stub/core_impl/monitor_cmd.rs b/src/stub/core_impl/monitor_cmd.rs index 50425a2..e4cfc1e 100644 --- a/src/stub/core_impl/monitor_cmd.rs +++ b/src/stub/core_impl/monitor_cmd.rs @@ -1,6 +1,5 @@ use super::prelude::*; use crate::protocol::commands::ext::MonitorCmd; - use crate::protocol::ConsoleOutput; impl GdbStubImpl { diff --git a/src/stub/core_impl/no_ack_mode.rs b/src/stub/core_impl/no_ack_mode.rs new file mode 100644 index 0000000..48b5173 --- /dev/null +++ b/src/stub/core_impl/no_ack_mode.rs @@ -0,0 +1,25 @@ +use super::prelude::*; +use crate::protocol::commands::ext::NoAckMode; + +impl GdbStubImpl { + pub(crate) fn handle_no_ack_mode( + &mut self, + _res: &mut ResponseWriter<'_, C>, + target: &mut T, + command: NoAckMode, + ) -> Result> { + if !target.use_no_ack_mode() { + return Ok(HandlerStatus::Handled); + } + + crate::__dead_code_marker!("no_ack_mode", "impl"); + + let handler_status = match command { + NoAckMode::QStartNoAckMode(_) => { + self.features.set_no_ack_mode(true); + HandlerStatus::NeedsOk + } + }; + Ok(handler_status) + } +} diff --git a/src/stub/core_impl/resume.rs b/src/stub/core_impl/resume.rs index e63381a..1a06e0b 100644 --- a/src/stub/core_impl/resume.rs +++ b/src/stub/core_impl/resume.rs @@ -1,17 +1,16 @@ use super::prelude::*; -use crate::protocol::commands::ext::Resume; - +use super::DisconnectReason; use crate::arch::Arch; -use crate::common::{Signal, Tid}; +use crate::common::Signal; +use crate::common::Tid; use crate::protocol::commands::_vCont::Actions; -use crate::protocol::{SpecificIdKind, SpecificThreadId}; +use crate::protocol::commands::ext::Resume; +use crate::protocol::SpecificIdKind; +use crate::protocol::SpecificThreadId; use crate::stub::MultiThreadStopReason; use crate::target::ext::base::reverse_exec::ReplayLogPosition; use crate::target::ext::base::ResumeOps; use crate::target::ext::catch_syscalls::CatchSyscallPosition; -use crate::FAKE_PID; - -use super::DisconnectReason; impl GdbStubImpl { pub(crate) fn handle_stop_resume( @@ -230,8 +229,16 @@ impl GdbStubImpl { // TODO: update this case when non-stop mode is implemented VContKind::Stop => return Err(Error::PacketUnexpected), + // GDB doesn't always respect `vCont?` responses that omit `;s;S`, and will try to + // send step packets regardless. Inform the user of this bug by issuing a + // `UnexpectedStepPacket` error, which is more useful than a generic + // `PacketUnexpected` error. + VContKind::Step | VContKind::StepWithSig(..) => { + return Err(Error::UnexpectedStepPacket) + } + // Instead of using `_ =>`, explicitly list out any remaining unguarded cases. - VContKind::RangeStep(..) | VContKind::Step | VContKind::StepWithSig(..) => { + VContKind::RangeStep(..) => { error!("GDB client sent resume action not reported by `vCont?`"); return Err(Error::PacketUnexpected); } @@ -257,11 +264,12 @@ impl GdbStubImpl { fn write_stop_common( &mut self, res: &mut ResponseWriter<'_, C>, + target: &mut T, tid: Option, signal: Signal, ) -> Result<(), Error> { res.write_str("T")?; - res.write_num(signal as u8)?; + res.write_num(signal.0)?; if let Some(tid) = tid { self.current_mem_tid = tid; @@ -272,7 +280,7 @@ impl GdbStubImpl { pid: self .features .multiprocess() - .then_some(SpecificIdKind::WithId(FAKE_PID)), + .then_some(SpecificIdKind::WithId(self.get_current_pid(target)?)), tid: SpecificIdKind::WithId(tid), })?; res.write_str(";")?; @@ -326,12 +334,12 @@ impl GdbStubImpl { let status = match stop_reason { MultiThreadStopReason::DoneStep => { res.write_str("S")?; - res.write_num(Signal::SIGTRAP as u8)?; + res.write_num(Signal::SIGTRAP.0)?; FinishExecStatus::Handled } MultiThreadStopReason::Signal(sig) => { res.write_str("S")?; - res.write_num(sig as u8)?; + res.write_num(sig.0)?; FinishExecStatus::Handled } MultiThreadStopReason::Exited(code) => { @@ -341,24 +349,24 @@ impl GdbStubImpl { } MultiThreadStopReason::Terminated(sig) => { res.write_str("X")?; - res.write_num(sig as u8)?; + res.write_num(sig.0)?; FinishExecStatus::Disconnect(DisconnectReason::TargetTerminated(sig)) } MultiThreadStopReason::SignalWithThread { tid, signal } => { - self.write_stop_common(res, Some(tid), signal)?; + self.write_stop_common(res, target, Some(tid), signal)?; FinishExecStatus::Handled } MultiThreadStopReason::SwBreak(tid) if guard_break!(support_sw_breakpoint) => { crate::__dead_code_marker!("sw_breakpoint", "stop_reason"); - self.write_stop_common(res, Some(tid), Signal::SIGTRAP)?; + self.write_stop_common(res, target, Some(tid), Signal::SIGTRAP)?; res.write_str("swbreak:;")?; FinishExecStatus::Handled } MultiThreadStopReason::HwBreak(tid) if guard_break!(support_hw_breakpoint) => { crate::__dead_code_marker!("hw_breakpoint", "stop_reason"); - self.write_stop_common(res, Some(tid), Signal::SIGTRAP)?; + self.write_stop_common(res, target, Some(tid), Signal::SIGTRAP)?; res.write_str("hwbreak:;")?; FinishExecStatus::Handled } @@ -367,7 +375,7 @@ impl GdbStubImpl { { crate::__dead_code_marker!("hw_watchpoint", "stop_reason"); - self.write_stop_common(res, Some(tid), Signal::SIGTRAP)?; + self.write_stop_common(res, target, Some(tid), Signal::SIGTRAP)?; use crate::target::ext::breakpoints::WatchKind; match kind { @@ -382,7 +390,7 @@ impl GdbStubImpl { MultiThreadStopReason::ReplayLog { tid, pos } if guard_reverse_exec!() => { crate::__dead_code_marker!("reverse_exec", "stop_reason"); - self.write_stop_common(res, tid, Signal::SIGTRAP)?; + self.write_stop_common(res, target, tid, Signal::SIGTRAP)?; res.write_str("replaylog:")?; res.write_str(match pos { @@ -400,7 +408,7 @@ impl GdbStubImpl { } if guard_catch_syscall!() => { crate::__dead_code_marker!("catch_syscall", "stop_reason"); - self.write_stop_common(res, tid, Signal::SIGTRAP)?; + self.write_stop_common(res, target, tid, Signal::SIGTRAP)?; res.write_str(match position { CatchSyscallPosition::Entry => "syscall_entry:", diff --git a/src/stub/core_impl/reverse_exec.rs b/src/stub/core_impl/reverse_exec.rs index 7b85d38..e7269a3 100644 --- a/src/stub/core_impl/reverse_exec.rs +++ b/src/stub/core_impl/reverse_exec.rs @@ -1,12 +1,11 @@ use super::prelude::*; -use crate::protocol::commands::ext::{ReverseCont, ReverseStep}; - use crate::arch::Arch; use crate::common::Tid; +use crate::protocol::commands::ext::ReverseCont; +use crate::protocol::commands::ext::ReverseStep; use crate::protocol::SpecificIdKind; -use crate::target::ext::base::reverse_exec::{ - ReverseCont as ReverseContTrait, ReverseStep as ReverseStepTrait, -}; +use crate::target::ext::base::reverse_exec::ReverseCont as ReverseContTrait; +use crate::target::ext::base::reverse_exec::ReverseStep as ReverseStepTrait; use crate::target::ext::base::ResumeOps; macro_rules! defn_ops { diff --git a/src/stub/core_impl/single_register_access.rs b/src/stub/core_impl/single_register_access.rs index 954a634..093e7f5 100644 --- a/src/stub/core_impl/single_register_access.rs +++ b/src/stub/core_impl/single_register_access.rs @@ -1,7 +1,7 @@ use super::prelude::*; +use crate::arch::Arch; +use crate::arch::RegId; use crate::protocol::commands::ext::SingleRegisterAccess; - -use crate::arch::{Arch, RegId}; use crate::target::ext::base::BaseOps; impl GdbStubImpl { diff --git a/src/stub/core_impl/target_xml.rs b/src/stub/core_impl/target_xml.rs index 2650ad2..bf3fc5c 100644 --- a/src/stub/core_impl/target_xml.rs +++ b/src/stub/core_impl/target_xml.rs @@ -1,7 +1,6 @@ use super::prelude::*; -use crate::protocol::commands::ext::TargetXml; - use crate::arch::Arch; +use crate::protocol::commands::ext::TargetXml; impl GdbStubImpl { pub(crate) fn handle_target_xml( diff --git a/src/stub/core_impl/x_upcase_packet.rs b/src/stub/core_impl/x_upcase_packet.rs index 9d865e5..aa36a44 100644 --- a/src/stub/core_impl/x_upcase_packet.rs +++ b/src/stub/core_impl/x_upcase_packet.rs @@ -1,7 +1,6 @@ use super::prelude::*; -use crate::protocol::commands::ext::XUpcasePacket; - use crate::arch::Arch; +use crate::protocol::commands::ext::XUpcasePacket; use crate::target::ext::base::BaseOps; impl GdbStubImpl { diff --git a/src/stub/error.rs b/src/stub/error.rs index 6d55997..8cdc18e 100644 --- a/src/stub/error.rs +++ b/src/stub/error.rs @@ -1,62 +1,42 @@ -use core::fmt::{self, Debug, Display}; - -use crate::arch::SingleStepGdbBehavior; -use crate::protocol::{PacketParseError, ResponseWriterError}; +use crate::protocol::PacketParseError; +use crate::protocol::ResponseWriterError; use crate::util::managed_vec::CapacityError; +use core::fmt::Debug; +use core::fmt::Display; +use core::fmt::{self}; + +/// An error that may occur while interacting with a +/// [`Connection`](crate::conn::Connection). +#[derive(Debug)] +pub enum ConnectionErrorKind { + /// Error initializing the session. + Init, + /// Error reading data. + Read, + /// Error writing data. + Write, +} -/// An error which may occur during a GDB debugging session. #[derive(Debug)] -#[non_exhaustive] -pub enum GdbStubError { - /// Connection Error while initializing the session. - ConnectionInit(C), - /// Connection Error while reading request. - ConnectionRead(C), - /// Connection Error while writing response. - ConnectionWrite(C), - - /// Client nack'd the last packet, but `gdbstub` doesn't implement - /// re-transmission. +pub(crate) enum InternalError { + /// Connection Error + Connection(C, ConnectionErrorKind), + /// Target encountered a fatal error. + TargetError(T), + ClientSentNack, - /// Packet cannot fit in the provided packet buffer. PacketBufferOverflow, - /// Could not parse the packet into a valid command. PacketParse(PacketParseError), - /// GDB client sent an unexpected packet. This should never happen! - /// Please re-run with `log` trace-level logging enabled and file an issue - /// at PacketUnexpected, - /// GDB client sent a packet with too much data for the given target. TargetMismatch, - /// Target encountered a fatal error. - TargetError(T), - /// Target responded with an unsupported stop reason. - /// - /// Certain stop reasons can only be used when their associated protocol - /// feature has been implemented. e.g: a Target cannot return a - /// `StopReason::HwBreak` if the hardware breakpoints IDET hasn't been - /// implemented. UnsupportedStopReason, - /// Target didn't report any active threads when there should have been at - /// least one running. - NoActiveThreads, - - /// The target has not opted into using implicit software breakpoints. - /// See [`Target::guard_rail_implicit_sw_breakpoints`] for more information. - /// - /// [`Target::guard_rail_implicit_sw_breakpoints`]: - /// crate::target::Target::guard_rail_implicit_sw_breakpoints + UnexpectedStepPacket, ImplicitSwBreakpoints, - /// The target has not indicated support for optional single stepping. See - /// [`Target::guard_rail_single_step_gdb_behavior`] for more information. - /// - /// If you encountered this error while using an `Arch` implementation - /// defined in `gdbstub_arch` and believe this is incorrect, please file an - /// issue at . - /// - /// [`Target::guard_rail_single_step_gdb_behavior`]: - /// crate::target::Target::guard_rail_single_step_gdb_behavior - SingleStepGdbBehavior(SingleStepGdbBehavior), + // DEVNOTE: this is a temporary workaround for something that can and should + // be caught at compile time via IDETs. That said, since i'm not sure when + // I'll find the time to cut a breaking release of gdbstub, I'd prefer to + // push out this feature as a non-breaking change now. + MissingCurrentActivePidImpl, // Internal - A non-fatal error occurred (with errno-style error code) // @@ -67,55 +47,100 @@ pub enum GdbStubError { NonFatalError(u8), } -impl From> for GdbStubError { - fn from(e: ResponseWriterError) -> Self { - GdbStubError::ConnectionWrite(e.0) +impl InternalError { + pub fn conn_read(e: C) -> Self { + InternalError::Connection(e, ConnectionErrorKind::Read) + } + + pub fn conn_write(e: C) -> Self { + InternalError::Connection(e, ConnectionErrorKind::Write) + } + + pub fn conn_init(e: C) -> Self { + InternalError::Connection(e, ConnectionErrorKind::Init) } } -impl From> for GdbStubError { - fn from(_: CapacityError) -> Self { - GdbStubError::PacketBufferOverflow +impl From> for InternalError { + fn from(e: ResponseWriterError) -> Self { + InternalError::Connection(e.0, ConnectionErrorKind::Write) } } +// these macros are used to keep the docs and `Display` impl in-sync + +macro_rules! unsupported_stop_reason { + () => { + "User error: cannot report stop reason without also implementing its corresponding IDET" + }; +} + +macro_rules! unexpected_step_packet { + () => { + "Received an unexpected `step` request. This is most-likely due to this GDB client bug: " + }; +} + +/// An error which may occur during a GDB debugging session. +/// +/// ## Additional Notes +/// +/// `GdbStubError`'s inherent `Display` impl typically contains enough context +/// for users to understand why the error occurred. +/// +/// That said, there are a few instances where the error condition requires +/// additional context. +/// +/// * * * +#[doc = concat!("_", unsupported_stop_reason!(), "_")] +/// +/// This is a not a bug with `gdbstub`. Rather, this is indicative of a bug in +/// your `gdbstub` integration. +/// +/// Certain stop reasons can only be used when their associated protocol feature +/// has been implemented. e.g: a Target cannot return a `StopReason::HwBreak` if +/// the hardware breakpoints IDET hasn't been implemented. +/// +/// Please double-check that you've implemented all the necessary `supports_` +/// methods related to the stop reason you're trying to report. +/// +/// * * * +#[doc = concat!("_", unexpected_step_packet!(), "_")] +/// +/// Unfortunately, there's nothing `gdbstub` can do to work around this bug. +/// +/// Until the issue is fixed upstream, certain architectures are essentially +/// forced to manually implement single-step support. +#[derive(Debug)] +pub struct GdbStubError { + kind: InternalError, +} + impl Display for GdbStubError where - C: Debug, - T: Debug, + C: Display, + T: Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use self::GdbStubError::*; - match self { - ConnectionInit(e) => write!(f, "Connection Error while initializing the session: {:?}", e), - ConnectionRead(e) => write!(f, "Connection Error while reading request: {:?}", e), - ConnectionWrite(e) => write!(f, "Connection Error while writing response: {:?}", e), + use self::InternalError::*; + const CONTEXT: &str = "See the `GdbStubError` docs for more details"; + match &self.kind { + Connection(e, ConnectionErrorKind::Init) => write!(f, "Connection Error while initializing the session: {}", e), + Connection(e, ConnectionErrorKind::Read) => write!(f, "Connection Error while reading request: {}", e), + Connection(e, ConnectionErrorKind::Write) => write!(f, "Connection Error while writing response: {}", e), ClientSentNack => write!(f, "Client nack'd the last packet, but `gdbstub` doesn't implement re-transmission."), - PacketBufferOverflow => write!(f, "Packet too big for provided buffer!"), - PacketParse(e) => write!(f, "Could not parse the packet into a valid command: {:?}", e), - PacketUnexpected => write!(f, "Client sent an unexpected packet. Please re-run with `log` trace-level logging enabled and file an issue at https://github.com/daniel5151/gdbstub/issues"), - TargetMismatch => write!(f, "GDB client sent a packet with too much data for the given target."), - TargetError(e) => write!(f, "Target threw a fatal error: {:?}", e), - UnsupportedStopReason => write!(f, "Target responded with an unsupported stop reason."), - NoActiveThreads => write!(f, "Target didn't report any active threads when there should have been at least one running."), - - ImplicitSwBreakpoints => write!(f, "Warning: The target has not opted into using implicit software breakpoints. See `Target::guard_rail_implicit_sw_breakpoints` for more information."), - SingleStepGdbBehavior(behavior) => { - use crate::arch::SingleStepGdbBehavior; - write!( - f, - "Warning: Mismatch between the targets' single-step support and arch-level single-step behavior: {} ", - match behavior { - SingleStepGdbBehavior::Optional => "", // unreachable, since optional single step will not result in an error - SingleStepGdbBehavior::Required => "GDB requires single-step support on this arch.", - SingleStepGdbBehavior::Ignored => "GDB ignores single-step support on this arch, yet the target has implemented support for it.", - SingleStepGdbBehavior::Unknown => "This arch's single-step behavior hasn't been tested yet: please conduct a test + upstream your findings!", - } - )?; - write!(f, "See `Target::guard_rail_single_step_gdb_behavior` for more information.") - }, - - NonFatalError(_) => write!(f, "Internal non-fatal error. End users should never see this! Please file an issue if you do!"), + PacketBufferOverflow => write!(f, "Received an oversized packet (did not fit in provided packet buffer)"), + PacketParse(e) => write!(f, "Failed to parse packet into a valid command: {:?}", e), + PacketUnexpected => write!(f, "Client sent an unexpected packet. This should never happen! Please re-run with `log` trace-level logging enabled and file an issue at https://github.com/daniel5151/gdbstub/issues"), + TargetMismatch => write!(f, "Received a packet with too much data for the given target"), + TargetError(e) => write!(f, "Target threw a fatal error: {}", e), + UnsupportedStopReason => write!(f, "{} {}", unsupported_stop_reason!(), CONTEXT), + UnexpectedStepPacket => write!(f, "{} {}", unexpected_step_packet!(), CONTEXT), + + ImplicitSwBreakpoints => write!(f, "Warning: The target has not opted into using implicit software breakpoints. See `Target::guard_rail_implicit_sw_breakpoints` for more information"), + MissingCurrentActivePidImpl => write!(f, "GDB client attempted to attach to a new process, but the target has not implemented support for `ExtendedMode::support_current_active_pid`"), + + NonFatalError(_) => write!(f, "Internal non-fatal error. You should never see this! Please file an issue if you do!"), } } } @@ -123,7 +148,48 @@ where #[cfg(feature = "std")] impl std::error::Error for GdbStubError where - C: Debug, - T: Debug, + C: Debug + Display, + T: Debug + Display, { } + +impl GdbStubError { + /// Check if the error was due to a target error. + pub fn is_target_error(&self) -> bool { + matches!(self.kind, InternalError::TargetError(..)) + } + + /// If the error was due to a target error, return the concrete error type. + pub fn into_target_error(self) -> Option { + match self.kind { + InternalError::TargetError(e) => Some(e), + _ => None, + } + } + + /// Check if the error was due to a connection error. + pub fn is_connection_error(&self) -> bool { + matches!(self.kind, InternalError::Connection(..)) + } + + /// If the error was due to a connection error, return the concrete error + /// type. + pub fn into_connection_error(self) -> Option<(C, ConnectionErrorKind)> { + match self.kind { + InternalError::Connection(e, kind) => Some((e, kind)), + _ => None, + } + } +} + +impl From> for GdbStubError { + fn from(kind: InternalError) -> Self { + GdbStubError { kind } + } +} + +impl From> for GdbStubError { + fn from(_: CapacityError) -> Self { + InternalError::PacketBufferOverflow.into() + } +} diff --git a/src/stub/mod.rs b/src/stub/mod.rs index 8cf08d9..b98c190 100644 --- a/src/stub/mod.rs +++ b/src/stub/mod.rs @@ -1,10 +1,14 @@ //! The core [`GdbStub`] type, used to drive a GDB debugging session for a //! particular [`Target`] over a given [`Connection`]. -use managed::ManagedSlice; - -use crate::conn::{Connection, ConnectionExt}; -use crate::target::Target; +pub use builder::GdbStubBuilder; +pub use builder::GdbStubBuilderError; +pub use core_impl::DisconnectReason; +pub use error::GdbStubError; +pub use stop_reason::BaseStopReason; +pub use stop_reason::IntoStopReason; +pub use stop_reason::MultiThreadStopReason; +pub use stop_reason::SingleThreadStopReason; mod builder; mod core_impl; @@ -13,19 +17,15 @@ mod stop_reason; pub mod state_machine; -pub use builder::{GdbStubBuilder, GdbStubBuilderError}; -pub use core_impl::DisconnectReason; -pub use error::GdbStubError; -pub use stop_reason::{ - BaseStopReason, IntoStopReason, MultiThreadStopReason, SingleThreadStopReason, -}; - -use GdbStubError as Error; +use self::error::InternalError; +use crate::conn::Connection; +use crate::conn::ConnectionExt; +use crate::target::Target; +use managed::ManagedSlice; /// Types and traits related to the [`GdbStub::run_blocking`] interface. pub mod run_blocking { use super::*; - use crate::conn::ConnectionExt; /// A set of user-provided methods required to run a GDB debugging session @@ -157,7 +157,7 @@ impl<'a, T: Target, C: Connection> GdbStub<'a, T, C> { pub fn run_blocking( self, target: &mut T, - ) -> Result> + ) -> Result> where C: ConnectionExt, E: run_blocking::BlockingEventLoop, @@ -167,7 +167,7 @@ impl<'a, T: Target, C: Connection> GdbStub<'a, T, C> { gdb = match gdb { state_machine::GdbStubStateMachine::Idle(mut gdb) => { // needs more data, so perform a blocking read on the connection - let byte = gdb.borrow_conn().read().map_err(Error::ConnectionRead)?; + let byte = gdb.borrow_conn().read().map_err(InternalError::conn_read)?; gdb.incoming_data(target, byte)? } @@ -179,12 +179,14 @@ impl<'a, T: Target, C: Connection> GdbStub<'a, T, C> { state_machine::GdbStubStateMachine::CtrlCInterrupt(gdb) => { // defer to the implementation on how it wants to handle the interrupt - let stop_reason = E::on_interrupt(target).map_err(Error::TargetError)?; + let stop_reason = + E::on_interrupt(target).map_err(InternalError::TargetError)?; gdb.interrupt_handled(target, stop_reason)? } state_machine::GdbStubStateMachine::Running(mut gdb) => { - use run_blocking::{Event as BlockingEventLoopEvent, WaitForStopReasonError}; + use run_blocking::Event as BlockingEventLoopEvent; + use run_blocking::WaitForStopReasonError; // block waiting for the target to return a stop reason let event = E::wait_for_stop_reason(target, gdb.borrow_conn()); @@ -198,10 +200,10 @@ impl<'a, T: Target, C: Connection> GdbStub<'a, T, C> { } Err(WaitForStopReasonError::Target(e)) => { - break Err(Error::TargetError(e)); + break Err(InternalError::TargetError(e).into()); } Err(WaitForStopReasonError::Connection(e)) => { - break Err(Error::ConnectionRead(e)); + break Err(InternalError::conn_read(e).into()); } } } @@ -216,7 +218,8 @@ impl<'a, T: Target, C: Connection> GdbStub<'a, T, C> { pub fn run_state_machine( mut self, target: &mut T, - ) -> Result, Error> { + ) -> Result, GdbStubError> + { // Check if the target hasn't explicitly opted into implicit sw breakpoints { let support_software_breakpoints = target @@ -225,33 +228,7 @@ impl<'a, T: Target, C: Connection> GdbStub<'a, T, C> { .unwrap_or(false); if !support_software_breakpoints && !target.guard_rail_implicit_sw_breakpoints() { - return Err(Error::ImplicitSwBreakpoints); - } - } - - // Check how the target's arch handles single stepping - { - use crate::arch::SingleStepGdbBehavior; - use crate::target::ext::base::ResumeOps; - - if let Some(ops) = target.base_ops().resume_ops() { - let support_single_step = match ops { - ResumeOps::SingleThread(ops) => ops.support_single_step().is_some(), - ResumeOps::MultiThread(ops) => ops.support_single_step().is_some(), - }; - - let behavior = target.guard_rail_single_step_gdb_behavior(); - - let return_error = match behavior { - SingleStepGdbBehavior::Optional => false, - SingleStepGdbBehavior::Required => !support_single_step, - SingleStepGdbBehavior::Ignored => support_single_step, - SingleStepGdbBehavior::Unknown => true, - }; - - if return_error { - return Err(Error::SingleStepGdbBehavior(behavior)); - } + return Err(InternalError::ImplicitSwBreakpoints.into()); } } @@ -259,7 +236,7 @@ impl<'a, T: Target, C: Connection> GdbStub<'a, T, C> { { self.conn .on_session_start() - .map_err(Error::ConnectionInit)?; + .map_err(InternalError::conn_init)?; } Ok(state_machine::GdbStubStateMachineInner::from_plain_gdbstub(self).into()) diff --git a/src/stub/state_machine.rs b/src/stub/state_machine.rs index 766d343..7b623dd 100644 --- a/src/stub/state_machine.rs +++ b/src/stub/state_machine.rs @@ -33,18 +33,21 @@ //! [`BlockingEventLoop`]: super::run_blocking::BlockingEventLoop //! [`GdbStub::run_blocking`]: super::GdbStub::run_blocking -use managed::ManagedSlice; - +use super::core_impl::FinishExecStatus; +use super::core_impl::GdbStubImpl; +use super::core_impl::State; +use super::DisconnectReason; +use super::GdbStub; use crate::arch::Arch; use crate::conn::Connection; use crate::protocol::recv_packet::RecvPacketStateMachine; -use crate::protocol::{Packet, ResponseWriter}; -use crate::stub::error::GdbStubError as Error; +use crate::protocol::Packet; +use crate::protocol::ResponseWriter; +use crate::stub::error::GdbStubError; +use crate::stub::error::InternalError; use crate::stub::stop_reason::IntoStopReason; use crate::target::Target; - -use super::core_impl::{FinishExecStatus, GdbStubImpl, State}; -use super::{DisconnectReason, GdbStub}; +use managed::ManagedSlice; /// State-machine interface to `GdbStub`. /// @@ -78,7 +81,6 @@ where // payloads, which are used when transitioning between states. pub mod state { use super::*; - use crate::stub::stop_reason::MultiThreadStopReason; // used internally when logging state transitions @@ -212,13 +214,13 @@ impl<'a, T: Target, C: Connection> GdbStubStateMachineInner<'a, state::Idle, mut self, target: &mut T, byte: u8, - ) -> Result, Error> { + ) -> Result, GdbStubError> { let packet_buffer = match self.i.recv_packet.pump(&mut self.i.packet_buffer, byte)? { Some(buf) => buf, None => return Ok(self.into()), }; - let packet = Packet::from_buf(target, packet_buffer).map_err(Error::PacketParse)?; + let packet = Packet::from_buf(target, packet_buffer).map_err(InternalError::PacketParse)?; let state = self .i .inner @@ -254,10 +256,10 @@ impl<'a, T: Target, C: Connection> GdbStubStateMachineInner<'a, state::Running, mut self, target: &mut T, reason: impl IntoStopReason, - ) -> Result, Error> { + ) -> Result, GdbStubError> { let mut res = ResponseWriter::new(&mut self.i.conn, target.use_rle()); let event = self.i.inner.finish_exec(&mut res, target, reason.into())?; - res.flush()?; + res.flush().map_err(InternalError::from)?; Ok(match event { FinishExecStatus::Handled => self @@ -272,21 +274,17 @@ impl<'a, T: Target, C: Connection> GdbStubStateMachineInner<'a, state::Running, } /// Pass a byte to the GDB stub. - /// - /// NOTE: unlike the `incoming_data` method in the `state::Idle` state, - /// this method does not perform any state transitions, and will - /// return a `GdbStubStateMachineInner` in the `state::Running` state. pub fn incoming_data( mut self, target: &mut T, byte: u8, - ) -> Result, Error> { + ) -> Result, GdbStubError> { let packet_buffer = match self.i.recv_packet.pump(&mut self.i.packet_buffer, byte)? { Some(buf) => buf, None => return Ok(self.into()), }; - let packet = Packet::from_buf(target, packet_buffer).map_err(Error::PacketParse)?; + let packet = Packet::from_buf(target, packet_buffer).map_err(InternalError::PacketParse)?; let state = self .i .inner @@ -333,7 +331,7 @@ impl<'a, T: Target, C: Connection> GdbStubStateMachineInner<'a, state::CtrlCInte self, target: &mut T, stop_reason: Option>, - ) -> Result, Error> { + ) -> Result, GdbStubError> { if self.state.from_idle { // target is stopped - we cannot report the stop reason yet Ok(self diff --git a/src/target/ext/auxv.rs b/src/target/ext/auxv.rs index c801af2..8760439 100644 --- a/src/target/ext/auxv.rs +++ b/src/target/ext/auxv.rs @@ -1,5 +1,6 @@ //! Access the target’s auxiliary vector. -use crate::target::{Target, TargetResult}; +use crate::target::Target; +use crate::target::TargetResult; /// Target Extension - Access the target’s auxiliary vector. pub trait Auxv: Target { diff --git a/src/target/ext/base/multithread.rs b/src/target/ext/base/multithread.rs index 693030e..e2293fd 100644 --- a/src/target/ext/base/multithread.rs +++ b/src/target/ext/base/multithread.rs @@ -3,7 +3,8 @@ use crate::arch::Arch; use crate::common::Signal; use crate::common::Tid; -use crate::target::{Target, TargetResult}; +use crate::target::Target; +use crate::target::TargetResult; /// Base required debugging operations for multi threaded targets. pub trait MultiThreadBase: Target { @@ -43,17 +44,25 @@ pub trait MultiThreadBase: Target { None } - /// Read bytes from the specified address range. + /// Read bytes from the specified address range and return the number of + /// bytes that were read. /// - /// If the requested address range could not be accessed (e.g: due to - /// MMU protection, unhanded page fault, etc...), an appropriate non-fatal - /// error should be returned. + /// Implementations may return a number `n` that is less than `data.len()` + /// to indicate that memory starting at `start_addr + n` cannot be + /// accessed. + /// + /// Implemenations may also return an appropriate non-fatal error if the + /// requested address range could not be accessed (e.g: due to MMU + /// protection, unhanded page fault, etc...). + /// + /// Implementations must guarantee that the returned number is less than or + /// equal `data.len()`. fn read_addrs( &mut self, start_addr: ::Usize, data: &mut [u8], tid: Tid, - ) -> TargetResult<(), Self>; + ) -> TargetResult; /// Write bytes to the specified address range. /// diff --git a/src/target/ext/base/single_register_access.rs b/src/target/ext/base/single_register_access.rs index 2887fef..efe06c0 100644 --- a/src/target/ext/base/single_register_access.rs +++ b/src/target/ext/base/single_register_access.rs @@ -1,7 +1,8 @@ //! Support for single-register read/write access. use crate::arch::Arch; -use crate::target::{Target, TargetResult}; +use crate::target::Target; +use crate::target::TargetResult; /// Target Extension - Support for single-register access. /// diff --git a/src/target/ext/base/singlethread.rs b/src/target/ext/base/singlethread.rs index c949886..40b002e 100644 --- a/src/target/ext/base/singlethread.rs +++ b/src/target/ext/base/singlethread.rs @@ -2,7 +2,8 @@ use crate::arch::Arch; use crate::common::Signal; -use crate::target::{Target, TargetResult}; +use crate::target::Target; +use crate::target::TargetResult; /// Base required debugging operations for single threaded targets. pub trait SingleThreadBase: Target { @@ -32,16 +33,24 @@ pub trait SingleThreadBase: Target { None } - /// Read bytes from the specified address range. + /// Read bytes from the specified address range and return the number of + /// bytes that were read. /// - /// If the requested address range could not be accessed (e.g: due to - /// MMU protection, unhanded page fault, etc...), an appropriate - /// non-fatal error should be returned. + /// Implementations may return a number `n` that is less than `data.len()` + /// to indicate that memory starting at `start_addr + n` cannot be + /// accessed. + /// + /// Implemenations may also return an appropriate non-fatal error if the + /// requested address range could not be accessed (e.g: due to MMU + /// protection, unhanded page fault, etc...). + /// + /// Implementations must guarantee that the returned number is less than or + /// equal `data.len()`. fn read_addrs( &mut self, start_addr: ::Usize, data: &mut [u8], - ) -> TargetResult<(), Self>; + ) -> TargetResult; /// Write bytes to the specified address range. /// diff --git a/src/target/ext/breakpoints.rs b/src/target/ext/breakpoints.rs index 8b40a04..cd44d99 100644 --- a/src/target/ext/breakpoints.rs +++ b/src/target/ext/breakpoints.rs @@ -1,7 +1,8 @@ //! Add/Remove various kinds of breakpoints. use crate::arch::Arch; -use crate::target::{Target, TargetResult}; +use crate::target::Target; +use crate::target::TargetResult; /// Target Extension - Set/Remove Breakpoints. pub trait Breakpoints: Target { diff --git a/src/target/ext/catch_syscalls.rs b/src/target/ext/catch_syscalls.rs index 694d8ad..b5f0ee9 100644 --- a/src/target/ext/catch_syscalls.rs +++ b/src/target/ext/catch_syscalls.rs @@ -1,7 +1,8 @@ //! Enable or disable catching syscalls from the inferior process. use crate::arch::Arch; -use crate::target::{Target, TargetResult}; +use crate::target::Target; +use crate::target::TargetResult; /// Target Extension - Enable and disable catching syscalls from the inferior /// process. diff --git a/src/target/ext/exec_file.rs b/src/target/ext/exec_file.rs index 0db9874..a819638 100644 --- a/src/target/ext/exec_file.rs +++ b/src/target/ext/exec_file.rs @@ -1,7 +1,7 @@ //! Provide exec-file path for the target. -use crate::target::{Target, TargetResult}; - use crate::common::Pid; +use crate::target::Target; +use crate::target::TargetResult; /// Target Extension - Provide current exec-file. /// diff --git a/src/target/ext/extended_mode.rs b/src/target/ext/extended_mode.rs index 817bec0..2c162ed 100644 --- a/src/target/ext/extended_mode.rs +++ b/src/target/ext/extended_mode.rs @@ -1,21 +1,10 @@ //! Enables [Extended Mode](https://sourceware.org/gdb/current/onlinedocs/gdb/Connecting.html) //! functionality when connecting using `target extended-remote`, such as //! spawning new processes and/or attaching to existing processes. -//! -//! # Disclaimer -//! -//! While this API has been end-to-end tested and confirmed working with a "toy" -//! target implementation (see the included `armv4t` example), it has _not_ been -//! "battle-tested" with a fully-featured extended-mode capable target. -//! -//! If you end up using this API to implement an extended-mode capable target, -//! _please_ file an issue on the repo detailing any bugs / usability issues you -//! may encountered while implementing this API! If everything happens to Just -//! Work as expected, nonetheless file an issue so that this disclaimer can be -//! removed in future releases! use crate::common::*; -use crate::target::{Target, TargetResult}; +use crate::target::Target; +use crate::target::TargetResult; /// Returned from `ExtendedMode::kill` /// @@ -92,6 +81,14 @@ pub trait ExtendedMode: Target { /// Attach to a new process with the specified PID. /// + /// Targets that wish to use `attach` are required to implement + /// [`CurrentActivePid`] (via `support_current_active_pid`), as the default + /// `gdbstub` behavior of always reporting a Pid of `1` will cause issues + /// when attaching to new processes. + /// + /// _Note:_ In the next API-breaking release of `gdbstub`, this coupling + /// will become a compile-time checked invariant. + /// /// In all-stop mode, all threads in the attached process are stopped; in /// non-stop mode, it may be attached without being stopped (if that is /// supported by the target). @@ -171,6 +168,13 @@ pub trait ExtendedMode: Target { fn support_configure_working_dir(&mut self) -> Option> { None } + + /// Support for reporting the current active Pid. Must be implemented in + /// order to use `attach`. + #[inline(always)] + fn support_current_active_pid(&mut self) -> Option> { + None + } } define_ext!(ExtendedModeOps, ExtendedMode); @@ -265,3 +269,24 @@ pub trait ConfigureWorkingDir: ExtendedMode { } define_ext!(ConfigureWorkingDirOps, ConfigureWorkingDir); + +/// Nested Target extension - Return the current active Pid. +pub trait CurrentActivePid: ExtendedMode { + /// Report the current active Pid. + /// + /// When implementing gdbstub on a platform that supports multiple + /// processes, the active PID needs to match the attached process. Failing + /// to do so will cause GDB to fail to attach to the target process. + /// + /// This should reflect the currently-debugged process which should be + /// updated when switching processes after calling + /// [`attach()`](ExtendedMode::attach). + /// + /// _Note:_ `gdbstub` doesn't yet support debugging multiple processes + /// _simultaneously_. If this is a feature you're interested in, please + /// leave a comment on this [tracking + /// issue](https://github.com/daniel5151/gdbstub/issues/124). + fn current_active_pid(&mut self) -> Result; +} + +define_ext!(CurrentActivePidOps, CurrentActivePid); diff --git a/src/target/ext/host_io.rs b/src/target/ext/host_io.rs index 77dbed0..1f5cfc6 100644 --- a/src/target/ext/host_io.rs +++ b/src/target/ext/host_io.rs @@ -1,17 +1,20 @@ //! Provide Host I/O operations for the target. -use bitflags::bitflags; - use crate::arch::Arch; use crate::target::Target; +use bitflags::bitflags; + +/// Host flags for opening files. +/// +/// Extracted from the GDB documentation at +/// [Open Flags](https://sourceware.org/gdb/current/onlinedocs/gdb/Open-Flags.html#Open-Flags), +/// and the LLDB source code at +/// [`lldb/include/lldb/Host/File.h`](https://github.com/llvm/llvm-project/blob/ec642ceebc1aacc8b16249df7734b8cf90ae2963/lldb/include/lldb/Host/File.h#L47-L66) +#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[repr(transparent)] +pub struct HostIoOpenFlags(u32); bitflags! { - /// Host flags for opening files. - /// - /// Extracted from the GDB documentation at - /// [Open Flags](https://sourceware.org/gdb/current/onlinedocs/gdb/Open-Flags.html#Open-Flags), - /// and the LLDB source code at - /// [`lldb/include/lldb/Host/File.h`](https://github.com/llvm/llvm-project/blob/ec642ceebc1aacc8b16249df7734b8cf90ae2963/lldb/include/lldb/Host/File.h#L47-L66) - pub struct HostIoOpenFlags: u32 { + impl HostIoOpenFlags: u32 { /// A read-only file. const O_RDONLY = 0x0; /// A write-only file. @@ -38,12 +41,16 @@ bitflags! { } } +/// Host file permissions. +/// +/// Extracted from the GDB documentation at +/// [mode_t Values](https://sourceware.org/gdb/current/onlinedocs/gdb/mode_005ft-Values.html#mode_005ft-Values) +#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[repr(transparent)] +pub struct HostIoOpenMode(u32); + bitflags! { - /// Host file permissions. - /// - /// Extracted from the GDB documentation at - /// [mode_t Values](https://sourceware.org/gdb/current/onlinedocs/gdb/mode_005ft-Values.html#mode_005ft-Values) - pub struct HostIoOpenMode: u32 { + impl HostIoOpenMode: u32 { /// A regular file. const S_IFREG = 0o100000; /// A directory. @@ -176,7 +183,7 @@ pub enum HostIoError { /// A target-specific fatal error. /// /// **WARNING:** Returning this error will immediately halt the target's - /// execution and return a `GdbStubError::TargetError`! + /// execution and return a [`GdbStubError`](crate::stub::GdbStubError)! /// /// Note that returning this error will _not_ notify the GDB client that the /// debugging session has been terminated, making it possible to resume diff --git a/src/target/ext/lldb_register_info_override.rs b/src/target/ext/lldb_register_info_override.rs index ccd1358..3c200da 100644 --- a/src/target/ext/lldb_register_info_override.rs +++ b/src/target/ext/lldb_register_info_override.rs @@ -38,10 +38,9 @@ impl<'a> Callback<'a> { /// runtime-configurable target, it's unlikely that you'll need to implement /// this extension. pub trait LldbRegisterInfoOverride: Target { - /// Invoke `reg_info.write(reg)` where `reg` is a - /// [`Register`](crate::arch::lldb::Register) struct to write information of - /// a single register or `reg_info.done()` if you want to end the - /// `qRegisterInfo` packet exchange. + /// Invoke `reg_info.write(reg)` where `reg` is a [`Register`] struct to + /// write information of a single register or `reg_info.done()` if you want + /// to end the `qRegisterInfo` packet exchange. fn lldb_register_info<'a>( &mut self, reg_id: usize, diff --git a/src/target/ext/memory_map.rs b/src/target/ext/memory_map.rs index 46ffd2a..2b918ce 100644 --- a/src/target/ext/memory_map.rs +++ b/src/target/ext/memory_map.rs @@ -1,5 +1,6 @@ //! Provide a memory map for the target. -use crate::target::{Target, TargetResult}; +use crate::target::Target; +use crate::target::TargetResult; /// Target Extension - Read the target's memory map. pub trait MemoryMap: Target { diff --git a/src/target/ext/monitor_cmd.rs b/src/target/ext/monitor_cmd.rs index 5317e67..c4bd20e 100644 --- a/src/target/ext/monitor_cmd.rs +++ b/src/target/ext/monitor_cmd.rs @@ -1,10 +1,11 @@ //! Create custom target-specific debugging commands accessible via GDB's //! `monitor` command! -use crate::target::Target; - +pub use crate::output; +pub use crate::outputln; pub use crate::protocol::ConsoleOutput; -pub use crate::{output, outputln}; + +use crate::target::Target; /// Target Extension - Handle custom GDB `monitor` commands. pub trait MonitorCmd: Target { diff --git a/src/target/ext/target_description_xml_override.rs b/src/target/ext/target_description_xml_override.rs index 7cb9011..79174b3 100644 --- a/src/target/ext/target_description_xml_override.rs +++ b/src/target/ext/target_description_xml_override.rs @@ -1,5 +1,6 @@ //! Override the target description XML specified by `Target::Arch`. -use crate::target::{Target, TargetResult}; +use crate::target::Target; +use crate::target::TargetResult; /// Target Extension - Override the target description XML specified by /// `Target::Arch`. diff --git a/src/target/mod.rs b/src/target/mod.rs index 985eea6..2b00d0b 100644 --- a/src/target/mod.rs +++ b/src/target/mod.rs @@ -151,7 +151,7 @@ //! &mut self, //! start_addr: u32, //! data: &mut [u8], -//! ) -> TargetResult<(), Self> { todo!() } +//! ) -> TargetResult { todo!() } //! //! fn write_addrs( //! &mut self, @@ -251,7 +251,7 @@ //! type is being used. e.g: on a 32-bit target, instead of cluttering up a //! method implementation with a parameter passed as `(addr: ::Usize)`, just write `(addr: u32)` directly. -use crate::arch::{Arch, SingleStepGdbBehavior}; +use crate::arch::Arch; pub mod ext; @@ -321,7 +321,8 @@ pub enum TargetError { /// A target-specific fatal error. /// /// **WARNING:** Returning this error will immediately terminate the GDB - /// debugging session, and return a top-level `GdbStubError::TargetError`! + /// debugging session, and return a + /// [`GdbStubError`](crate::stub::GdbStubError)! Fatal(E), } @@ -409,7 +410,7 @@ pub trait Target { /// # &mut self, /// # start_addr: u32, /// # data: &mut [u8], - /// # ) -> TargetResult<(), Self> { todo!() } + /// # ) -> TargetResult { todo!() } /// # /// # fn write_addrs( /// # &mut self, @@ -430,10 +431,6 @@ pub trait Target { /// handler must explicitly **opt-in** to this somewhat surprising GDB /// feature by overriding this method to return `true`. /// - /// If you are reading these docs after having encountered a - /// [`GdbStubError::ImplicitSwBreakpoints`] error, it's quite likely that - /// you'll want to implement explicit support for software breakpoints. - /// /// # Context /// /// An "implicit" software breakpoint is set by the GDB client by manually @@ -483,51 +480,41 @@ pub trait Target { /// e.g: On targets without native support for hardware single-stepping, /// calling `stepi` in GDB will result in the GDB client setting a temporary /// breakpoint on the next instruction + resuming via `continue` instead. - /// - /// [`GdbStubError::ImplicitSwBreakpoints`]: - /// crate::stub::GdbStubError::ImplicitSwBreakpoints #[inline(always)] fn guard_rail_implicit_sw_breakpoints(&self) -> bool { false } - /// Override the arch-level value for [`Arch::single_step_gdb_behavior`]. - /// - /// If you are reading these docs after having encountered a - /// [`GdbStubError::SingleStepGdbBehavior`] error, you may need to either: - /// - /// - implement support for single-step - /// - disable existing support for single step - /// - be a Good Citizen and perform a quick test to see what kind of - /// behavior your Arch exhibits. - /// - /// # WARNING + /// Enable/disable support for activating "no ack mode". /// - /// Unless you _really_ know what you're doing (e.g: working on a dynamic - /// target implementation, attempting to fix the underlying bug, etc...), - /// you should **not** override this method, and instead follow the advice - /// the error gives you. - /// - /// Incorrectly setting this method may lead to "unexpected packet" runtime - /// errors! - /// - /// # Details - /// - /// This method provides an "escape hatch" for disabling a workaround for a - /// bug in the mainline GDB client implementation. - /// - /// To squelch all errors, this method can be set to return - /// [`SingleStepGdbBehavior::Optional`] (though as mentioned above - you - /// should only do so if you're sure that's the right behavior). - /// - /// For more information, see the documentation for - /// [`Arch::single_step_gdb_behavior`]. + /// By default, this method returns `true`. /// - /// [`GdbStubError::SingleStepGdbBehavior`]: - /// crate::stub::GdbStubError::SingleStepGdbBehavior + /// _Author's note:_ Unless you're using `gdbstub` with a truly unreliable + /// transport line (e.g: a noisy serial connection), it's best to support + /// "no ack mode", as it can substantially improve debugging latency. + /// + /// **Warning:** `gdbstub` doesn't currently implement all necessary + /// features for running correctly over a unreliable transport! See issue + /// [\#137](https://github.com/daniel5151/gdbstub/issues/137) for details. + /// + /// # What is "No Ack Mode"? + /// + /// From the [GDB RSP docs](https://sourceware.org/gdb/onlinedocs/gdb/Packet-Acknowledgment.html#Packet-Acknowledgment): + /// + /// > By default, when either the host or the target machine receives a + /// > packet, the first response expected is an acknowledgment: either '+' + /// > (to indicate the package was received correctly) or '-' (to request + /// > retransmission). This mechanism allows the GDB remote protocol to + /// > operate over unreliable transport mechanisms, such as a serial line. + /// > + /// > In cases where the transport mechanism is itself reliable (such as a + /// > pipe or TCP connection), the '+'/'-' acknowledgments are redundant. It + /// > may be desirable to disable them in that case to reduce communication + /// > overhead, or for other reasons. This can be accomplished by means of + /// > the 'QStartNoAckMode' packet #[inline(always)] - fn guard_rail_single_step_gdb_behavior(&self) -> SingleStepGdbBehavior { - ::single_step_gdb_behavior() + fn use_no_ack_mode(&self) -> bool { + true } /// Enable/disable using the more efficient `X` packet to write to target @@ -725,8 +712,8 @@ macro_rules! impl_dyn_target { __delegate!(fn base_ops(&mut self) -> ext::base::BaseOps<'_, Self::Arch, Self::Error>); __delegate!(fn guard_rail_implicit_sw_breakpoints(&self) -> bool); - __delegate!(fn guard_rail_single_step_gdb_behavior(&self) -> SingleStepGdbBehavior); + __delegate!(fn use_no_ack_mode(&self) -> bool); __delegate!(fn use_x_upcase_packet(&self) -> bool); __delegate!(fn use_resume_stub(&self) -> bool); __delegate!(fn use_rle(&self) -> bool); -- cgit v1.2.3