Repository: daniel5151/gdbstub Branch: master Commit: 1bc505ff9ef7 Files: 266 Total size: 993.2 KB Directory structure: gitextract_vgu50cw1/ ├── .cargo/ │ └── config.toml ├── .git-blame-ignore-revs ├── .github/ │ ├── FUNDING.yml │ ├── pull_request_template.md │ └── workflows/ │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── docs/ │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ └── transition_guide.md ├── example_no_std/ │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── check_size.sh │ ├── dump_asm.sh │ ├── rustfmt.toml │ └── src/ │ ├── conn.rs │ ├── gdb.rs │ ├── main.rs │ └── print_str.rs ├── examples/ │ ├── armv4t/ │ │ ├── README.md │ │ ├── emu.rs │ │ ├── gdb/ │ │ │ ├── auxv.rs │ │ │ ├── breakpoints.rs │ │ │ ├── catch_syscalls.rs │ │ │ ├── exec_file.rs │ │ │ ├── extended_mode.rs │ │ │ ├── flash.rs │ │ │ ├── host_io.rs │ │ │ ├── libraries.rs │ │ │ ├── lldb_register_info_override.rs │ │ │ ├── memory_map.rs │ │ │ ├── mod.rs │ │ │ ├── monitor_cmd.rs │ │ │ ├── section_offsets.rs │ │ │ ├── target_description_xml_override.rs │ │ │ └── tracepoints.rs │ │ ├── main.rs │ │ ├── mem_sniffer.rs │ │ └── test_bin/ │ │ ├── .gdbinit │ │ ├── .gitignore │ │ ├── compile_test.sh │ │ ├── test.c │ │ ├── test.elf │ │ └── test.ld │ └── armv4t_multicore/ │ ├── README.md │ ├── emu.rs │ ├── gdb.rs │ ├── main.rs │ ├── mem_sniffer.rs │ └── test_bin/ │ ├── .gdbinit │ ├── .gitignore │ ├── compile_test.sh │ ├── test.c │ ├── test.elf │ └── test.ld ├── gdbstub_arch/ │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── LICENSE │ ├── README.md │ └── src/ │ ├── aarch64/ │ │ ├── core.xml │ │ ├── fpu.xml │ │ ├── mod.rs │ │ ├── reg/ │ │ │ ├── aarch64_core.rs │ │ │ ├── id.rs │ │ │ └── mod.rs │ │ └── sysregs.xml │ ├── arm/ │ │ ├── mod.rs │ │ └── reg/ │ │ ├── arm_core.rs │ │ ├── id.rs │ │ └── mod.rs │ ├── lib.rs │ ├── mips/ │ │ ├── mod.rs │ │ └── reg/ │ │ ├── id.rs │ │ ├── mips.rs │ │ └── mod.rs │ ├── msp430/ │ │ ├── mod.rs │ │ └── reg/ │ │ ├── id.rs │ │ ├── mod.rs │ │ └── msp430.rs │ ├── ppc/ │ │ ├── mod.rs │ │ └── reg/ │ │ ├── common.rs │ │ ├── id.rs │ │ └── mod.rs │ ├── riscv/ │ │ ├── mod.rs │ │ ├── reg/ │ │ │ ├── id.rs │ │ │ ├── mod.rs │ │ │ └── riscv.rs │ │ ├── rv32i.xml │ │ └── rv64i.xml │ ├── wasm/ │ │ ├── addr.rs │ │ ├── mod.rs │ │ └── reg/ │ │ ├── id.rs │ │ ├── mod.rs │ │ └── wasm_regs.rs │ └── x86/ │ ├── mod.rs │ └── reg/ │ ├── core32.rs │ ├── core64.rs │ ├── id.rs │ └── mod.rs ├── rustfmt.toml ├── scripts/ │ ├── check_target_delegation.sh │ └── test_dead_code_elim.sh └── src/ ├── arch.rs ├── common/ │ ├── mod.rs │ └── signal.rs ├── conn/ │ ├── impls/ │ │ ├── boxed.rs │ │ ├── mod.rs │ │ ├── tcpstream.rs │ │ └── unixstream.rs │ └── mod.rs ├── internal/ │ ├── be_bytes.rs │ ├── le_bytes.rs │ └── mod.rs ├── lib.rs ├── protocol/ │ ├── commands/ │ │ ├── _QAgent.rs │ │ ├── _QCatchSyscalls.rs │ │ ├── _QDisableRandomization.rs │ │ ├── _QEnvironmentHexEncoded.rs │ │ ├── _QEnvironmentReset.rs │ │ ├── _QEnvironmentUnset.rs │ │ ├── _QSetWorkingDir.rs │ │ ├── _QStartNoAckMode.rs │ │ ├── _QStartupWithShell.rs │ │ ├── _QTBuffer_upcase.rs │ │ ├── _QTDP.rs │ │ ├── _QTDPsrc.rs │ │ ├── _QTFrame.rs │ │ ├── _QTStart.rs │ │ ├── _QTStop.rs │ │ ├── _QTinit.rs │ │ ├── _bc.rs │ │ ├── _bs.rs │ │ ├── _c.rs │ │ ├── _d_upcase.rs │ │ ├── _g.rs │ │ ├── _g_upcase.rs │ │ ├── _h_upcase.rs │ │ ├── _k.rs │ │ ├── _m.rs │ │ ├── _m_upcase.rs │ │ ├── _p.rs │ │ ├── _p_upcase.rs │ │ ├── _qAttached.rs │ │ ├── _qC.rs │ │ ├── _qHostInfo.rs │ │ ├── _qOffsets.rs │ │ ├── _qProcessInfo.rs │ │ ├── _qRcmd.rs │ │ ├── _qRegisterInfo.rs │ │ ├── _qSupported.rs │ │ ├── _qTBuffer.rs │ │ ├── _qTP.rs │ │ ├── _qTStatus.rs │ │ ├── _qTfP.rs │ │ ├── _qTfV.rs │ │ ├── _qThreadExtraInfo.rs │ │ ├── _qTsP.rs │ │ ├── _qTsV.rs │ │ ├── _qWasmCallStack.rs │ │ ├── _qWasmGlobal.rs │ │ ├── _qWasmLocal.rs │ │ ├── _qWasmStackValue.rs │ │ ├── _qXfer_auxv_read.rs │ │ ├── _qXfer_exec_file.rs │ │ ├── _qXfer_features_read.rs │ │ ├── _qXfer_libraries_read.rs │ │ ├── _qXfer_libraries_svr4_read.rs │ │ ├── _qXfer_memory_map.rs │ │ ├── _qfThreadInfo.rs │ │ ├── _qsThreadInfo.rs │ │ ├── _r_upcase.rs │ │ ├── _s.rs │ │ ├── _t_upcase.rs │ │ ├── _vAttach.rs │ │ ├── _vCont.rs │ │ ├── _vFile_close.rs │ │ ├── _vFile_fstat.rs │ │ ├── _vFile_open.rs │ │ ├── _vFile_pread.rs │ │ ├── _vFile_pwrite.rs │ │ ├── _vFile_readlink.rs │ │ ├── _vFile_setfs.rs │ │ ├── _vFile_unlink.rs │ │ ├── _vFlashDone.rs │ │ ├── _vFlashErase.rs │ │ ├── _vFlashWrite.rs │ │ ├── _vKill.rs │ │ ├── _vRun.rs │ │ ├── _x_lowcase.rs │ │ ├── _x_upcase.rs │ │ ├── breakpoint.rs │ │ ├── exclamation_mark.rs │ │ └── question_mark.rs │ ├── commands.rs │ ├── common/ │ │ ├── hex.rs │ │ ├── lists.rs │ │ ├── mod.rs │ │ ├── qxfer.rs │ │ └── thread_id.rs │ ├── console_output.rs │ ├── mod.rs │ ├── packet.rs │ ├── recv_packet.rs │ └── response_writer.rs ├── stub/ │ ├── builder.rs │ ├── core_impl/ │ │ ├── auxv.rs │ │ ├── base.rs │ │ ├── breakpoints.rs │ │ ├── catch_syscalls.rs │ │ ├── exec_file.rs │ │ ├── extended_mode.rs │ │ ├── flash.rs │ │ ├── host_io.rs │ │ ├── host_process_info.rs │ │ ├── libraries.rs │ │ ├── lldb_register_info.rs │ │ ├── memory_map.rs │ │ ├── monitor_cmd.rs │ │ ├── no_ack_mode.rs │ │ ├── resume.rs │ │ ├── reverse_exec.rs │ │ ├── section_offsets.rs │ │ ├── single_register_access.rs │ │ ├── target_xml.rs │ │ ├── thread_extra_info.rs │ │ ├── tracepoints.rs │ │ ├── wasm.rs │ │ ├── x_lowcase_packet.rs │ │ └── x_upcase_packet.rs │ ├── core_impl.rs │ ├── error.rs │ ├── mod.rs │ ├── state_machine.rs │ └── stop_reason.rs ├── target/ │ ├── ext/ │ │ ├── auxv.rs │ │ ├── base/ │ │ │ ├── mod.rs │ │ │ ├── multithread.rs │ │ │ ├── reverse_exec.rs │ │ │ ├── single_register_access.rs │ │ │ └── singlethread.rs │ │ ├── breakpoints.rs │ │ ├── catch_syscalls.rs │ │ ├── exec_file.rs │ │ ├── extended_mode.rs │ │ ├── flash.rs │ │ ├── host_info.rs │ │ ├── host_io.rs │ │ ├── libraries.rs │ │ ├── lldb_register_info_override.rs │ │ ├── memory_map.rs │ │ ├── mod.rs │ │ ├── monitor_cmd.rs │ │ ├── process_info.rs │ │ ├── section_offsets.rs │ │ ├── target_description_xml_override.rs │ │ ├── thread_extra_info.rs │ │ ├── tracepoints.rs │ │ └── wasm.rs │ └── mod.rs └── util/ ├── dead_code_marker.rs ├── managed_vec.rs └── mod.rs ================================================ FILE CONTENTS ================================================ ================================================ FILE: .cargo/config.toml ================================================ [target.'cfg(all())'] rustflags = [ "-Dfuture_incompatible", "-Dnonstandard_style", "-Drust_2018_idioms", "-Wmissing_docs", "-Wunexpected_cfgs", "-Wunsafe_op_in_unsafe_fn", "-Wclippy::dbg_macro", "-Wclippy::debug_assert_with_mut_call", "-Wclippy::disallowed_types", "-Wclippy::filter_map_next", "-Wclippy::fn_params_excessive_bools", "-Wclippy::imprecise_flops", "-Wclippy::inefficient_to_string", "-Wclippy::let_unit_value", "-Wclippy::linkedlist", "-Wclippy::lossy_float_literal", "-Wclippy::macro_use_imports", "-Wclippy::map_flatten", "-Wclippy::needless_borrow", "-Wclippy::needless_continue", "-Wclippy::option_option", "-Wclippy::ref_option_ref", "-Wclippy::rest_pat_in_fully_bound_structs", "-Wclippy::string_to_string", "-Wclippy::suboptimal_flops", "-Wclippy::verbose_file_reads", # "-Wclippy::unused_self", # might be interesting to explore this... # deny explicit panic paths "-Wclippy::panic", "-Wclippy::todo", "-Wclippy::unimplemented", "-Wclippy::unreachable", "-Aclippy::collapsible_else_if", "-Aclippy::collapsible_if", "-Aclippy::too_many_arguments", "-Aclippy::type_complexity", "-Aclippy::bool_assert_comparison", # Primarily due to rust-lang/rust#8995 # # If this ever gets fixed, it's be possible to rewrite complex types using # inherent associated type aliases. # # For example, instead of writing this monstrosity: # # Result::Usize>>, Self::Error> # # ...it could be rewritten as: # # type StopReason = MultiThreadStopReason<::Usize>>; # Result, Self::Error> "-Aclippy::type_complexity", "-Aclippy::manual_range_patterns", ] ================================================ FILE: .git-blame-ignore-revs ================================================ # fmt everything using imports_granularity = "Item" e2329b80c2d51ee0ab4678365a35b27b51f32235 ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms ko_fi: prilik ================================================ FILE: .github/pull_request_template.md ================================================ ### Description e.g: This PR implements the `foobar` extension, based off the GDB documentation [here](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html). Closes #(issue number) ### API Stability - [ ] This PR does not require a breaking API change ### Checklist - Documentation - [ ] Ensured any public-facing `rustdoc` formatting looks good (via `cargo doc`) - [ ] (if appropriate) Added feature to "Debugging Features" in README.md - Validation - [ ] Included output of running `examples/armv4t` with `RUST_LOG=trace` + any relevant GDB output under the "Validation" section below - [ ] Included output of running `./example_no_std/check_size.sh` before/after changes under the "Validation" section below - _If implementing a new protocol extension IDET_ - [ ] Included a basic sample implementation in `examples/armv4t` - [ ] IDET can be optimized out (confirmed via `./example_no_std/check_size.sh`) - [ ] **OR** implementation requires introducing non-optional binary bloat (please elaborate under "Description") - _If upstreaming an `Arch` implementation_ - [ ] I have tested this code in my project, and to the best of my knowledge, it is working as intended. ### Validation
GDB output ``` !!!!! EXAMPLE OUTPUT !!!!! (gdb) info mem Using memory regions provided by the target. Num Enb Low Addr High Addr Attrs 0 y 0x00000000 0x100000000 rw nocache ```
armv4t output ``` !!!!! EXAMPLE OUTPUT !!!!! Finished dev [unoptimized + debuginfo] target(s) in 0.01s Running `target/debug/examples/armv4t` loading section ".text" into memory from [0x55550000..0x55550078] Setting PC to 0x55550000 Waiting for a GDB connection on "127.0.0.1:9001"... Debugger connected from 127.0.0.1:37142 TRACE gdbstub::gdbstub_impl > <-- + TRACE gdbstub::gdbstub_impl > <-- $qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;xmlRegisters=i386#6a TRACE gdbstub::protocol::response_writer > --> $PacketSize=1000;vContSupported+;multiprocess+;QStartNoAckMode+;ReverseContinue+;ReverseStep+;QDisableRandomization+;QEnvironmentHexEncoded+;QEnvironmentUnset+;QEnvironmentReset+;QStartupWithShell+;QSetWorkingDir+;swbreak+;hwbreak+;qXfer:features:read+;qXfer:memory-map:read+#e4 TRACE gdbstub::gdbstub_impl > <-- + TRACE gdbstub::gdbstub_impl > <-- $vMustReplyEmpty#3a INFO gdbstub::gdbstub_impl > Unknown command: vMustReplyEmpty TRACE gdbstub::protocol::response_writer > --> $#00 TRACE gdbstub::gdbstub_impl > <-- + TRACE gdbstub::gdbstub_impl > <-- $QStartNoAckMode#b0 TRACE gdbstub::protocol::response_writer > --> $OK#9a TRACE gdbstub::gdbstub_impl > <-- + TRACE gdbstub::gdbstub_impl > <-- $Hgp0.0#ad TRACE gdbstub::protocol::response_writer > --> $OK#9a TRACE gdbstub::gdbstub_impl > <-- $qXfer:features:read:target.xml:0,ffb#79 TRACE gdbstub::protocol::response_writer > --> $larmv4t#bb TRACE gdbstub::gdbstub_impl > <-- $qTStatus#49 INFO gdbstub::gdbstub_impl > Unknown command: qTStatus TRACE gdbstub::protocol::response_writer > --> $#00 TRACE gdbstub::gdbstub_impl > <-- $?#3f TRACE gdbstub::protocol::response_writer > --> $S05#b8 TRACE gdbstub::gdbstub_impl > <-- $qfThreadInfo#bb TRACE gdbstub::protocol::response_writer > --> $mp01.01#cd TRACE gdbstub::gdbstub_impl > <-- $qsThreadInfo#c8 TRACE gdbstub::protocol::response_writer > --> $l#6c TRACE gdbstub::gdbstub_impl > <-- $qAttached:1#fa GDB queried if it was attached to a process with PID 1 TRACE gdbstub::protocol::response_writer > --> $1#31 TRACE gdbstub::gdbstub_impl > <-- $Hc-1#09 TRACE gdbstub::protocol::response_writer > --> $OK#9a TRACE gdbstub::gdbstub_impl > <-- $qC#b4 INFO gdbstub::gdbstub_impl > Unknown command: qC TRACE gdbstub::protocol::response_writer > --> $#00 TRACE gdbstub::gdbstub_impl > <-- $g#67 TRACE gdbstub::protocol::response_writer > --> $00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000107856341200005555xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx10000000#66 TRACE gdbstub::gdbstub_impl > <-- $qfThreadInfo#bb TRACE gdbstub::protocol::response_writer > --> $mp01.01#cd TRACE gdbstub::gdbstub_impl > <-- $qsThreadInfo#c8 TRACE gdbstub::protocol::response_writer > --> $l#6c TRACE gdbstub::gdbstub_impl > <-- $qXfer:memory-map:read::0,ffb#18 TRACE gdbstub::protocol::response_writer > --> $l #75 TRACE gdbstub::gdbstub_impl > <-- $m55550000,4#61 TRACE gdbstub::protocol::response_writer > --> $04b02de5#26 TRACE gdbstub::gdbstub_impl > <-- $m5554fffc,4#35 TRACE gdbstub::protocol::response_writer > --> $00000000#7e TRACE gdbstub::gdbstub_impl > <-- $m55550000,4#61 TRACE gdbstub::protocol::response_writer > --> $04b02de5#26 TRACE gdbstub::gdbstub_impl > <-- $m5554fffc,4#35 TRACE gdbstub::protocol::response_writer > --> $00000000#7e TRACE gdbstub::gdbstub_impl > <-- $m55550000,2#5f TRACE gdbstub::protocol::response_writer > --> $04b0#f6 TRACE gdbstub::gdbstub_impl > <-- $m5554fffe,2#35 TRACE gdbstub::protocol::response_writer > --> $0000#7a TRACE gdbstub::gdbstub_impl > <-- $m5554fffc,2#33 TRACE gdbstub::protocol::response_writer > --> $0000#7a TRACE gdbstub::gdbstub_impl > <-- $m55550000,2#5f TRACE gdbstub::protocol::response_writer > --> $04b0#f6 TRACE gdbstub::gdbstub_impl > <-- $m5554fffe,2#35 TRACE gdbstub::protocol::response_writer > --> $0000#7a TRACE gdbstub::gdbstub_impl > <-- $m5554fffc,2#33 TRACE gdbstub::protocol::response_writer > --> $0000#7a TRACE gdbstub::gdbstub_impl > <-- $m55550000,4#61 TRACE gdbstub::protocol::response_writer > --> $04b02de5#26 TRACE gdbstub::gdbstub_impl > <-- $m5554fffc,4#35 TRACE gdbstub::protocol::response_writer > --> $00000000#7e TRACE gdbstub::gdbstub_impl > <-- $m55550000,4#61 TRACE gdbstub::protocol::response_writer > --> $04b02de5#26 TRACE gdbstub::gdbstub_impl > <-- $m5554fffc,4#35 TRACE gdbstub::protocol::response_writer > --> $00000000#7e TRACE gdbstub::gdbstub_impl > <-- $m55550000,4#61 TRACE gdbstub::protocol::response_writer > --> $04b02de5#26 TRACE gdbstub::gdbstub_impl > <-- $m55550000,4#61 TRACE gdbstub::protocol::response_writer > --> $04b02de5#26 TRACE gdbstub::gdbstub_impl > <-- $m55550000,4#61 TRACE gdbstub::protocol::response_writer > --> $04b02de5#26 TRACE gdbstub::gdbstub_impl > <-- $m0,4#fd TRACE gdbstub::protocol::response_writer > --> $00000000#7e ```
Before/After `./example_no_std/check_size.sh` output ### Before ``` !!!!! EXAMPLE OUTPUT !!!!! target/release/gdbstub-nostd : section size addr .interp 28 680 .note.gnu.build-id 36 708 .note.ABI-tag 32 744 .gnu.hash 36 776 .dynsym 360 816 .dynstr 193 1176 .gnu.version 30 1370 .gnu.version_r 48 1400 .rela.dyn 408 1448 .init 27 4096 .plt 16 4128 .plt.got 8 4144 .text 15253 4160 .fini 13 19416 .rodata 906 20480 .eh_frame_hdr 284 21388 .eh_frame 1432 21672 .init_array 8 28072 .fini_array 8 28080 .dynamic 448 28088 .got 136 28536 .data 8 28672 .bss 8 28680 .comment 43 0 Total 19769 ``` ### After ``` !!!!! EXAMPLE OUTPUT !!!!! target/release/gdbstub-nostd : section size addr .interp 28 680 .note.gnu.build-id 36 708 .note.ABI-tag 32 744 .gnu.hash 36 776 .dynsym 360 816 .dynstr 193 1176 .gnu.version 30 1370 .gnu.version_r 48 1400 .rela.dyn 408 1448 .init 27 4096 .plt 16 4128 .plt.got 8 4144 .text 15253 4160 .fini 13 19416 .rodata 906 20480 .eh_frame_hdr 284 21388 .eh_frame 1432 21672 .init_array 8 28072 .fini_array 8 28080 .dynamic 448 28088 .got 136 28536 .data 8 28672 .bss 8 28680 .comment 43 0 Total 19769 ```
================================================ FILE: .github/workflows/ci.yml ================================================ on: [push, pull_request] name: ci jobs: test: name: clippy + tests + docs runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - name: cargo clippy uses: actions-rs/cargo@v1 with: command: clippy args: --workspace --tests --examples --features=std -- -D warnings # don't forget the no_std example! - name: cargo clippy (example_no_std) uses: actions-rs/cargo@v1 with: command: clippy args: --manifest-path example_no_std/Cargo.toml - name: check dyn Target delegations run: ./scripts/check_target_delegation.sh shell: bash - name: cargo test uses: actions-rs/cargo@v1 with: command: test args: --workspace --features=std - name: no panics in example_no_std run: ./example_no_std/dump_asm.sh shell: bash - name: cargo doc run: cargo doc --workspace --features=std env: RUSTDOCFLAGS: "-Dwarnings" rustfmt: name: rustfmt (nightly) runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: nightly override: true components: rustfmt - name: cargo +nightly fmt uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check # don't forget the no_std example! - name: cargo +nightly fmt (example_no_std) uses: actions-rs/cargo@v1 with: command: fmt args: --manifest-path example_no_std/Cargo.toml ================================================ FILE: .gitignore ================================================ /target **/*.rs.bk Cargo.lock **/.gdb_history # The GDB client may core dump if the target is implemented incorrectly **/core .vscode ================================================ FILE: CHANGELOG.md ================================================ 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.10 #### New Features - `GdbStubStateMachineInner::report_stop_with_regs` - new API to pass expedited register values in the stop reply T-packet. [\#189](https://github.com/daniel5151/gdbstub/pull/189) ([cfallin](https://github.com/cfallin)) #### New Protocol Extensions - `Wasm` - (LLDB extension) Wasm-specific actions (i.e: reading Wasm call stack, and global/local/stack vars). [\#188](https://github.com/daniel5151/gdbstub/pull/188) ([cfallin](https://github.com/cfallin)) - `ProcessInfo` + `HostInfo` - (LLDB extension) Report key/value metadata about the host / process being debugged. [\#190](https://github.com/daniel5151/gdbstub/pull/190) ([cfallin](https://github.com/cfallin)) #### Internal Improvements - Dependencies: Switch from `paste` to `pastey`. [\#187](https://github.com/daniel5151/gdbstub/pull/187) ([Maiux92](https://github.com/Maiux92)) # 0.7.9 #### New Protocol Extensions - `MultiThreadSchedulerLocking` - Support running in [Scheduler Locking](https://sourceware.org/gdb/current/onlinedocs/gdb#index-scheduler-locking-mode) mode. [\#179](https://github.com/daniel5151/gdbstub/pull/179) ([Satar07](https://github.com/Satar07)) - `Libraries` - List a target's loaded libraries. [\#183](https://github.com/daniel5151/gdbstub/pull/183) ([mrexodia](https://github.com/mrexodia)) - _Note:_ This is a platform-agnostic version of the existing System-V/Unix-specific `LibrariesSvr4` feature, which landed in `0.7.1` #### Bugfixes - Fixed a bug in the RLE encoding where a `$` would be inserted in the packet when encoding runs of 8 chars (e.g: `00000000`). [\#182](https://github.com/daniel5151/gdbstub/pull/182) ([mrexodia](https://github.com/mrexodia)) # 0.7.8 #### New Features - `HostIo` - Add HostIoErrno::{EIO,ENOSYS} variants. [\#175](https://github.com/daniel5151/gdbstub/pull/175) ([yodel](https://github.com/yodel)) # 0.7.7 #### Bugfixes - Add missing `alloc::borrow::ToOwned` import when building `no_std` with `alloc`. [\#174](https://github.com/daniel5151/gdbstub/pull/174) ([AdamKMeller](https://github.com/AdamKMeller)) # 0.7.6 #### New Protocol Extensions - `Flash` - Support for GDB [flash commands](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Packets.html#Packets) (e.g: `load`). [\#172](https://github.com/daniel5151/gdbstub/pull/172) ([eulerdisk](https://github.com/eulerdisk)) # 0.7.5 #### New Protocol Extensions - `Tracepoints` - Basic [tracepoint extension](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Tracepoints.html) support. [\#160](https://github.com/daniel5151/gdbstub/pull/160) ([cczetier](https://github.com/cczetier)) - _Note:_ Most fundamental tracepoint operations are supported, but there quite a few packets / APIs that are not yet implemented. Please see the module documentation for additional details. #### Bugfixes - Fixed case-insensitive filename collision issue [\#166](https://github.com/daniel5151/gdbstub/issues/166) introduced in `0.7.4` # 0.7.4 (Yanked) _This version was yanked, as it introduced two files - `qTBuffer.rs` and `QTBuffer.rs` - that resulted in filename collisions when cloning `gdbstub` on case-insensitive filesystems._ # 0.7.3 #### New Features - Add new `core_error` feature, to have `GdbStubError` impl `core::error::Error`. [\#154](https://github.com/daniel5151/gdbstub/pull/154) ([ultimaweapon](https://github.com/ultimaweapon)) - _Note:_ Out of an abundance of caution, this has been put behind a feature-flag, as while `gdbstub` doesn't claim a strict MSRV at this time, it seemed unwise to have a PATCH release break folks stuck on a pre-1.81 Rust toolchain. # 0.7.2 #### Bugfixes - Add workaround for vCont packets that specify a '0' (Any) thread-id - For more context, see [`e9a5296c`](https://github.com/daniel5151/gdbstub/commit/e9a5296c4d02f4b5b73d5738654a33d01afa8711) #### Internal Improvements - Various README tweaks - Various clippy lint fixes - Fix incorrect valid-addr check in armv4t example # 0.7.1 #### New Protocol Extensions - `LibrariesSvr4` - List an SVR4 (System-V/Unix) target's libraries. [\#142](https://github.com/daniel5151/gdbstub/pull/142) ([alexcrichton](https://github.com/alexcrichton)) # 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. [\#115](https://github.com/daniel5151/gdbstub/pull/115) ([geigerzaehler](https://github.com/geigerzaehler)) - `HostIo` - `bitflags` has been updated from `1.x` to `2.x`, affecting the type of `HostIoOpenFlags` and `HostIoOpenMode` [\#138](https://github.com/daniel5151/gdbstub/pull/138) ([qwandor](https://github.com/qwandor)) #### 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 - Avoid truncating `X` packets that contain `:` and `,` as part of the payload. [\#121](https://github.com/daniel5151/gdbstub/pull/121) ([709924470](https://github.com/709924470)) #### Internal Improvements - Various README tweaks - Remove some `unsafe` code - CI improvements - Run no-panic checks on `example_no_std` - Run CI on docs # 0.6.3 #### New Features - `SingleRegisterAccess`: Support reporting unavailable regs [\#107](https://github.com/daniel5151/gdbstub/pull/107) ([ptosi](https://github.com/ptosi)) # 0.6.2 #### New Protocol Extensions - `MultiThreadBase > ThreadExtraInfo` - Provide extra information per-thread. [\#106](https://github.com/daniel5151/gdbstub/pull/106) ([thefaxman](https://github.com/thefaxman)) - `LldbRegisterInfo` - (LLDB specific) Report register information in the LLDB format. [\#103](https://github.com/daniel5151/gdbstub/pull/103) ([jawilk](https://github.com/jawilk)) - This information can be statically included as part of the `Arch` implemention, or dynamically reported via the `LldbRegisterInfoOverride` IDET. #### Bugfixes - Report thread ID in response to `?` packet. [\#105](https://github.com/daniel5151/gdbstub/pull/105) ([thefaxman](https://github.com/thefaxman)) #### Internal Improvements - Tweak enabled clippy lints - Added a light dusting of `#[inline]` across the packet parsing code, crunching the code down even further - Expanded on "no-panic guarantee" docs # 0.6.1 #### New Features - add LLDB-specific HostIoOpenFlags [\#100](https://github.com/daniel5151/gdbstub/pull/100) ([mrk](https://github.com/mrk-its)) # 0.6.0 After over a half-year of development, `gdbstub` 0.6 has finally been released! This massive release delivers a slew of new protocol extensions, internal improvements, and key API improvements. Some highlights include: - A new _non-blocking_ `GdbStubStateMachine` API, enabling `gdbstub` to integrate nicely with async event loops! - Moreover, on `no_std` platforms, this new API enables `gdbstub` to be driven directly via breakpoint/serial interrupt handlers! - This API is already being used in several Rust kernel projects, such as [`vmware-labs/node-replicated-kernel`](https://github.com/vmware-labs/node-replicated-kernel/tree/4326704/kernel/src/arch/x86_64/gdb) and [`betrusted-io/xous-core`](https://github.com/betrusted-io/xous-core/blob/7d3d710/kernel/src/debug/gdb_server.rs) to enable bare-metal, in-kernel debugging. - `gdbstub` is now entirely **panic free** in release builds! - \* subject to `rustc`'s compiler optimizations - This was a pretty painstaking effort, but the end result is a substantial reduction in binary size on `no_std` platforms. - Tons of new and exciting protocol extensions, including but not limited to: - Support for remote file I/O (reading/writing files to the debug target) - Fetching remote memory maps - Catching + reporting syscall entry/exit conditions - ...and many more! - A new license: `gdbstub` is licensed under MIT OR Apache-2.0 See the [changelog](https://github.com/daniel5151/gdbstub/blob/dev/0.6/CHANGELOG.md) for a comprehensive rundown of all the new features. While this release does come with quite a few breaking changes, the core IDET-based `Target` API has remained much the same, which should make porting code over from 0.5.x to 0.6 pretty mechanical. See the [`transition_guide.md`](./docs/transition_guide.md) for guidance on upgrading from `0.5.x` to `0.6`. And as always, a huge shoutout to the folks who contributed PRs, Issues, and ideas to `gdbstub` - this release wouldn't have been possible without you! Special shoutouts to [gz](https://github.com/gz) and [xobs](https://github.com/xobs) for helping me test and iterate on the new bare-metal state machine API, and [bet4it](https://github.com/bet4it) for pointing out and implementing many useful API improvements and internal refactors. Cheers! #### New Features - The new `GdbStubStateMachine` API gives users the power and flexibility to integrate `gdbstub` into their project-specific event loop infrastructure. - e.g: A global instance of `GdbStubStateMachine` can be driven directly from bare-metal interrupt handlers in `no_std` environments - e.g: A project using `async`/`await` can wrap `GdbStubStateMachine` in a task, yielding execution while waiting for the target to resume / new data to arrive down the `Connection` - Removed all panicking code from `gdbstub` - See the [commit message](https://github.com/daniel5151/gdbstub/commit/ecbbaf72e01293b410ef3bc5970d18aa81e45599) for more details on how this was achieved. - Introduced strongly-typed enum for protocol defined signal numbers (instead of using bare `u8`s) - Added basic feature negotiation to support clients that don't support `multiprocess+` extensions. - Relicensed `gdbstub` under MIT OR Apache-2.0 [\#68](https://github.com/daniel5151/gdbstub/pull/68) - Added several new "guard rails" to avoid common integration footguns: - `Target::guard_rail_implicit_sw_breakpoints` - guards against the GDB client silently overriding target instructions with breakpoints if `SwBreakpoints` hasn't been implemented. - `Target::guard_rail_single_step_gdb_behavior` - guards against a GDB client bug where support for single step may be required / ignored on certain platforms (e.g: required on x86, ignored on MIPS) - Added several new "toggle switches" to enable/disable parts of the protocol (all default to `true`) - `Target::use_x_upcase_packet` - toggle support for the more efficient `X` memory write packet - `Target::use_resume_stub` - toggle `gdbstub`'s built-in "stub" resume handler that returns `SIGRAP` if a target doesn't implement support for resumption - `Target::use_rle` - toggle whether outgoing packets are Run Length Encoded (RLE) #### New Protocol Extensions - `MemoryMap` - Get memory map XML file from the target. [\#54](https://github.com/daniel5151/gdbstub/pull/54) ([Tiwalun](https://github.com/Tiwalun)) - `CatchSyscalls` - Enable and disable catching syscalls from the inferior process. [\#57](https://github.com/daniel5151/gdbstub/pull/57) ([mchesser](https://github.com/mchesser)) - `HostIo` - Perform I/O operations on host. [\#66](https://github.com/daniel5151/gdbstub/pull/66) ([bet4it](https://github.com/bet4it)) - Support for all Host I/O operations: `open`, `close`, `pread`, `pwrite`, `fstat`, `unlink`, `readlink`, `setfs` - `ExecFile` - Get full absolute path of the file that was executed to create a process running on the remote system. [\#69](https://github.com/daniel5151/gdbstub/pull/69) ([bet4it](https://github.com/bet4it)) - `Auxv` - Access the target’s auxiliary vector. [\#86](https://github.com/daniel5151/gdbstub/pull/86) ([bet4it](https://github.com/bet4it)) - Implement `X` packet - More efficient bulk-write to memory (superceding the `M` packet). [\#82](https://github.com/daniel5151/gdbstub/pull/82) ([gz](https://github.com/gz)) #### Breaking API Changes - `Connection` API: - Removed the `read` and `peek` methods from `Connection` - These have been moved to the new `ConnectionExt` trait, which is used in the new `GdbStub::run_blocking` API - `Arch` API: - Dynamic read_register + RegId support. [\#85](https://github.com/daniel5151/gdbstub/pull/85) ([bet4it](https://github.com/bet4it)) - `Target` APIs: - prefix all IDET methods with `support_` - _makes it far easier to tell at-a-glance whether a method is an IDET, or an actual handler method. - Introduce strongly-typed enum for protocol defined signal numbers (instead of using bare `u8`s) - `Base` API: - Make single-stepping optional [\#92](https://github.com/daniel5151/gdbstub/pull/92) - Remove `GdbInterrupt` type (interrupt handling lifted to higher-level APIs) - Remove `ResumeAction` type (in favor of separate methods for various resume types) - `Breakpoints` API: - `HwWatchpoint`: Plumb watchpoint `length` parameter to public API - `TargetXml` API: - Support for `` in target.xml, which required including the `annex` parameter in the handler method. - `annex` is set to `b"target.xml"` on the fist call, though it may be set to other values in subsequent calls if `` is being used. - Pass `PacketBuf`-backed `&mut [u8]` as a response buffer to various APIs [\#72](https://github.com/daniel5151/gdbstub/pull/72) ([bet4it](https://github.com/bet4it)) - Improvement over the callback-based approach. - This change is possible thanks to a clause in the GDB spec that specifies that responses will never exceed the size of the `PacketBuf`. - Also see [\#70](https://github.com/daniel5151/gdbstub/pull/70), which tracks some other methods that might be refactored to use this approach in the future. #### Internal Improvements - Documentation - Fix crates.io badges [\#71](https://github.com/daniel5151/gdbstub/pull/71) ([atouchet](https://github.com/atouchet)) - Add `uhyve` to real-world examples [\#73](https://github.com/daniel5151/gdbstub/pull/73) ([mkroening](https://github.com/mkroening)) - Use stable `clippy` in CI - Enable logging for responses with only alloc [\#78](https://github.com/daniel5151/gdbstub/pull/78) ([gz](https://github.com/gz)) - Lots of internal refactoring and cleanup # 0.5.0 While the overall structure of the API has remained the same, `0.5.0` does introduce a few breaking API changes that require some attention. That being said, it should not be a difficult migration, and updating to `0.5.0` from `0.4` shouldn't take more than 10 mins of refactoring. Check out [`transition_guide.md`](./docs/transition_guide.md) for guidance on upgrading from `0.4.x` to `0.5`. #### New Features - Implement Run-Length-Encoding (RLE) on outgoing packets - _This significantly cuts down on the data being transferred over the wire when reading from registers/memory_ - Add target-specific `kind: Arch::BreakpointKind` parameters to the Breakpoint API - _While emulated systems typically implement breakpoints by pausing execution once the PC hits a certain value, "real" systems typically need to patch the instruction stream with a breakpoint instruction. On systems with variable-sized instructions, this `kind` parameter specifies the size of the instruction that should be injected._ - Implement `ResumeAction::{Step,Continue}WithSignal` - Added the `Exited(u8)`, `Terminated(u8)`, and `ReplayLog("begin"|"end")` stop reasons. - Added `DisconnectReason::Exited(u8)` and `DisconnectReason::Terminated(u8)`. - Reworked the `MultiThreadOps::resume` API to be significantly more ergonomic and efficient - See the [transition guide](https://github.com/daniel5151/gdbstub/blob/master/docs/transition_guide.md#new-multithreadopsresume-api) for more details. #### New Protocol Extensions - `{Single,Multi}ThreadReverse{Step,Continue}` - Support for reverse-step and reverse-continue. [\#48](https://github.com/daniel5151/gdbstub/pull/48 ) ([DrChat](https://github.com/DrChat)) - `{Single,Multi}ThreadRangeStepping` - Optional optimized [range stepping](https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#range-stepping) support. #### Breaking Arch Changes - **`gdbstub::arch` has been moved into a separate `gdbstub_arch` crate** - _See [\#45](https://github.com/daniel5151/gdbstub/issues/45) for details on why this was done._ - (x86) Break GPRs & SRs into individual fields/variants [\#34](https://github.com/daniel5151/gdbstub/issues/34) #### Breaking API Changes - Base Protocol Refactors - Reworked the `MultiThreadOps::resume` API - Added a wrapper around the raw `check_gdb_interrupt` callback, hiding the underlying implementation details - Extracted base protocol single-register access methods (`{read,write}_register`) into separate `SingleRegisterAccess` trait - _These are optional GDB protocol methods, and as such, should be modeled as IDETs_ - Protocol Extension Refactors - Consolidated the `{Hw,Sw}Breakpoints/Watchpoints` IDETs under a single `Breakpoints` IDET + sub-IDETs - Added new arch-specific `kind: Arch::BreakpointKind` parameter to `add_{hw,sw}_breakpoint` methods - Renamed `target::ext::extended_mod::ConfigureASLR{Ops}` to `ConfigureAslr{Ops}` (clippy::upper_case_acronyms) - Added `{Step,Continue}WithSignal` variants to `target::ext::base::ResumeAction` - Trait Changes - `arch::Arch`: Added `type BreakpointKind`. Required to support arch-specific breakpoint kinds - `arch::Arch`: (very minor) Added [`num_traits::FromPrimitive`](https://docs.rs/num/0.4.0/num/traits/trait.FromPrimitive.html) bound to `Arch::Usize` - `arch::Registers`: Added `type ProgramCounter` and associated `fn pc(&self) -> Self::ProgramCounter` method. Added preemptively in anticipation of future GDB Agent support - Removed the `Halted` stop reason (more accurate to simply return `{Exited|Terminated}(SIGSTOP)` instead). - Removed the `Halted` disconnect reason (replaced with the `Exited` and `Terminated` stop reasons instead). - Removed the implicit `ExtendedMode` attached PID tracking when `alloc` was available. See [`23b56038`](https://github.com/daniel5151/gdbstub/commit/23b56038) rationale behind this change. #### Internal Improvements - Split monolithic `GdbStubImpl` implementation into separate files (by protocol extension) - Finally rewrite + optimize `GdbStubImpl::do_vcont`, along with streamlining its interactions with the legacy `s` and `c` packets - Sprinkle more IDET-based dead code elimination hints (notably wrt. stop reasons) - Remove the default `self.current_mem_tid` hack, replacing it with a much more elegant solution - Packet Parser improvements - Remove last remaining bit of UTF-8 related code - Eliminate as much panicking bounds-checking code as possible - support efficient parsing of packets that are parsed differently depending on active protocol extension (namely, the breakpoint packets) - (currently unused) Zero-cost support for parsing `Z` and `z` packets with embedded agent bytecode expressions - Use intra-doc links whenever possible #### Bugfixes - Fix `RiscvRegId` for `arch::riscv::Riscv64` [\#46](https://github.com/daniel5151/gdbstub/issues/46) ([fzyz999](https://github.com/fzyz999)) # 0.4.5 #### New Protocol Extensions - `TargetDescriptionXmlOverride` - Allow targets to override the target description XML file (`target.xml`) specified by `Target::Arch::target_description_xml`. This is useful in cases where a `Target` is expected to be generic over multiple architectures. [\#43](https://github.com/daniel5151/gdbstub/pull/43) (with help from [DrChat](https://github.com/DrChat)) # 0.4.4 #### Bugfixes - use `write!` instead of `writeln!` in `output!` macro [\#41](https://github.com/daniel5151/gdbstub/issues/41) # 0.4.3 #### New Arch Implementations - Implement `RegId` for Mips/Mips64 [\#38](https://github.com/daniel5151/gdbstub/pull/38) ([starfleetcadet75](https://github.com/starfleetcadet75)) - Implement `RegId` for MSP430 [\#38](https://github.com/daniel5151/gdbstub/pull/38) ([starfleetcadet75](https://github.com/starfleetcadet75)) # 0.4.2 #### Packaging - Exclude test object files from package [\#37](https://github.com/daniel5151/gdbstub/pull/37) ([keiichiw](https://github.com/keiichiw)) # 0.4.1 #### New Arch Implementations - Implement `RegId` for x86/x86_64 [\#34](https://github.com/daniel5151/gdbstub/pull/34) ([keiichiw](https://github.com/keiichiw)) #### Bugfixes - Switch fatal error signal from `T06` to `S05`, - specify cfg-if 0.1.10 or later [\#33](https://github.com/daniel5151/gdbstub/pull/33) ([keiichiw](https://github.com/keiichiw)) - `cargo build` fails if cfg-if is 0.1.9 or older #### Internal Improvements - Don't hard-code u64 when parsing packets (use big-endian byte arrays + late conversion to `Target::Arch::Usize`). # 0.4.0 This version includes a _major_ API overhaul, alongside a slew of new features and general improvements. While updating to `0.4.0` will require some substantial code modifications, it's well worth the effort, as `0.4.0` is the safest, leanest, and most featureful release of `gdbstub` yet! Fun fact: Even after adding a _bunch_ of new features and bug-fixes, the in-tree `example_no_std` has remained just as small! The example on the `semver-fix-0.2.2` branch is `20251` bytes, while the example on `0.4.0` is `20246` bytes. #### Breaking API Changes - Rewrite the `Target` API in terms of "Inlineable Dyn Extension Traits" (IDETs) - _By breaking up `Target` into smaller pieces which can be mixed-and-matched, it not only makes it easier to get up-and-running with `gdbstub`, but it also unlocks a lot of awesome internal optimizations:_ - Substantially reduces binary-size footprint by guaranteeing dead-code-elimination of parsing/handling unimplemented GDB protocol features. - Compile-time enforcement that certain groups of methods are implemented in-tandem (e.g: `add_sw_breakpoint` and `remove_sw_breakpoint`). - Update the `Target` API with support for non-fatal error handling. - _The old approach of only allowing \*fatal\* errors was woefully inadequate when dealing with potentially fallible operations such as reading from unauthorized memory (which GDB likes to do a bunch), or handling non-fatal `std::io::Error` that occur as a result of `ExtendedMode` operations. The new `TargetResult`/`TargetError` result is much more robust, and opens to door to supporting additional error handling extensions (such as LLDB's ASCII Errors)._ - Update the `Connection` trait with new methods (`flush` - required, `write_all`, `on_session_start`) - Lift `Registers::RegId` to `Arch::RegId`, and introduce new temporary `RegIdImpl` solution for avoiding breaking API changes due to new `RegId` implementations (see [\#29](https://github.com/daniel5151/gdbstub/pull/29)) - Mark various `RegId` enums as `#[non_exhaustive]`, allowing more registers to be added if need be. - Error types are now marked as `#[non_exhaustive]`. #### New Protocol Extensions - `ExtendedMode` - Allow targets to run new processes / attach to existing processes / restart execution. - Includes support for `set disable-randomization`, `set environment`, `set startup-with-shell`, and `set cwd` and `cd`. - `SectionOffsets` - Get section/segment relocation offsets from the target. [\#30](https://github.com/daniel5151/gdbstub/pull/30) ([mchesser](https://github.com/mchesser)) - Uses the `qOffsets` packet under-the-hood. #### Bugfixes - Fix issues related to selecting the incorrect thread after hitting a breakpoint in multi-threaded targets. - Ensure that `set_nodelay` is set when using a `TcpStream` as a `Connection` (via the new `Connection::on_session_start` API) - _This should result in a noticeable performance improvement when debugging over TCP._ #### Internal Improvements - Removed `btou` dependency. - Removed all `UTF-8` aware `str` handling code. - _GDB uses a pure ASCII protocol, so including code to deal with UTF-8 resulted in unnecessary binary bloat._ # 0.3.0 (formerly 0.2.2) This version contains a few minor breaking changes from `0.2.1`. These are only surface-level changes, and can be fixed with minimal effort. Version `0.3.0` is identical to the yanked version `0.2.2`, except that it adheres to `cargo`'s [modified SemVer rule](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field) which states that the pre-`0.x.y` breaking changes should still bump the minor version. Thanks to [h33p](https://github.com/h33p) for reporting this issue ([\#27](https://github.com/daniel5151/gdbstub/issues/27)) #### Breaking API Changes - Update `Target::resume` API to replace raw `&mut dyn Iterator` with a functionally identical concrete `Actions` iterator. - Mark the `StopReason` enum as `#[non_exhaustive]`, allowing further types to be added without being considered as an API breaking change. #### New Protocol Extensions - Add `Target::read/write_register` support (to support single register accesses) [\#22](https://github.com/daniel5151/gdbstub/pull/22) ([thomashk0](https://github.com/thomashk0)) - Add `StopReason::Signal(u8)` variant, to send arbitrary signal codes [\#19](https://github.com/daniel5151/gdbstub/pull/19) ([mchesser](https://github.com/mchesser)) #### New Arch Implementations - Add partial RISC-V support (only integer ISA at the moment) [\#21](https://github.com/daniel5151/gdbstub/pull/21) ([thomashk0](https://github.com/thomashk0)) - Add i386 (x86) support [\#23](https://github.com/daniel5151/gdbstub/pull/23) ([jamcleod](https://github.com/jamcleod)) - Add 32-bit PowerPC support [\#25](https://github.com/daniel5151/gdbstub/pull/25) ([jamcleod](https://github.com/jamcleod)) # 0.2.1 #### New Arch Implementations - Add x86_64 support [\#11](https://github.com/daniel5151/gdbstub/pull/11) ([jamcleod](https://github.com/jamcleod)) - Add Mips and Mips64 support [\#13](https://github.com/daniel5151/gdbstub/pull/13) ([starfleetcadet75](https://github.com/starfleetcadet75)) #### Internal Improvements - Documentation improvements - Document PC adjustment requirements in `Target::resume` - Add docs on handling non-fatal invalid memory reads/writes in `Target::read/write_addrs`. # 0.2.0 _start of changelog_ ================================================ FILE: Cargo.toml ================================================ [package] name = "gdbstub" description = "An implementation of the GDB Remote Serial Protocol in Rust" authors = ["Daniel Prilik "] version = "0.7.10" license = "MIT OR Apache-2.0" edition = "2018" readme = "README.md" documentation = "https://docs.rs/gdbstub" homepage = "https://github.com/daniel5151/gdbstub" repository = "https://github.com/daniel5151/gdbstub" keywords = ["gdb", "emulation", "no_std", "debugging"] categories = ["development-tools::debugging", "embedded", "emulators", "network-programming", "no-std"] exclude = ["examples/**/*.elf", "examples/**/*.o"] [dependencies] bitflags = "2.3.1" cfg-if = "1.0" log = "0.4" managed = { version = "0.8", default-features = false } num-traits = { version = "0.2", default-features = false } pastey = "0.2.1" [dev-dependencies] gdbstub_arch = { path = "./gdbstub_arch/" } armv4t_emu = "0.1" pretty_env_logger = "0.4" goblin = "0.4" [features] default = ["std", "trace-pkt"] alloc = ["managed/alloc"] std = ["alloc"] trace-pkt = ["alloc"] paranoid_unsafe = [] core_error = [] # INTERNAL: enables the `__dead_code_marker!` macro. # used as part of the `scripts/test_dead_code_elim.sh` __dead_code_marker = [] [[example]] name = "armv4t" required-features = ["std"] [[example]] name = "armv4t_multicore" required-features = ["std"] [workspace] members = ["gdbstub_arch"] exclude = ["example_no_std"] ================================================ FILE: LICENSE ================================================ gdbstub is dual-licensed under either * MIT License (docs/LICENSE-MIT or http://opensource.org/licenses/MIT) * Apache License, Version 2.0 (docs/LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) at your option. ================================================ FILE: README.md ================================================ # gdbstub [![](https://img.shields.io/crates/v/gdbstub.svg)](https://crates.io/crates/gdbstub) [![](https://docs.rs/gdbstub/badge.svg)](https://docs.rs/gdbstub) [![](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](./LICENSE) 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 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) - [Changelog](CHANGELOG.md) - [0.5 to 0.6 Transition Guide](docs/transition_guide.md) Why use `gdbstub`? - **Excellent Ergonomics** - Instead of simply exposing the underlying GDB protocol "warts and all", `gdbstub` tries to abstract as much of the raw GDB protocol details from the user. - Instead of having to dig through [obscure XML files deep the GDB codebase](https://github.com/bminor/binutils-gdb/tree/master/gdb/features) just to read/write from CPU/architecture registers, `gdbstub` comes with a community-curated collection of [built-in architecture definitions](https://docs.rs/gdbstub_arch) for most popular platforms! - Organizes GDB's countless optional protocol extensions into a coherent, understandable, and type-safe hierarchy of traits. - Automatically handles client/server protocol feature negotiation, without needing to micro-manage the specific [`qSupported` packet](https://sourceware.org/gdb/onlinedocs/gdb/General-Query-Packets.html#qSupported) response. - `gdbstub` makes _extensive_ use of Rust's powerful type system + generics to enforce protocol invariants at compile time, minimizing the number of tricky protocol details end users have to worry about. - Using a novel technique called [**Inlineable Dyn Extension Traits**](#zero-overhead-protocol-extensions) (IDETs), `gdbstub` enables fine-grained control over active protocol extensions _without_ relying on clunky `cargo` features or the use of `unsafe` code! - **Easy to Integrate** - `gdbstub`'s API is designed to be a "drop in" solution when you want to add debugging support into a project, and shouldn't require any large refactoring effort to integrate into an existing project. - **`#![no_std]` Ready & Size Optimized** - `gdbstub` is a **`no_std` first** library, whereby all protocol features are required to be `no_std` compatible. - `gdbstub` does not require _any_ dynamic memory allocation, and can be configured to use fixed-size, pre-allocated buffers. This enables `gdbstub` to be used on even the most resource constrained, no-[`alloc`](https://doc.rust-lang.org/alloc/) platforms. - `gdbstub` is entirely **panic free** in most minimal configurations\*, resulting in substantially smaller and more robust code. - \*See the [Writing panic-free code](#writing-panic-free-code) section below for more details. - `gdbstub` is transport-layer agnostic, and uses a basic [`Connection`](https://docs.rs/gdbstub/latest/gdbstub/conn/trait.Connection.html) interface to communicate with the GDB server. As long as target has some method of performing in-order, serial, byte-wise I/O (e.g: putchar/getchar over UART), it's possible to run `gdbstub` on it! - "You don't pay for what you don't use": All code related to parsing/handling protocol extensions is guaranteed to be dead-code-eliminated from an optimized binary if left unimplemented. See the [Zero-overhead Protocol Extensions](#zero-overhead-protocol-extensions) section below for more details. - `gdbstub`'s minimal configuration has an incredibly low binary size + RAM overhead, enabling it to be used on even the most resource-constrained microcontrollers. - When compiled in release mode, using all the tricks outlined in [`min-sized-rust`](https://github.com/johnthagen/min-sized-rust), a baseline `gdbstub` implementation can weigh in at **_less than 10kb of `.text` + `.rodata`!_** \* - \*Exact numbers vary by target platform, compiler version, and `gdbstub` revision. In mixed-language projects, cross-language LTO may be required ([\#101](https://github.com/daniel5151/gdbstub/issues/101#issuecomment-1264444815)). Data was collected using the included `example_no_std` project compiled on x86_64. ### Can I Use `gdbstub` in Production? **Yes, as long as you don't mind some API churn until `1.0.0` is released.** Due to `gdbstub`'s heavy use of Rust's type system in enforcing GDB protocol invariants at compile time, it's often been the case that implementing new GDB protocol features has required making some breaking API changes. While these changes are typically quite minor, they are nonetheless semver-breaking, and may require a code-change when moving between versions. Any particularly involved changes will typically be documented in a dedicated [transition guide](docs/transition_guide.md) document. That being said, `gdbstub` has already been integrated into [many real-world projects](#real-world-examples) since its initial `0.1` release, and empirical evidence suggests that it seems to be doing its job quite well! Thusfar, most reported issues have been caused by improperly implemented `Target` and/or `Arch` implementations, while the core `gdbstub` library itself has proven to be reasonably bug-free. See the [Future Plans + Roadmap to `1.0.0`](#future-plans--roadmap-to-100) for more information on what features `gdbstub` still needs to implement before committing to API stability with version `1.0.0`. ## Debugging Features The GDB Remote Serial Protocol is surprisingly complex, supporting advanced features such as remote file I/O, spawning new processes, "rewinding" program execution, and much, _much_ more. Thankfully, most of these features are completely optional, and getting a basic debugging session up-and-running only requires implementing a few basic methods: - Base GDB Protocol - Read/Write memory - Read/Write registers - Enumerating threads Yep, that's right! That's all it takes to get `gdb` connected! Of course, most use-cases will want to support additional debugging features as well. At the moment, `gdbstub` implements the following GDB protocol extensions: - Automatic target architecture + feature configuration - Resume - Continue - Single Step - Range Step - _Reverse_ Step/Continue - Breakpoints - Software Breakpoints - Hardware Breakpoints - Read/Write/Access Watchpoints (i.e: value breakpoints) - Extended Mode - Launch new processes - Attach to an existing process - Kill an existing process - Pass env vars + args to spawned processes - Change working directory - Enable/disable ASLR - Read Memory Map (`info mem`) - Read Section/Segment relocation offsets - Handle custom `monitor` Commands - Extend the GDB protocol with custom debug commands using GDB's `monitor` command! - Host I/O - Access the remote target's filesystem to read/write file - Can be used to automatically read the remote executable on attach (using `ExecFile`) - Read auxiliary vector (`info auxv`) - Extra thread info (`info threads`) - Extra library information (`info sharedlibraries` or `info shared`) - Tracepoints - Configure tracepoints and actions to perform when hit - Select and interrogate collected trace frames - _Note:_ Feature support is not exhaustive, and many feature haven't been implemented yet. - Flash operations (`load`) _Note:_ GDB features are implemented on an as-needed basis by `gdbstub`'s contributors. If there's a missing GDB feature that you'd like `gdbstub` to implement, please file an issue and/or open a PR! For a full list of GDB remote features, check out the [GDB Remote Configuration Docs](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Configuration.html) for a table of GDB commands + their corresponding Remote Serial Protocol packets. ### Zero-overhead Protocol Extensions Using a technique called **Inlineable Dyn Extension Traits** (IDETs), `gdbstub` is able to leverage the Rust compiler's powerful optimization passes to ensure any unused features are dead-code-eliminated in release builds _without_ having to rely on compile-time features flags! For example, if your target doesn't implement a custom GDB `monitor` command handler, the resulting binary won't include any code related to parsing / handling the underlying `qRcmd` packet! If you're interested in the low-level technical details of how IDETs work, I've included a brief writeup in the documentation [here](https://docs.rs/gdbstub/latest/gdbstub/target/ext/index.html#how-protocol-extensions-work---inlineable-dyn-extension-traits-idets). ## Feature flags By default, the `std` and `alloc` features are enabled. When using `gdbstub` in `#![no_std]` contexts, make sure to set `default-features = false`. - `alloc` - Implement `Connection` for `Box`. - Log outgoing packets via `log::trace!` (uses a heap-allocated output buffer). - Provide built-in implementations for certain protocol features: - Use a heap-allocated packet buffer in `GdbStub` (if none is provided via `GdbStubBuilder::with_packet_buffer`). - (Monitor Command) Use a heap-allocated output buffer in `ConsoleOutput`. - `std` (implies `alloc`) - Implement `Connection` for [`TcpStream`](https://doc.rust-lang.org/std/net/struct.TcpStream.html) and [`UnixStream`](https://doc.rust-lang.org/std/os/unix/net/struct.UnixStream.html). - Implement [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html) for `gdbstub::Error`. - Add a `TargetError::Io` variant to simplify `std::io::Error` handling from Target methods. - `paranoid_unsafe` - Please refer to the [`unsafe` in `gdbstub`](#unsafe-in-gdbstub) section below for more details. - `core_error` - Make `GdbStubError` implement [`core::error::Error`](https://doc.rust-lang.org/core/error/trait.Error.html) instead of `std::error::Error`. ## Examples ### Real-World Examples While some of these projects may use older versions of `gdbstub`, they can nonetheless serve as useful examples of what a typical `gdbstub` integration might look like. If you end up using `gdbstub` in your project, consider opening a PR and adding it to this list! - Virtual Machine Monitors (VMMs) - [OpenVMM/OpenHCL](https://openvmm.dev/reference/dev_feats/gdbstub.html) - (Microsoft) Modular, cross-platform, async-first VMM, with paravisor support - [firecracker](https://github.com/firecracker-microvm/firecracker/blob/main/docs/gdb-debugging.md) - (Amazon) Secure and fast microVMs for serverless computing - [crosvm](https://google.github.io/crosvm/running_crosvm/advanced_usage.html#gdb-support) - (Google) The Chrome OS VMM - [cloud-hypervisor](https://github.com/cloud-hypervisor/cloud-hypervisor) - A VMM for modern cloud workloads - [uhyve](https://github.com/hermitcore/uhyve) - A minimal hypervisor for [RustyHermit](https://github.com/hermitcore/rusty-hermit) - OS Kernels (using `gdbstub` on `no_std`) - [COCONUT-SVSM](https://github.com/coconut-svsm/svsm) - VM Service Module (SVSM), supporting Confidential VMs (CVMs) - [`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/57f953c2/kernel/src/arch/x86_64/gdb) - An (experimental) research OS kernel for x86-64 (amd64) machines - [`patina`](https://github.com/OpenDevicePartnership/patina/blob/main/core/patina_debugger/README.md) - A UEFI compliant pure rust DXE core - Emulators - [obliteration](https://github.com/obhq/obliteration) - Kernel + VMM for running PS4 software on PCs - [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) - [gamegirl](https://github.com/anellie/gamegirl) - A Gameboy (Color/Advance) emulator - [bevy-atari](https://github.com/mrk-its/bevy-atari) - An Atari XL/XE Emulator (MOS 6502) - [rmips](https://github.com/starfleetcadet75/rmips) - MIPS R3000 virtual machine simulator - [clicky](https://github.com/daniel5151/clicky/) - Emulator for classic clickwheel iPods (dual-core ARMv4T) - [ts7200](https://github.com/daniel5151/ts7200/) - Emulator for the TS-7200 SoC (ARMv4T) - [vaporstation](https://github.com/Colin-Suckow/vaporstation) - A Playstation One emulator (MIPS) - [microcorruption-emu](https://github.com/sapir/microcorruption-emu) - Emulator for the microcorruption.com ctf (MSP430) - Other - [probe-rs](https://probe.rs/) - A modern, embedded debugging toolkit - [udbserver](https://github.com/bet4it/udbserver) - Plug-in GDB debugging for the [Unicorn Engine](https://www.unicorn-engine.org/) (Multi Architecture) - [enarx](https://github.com/enarx/enarx) - An open source framework for running applications in Trusted Execution Environments - [icicle-emu](https://github.com/icicle-emu/icicle-emu) - An experimental fuzzing-specific, multi-architecture emulation framework ### In-tree "Toy" Examples These examples are built as part of the CI, and are guaranteed to be kept up to date with the latest version of `gdbstub`'s API. - `armv4t` - `./examples/armv4t/` - An incredibly simple ARMv4T-based system emulator with `gdbstub` support. - **Implements (almost) all available `target::ext` features.** This makes it a great resource when first implementing a new protocol extension! - `armv4t_multicore` - `./examples/armv4t_multicore/` - A dual-core variation of the `armv4t` example. - Implements the core of `gdbstub`'s multithread extensions API, but not much else. - `example_no_std` - `./example_no_std` - An _extremely_ minimal example which shows off how `gdbstub` can be used in a `#![no_std]` project. - Unlike the `armv4t/armv4t_multicore` examples, this project does _not_ include a working emulator, and simply stubs all `gdbstub` functions. - Doubles as a test-bed for tracking `gdbstub`'s approximate binary footprint (via the `check_size.sh` script), as well as validating certain dead-code-elimination optimizations. ## `unsafe` in `gdbstub` `gdbstub` limits its use of `unsafe` to a bare minimum, with all uses of `unsafe` required to have a corresponding `// SAFETY` comment as justification. For those paranoid about trusting third-party unsafe code, `gdbstub` comes with an opt-in `paranoid_unsafe` feature, which enables `#![forbid(unsafe_code)]` on the entire `gdbstub` crate, swapping out all instances of `unsafe` code with equivalent (albeit less-performant) alternatives. The following list exhaustively documents all uses of `unsafe` in `gdbstub`: - With `default` features - 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` - 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. ## Writing panic-free code Ideally, the Rust compiler would have some way to opt-in to a strict "no-panic" mode. Unfortunately, at the time of writing (2022/04/24), no such mode exists. As such, the only way to avoid the Rust compiler + stdlib's implicit panics is by being _very careful_ when writing code, and _manually checking_ that those panicking paths get optimized out! And when I say "manually checking", I mean [checking generated asm output](example_no_std/dump_asm.sh). Why even go through this effort? - Panic infrastructure can be _expensive_, and when you're optimizing for embedded, `no_std` use-cases, panic infrastructure brings in hundreds of additional bytes into the final binary. - `gdbstub` can be used to implement low-level debuggers, and if the debugger itself panics, well... it's not like you can debug it all that easily! As such, **`gdbstub` promises to introduce zero additional panics** into an existing project, subject to the following conditions: 1. The binary is compiled in release mode - \*subject to the specific `rustc` version being used (codegen and optimization vary between versions) - \*different hardware architectures may be subject to different compiler optimizations - i.e: at this time, only `x86` is actively tested to be panic-free 2. `gdbstub`'s `paranoid_unsafe` cargo feature is _disabled_ - LLVM is unable to omit certain `panic` checks without requiring a bit of `unsafe` code - See the [`unsafe` in `gdbstub`](#unsafe-in-gdbstub) section for more details 3. The `Arch` implementation being used doesn't include panicking code - _Note:_ The arch implementations under `gdbstub_arch` are _not_ guaranteed to be panic free! - If you do spot a panicking arch in `gdbstub_arch`, consider opening a PR to fix it If you're using `gdbstub` in a no-panic project and have determined that `gdbstub` is at fault for introducing a panicking code path, please file an issue! ## Future Plans + Roadmap to `1.0.0` While the vast majority of GDB protocol features (e.g: remote filesystem support, tracepoint packets, most query packets, etc...) should _not_ require breaking API changes, the following features will most likely require at least some breaking API changes, and should therefore be implemented prior to `1.0.0`. 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 ([\#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`) - [ ] [Non-Stop Mode](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Non_002dStop.html#Remote-Non_002dStop) - [x] Have a working example of `gdbstub` running in a "bare-metal" `#![no_std]` environment. Additionally, while not _strict_ blockers to `1.0.0`, it would be good to explore these features as well: - [ ] Should `gdbstub` commit to a MSRV? - [ ] Remove lingering instances of `RawRegId` from `gdbstub_arch` ([\#29](https://github.com/daniel5151/gdbstub/issues/29)) - [x] Exposing `async/await` interfaces (particularly wrt. handling GDB client interrupts) ([\#36](https://github.com/daniel5151/gdbstub/issues/36)) - [ ] 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) ([\#137](https://github.com/daniel5151/gdbstub/issues/137)) ## License gdbstub is free and open source! All code in this repository is dual-licensed under either: * MIT License ([LICENSE-MIT](docs/LICENSE-MIT) or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT)) * Apache License, Version 2.0 ([LICENSE-APACHE](docs/LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)) at your option. This means you can select the license you prefer! This dual-licensing approach is the de-facto standard in the Rust ecosystem and there are [very good reasons](https://github.com/daniel5151/gdbstub/issues/68) to include both. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. ================================================ FILE: docs/LICENSE-APACHE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS ================================================ FILE: docs/LICENSE-MIT ================================================ MIT License Copyright (c) 2021 Daniel Prilik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: docs/transition_guide.md ================================================ # Transition Guide This document provides a brief overview of breaking changes between major `gdbstub` releases, along with tips/tricks/suggestions on how to migrate between `gdbstub` releases. This document does _not_ discuss any new features that might have been added between releases. For a comprehensive overview of what's been _added_ to `gdbstub` (as opposed to what's _changed_), check out the [`CHANGELOG.md`](../CHANGELOG.md). > _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. The following guide is a **best-effort** attempt to document all the changes, but there are some parts that may be missing / incomplete. ##### General API change - _lots_ of renaming + exported type reorganization Many types have been renamed, and many import paths have changed in `0.6`. Exhaustively listing them would be nearly impossible, but suffice it to say, you will need to tweak your imports. ##### `Connection` API changes > _Note:_ If you haven't implemented `Connection` yourself (i.e: you are using one of the built-in `Connection` impls on `TcpStream`/`UnixStream`), you can skip this section. The blocking `read` method and non-blocking `peek` methods have been removed from the base `Connection` API, and have been moved to a new `ConnectionExt` type. For more context around this change, please refer to [Moving from `GdbStub::run` to `GdbStub::run_blocking`](#moving-from-gdbstubrun-to-gdbstubrun_blocking). Porting a `0.5` `Connection` to `0.6` is incredibly straightforward - you simply split your existing implementation in two: ```rust // ==== 0.5.x ==== // impl Connection for MyConnection { type Error = MyError; fn write(&mut self, byte: u8) -> Result<(), Self::Error> { .. } fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { .. } fn read(&mut self) -> Result { .. } fn peek(&mut self) -> Result, Self::Error> { .. } fn flush(&mut self) -> Result<(), Self::Error> { .. } fn on_session_start(&mut self) -> Result<(), Self::Error> { .. } } // ==== 0.6.0 ==== // impl Connection for MyConnection { type Error = MyError; fn write(&mut self, byte: u8) -> Result<(), Self::Error> { .. } fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { .. } fn flush(&mut self) -> Result<(), Self::Error> { .. } fn on_session_start(&mut self) -> Result<(), Self::Error> { .. } } impl ConnectionExt for MyConnection { type Error = MyError; fn read(&mut self) -> Result { .. } fn peek(&mut self) -> Result, Self::Error> { .. } } ``` ##### `Arch` API - `RegId::from_raw_id` > _Note:_ If you haven't implemented `Arch` yourself (i.e: you are any of the `Arch` impls from `gdbstub_arch`), you can skip this section. The `Arch` API has had one breaking changes: The `RegId::from_raw_id` method's "register size" return value has been changed from `usize` to `Option`. If the register size is `Some`, `gdbstub` will include a runtime check to ensures that the target implementation does not send back more bytes than the register allows when responding to single-register read requests. If the register size is `None`, `gdbstub` will _omit_ this runtime check, and trust that the target's implementation of `read_register` is correct. _Porting advice:_ If your `Arch` implementation targets a specific architecture, it is _highly recommended_ that you simply wrap your existing size value with `Some`. This API change was made to support dynamic `Arch` implementations, whereby the behavior of the `Arch` varies on the runtime state of the program (e.g: in multi-system emulators), and there is not "fixed" register size per id. ##### `Target` API - IDET methods are now prefixed with `supports_` All IDET methods have been prefixed with `supports_`, to make it easier to tell at-a-glance which methods are actual handler methods, and which are simply IDET plumbing. As such, when porting target code from `0.5` to `0.6`, before you dive into any functional changes, you should take a moment to find and rename any methods that have had their name changed. ##### `Target` API - Introducing `enum Signal` In prior versions of `gdbstub`, signals were encoded as raw `u8` values. This wasn't very user-friendly, as it meant users had to manually locate the signal-to-integer mapping table themselves when working with signals in code. `0.6` introduces a new `enum Signal` which encodes this information within `gdbstub` itself. This new `Signal` type has replaced `u8` in any places that a `u8` was used to represent a signal, such as in `StopReason::Signal`, or as part of the various `resume` APIs. _Porting advice:_ The Rust compiler should catch any type errors due to this change, making it easy to swap out any instances of `u8` with the new `Signal` type. ##### `HwWatchpoint` API - Plumb watchpoint `length` parameter to public API The watchpoint API has been updated to include a new `length` parameter, specifying what range of memory addresses the watchpoint should encompass. ##### `TargetXmlOverride` API - Return data via `&mut [u8]` buffer In an effort to unify the implementations of various new `qXfer`-backed protocol extensions, the existing `TargetXmlOverride` has been changed from returning a `&str` value to using a `std::io::Read`-style "write the data into a `&mut [u8]` buffer" API. Porting a `0.5` `TargetDescriptionXmlOverride` to `0.6` is straightforward, though a bit boilerplate-y. ```rust // ==== 0.5.x ==== // impl target::ext::target_description_xml_override::TargetDescriptionXmlOverride for Emu { fn target_description_xml(&self) -> &str { r#"armv4t"# } } // ==== 0.6.0 ==== // pub fn copy_to_buf(data: &[u8], buf: &mut [u8]) -> usize { let len = data.len(); let buf = &mut buf[..len]; buf.copy_from_slice(data); len } pub fn copy_range_to_buf(data: &[u8], offset: u64, length: usize, buf: &mut [u8]) -> usize { let offset = match usize::try_from(offset) { Ok(v) => v, Err(_) => return 0, }; let len = data.len(); let data = &data[len.min(offset)..len.min(offset + length)]; copy_to_buf(data, buf) } impl target::ext::target_description_xml_override::TargetDescriptionXmlOverride for Emu { fn target_description_xml( &self, offset: u64, length: usize, buf: &mut [u8], ) -> TargetResult { let xml = r#"armv4t"# .trim() .as_bytes(); Ok(copy_range_to_buf(xml, offset, length, buf)) } } ``` ##### Updates to `{Single,Multi}ThreadOps::resume` API `0.6` includes three fairly major behavioral changes to the `resume` method: ###### Support for `resume` is now entirely optional There are quite a few use cases where it might make sense to debug a target that does _not_ support resumption, e.g: a post-mortem debugging session, or when debugging crash dumps. In these cases, past version of `gdbstub` would force the user to nonetheless implement "stub" methods for resuming these targets, along with forcing users to pay the "cost" of including all the handler code related to resumption (of which there is quite a bit.) In `0.6`, all resume-related functionality has been extracted out of `{Single,Multi}ThreadBase`, and split into new `{Singe,Multi}ThreadResume` IDETs. ###### Removing `ResumeAction`, and making single-step support optional The GDB protocol only requires that targets implement support for _continuing_ execution - support for instruction-level single-step execution is totally optional. > Note: this isn't actually true in practice, thanks to a bug in the mainline GDB client... See the docs for `Target::use_optional_single_step` for details... To model this behavior, `0.6` has split single-step support into its own IDET, in a manner similar to how optimized range step support was handled in `0.5`. In doing so, the `enum ResumeAction` type could be removed entirely, as single-step resume was to be handled in its own method. ###### Removing `gdb_interrupt: GdbInterrupt`, and making `resume` non-blocking In past versions of `gdbstub`, the `resume` API would _block_ the thread waiting for the target to hit some kind of stop condition. In this model, checking for pending GDB interrupts was quite unergonomic, requiring that the thread periodically wake up and check whether an interrupt has arrived via the `GdbInterrupt` type. `gdbstub` `0.6` introduces a new paradigm of driving target execution, predicated on the idea that the target's `resume` method _does not block_, instead yielding execution immediately, and deferring the responsibility of "selecting" between incoming stop events and GDB interrupts to higher levels of the `gdbstub` "stack". In practice, this means that much of the logic that used to live in the `resume` implementation will now move into upper-levels of the `gdbstub` API, with the `resume` API serving more of a "bookkeeping" purpose, recording what kind of resumption mode the GDB client has requested from the target, while not actually resuming the target itself. For more context around this change, please refer to [Moving from `GdbStub::run` to `GdbStub::run_blocking`](#moving-from-gdbstubrun-to-gdbstubrun_blocking). ###### Example: migrating `resume` from `0.5` to `0.6` Much of the code contained within methods such as `block_until_stop_reason_or_interrupt` will be lifted into upper layers of the `gdbstub` API, leaving behind just a small bit of code in the target's `resume` method to perform "bookkeeping" regarding how the GDB client requested the target to be resumed. ```rust // ==== 0.5.x ==== // impl SingleThreadOps for Emu { fn resume( &mut self, action: ResumeAction, gdb_interrupt: GdbInterrupt<'_>, ) -> Result, Self::Error> { match action { ResumeAction::Step => self.do_single_step(), ResumeAction::Continue => self.block_until_stop_reason_or_interrupt(action, || gdb_interrupt.pending()), _ => self.handle_resume_with_signal(action), } } } // ==== 0.6.0 ==== // impl SingleThreadBase for Emu { // resume has been split into a separate IDET #[inline(always)] fn support_resume( &mut self ) -> Option> { Some(self) } } impl SingleThreadResume for Emu { fn resume( &mut self, signal: Option, ) -> Result<(), Self::Error> { // <-- no longer returns a stop reason! if let Some(signal) = signal { self.handle_signal(signal)?; } // upper layers of the `gdbstub` API will be responsible for "driving" // target execution - `resume` simply performs book keeping on _how_ the // target should be resumed. self.set_execution_mode(ExecMode::Continue)?; Ok(()) } // single-step support has been split into a separate IDET #[inline(always)] fn support_single_step( &mut self ) -> Option> { Some(self) } } impl SingleThreadSingleStep for Emu { fn step(&mut self, signal: Option) -> Result<(), Self::Error> { if let Some(signal) = signal { self.handle_signal(signal)?; } self.set_execution_mode(ExecMode::Step)?; Ok(()) } } ``` ##### Moving from `GdbStub::run` to `GdbStub::run_blocking` With the introduction of the new state-machine API, the responsibility of reading incoming has been lifted out of `gdbstub` itself, and is now something implementations are responsible for . The alternative approach would've been to have `Connection` include multiple different `read`-like methods for various kinds of paradigms - such as `async`/`await`, `epoll`, etc... > TODO. In the meantime, I would suggest looking at rustdoc for details on how to use `GdbStub::run_blocking`... ## `0.4` -> `0.5` While the overall structure of the API has remained the same, `0.5.0` does introduce a few breaking API changes that require some attention. That being said, it should not be a difficult migration, and updating to `0.5.0` from `0.4` shouldn't take more than 10 mins of refactoring. ##### Consolidating the `{Hw,Sw}Breakpoint/Watchpoint` IDETs under the newly added `Breakpoints` IDETs. The various breakpoint IDETs that were previously directly implemented on the top-level `Target` trait have now been consolidated under a single `Breakpoints` IDET. This is purely an organizational change, and will not require rewriting any existing `{add, remove}_{sw_break,hw_break,watch}point` implementations. Porting from `0.4` to `0.5` should be as simple as: ```rust // ==== 0.4.x ==== // impl Target for Emu { fn sw_breakpoint(&mut self) -> Option> { Some(self) } fn hw_watchpoint(&mut self) -> Option> { Some(self) } } impl target::ext::breakpoints::SwBreakpoint for Emu { fn add_sw_breakpoint(&mut self, addr: u32) -> TargetResult { ... } fn remove_sw_breakpoint(&mut self, addr: u32) -> TargetResult { ... } } impl target::ext::breakpoints::HwWatchpoint for Emu { fn add_hw_watchpoint(&mut self, addr: u32, kind: WatchKind) -> TargetResult { ... } fn remove_hw_watchpoint(&mut self, addr: u32, kind: WatchKind) -> TargetResult { ... } } // ==== 0.5.0 ==== // impl Target for Emu { // (New Method) // fn breakpoints(&mut self) -> Option> { Some(self) } } impl target::ext::breakpoints::Breakpoints for Emu { fn sw_breakpoint(&mut self) -> Option> { Some(self) } fn hw_watchpoint(&mut self) -> Option> { Some(self) } } // (Almost Unchanged) // impl target::ext::breakpoints::SwBreakpoint for Emu { // /-- New `kind` parameter // \/ fn add_sw_breakpoint(&mut self, addr: u32, _kind: arch::arm::ArmBreakpointKind) -> TargetResult { ... } fn remove_sw_breakpoint(&mut self, addr: u32, _kind: arch::arm::ArmBreakpointKind) -> TargetResult { ... } } // (Unchanged) // impl target::ext::breakpoints::HwWatchpoint for Emu { fn add_hw_watchpoint(&mut self, addr: u32, kind: WatchKind) -> TargetResult { ... } fn remove_hw_watchpoint(&mut self, addr: u32, kind: WatchKind) -> TargetResult { ... } } ``` ##### Single-register access methods (`{read,write}_register`) are now a separate `SingleRegisterAccess` trait Single register access is not a required part of the GDB protocol, and as such, has been moved out into its own IDET. This is a purely organizational change, and will not require rewriting any existing `{read,write}_register` implementations. Porting from `0.4` to `0.5` should be as simple as: ```rust // ==== 0.4.x ==== // impl SingleThreadOps for Emu { fn read_register(&mut self, reg_id: arch::arm::reg::id::ArmCoreRegId, dst: &mut [u8]) -> TargetResult<(), Self> { ... } fn write_register(&mut self, reg_id: arch::arm::reg::id::ArmCoreRegId, val: &[u8]) -> TargetResult<(), Self> { ... } } // ==== 0.5.0 ==== // impl SingleThreadOps for Emu { // (New Method) // fn single_register_access(&mut self) -> Option> { Some(self) } } impl target::ext::base::SingleRegisterAccess<()> for Emu { // /-- New `tid` parameter (ignored on single-threaded systems) // \/ fn read_register(&mut self, _tid: (), reg_id: arch::arm::reg::id::ArmCoreRegId, dst: &mut [u8]) -> TargetResult<(), Self> { ... } fn write_register(&mut self, _tid: (), reg_id: arch::arm::reg::id::ArmCoreRegId, val: &[u8]) -> TargetResult<(), Self> { ... } } ``` ##### New `MultiThreadOps::resume` API In `0.4`, resuming a multithreaded target was done using an `Actions` iterator passed to a single `resume` method. In hindsight, this approach had a couple issues: - It was impossible to statically enforce the property that the `Actions` iterator was guaranteed to return at least one element, often forcing users to manually `unwrap` - The iterator machinery was quite heavy, and did not optimize very effectively - Handling malformed packets encountered during iteration was tricky, as the user-facing API exposed an infallible iterator, thereby complicating the internal error handling - Adding new kinds of `ResumeAction` (e.g: range stepping) required a breaking change, and forced users to change their `resume` method implementation regardless whether or not their target ended up using said action. In `0.5`, the API has been refactored to address some of these issues, and the single `resume` method has now been split into multiple "lifecycle" methods: 1. `resume` - As before, when `resume` is called the target should resume execution. - But how does the target know how each thread should be resumed? That's where the next method comes in... 1. `set_resume_action` - This method is called prior to `resume`, and notifies the target how a particular `Tid` should be resumed. 1. (optionally) `set_resume_action_range_step` - If the target supports optimized range-stepping, it can opt to implement the newly added `MultiThreadRangeStepping` IDET which includes this method. - Targets that aren't interested in optimized range-stepping can skip this method! 1. `clear_resume_actions` - After the target returns a `ThreadStopReason` from `resume`, this method will be called to reset the previously set per-`tid` resume actions. NOTE: This change does mean that targets are now responsible for maintaining some internal state that maps `Tid`s to `ResumeAction`s. Thankfully, this isn't difficult at all, and can as simple as maintaining a `HashMap`. Please refer to the in-tree `armv4t_multicore` example for an example of how this new `resume` flow works. ================================================ FILE: example_no_std/.gitignore ================================================ /target .gdb_history *.s ================================================ FILE: example_no_std/Cargo.toml ================================================ [package] name = "gdbstub-nostd" version = "0.1.0" authors = ["Daniel Prilik "] edition = "2018" [features] __dead_code_marker = ["gdbstub/__dead_code_marker"] [dependencies] gdbstub = { path = "../", default-features = false } gdbstub_arch = { path = "../gdbstub_arch", default-features = false } libc = { version = "0.2", default-features = false } log = { version = "0.4", features = ["release_max_level_off"] } [profile.dev] panic = "abort" [profile.release] panic = "abort" opt-level = 's' # Optimize for size. lto = true codegen-units = 1 debug = true ================================================ FILE: example_no_std/README.md ================================================ # example_no_std This basic example is used to benchmark how large `gdbstub`'s binary footprint is in `#![no_std]` contexts. It uses many of the [`min-sized-rust`](https://github.com/johnthagen/min-sized-rust) guidelines to crunch down the binary size. This includes directly linking against `libc` to perform I/O, and avoiding and and all uses of Rust's [heavy formatting machinery](https://jamesmunns.com/blog/fmt-unreasonably-expensive/). While not perfect, this example should give a rough estimate of what a typical embedded system `gdbstub` integration might look like. Oh, and please excuse the _terrible_ sockets code in `conn.rs`. I've never worked with raw C sockets, and that code was very haphazardly thrown together. If you're so inclined, I'd more than happily merge the PR that improves it's implementation! ================================================ FILE: example_no_std/check_size.sh ================================================ #!/bin/bash set -e cd "$(dirname "$(realpath $0)")" # checks the size of the resulting --release level binary (that's been stripped) cargo build --release cargo bloat --release --split-std -n 100 strip target/release/gdbstub-nostd size -A -t target/release/gdbstub-nostd ================================================ FILE: example_no_std/dump_asm.sh ================================================ #!/bin/bash set -e cd "$(dirname "$(realpath "$0")")" if ! command -v rustfilt &> /dev/null then cargo install rustfilt fi rm -rf ./target cargo rustc --release -- --emit asm -C "llvm-args=-x86-asm-syntax=intel" cat ./target/release/deps/gdbstub_nostd-*.s | rustfilt > asm.s sed -i -E '/\.(cfi_def_cfa_offset|cfi_offset|cfi_startproc|cfi_endproc|size)/d' asm.s if [ -n "$EXTRA_TRIM" ]; then sed -i -E '/\.(Ltmp|file|loc)/d' asm.s sed -i -E '/.section\t.debug_loc/,$d' asm.s fi echo "asm emitted to asm.s" if grep "core::panicking::panic_fmt" asm.s then echo "found panic in example_no_std!" exit 1 else echo "no panics in example_no_std" fi ================================================ FILE: example_no_std/rustfmt.toml ================================================ wrap_comments = true ================================================ FILE: example_no_std/src/conn.rs ================================================ use gdbstub::conn::Connection; pub struct TcpConnection { sock: i32, fd: i32, } impl TcpConnection { pub fn new_localhost(port: u16) -> Result { unsafe { let sockaddr = libc::sockaddr_in { #[cfg(any( target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "dragonfly" ))] sin_len: core::mem::size_of::() as _, sin_family: libc::AF_INET as _, sin_port: port.to_be(), // 127.0.0.1 sin_addr: libc::in_addr { s_addr: 0x7f000001u32.to_be(), }, sin_zero: [0; 8], }; let socklen = core::mem::size_of::(); let sock = libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0); if sock == -1 { return Err("could not create listen socket"); } let reuseaddr: i32 = 1; if libc::setsockopt( sock, libc::SOL_SOCKET, libc::SO_REUSEADDR, &reuseaddr as *const _ as *const libc::c_void, core::mem::size_of::() as u32, ) < 0 { return Err("could not set SO_REUSEADDR"); } if libc::bind(sock, &sockaddr as *const _ as _, socklen as u32) < 0 { return Err("could not bind socket"); } if libc::listen(sock, 1) < 0 { return Err("could not open socket for listening"); } let fd = libc::accept(sock, core::ptr::null_mut(), &mut 0); if fd < 0 { return Err("could not accept socket connection"); } Ok(TcpConnection { sock, fd }) } } pub fn read(&mut self) -> Result { let mut buf = [0]; let ret = unsafe { libc::read(self.fd, buf.as_mut_ptr() as _, 1) }; if ret == -1 || ret != 1 { Err("socket read failed") } else { Ok(buf[0]) } } #[allow(dead_code)] pub fn peek(&mut self) -> Result, &'static str> { let mut buf = [0]; let ret = unsafe { libc::recv( self.fd, buf.as_mut_ptr() as *mut _, buf.len(), libc::MSG_PEEK, ) }; if ret == -1 || ret != 1 { Err("socket peek failed") } else { Ok(Some(buf[0])) } } } impl Drop for TcpConnection { fn drop(&mut self) { unsafe { libc::close(self.fd); libc::close(self.sock); } } } impl Connection for TcpConnection { type Error = &'static str; fn write(&mut self, b: u8) -> Result<(), &'static str> { let buf = [b]; let ret = unsafe { libc::write(self.fd, buf.as_ptr() as _, 1) }; if ret == -1 || ret != 1 { Err("socket write failed") } else { Ok(()) } } fn flush(&mut self) -> Result<(), &'static str> { // huh, apparently flushing isn't a "thing" for Tcp streams. // see https://doc.rust-lang.org/src/std/net/tcp.rs.html#592-609 Ok(()) } } ================================================ FILE: example_no_std/src/gdb.rs ================================================ use crate::print_str::print_str; use gdbstub::common::Signal; use gdbstub::common::Tid; use gdbstub::target; use gdbstub::target::ext::base::multithread::MultiThreadBase; use gdbstub::target::ext::base::multithread::MultiThreadResume; use gdbstub::target::Target; use gdbstub::target::TargetResult; pub struct DummyTarget {} impl DummyTarget { pub fn new() -> DummyTarget { DummyTarget {} } } impl Target for DummyTarget { type Arch = gdbstub_arch::arm::Armv4t; type Error = &'static str; #[inline(always)] fn base_ops(&mut self) -> target::ext::base::BaseOps<'_, Self::Arch, Self::Error> { target::ext::base::BaseOps::MultiThread(self) } // disable `QStartNoAckMode` in order to save space #[inline(always)] fn use_no_ack_mode(&self) -> bool { false } // disable X packet optimization in order to save space #[inline(always)] fn use_x_upcase_packet(&self) -> bool { false } // disable fork events to save space #[inline(always)] fn use_fork_stop_reason(&self) -> bool { false } // disable vfork events to save space #[inline(always)] fn use_vfork_stop_reason(&self) -> bool { false } // disable vforkdone events to save space #[inline(always)] fn use_vforkdone_stop_reason(&self) -> bool { false } #[inline(always)] fn support_breakpoints( &mut self, ) -> Option> { Some(self) } } // NOTE: to try and make this a marginally more realistic estimate of // `gdbstub`'s library overhead, non-IDET methods are marked as // `#[inline(never)]` to prevent the optimizer from too aggressively coalescing // the stubbed implementations. // // EXCEPTION: `list_active_threads` accepts a closure arg, and should be // be inlined for smaller codegen impl MultiThreadBase for DummyTarget { #[inline(never)] fn read_registers( &mut self, _regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs, _tid: Tid, ) -> TargetResult<(), Self> { print_str("> read_registers"); Ok(()) } #[inline(never)] fn write_registers( &mut self, _regs: &gdbstub_arch::arm::reg::ArmCoreRegs, _tid: Tid, ) -> TargetResult<(), Self> { print_str("> write_registers"); Ok(()) } #[inline(never)] fn read_addrs( &mut self, _start_addr: u32, data: &mut [u8], _tid: Tid, // same address space for each core ) -> TargetResult { print_str("> read_addrs"); data.iter_mut().for_each(|b| *b = 0x55); Ok(data.len()) } #[inline(never)] fn write_addrs( &mut self, _start_addr: u32, _data: &[u8], _tid: Tid, // same address space for each core ) -> TargetResult<(), Self> { print_str("> write_addrs"); Ok(()) } #[inline(always)] // !! EXCEPTION !! fn list_active_threads( &mut self, register_thread: &mut dyn FnMut(Tid), ) -> Result<(), Self::Error> { print_str("> list_active_threads"); register_thread(Tid::new(1).unwrap()); register_thread(Tid::new(2).unwrap()); Ok(()) } #[inline(always)] fn support_resume( &mut self, ) -> Option> { Some(self) } } impl MultiThreadResume for DummyTarget { #[inline(never)] fn resume(&mut self) -> Result<(), Self::Error> { print_str("> resume"); Ok(()) } #[inline(never)] fn clear_resume_actions(&mut self) -> Result<(), Self::Error> { print_str("> clear_resume_actions"); Ok(()) } #[inline(never)] fn set_resume_action_continue( &mut self, _tid: Tid, _signal: Option, ) -> Result<(), Self::Error> { print_str("> set_resume_action_continue"); Ok(()) } } impl target::ext::breakpoints::Breakpoints for DummyTarget { #[inline(always)] fn support_sw_breakpoint( &mut self, ) -> Option> { Some(self) } } impl target::ext::breakpoints::SwBreakpoint for DummyTarget { #[inline(never)] fn add_sw_breakpoint( &mut self, _addr: u32, _kind: gdbstub_arch::arm::ArmBreakpointKind, ) -> TargetResult { Ok(true) } #[inline(never)] fn remove_sw_breakpoint( &mut self, _addr: u32, _kind: gdbstub_arch::arm::ArmBreakpointKind, ) -> TargetResult { Ok(true) } } ================================================ FILE: example_no_std/src/main.rs ================================================ //! A basic `no_std` example that's used to ballpark estimate how large //! `gdbstub`'s binary footprint is resource-restricted environments. #![no_std] #![no_main] use crate::print_str::print_str; use gdbstub::stub::state_machine::GdbStubStateMachine; use gdbstub::stub::DisconnectReason; use gdbstub::stub::GdbStubBuilder; use gdbstub::stub::MultiThreadStopReason; mod conn; mod gdb; mod print_str; #[panic_handler] fn panic(_info: &core::panic::PanicInfo<'_>) -> ! { loop {} } fn rust_main() -> Result<(), i32> { print_str("Running example_no_std..."); let mut target = gdb::DummyTarget::new(); let conn = match conn::TcpConnection::new_localhost(9001) { Ok(c) => c, Err(e) => { print_str("could not start TCP server:"); print_str(e); return Err(-1); } }; let mut buf = [0; 4096]; let gdb = GdbStubBuilder::new(conn) .with_packet_buffer(&mut buf) .build() .map_err(|_| 1)?; print_str("Starting GDB session..."); let mut gdb = gdb.run_state_machine(&mut target).map_err(|_| 1)?; let res = loop { gdb = match gdb { GdbStubStateMachine::Idle(mut gdb) => { let byte = gdb.borrow_conn().read().map_err(|_| 1)?; match gdb.incoming_data(&mut target, byte) { Ok(gdb) => gdb, Err(e) => break Err(e), } } GdbStubStateMachine::Running(gdb) => { match gdb.report_stop(&mut target, MultiThreadStopReason::DoneStep) { Ok(gdb) => gdb, Err(e) => break Err(e), } } GdbStubStateMachine::CtrlCInterrupt(gdb) => { match gdb.interrupt_handled(&mut target, None::>) { Ok(gdb) => gdb, Err(e) => break Err(e), } } GdbStubStateMachine::Disconnected(gdb) => break Ok(gdb.get_reason()), } }; match res { Ok(disconnect_reason) => match disconnect_reason { DisconnectReason::Disconnect => print_str("GDB Disconnected"), DisconnectReason::TargetExited(_) => print_str("Target exited"), DisconnectReason::TargetTerminated(_) => print_str("Target halted"), DisconnectReason::Kill => print_str("GDB sent a kill command"), }, Err(e) => { if e.is_target_error() { print_str("Target raised a fatal error"); } else { print_str("gdbstub internal error"); } } } Ok(()) } #[no_mangle] extern "C" fn main(_argc: isize, _argv: *const *const u8) -> isize { if let Err(e) = rust_main() { return e as isize; } 0 } ================================================ FILE: example_no_std/src/print_str.rs ================================================ pub fn print_str(s: &str) { unsafe { libc::write(1, s.as_ptr() as _, s.len()); libc::write(1, "\n".as_ptr() as _, 1); } } ================================================ FILE: examples/armv4t/README.md ================================================ # armv4t An incredibly simple emulator to run elf binaries compiled with `arm-none-eabi-cc -march=armv4t`. This emulator isn't based off any particular system -- it's moreso just a test-bed for showing off various bits of `gdbstub` functionality. ## Usage Run `gdb-arm-none-eabi` (or alternatively, `gdb-multiarch`) from the `test_bin` directory to automatically connect to the emulator + load debug symbols for the emulated binary. This example can be run using: ```bash cargo run --example armv4t --features=std ``` **NOTE:** If debug symbols couldn't be loaded, try rebuilding `test.elf` locally (requires the `arm-none-eabi` toolchain to be installed), and recompiling the example. ### Unix Domain Sockets GDB versions since \~2018 support running a debugging session over Unix Domain Sockets (UDS). Debugging over UDS can feel much snappier than debugging over loopback TCP. Running the example with the `--uds` flag will bind the GdbStub to a socket at `/tmp/armv4t_gdb`. This feature is only supported on Unix-like systems. ================================================ FILE: examples/armv4t/emu.rs ================================================ 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; use gdbstub::target::ext::tracepoints::NewTracepoint; use gdbstub::target::ext::tracepoints::SourceTracepoint; use gdbstub::target::ext::tracepoints::Tracepoint; use gdbstub::target::ext::tracepoints::TracepointAction; use gdbstub::target::ext::tracepoints::TracepointEnumerateState; use std::collections::BTreeMap; const HLE_RETURN_ADDR: u32 = 0x12345678; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Event { DoneStep, Halted, Break, WatchWrite(u32), WatchRead(u32), } pub enum ExecMode { Step, Continue, RangeStep(u32, u32), } #[derive(Debug)] pub struct TraceFrame { pub number: Tracepoint, pub snapshot: Cpu, } /// incredibly barebones armv4t-based emulator pub struct Emu { start_addr: u32, // example custom register. only read/written to from the GDB client pub(crate) custom_reg: u32, pub(crate) exec_mode: ExecMode, pub(crate) cpu: Cpu, pub(crate) mem: ExampleMem, pub(crate) watchpoints: Vec, pub(crate) breakpoints: Vec, pub(crate) files: Vec>, pub(crate) tracepoints: BTreeMap< Tracepoint, ( NewTracepoint, Vec>, Vec>, ), >, pub(crate) traceframes: Vec, pub(crate) tracepoint_enumerate_state: TracepointEnumerateState, pub(crate) tracing: bool, pub(crate) selected_frame: Option, pub(crate) reported_pid: Pid, } impl Emu { pub fn new(program_elf: &[u8]) -> DynResult { // set up emulated system let mut cpu = Cpu::new(); let mut mem = ExampleMem::new(); // load ELF let elf_header = goblin::elf::Elf::parse(program_elf)?; // copy all in-memory sections from the ELF file into system RAM let sections = elf_header .section_headers .iter() .filter(|h| h.is_alloc() && h.sh_type != goblin::elf::section_header::SHT_NOBITS); for h in sections { eprintln!( "loading section {:?} into memory from [{:#010x?}..{:#010x?}]", elf_header.shdr_strtab.get_at(h.sh_name).unwrap(), h.sh_addr, h.sh_addr + h.sh_size, ); for (i, b) in program_elf[h.file_range().unwrap()].iter().enumerate() { mem.w8(h.sh_addr as u32 + i as u32, *b); } } // setup execution state eprintln!("Setting PC to {:#010x?}", elf_header.entry); cpu.reg_set(Mode::User, reg::SP, 0x10000000); cpu.reg_set(Mode::User, reg::LR, HLE_RETURN_ADDR); cpu.reg_set(Mode::User, reg::PC, elf_header.entry as u32); cpu.reg_set(Mode::User, reg::CPSR, 0x10); // user mode Ok(Emu { start_addr: elf_header.entry as u32, custom_reg: 0x12345678, exec_mode: ExecMode::Continue, cpu, mem, watchpoints: Vec::new(), breakpoints: Vec::new(), files: Vec::new(), tracepoints: BTreeMap::new(), traceframes: Vec::new(), tracepoint_enumerate_state: Default::default(), tracing: false, selected_frame: None, reported_pid: Pid::new(1).unwrap(), }) } pub(crate) fn reset(&mut self) { self.cpu.reg_set(Mode::User, reg::SP, 0x10000000); self.cpu.reg_set(Mode::User, reg::LR, HLE_RETURN_ADDR); self.cpu.reg_set(Mode::User, reg::PC, self.start_addr); self.cpu.reg_set(Mode::User, reg::CPSR, 0x10); } /// single-step the interpreter pub fn step(&mut self) -> Option { if self.tracing { let pc = self.cpu.reg_get(self.cpu.mode(), reg::PC); let frames: Vec<_> = self .tracepoints .iter() .filter(|(_tracepoint, (ctp, _source, _actions))| ctp.enabled && ctp.addr == pc) .map(|(tracepoint, _definition)| { // our `tracepoint_define` restricts our loaded tracepoints to only contain // register collect actions. instead of only collecting the registers requested // in the register mask and recording a minimal trace frame, we just collect // all of them by cloning the cpu itself. TraceFrame { number: *tracepoint, snapshot: self.cpu, } }) .collect(); self.traceframes.extend(frames); } let mut hit_watchpoint = None; let mut sniffer = MemSniffer::new(&mut self.mem, &self.watchpoints, |access| { hit_watchpoint = Some(access) }); self.cpu.step(&mut sniffer); let pc = self.cpu.reg_get(Mode::User, reg::PC); if let Some(access) = hit_watchpoint { let fixup = if self.cpu.thumb_mode() { 2 } else { 4 }; self.cpu.reg_set(Mode::User, reg::PC, pc - fixup); return Some(match access.kind { AccessKind::Read => Event::WatchRead(access.addr), AccessKind::Write => Event::WatchWrite(access.addr), }); } if self.breakpoints.contains(&pc) { return Some(Event::Break); } if pc == HLE_RETURN_ADDR { return Some(Event::Halted); } None } /// run the emulator in accordance with the currently set `ExecutionMode`. /// /// since the emulator runs in the same thread as the GDB loop, the emulator /// will use the provided callback to poll the connection for incoming data /// every 1024 steps. pub fn run(&mut self, mut poll_incoming_data: impl FnMut() -> bool) -> RunEvent { match self.exec_mode { ExecMode::Step => RunEvent::Event(self.step().unwrap_or(Event::DoneStep)), ExecMode::Continue => { let mut cycles = 0; loop { if cycles % 1024 == 0 { // poll for incoming data if poll_incoming_data() { break RunEvent::IncomingData; } } cycles += 1; if let Some(event) = self.step() { break RunEvent::Event(event); }; } } // just continue, but with an extra PC check ExecMode::RangeStep(start, end) => { let mut cycles = 0; loop { if cycles % 1024 == 0 { // poll for incoming data if poll_incoming_data() { break RunEvent::IncomingData; } } cycles += 1; if let Some(event) = self.step() { break RunEvent::Event(event); }; if !(start..end).contains(&self.cpu.reg_get(self.cpu.mode(), reg::PC)) { break RunEvent::Event(Event::DoneStep); } } } } } } pub enum RunEvent { IncomingData, Event(Event), } ================================================ FILE: examples/armv4t/gdb/auxv.rs ================================================ 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 { let auxv = b"\x00\x00\x00\x00\x00\x00\x00\x00"; Ok(copy_range_to_buf(auxv, offset, length, buf)) } } ================================================ FILE: examples/armv4t/gdb/breakpoints.rs ================================================ use crate::emu::Emu; use gdbstub::target; use gdbstub::target::ext::breakpoints::WatchKind; use gdbstub::target::TargetResult; impl target::ext::breakpoints::Breakpoints for Emu { #[inline(always)] fn support_sw_breakpoint( &mut self, ) -> Option> { Some(self) } #[inline(always)] fn support_hw_watchpoint( &mut self, ) -> Option> { Some(self) } } impl target::ext::breakpoints::SwBreakpoint for Emu { fn add_sw_breakpoint( &mut self, addr: u32, _kind: gdbstub_arch::arm::ArmBreakpointKind, ) -> TargetResult { self.breakpoints.push(addr); Ok(true) } fn remove_sw_breakpoint( &mut self, addr: u32, _kind: gdbstub_arch::arm::ArmBreakpointKind, ) -> TargetResult { match self.breakpoints.iter().position(|x| *x == addr) { None => return Ok(false), Some(pos) => self.breakpoints.remove(pos), }; Ok(true) } } impl target::ext::breakpoints::HwWatchpoint for Emu { fn add_hw_watchpoint( &mut self, addr: u32, len: u32, kind: WatchKind, ) -> TargetResult { for addr in addr..(addr + len) { match kind { WatchKind::Write => self.watchpoints.push(addr), WatchKind::Read => self.watchpoints.push(addr), WatchKind::ReadWrite => self.watchpoints.push(addr), }; } Ok(true) } fn remove_hw_watchpoint( &mut self, addr: u32, len: u32, kind: WatchKind, ) -> TargetResult { for addr in addr..(addr + len) { let pos = match self.watchpoints.iter().position(|x| *x == addr) { None => return Ok(false), Some(pos) => pos, }; match kind { WatchKind::Write => self.watchpoints.remove(pos), WatchKind::Read => self.watchpoints.remove(pos), WatchKind::ReadWrite => self.watchpoints.remove(pos), }; } Ok(true) } } ================================================ FILE: examples/armv4t/gdb/catch_syscalls.rs ================================================ use crate::gdb::Emu; use gdbstub::target; use gdbstub::target::ext::catch_syscalls::SyscallNumbers; // This implementation is for illustrative purposes only. If the target doesn't // support syscalls then there is no need to implement this extension impl target::ext::catch_syscalls::CatchSyscalls for Emu { fn enable_catch_syscalls( &mut self, filter: Option>, ) -> target::TargetResult<(), Self> { match filter { Some(numbers) => eprintln!( "Enabled catching syscalls: {:?}", numbers.collect::>() ), None => eprintln!("Enabled catching all syscalls"), } Ok(()) } fn disable_catch_syscalls(&mut self) -> target::TargetResult<(), Self> { eprintln!("Disabled catching syscalls"); Ok(()) } } ================================================ FILE: examples/armv4t/gdb/exec_file.rs ================================================ use super::copy_range_to_buf; use crate::emu::Emu; use gdbstub::common::Pid; use gdbstub::target; use gdbstub::target::TargetResult; impl target::ext::exec_file::ExecFile for Emu { fn get_exec_file( &self, _pid: Option, offset: u64, length: usize, buf: &mut [u8], ) -> TargetResult { let filename = b"/test.elf"; Ok(copy_range_to_buf(filename, offset, length, buf)) } } ================================================ FILE: examples/armv4t/gdb/extended_mode.rs ================================================ use crate::emu::Emu; use gdbstub::common::Pid; use gdbstub::target; 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; /*===================================== = Extended Mode = =====================================*/ // This is a stub implementation of GDB's Extended Mode extensions. // // Truth be told, this particular emulator is _not_ very well suited to running // in extended mode, as it doesn't technically spawn/attach to any process. // Nonetheless, it's useful to have a stubbed implementation in-tree which can // be used for basic usability / regression testing. // // If you happen to implement a "proper" extended mode gdbstub, feel free to // file an issue / open a PR that links to your project! impl target::ext::extended_mode::ExtendedMode for Emu { fn kill(&mut self, pid: Option) -> TargetResult { eprintln!("GDB sent a kill request for pid {:?}", pid); Ok(ShouldTerminate::No) } fn restart(&mut self) -> Result<(), Self::Error> { eprintln!("GDB sent a restart request"); Ok(()) } fn attach(&mut self, pid: Pid) -> TargetResult<(), Self> { 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 { // simplified example: assume UTF-8 filenames / args // // To be 100% pedantically correct, consider converting to an `OsStr` in the // least lossy way possible (e.g: using the `from_bytes` extension from // `std::os::unix::ffi::OsStrExt`). let filename = match filename { None => None, Some(raw) => Some(core::str::from_utf8(raw).map_err(drop)?), }; let args = args .map(|raw| core::str::from_utf8(raw).map_err(drop)) .collect::, _>>()?; eprintln!( "GDB tried to run a new process with filename {:?}, and args {:?}", filename, args ); self.reset(); // when running in single-threaded mode, this PID can be anything Ok(Pid::new(1337).unwrap()) } fn query_if_attached(&mut self, pid: Pid) -> TargetResult { eprintln!( "GDB queried if it was attached to a process with PID {}", pid ); Ok(AttachKind::Attach) } #[inline(always)] fn support_configure_aslr( &mut self, ) -> Option> { Some(self) } #[inline(always)] fn support_configure_env( &mut self, ) -> Option> { Some(self) } #[inline(always)] fn support_configure_startup_shell( &mut self, ) -> Option> { Some(self) } #[inline(always)] fn support_configure_working_dir( &mut self, ) -> Option> { Some(self) } #[inline(always)] fn support_current_active_pid( &mut self, ) -> Option> { Some(self) } } impl target::ext::extended_mode::ConfigureAslr for Emu { fn cfg_aslr(&mut self, enabled: bool) -> TargetResult<(), Self> { eprintln!("GDB {} ASLR", if enabled { "enabled" } else { "disabled" }); Ok(()) } } impl target::ext::extended_mode::ConfigureEnv for Emu { fn set_env(&mut self, key: &[u8], val: Option<&[u8]>) -> TargetResult<(), Self> { // simplified example: assume UTF-8 key/val env vars let key = core::str::from_utf8(key).map_err(drop)?; let val = match val { None => None, Some(raw) => Some(core::str::from_utf8(raw).map_err(drop)?), }; eprintln!("GDB tried to set a new env var: {:?}={:?}", key, val); Ok(()) } fn remove_env(&mut self, key: &[u8]) -> TargetResult<(), Self> { let key = core::str::from_utf8(key).map_err(drop)?; eprintln!("GDB tried to set remove a env var: {:?}", key); Ok(()) } fn reset_env(&mut self) -> TargetResult<(), Self> { eprintln!("GDB tried to reset env vars"); Ok(()) } } impl target::ext::extended_mode::ConfigureStartupShell for Emu { fn cfg_startup_with_shell(&mut self, enabled: bool) -> TargetResult<(), Self> { eprintln!( "GDB {} startup with shell", if enabled { "enabled" } else { "disabled" } ); Ok(()) } } impl target::ext::extended_mode::ConfigureWorkingDir for Emu { fn cfg_working_dir(&mut self, dir: Option<&[u8]>) -> TargetResult<(), Self> { let dir = match dir { None => None, Some(raw) => Some(core::str::from_utf8(raw).map_err(drop)?), }; match dir { None => eprintln!("GDB reset the working directory"), Some(dir) => eprintln!("GDB set the working directory to {:?}", dir), } Ok(()) } } impl target::ext::extended_mode::CurrentActivePid for Emu { fn current_active_pid(&mut self) -> Result { Ok(self.reported_pid) } } ================================================ FILE: examples/armv4t/gdb/flash.rs ================================================ use crate::emu::Emu; use gdbstub::target; use gdbstub::target::TargetResult; impl target::ext::flash::Flash for Emu { fn flash_erase(&mut self, start_addr: u32, length: u32) -> TargetResult<(), Self> { log::info!("flash_erase start_addr: {start_addr:08x}, length: {length:08x}"); Ok(()) } fn flash_write(&mut self, start_addr: u32, _data: &[u8]) -> TargetResult<(), Self> { log::info!("flash_write start_addr: {start_addr:08x}"); Ok(()) } fn flash_done(&mut self) -> TargetResult<(), Self> { log::info!("flash_done"); Ok(()) } } ================================================ FILE: examples/armv4t/gdb/host_io.rs ================================================ 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; impl target::ext::host_io::HostIo for Emu { #[inline(always)] fn support_open(&mut self) -> Option> { Some(self) } #[inline(always)] fn support_close(&mut self) -> Option> { Some(self) } #[inline(always)] fn support_pread(&mut self) -> Option> { Some(self) } #[inline(always)] fn support_pwrite(&mut self) -> Option> { Some(self) } #[inline(always)] fn support_fstat(&mut self) -> Option> { Some(self) } #[inline(always)] fn support_unlink(&mut self) -> Option> { Some(self) } #[inline(always)] fn support_readlink(&mut self) -> Option> { Some(self) } #[inline(always)] fn support_setfs(&mut self) -> Option> { Some(self) } } impl target::ext::host_io::HostIoOpen for Emu { fn open( &mut self, filename: &[u8], flags: HostIoOpenFlags, _mode: HostIoOpenMode, ) -> HostIoResult { if filename.starts_with(b"/proc") { return Err(HostIoError::Errno(HostIoErrno::ENOENT)); } // In this example, the test binary is compiled into the binary itself as the // `TEST_PROGRAM_ELF` array using `include_bytes!`. As such, we must "spoof" the // existence of a real file, which will actually be backed by the in-binary // `TEST_PROGRAM_ELF` array. if filename == b"/test.elf" { return Ok(0); } let path = std::str::from_utf8(filename).map_err(|_| HostIoError::Errno(HostIoErrno::ENOENT))?; let mut read = false; let mut write = false; if flags.contains(HostIoOpenFlags::O_RDWR) { read = true; write = true; } else if flags.contains(HostIoOpenFlags::O_WRONLY) { write = true; } else { read = true; } let file = std::fs::OpenOptions::new() .read(read) .write(write) .append(flags.contains(HostIoOpenFlags::O_APPEND)) .create(flags.contains(HostIoOpenFlags::O_CREAT)) .truncate(flags.contains(HostIoOpenFlags::O_TRUNC)) .create_new(flags.contains(HostIoOpenFlags::O_EXCL)) .open(path)?; let n = match self.files.iter_mut().enumerate().find(|(_, f)| f.is_none()) { Some((n, free_file)) => { *free_file = Some(file); n } None => { self.files.push(Some(file)); self.files.len() - 1 } }; Ok(n as u32 + FD_RESERVED) } } impl target::ext::host_io::HostIoClose for Emu { fn close(&mut self, fd: u32) -> HostIoResult<(), Self> { if fd < FD_RESERVED { return Ok(()); } let file = match self.files.get_mut((fd - FD_RESERVED) as usize) { Some(file) => file, _ => return Err(HostIoError::Errno(HostIoErrno::EBADF)), }; file.take().ok_or(HostIoError::Errno(HostIoErrno::EBADF))?; while let Some(None) = self.files.last() { self.files.pop(); } Ok(()) } } impl target::ext::host_io::HostIoPread for Emu { fn pread<'a>( &mut self, fd: u32, count: usize, offset: u64, buf: &mut [u8], ) -> HostIoResult { if fd < FD_RESERVED { if fd == 0 { return Ok(copy_range_to_buf(TEST_PROGRAM_ELF, offset, count, buf)); } else { return Err(HostIoError::Errno(HostIoErrno::EBADF)); } } let file = match self.files.get_mut((fd - FD_RESERVED) as usize) { Some(Some(file)) => file, _ => return Err(HostIoError::Errno(HostIoErrno::EBADF)), }; file.seek(std::io::SeekFrom::Start(offset))?; let n = file.read(buf)?; Ok(n) } } impl target::ext::host_io::HostIoPwrite for Emu { fn pwrite(&mut self, fd: u32, offset: u32, data: &[u8]) -> HostIoResult { if fd < FD_RESERVED { return Err(HostIoError::Errno(HostIoErrno::EACCES)); } let file = match self.files.get_mut((fd - FD_RESERVED) as usize) { Some(Some(file)) => file, _ => return Err(HostIoError::Errno(HostIoErrno::EBADF)), }; file.seek(std::io::SeekFrom::Start(offset as u64))?; let n = file.write(data)?; Ok(n as u32) } } impl target::ext::host_io::HostIoFstat for Emu { fn fstat(&mut self, fd: u32) -> HostIoResult { if fd < FD_RESERVED { if fd == 0 { return Ok(HostIoStat { st_dev: 0, st_ino: 0, st_mode: HostIoOpenMode::empty(), st_nlink: 0, st_uid: 0, st_gid: 0, st_rdev: 0, st_size: TEST_PROGRAM_ELF.len() as u64, st_blksize: 0, st_blocks: 0, st_atime: 0, st_mtime: 0, st_ctime: 0, }); } else { return Err(HostIoError::Errno(HostIoErrno::EBADF)); } } let metadata = match self.files.get((fd - FD_RESERVED) as usize) { Some(Some(file)) => file.metadata()?, _ => return Err(HostIoError::Errno(HostIoErrno::EBADF)), }; macro_rules! time_to_secs { ($time:expr) => { $time .map_err(|_| HostIoError::Errno(HostIoErrno::EACCES))? .duration_since(std::time::SystemTime::UNIX_EPOCH) .map_err(|_| HostIoError::Errno(HostIoErrno::EACCES))? .as_secs() as u32 }; } let atime = time_to_secs!(metadata.accessed()); let mtime = time_to_secs!(metadata.modified()); let ctime = time_to_secs!(metadata.created()); Ok(HostIoStat { st_dev: 0, st_ino: 0, st_mode: HostIoOpenMode::empty(), st_nlink: 0, st_uid: 0, st_gid: 0, st_rdev: 0, st_size: metadata.len(), st_blksize: 0, st_blocks: 0, st_atime: atime, st_mtime: mtime, st_ctime: ctime, }) } } impl target::ext::host_io::HostIoUnlink for Emu { fn unlink(&mut self, filename: &[u8]) -> HostIoResult<(), Self> { let path = std::str::from_utf8(filename).map_err(|_| HostIoError::Errno(HostIoErrno::ENOENT))?; std::fs::remove_file(path)?; Ok(()) } } impl target::ext::host_io::HostIoReadlink for Emu { fn readlink<'a>(&mut self, filename: &[u8], buf: &mut [u8]) -> HostIoResult { if filename == b"/proc/1/exe" { // Support `info proc exe` command let exe = b"/test.elf"; return Ok(copy_to_buf(exe, buf)); } else if filename == b"/proc/1/cwd" { // Support `info proc cwd` command let cwd = b"/"; return Ok(copy_to_buf(cwd, buf)); } else if filename.starts_with(b"/proc") { return Err(HostIoError::Errno(HostIoErrno::ENOENT)); } let path = std::str::from_utf8(filename).map_err(|_| HostIoError::Errno(HostIoErrno::ENOENT))?; let link = std::fs::read_link(path)?; let data = link .to_str() .ok_or(HostIoError::Errno(HostIoErrno::ENOENT))? .as_bytes(); if data.len() <= buf.len() { Ok(copy_to_buf(data, buf)) } else { Err(HostIoError::Errno(HostIoErrno::ENAMETOOLONG)) } } } impl target::ext::host_io::HostIoSetfs for Emu { fn setfs(&mut self, _fs: FsKind) -> HostIoResult<(), Self> { Ok(()) } } ================================================ FILE: examples/armv4t/gdb/libraries.rs ================================================ use super::copy_range_to_buf; use crate::emu::Emu; use gdbstub::target; use gdbstub::target::TargetResult; impl target::ext::libraries::LibrariesSvr4 for Emu { fn get_libraries_svr4( &self, offset: u64, length: usize, buf: &mut [u8], ) -> TargetResult { // `l_ld` is the address of the `PT_DYNAMIC` ELF segment, so fake an // address here. // // The `main-lm`, `lm`, and `lmid` seem to refer to in-memory structures // which gdb may read, but gdb also seems to work well enough if they're // null-ish or otherwise pointing to non-present things. let xml = r#" "# .trim() .as_bytes(); Ok(copy_range_to_buf(xml, offset, length, buf)) } } impl target::ext::libraries::Libraries for Emu { fn get_libraries( &self, offset: u64, length: usize, buf: &mut [u8], ) -> TargetResult { // This is the Windows/generic library list format, which uses segment // addresses instead of the SVR4 link_map structure. // // Note: on Windows, the `address` is not the image base, but the // address of the first section (typically .text). let xml = r#" "# .trim() .as_bytes(); Ok(copy_range_to_buf(xml, offset, length, buf)) } } ================================================ FILE: examples/armv4t/gdb/lldb_register_info_override.rs ================================================ 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; use gdbstub::target::ext::lldb_register_info_override::CallbackToken; use gdbstub_arch::arm::reg::id::ArmCoreRegId; // (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 // 25*4 in the 'g'/'G' packets, so we add 8 padding registers here. Please see // gdbstub/examples/armv4t/gdb/target_description_xml_override.rs for more info. impl target::ext::lldb_register_info_override::LldbRegisterInfoOverride for Emu { fn lldb_register_info<'a>( &mut self, reg_id: usize, reg_info: Callback<'a>, ) -> Result, Self::Error> { match ArmCoreRegIdCustom::from_raw_id(reg_id) { Some((_, None)) | None => Ok(reg_info.done()), Some((r, Some(size))) => { let name: String = match r { // For the purpose of demonstration, we end the qRegisterInfo packet exchange // when reaching the Time register id, so that this register can only be // explicitly queried via the single-register read packet. ArmCoreRegIdCustom::Time => return Ok(reg_info.done()), ArmCoreRegIdCustom::Core(ArmCoreRegId::Gpr(i)) => match i { 0 => "r0", 1 => "r1", 2 => "r2", 3 => "r3", 4 => "r4", 5 => "r5", 6 => "r6", 7 => "r7", 8 => "r8", 9 => "r9", 10 => "r10", 11 => "r11", 12 => "r12", _ => "unknown", }, ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) => "sp", ArmCoreRegIdCustom::Core(ArmCoreRegId::Lr) => "lr", ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) => "pc", ArmCoreRegIdCustom::Core(ArmCoreRegId::Fpr(_i)) => "padding", ArmCoreRegIdCustom::Core(ArmCoreRegId::Fps) => "padding", ArmCoreRegIdCustom::Core(ArmCoreRegId::Cpsr) => "cpsr", ArmCoreRegIdCustom::Custom => "custom", _ => "unknown", } .into(); let encoding = match r { ArmCoreRegIdCustom::Core(ArmCoreRegId::Gpr(_i)) => Encoding::Uint, ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) | ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) | ArmCoreRegIdCustom::Core(ArmCoreRegId::Cpsr) | ArmCoreRegIdCustom::Custom => Encoding::Uint, _ => Encoding::Vector, }; let format = match r { ArmCoreRegIdCustom::Core(ArmCoreRegId::Gpr(_i)) => Format::Hex, ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) | ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) | ArmCoreRegIdCustom::Core(ArmCoreRegId::Cpsr) | ArmCoreRegIdCustom::Custom => Format::Hex, _ => Format::VectorUInt8, }; let set: String = match r { ArmCoreRegIdCustom::Core(ArmCoreRegId::Gpr(_i)) => "General Purpose Registers", ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) | ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) | ArmCoreRegIdCustom::Core(ArmCoreRegId::Cpsr) | ArmCoreRegIdCustom::Custom => "General Purpose Registers", _ => "Floating Point Registers", } .into(); let generic = match r { ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) => Some(Generic::Sp), ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) => Some(Generic::Pc), _ => None, }; let reg = Register { name: &name, alt_name: None, bitsize: (usize::from(size)) * 8, offset: reg_id * (usize::from(size)), encoding, format, set: &set, gcc: None, dwarf: Some(reg_id), generic, container_regs: None, invalidate_regs: None, }; Ok(reg_info.write(reg)) } } } } ================================================ FILE: examples/armv4t/gdb/memory_map.rs ================================================ 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( &self, offset: u64, length: usize, buf: &mut [u8], ) -> TargetResult { let memory_map = r#" 0x1000 "# .trim() .as_bytes(); Ok(copy_range_to_buf(memory_map, offset, length, buf)) } } ================================================ FILE: examples/armv4t/gdb/mod.rs ================================================ use crate::emu::Emu; use crate::emu::ExecMode; use armv4t_emu::reg; use armv4t_emu::Memory; use core::convert::TryInto; use gdbstub::common::Signal; use gdbstub::target; 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; // Additional GDB extensions mod auxv; mod breakpoints; mod catch_syscalls; mod exec_file; mod extended_mode; mod flash; mod host_io; mod libraries; mod lldb_register_info_override; mod memory_map; mod monitor_cmd; mod section_offsets; mod target_description_xml_override; pub(crate) mod tracepoints; /// Turn a `ArmCoreRegId` into an internal register number of `armv4t_emu`. fn cpu_reg_id(id: ArmCoreRegId) -> Option { match id { ArmCoreRegId::Gpr(i) => Some(i), ArmCoreRegId::Sp => Some(reg::SP), ArmCoreRegId::Lr => Some(reg::LR), ArmCoreRegId::Pc => Some(reg::PC), ArmCoreRegId::Cpsr => Some(reg::CPSR), _ => None, } } /// Copy all bytes of `data` to `buf`. /// Return the size of data copied. pub fn copy_to_buf(data: &[u8], buf: &mut [u8]) -> usize { let len = buf.len().min(data.len()); buf[..len].copy_from_slice(&data[..len]); len } /// Copy a range of `data` (start at `offset` with a size of `length`) to `buf`. /// Return the size of data copied. Returns 0 if `offset >= buf.len()`. /// /// Mainly used by qXfer:_object_:read commands. pub fn copy_range_to_buf(data: &[u8], offset: u64, length: usize, buf: &mut [u8]) -> usize { let offset = offset as usize; if offset > data.len() { return 0; } let start = offset; let end = (offset + length).min(data.len()); copy_to_buf(&data[start..end], buf) } impl Target for Emu { // As an example, I've defined a custom architecture based off // `gdbstub_arch::arm::Armv4t`. The implementation is in the `custom_arch` // module at the bottom of this file. // // unless you're working with a particularly funky architecture that uses custom // registers, you should probably stick to using the simple `target.xml` // implementations from the `gdbstub_arch` repo (i.e: `target.xml` files that // only specify the and s of the arch, instead of // listing out all the registers out manually). type Arch = custom_arch::Armv4tCustom; type Error = &'static str; // --------------- IMPORTANT NOTE --------------- // Always remember to annotate IDET enable methods with `inline(always)`! // Without this annotation, LLVM might fail to dead-code-eliminate nested IDET // implementations, resulting in unnecessary binary bloat. #[inline(always)] fn base_ops(&mut self) -> target::ext::base::BaseOps<'_, Self::Arch, Self::Error> { target::ext::base::BaseOps::SingleThread(self) } #[inline(always)] fn support_breakpoints( &mut self, ) -> Option> { Some(self) } #[inline(always)] fn support_extended_mode( &mut self, ) -> Option> { Some(self) } #[inline(always)] fn support_monitor_cmd(&mut self) -> Option> { Some(self) } #[inline(always)] fn support_section_offsets( &mut self, ) -> Option> { Some(self) } #[inline(always)] fn support_target_description_xml_override( &mut self, ) -> Option< target::ext::target_description_xml_override::TargetDescriptionXmlOverrideOps<'_, Self>, > { Some(self) } #[inline(always)] fn support_lldb_register_info_override( &mut self, ) -> Option> { Some(self) } #[inline(always)] fn support_memory_map(&mut self) -> Option> { Some(self) } #[inline(always)] fn support_catch_syscalls( &mut self, ) -> Option> { Some(self) } #[inline(always)] fn support_host_io(&mut self) -> Option> { Some(self) } #[inline(always)] fn support_exec_file(&mut self) -> Option> { Some(self) } #[inline(always)] fn support_auxv(&mut self) -> Option> { Some(self) } #[inline(always)] fn support_libraries_svr4( &mut self, ) -> Option> { Some(self) } #[inline(always)] fn support_libraries(&mut self) -> Option> { Some(self) } #[inline(always)] fn support_tracepoints( &mut self, ) -> Option> { Some(self) } #[inline(always)] fn support_flash_operations(&mut self) -> Option> { Some(self) } } impl SingleThreadBase for Emu { fn read_registers( &mut self, regs: &mut custom_arch::ArmCoreRegsCustom, ) -> TargetResult<(), Self> { // if we selected a frame from a tracepoint, return registers from that snapshot let cpu = self .selected_frame .and_then(|selected| self.traceframes.get(selected)) .map(|frame| frame.snapshot) .unwrap_or_else(|| self.cpu); let mode = cpu.mode(); for i in 0..13 { regs.core.r[i] = cpu.reg_get(mode, i as u8); } regs.core.sp = cpu.reg_get(mode, reg::SP); regs.core.lr = cpu.reg_get(mode, reg::LR); regs.core.pc = cpu.reg_get(mode, reg::PC); regs.core.cpsr = cpu.reg_get(mode, reg::CPSR); regs.custom = self.custom_reg; Ok(()) } fn write_registers(&mut self, regs: &custom_arch::ArmCoreRegsCustom) -> TargetResult<(), Self> { if self.selected_frame.is_some() { // we can't modify registers in a tracepoint frame return Err(TargetError::NonFatal); } let mode = self.cpu.mode(); for i in 0..13 { self.cpu.reg_set(mode, i, regs.core.r[i as usize]); } self.cpu.reg_set(mode, reg::SP, regs.core.sp); self.cpu.reg_set(mode, reg::LR, regs.core.lr); self.cpu.reg_set(mode, reg::PC, regs.core.pc); self.cpu.reg_set(mode, reg::CPSR, regs.core.cpsr); self.custom_reg = regs.custom; Ok(()) } #[inline(always)] fn support_single_register_access( &mut self, ) -> Option> { Some(self) } fn read_addrs(&mut self, start_addr: u32, data: &mut [u8]) -> TargetResult { if self.selected_frame.is_some() { // we only support register collection actions for our tracepoint frames. // if we have a selected frame, then we don't have any memory we can // return from the frame snapshot. return Ok(0); } // this is a simple emulator, with RAM covering the entire 32 bit address space for (addr, val) in (start_addr..).zip(data.iter_mut()) { *val = self.mem.r8(addr) } Ok(data.len()) } fn write_addrs(&mut self, start_addr: u32, data: &[u8]) -> TargetResult<(), Self> { if self.selected_frame.is_some() { // we can't modify memory in a tracepoint frame return Err(TargetError::NonFatal); } // this is a simple emulator, with RAM covering the entire 32 bit address space for (addr, val) in (start_addr..).zip(data.iter().copied()) { self.mem.w8(addr, val) } Ok(()) } #[inline(always)] fn support_resume( &mut self, ) -> Option> { Some(self) } } impl SingleThreadResume for Emu { fn resume(&mut self, signal: Option) -> Result<(), Self::Error> { // Upon returning from the `resume` method, the target being debugged should be // configured to run according to whatever resume actions the GDB client has // specified (as specified by `set_resume_action`, `resume_range_step`, // `reverse_{step, continue}`, etc...) // // In this basic `armv4t` example, the `resume` method simply sets the exec mode // of the emulator's interpreter loop and returns. // // In more complex implementations, it's likely that the target being debugged // will be running in another thread / process, and will require some kind of // external "orchestration" to set it's execution mode (e.g: modifying the // target's process state via platform specific debugging syscalls). if signal.is_some() { return Err("no support for continuing with signal"); } self.exec_mode = ExecMode::Continue; Ok(()) } #[inline(always)] fn support_reverse_cont( &mut self, ) -> Option> { Some(self) } #[inline(always)] fn support_reverse_step( &mut self, ) -> Option> { Some(self) } #[inline(always)] fn support_single_step( &mut self, ) -> Option> { Some(self) } #[inline(always)] fn support_range_step( &mut self, ) -> Option> { Some(self) } } impl target::ext::base::singlethread::SingleThreadSingleStep for Emu { fn step(&mut self, signal: Option) -> Result<(), Self::Error> { if signal.is_some() { return Err("no support for stepping with signal"); } self.exec_mode = ExecMode::Step; Ok(()) } } impl target::ext::base::single_register_access::SingleRegisterAccess<()> for Emu { fn read_register( &mut self, _tid: (), reg_id: custom_arch::ArmCoreRegIdCustom, buf: &mut [u8], ) -> TargetResult { match reg_id { custom_arch::ArmCoreRegIdCustom::Core(reg_id) => { if let Some(i) = cpu_reg_id(reg_id) { let w = self.cpu.reg_get(self.cpu.mode(), i); buf.copy_from_slice(&w.to_le_bytes()); Ok(buf.len()) } else { Err(().into()) } } custom_arch::ArmCoreRegIdCustom::Custom => { buf.copy_from_slice(&self.custom_reg.to_le_bytes()); Ok(buf.len()) } custom_arch::ArmCoreRegIdCustom::Time => { buf.copy_from_slice( &(std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_millis() as u32) .to_le_bytes(), ); Ok(buf.len()) } custom_arch::ArmCoreRegIdCustom::Unavailable => Ok(0), } } fn write_register( &mut self, _tid: (), reg_id: custom_arch::ArmCoreRegIdCustom, val: &[u8], ) -> TargetResult<(), Self> { let w = u32::from_le_bytes( val.try_into() .map_err(|_| TargetError::Fatal("invalid data"))?, ); match reg_id { custom_arch::ArmCoreRegIdCustom::Core(reg_id) => { if let Some(i) = cpu_reg_id(reg_id) { self.cpu.reg_set(self.cpu.mode(), i, w); Ok(()) } else { Err(().into()) } } custom_arch::ArmCoreRegIdCustom::Custom => { self.custom_reg = w; Ok(()) } // ignore writes custom_arch::ArmCoreRegIdCustom::Unavailable | custom_arch::ArmCoreRegIdCustom::Time => Ok(()), } } } impl target::ext::base::reverse_exec::ReverseCont<()> for Emu { fn reverse_cont(&mut self) -> Result<(), Self::Error> { // FIXME: actually implement reverse step eprintln!( "FIXME: Not actually reverse-continuing. Performing forwards continue instead..." ); self.exec_mode = ExecMode::Continue; Ok(()) } } impl target::ext::base::reverse_exec::ReverseStep<()> for Emu { fn reverse_step(&mut self, _tid: ()) -> Result<(), Self::Error> { // FIXME: actually implement reverse step eprintln!( "FIXME: Not actually reverse-stepping. Performing single forwards step instead..." ); self.exec_mode = ExecMode::Step; Ok(()) } } impl target::ext::base::singlethread::SingleThreadRangeStepping for Emu { fn resume_range_step(&mut self, start: u32, end: u32) -> Result<(), Self::Error> { self.exec_mode = ExecMode::RangeStep(start, end); Ok(()) } } mod custom_arch { use core::num::NonZeroUsize; 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; /// Implements `Arch` for ARMv4T pub enum Armv4tCustom {} #[derive(Debug, Default, Clone, Eq, PartialEq)] pub struct ArmCoreRegsCustom { pub core: ArmCoreRegs, pub custom: u32, } impl Registers for ArmCoreRegsCustom { type ProgramCounter = u32; fn pc(&self) -> Self::ProgramCounter { self.core.pc } fn gdb_serialize(&self, mut write_byte: impl FnMut(Option)) { self.core.gdb_serialize(&mut write_byte); macro_rules! write_bytes { ($bytes:expr) => { for b in $bytes { write_byte(Some(*b)) } }; } write_bytes!(&self.custom.to_le_bytes()); } fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { // ensure bytes.chunks_exact(4) won't panic if !bytes.len().is_multiple_of(4) { return Err(()); } use core::convert::TryInto; let mut regs = bytes .chunks_exact(4) .map(|c| u32::from_le_bytes(c.try_into().unwrap())); // copied from ArmCoreRegs { for reg in self.core.r.iter_mut() { *reg = regs.next().ok_or(())? } self.core.sp = regs.next().ok_or(())?; self.core.lr = regs.next().ok_or(())?; self.core.pc = regs.next().ok_or(())?; // Floating point registers (unused) for _ in 0..25 { regs.next().ok_or(())?; } self.core.cpsr = regs.next().ok_or(())?; } self.custom = regs.next().ok_or(())?; if regs.next().is_some() { return Err(()); } Ok(()) } } #[derive(Debug)] pub enum ArmCoreRegIdCustom { Core(ArmCoreRegId), Custom, // not sent as part of `struct ArmCoreRegsCustom`, and only accessible via the single // register read/write functions Time, /// This pseudo-register is valid but never available Unavailable, } impl RegId for ArmCoreRegIdCustom { fn from_raw_id(id: usize) -> Option<(Self, Option)> { let reg = match id { 26 => Self::Custom, 27 => Self::Time, 28 => Self::Unavailable, _ => { let (reg, size) = ArmCoreRegId::from_raw_id(id)?; return Some((Self::Core(reg), size)); } }; Some((reg, Some(NonZeroUsize::new(4)?))) } } impl Arch for Armv4tCustom { type Usize = u32; type Registers = ArmCoreRegsCustom; type RegId = ArmCoreRegIdCustom; type BreakpointKind = ArmBreakpointKind; // for _purely demonstrative purposes_, i'll return dummy data from this // function, as it will be overwritten by TargetDescriptionXmlOverride. // // See `examples/armv4t/gdb/target_description_xml_override.rs` // // in an actual implementation, you'll want to return an actual string here! fn target_description_xml() -> Option<&'static str> { Some("never gets returned") } // (LLDB extension) // // for _purely demonstrative purposes_, even though this provides a working // example, it will get overwritten by RegisterInfoOverride. // // See `examples/armv4t/gdb/register_info_override.rs` fn lldb_register_info(reg_id: usize) -> Option> { match ArmCoreRegIdCustom::from_raw_id(reg_id) { Some((_, None)) | None => Some(RegisterInfo::Done), Some((r, Some(size))) => { let name = match r { // For the purpose of demonstration, we end the qRegisterInfo packet // exchange when reaching the Time register id, so that this register can // only be explicitly queried via the single-register read packet. ArmCoreRegIdCustom::Time => return Some(RegisterInfo::Done), ArmCoreRegIdCustom::Core(ArmCoreRegId::Gpr(i)) => match i { 0 => "r0", 1 => "r1", 2 => "r2", 3 => "r3", 4 => "r4", 5 => "r5", 6 => "r6", 7 => "r7", 8 => "r8", 9 => "r9", 10 => "r10", 11 => "r11", 12 => "r12", _ => "unknown", }, ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) => "sp", ArmCoreRegIdCustom::Core(ArmCoreRegId::Lr) => "lr", ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) => "pc", ArmCoreRegIdCustom::Core(ArmCoreRegId::Fpr(_i)) => "padding", ArmCoreRegIdCustom::Core(ArmCoreRegId::Fps) => "padding", ArmCoreRegIdCustom::Core(ArmCoreRegId::Cpsr) => "cpsr", ArmCoreRegIdCustom::Custom => "custom", ArmCoreRegIdCustom::Unavailable => "Unavailable", _ => "unknown", }; let encoding = match r { ArmCoreRegIdCustom::Core(ArmCoreRegId::Gpr(_i)) => Encoding::Uint, ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) | ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) | ArmCoreRegIdCustom::Core(ArmCoreRegId::Cpsr) | ArmCoreRegIdCustom::Unavailable | ArmCoreRegIdCustom::Custom => Encoding::Uint, _ => Encoding::Vector, }; let format = match r { ArmCoreRegIdCustom::Core(ArmCoreRegId::Gpr(_i)) => Format::Hex, ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) | ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) | ArmCoreRegIdCustom::Core(ArmCoreRegId::Cpsr) | ArmCoreRegIdCustom::Unavailable | ArmCoreRegIdCustom::Custom => Format::Hex, _ => Format::VectorUInt8, }; let set = match r { ArmCoreRegIdCustom::Core(ArmCoreRegId::Gpr(_i)) => { "General Purpose Registers" } ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) | ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) | ArmCoreRegIdCustom::Core(ArmCoreRegId::Cpsr) | ArmCoreRegIdCustom::Unavailable | ArmCoreRegIdCustom::Custom => "General Purpose Registers", _ => "Floating Point Registers", }; let generic = match r { ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) => Some(Generic::Sp), ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) => Some(Generic::Pc), _ => None, }; let reg = Register { name, alt_name: None, bitsize: (usize::from(size)) * 8, offset: reg_id * (usize::from(size)), encoding, format, set, gcc: None, dwarf: Some(reg_id), generic, container_regs: None, invalidate_regs: None, }; Some(RegisterInfo::Register(reg)) } } } } } ================================================ FILE: examples/armv4t/gdb/monitor_cmd.rs ================================================ 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( &mut self, cmd: &[u8], mut out: ConsoleOutput<'_>, ) -> Result<(), Self::Error> { let cmd = match core::str::from_utf8(cmd) { Ok(cmd) => cmd, Err(_) => { outputln!(out, "command must be valid UTF-8"); return Ok(()); } }; match cmd { "" => outputln!(out, "Sorry, didn't catch that. Try `monitor ping`!"), "ping" => outputln!(out, "pong!"), _ => outputln!(out, "I don't know how to handle '{}'", cmd), }; Ok(()) } } ================================================ FILE: examples/armv4t/gdb/section_offsets.rs ================================================ use crate::gdb::Emu; use gdbstub::target; use gdbstub::target::ext::section_offsets::Offsets; // This implementation is for illustrative purposes only. If the offsets are // guaranteed to be zero, this extension does not need to be implemented. impl target::ext::section_offsets::SectionOffsets for Emu { fn get_section_offsets(&mut self) -> Result, Self::Error> { Ok(Offsets::Sections { text: 0, data: 0, bss: None, }) } } ================================================ FILE: examples/armv4t/gdb/target_description_xml_override.rs ================================================ use super::copy_range_to_buf; use crate::emu::Emu; use gdbstub::target; use gdbstub::target::TargetError; use gdbstub::target::TargetResult; impl target::ext::target_description_xml_override::TargetDescriptionXmlOverride for Emu { fn target_description_xml( &self, annex: &[u8], offset: u64, length: usize, buf: &mut [u8], ) -> TargetResult { let xml = match annex { b"target.xml" => TARGET_XML.trim(), b"extra.xml" => EXTRA_XML.trim(), _ => return Err(TargetError::NonFatal), }; Ok(copy_range_to_buf( xml.trim().as_bytes(), offset, length, buf, )) } } const TARGET_XML: &str = r#" armv4t "#; const EXTRA_XML: &str = r#" "#; ================================================ FILE: examples/armv4t/gdb/tracepoints.rs ================================================ use crate::emu::Emu; use gdbstub::target; use gdbstub::target::ext::tracepoints::ExperimentExplanation; use gdbstub::target::ext::tracepoints::ExperimentStatus; use gdbstub::target::ext::tracepoints::FrameDescription; use gdbstub::target::ext::tracepoints::FrameRequest; use gdbstub::target::ext::tracepoints::NewTracepoint; use gdbstub::target::ext::tracepoints::SourceTracepoint; use gdbstub::target::ext::tracepoints::TraceBufferConfig; use gdbstub::target::ext::tracepoints::Tracepoint; use gdbstub::target::ext::tracepoints::TracepointAction; use gdbstub::target::ext::tracepoints::TracepointEnumerateState; use gdbstub::target::ext::tracepoints::TracepointEnumerateStep; use gdbstub::target::ext::tracepoints::TracepointStatus; use gdbstub::target::TargetError; use gdbstub::target::TargetResult; impl Emu { fn step_to_next_tracepoint(&self, tp: Tracepoint) -> TracepointEnumerateStep { let next_tp = self.tracepoints.range(tp..).nth(1); if let Some((tp, (new_tp, _, _))) = next_tp { TracepointEnumerateStep::Next { tp: *tp, addr: new_tp.addr, } } else { // No more tracepoints TracepointEnumerateStep::Done } } } impl target::ext::tracepoints::Tracepoints for Emu { fn tracepoints_init(&mut self) -> TargetResult<(), Self> { self.tracepoints.clear(); self.traceframes.clear(); Ok(()) } fn tracepoint_create_begin(&mut self, tp: NewTracepoint) -> TargetResult<(), Self> { self.tracepoints.insert(tp.number, (tp, vec![], vec![])); Ok(()) } fn tracepoint_create_continue( &mut self, tp: Tracepoint, action: &TracepointAction<'_, u32>, ) -> TargetResult<(), Self> { if let &TracepointAction::Registers { mask: _ } = &action { // we only handle register collection actions for the simple // case } else { return Err(TargetError::NonFatal); } self.tracepoints .get_mut(&tp) .map(move |(_ctp, _source, actions)| actions.push(action.get_owned())) .ok_or(TargetError::Fatal("extend on non-existing tracepoint")) } fn tracepoint_create_complete(&mut self, _tp: Tracepoint) -> TargetResult<(), Self> { /* nothing to do */ Ok(()) } fn tracepoint_status( &self, tp: Tracepoint, _addr: u32, ) -> TargetResult { // We don't collect "real" trace buffer frames, so just report hit count // and say the number of bytes is always 0. // Because we don't implement "while-stepping" actions, we don't need to // also check that `addr` matches. Ok(TracepointStatus { hit_count: self .traceframes .iter() .filter(|frame| frame.number.0 == tp.0) .count() as u64, bytes_used: 0, }) } fn tracepoint_enumerate_state(&mut self) -> &mut TracepointEnumerateState { &mut self.tracepoint_enumerate_state } fn tracepoint_enumerate_start( &mut self, tp: Option, f: &mut dyn FnMut(&NewTracepoint), ) -> TargetResult, Self> { let tp = match tp { Some(tp) => tp, None => { // We have no tracepoints to report if self.tracepoints.is_empty() { return Ok(TracepointEnumerateStep::Done); } else { // Start enumerating at the first one *self.tracepoints.keys().next().unwrap() } } }; // Report our tracepoint (f)(&self.tracepoints[&tp].0); let ret = if !self.tracepoints[&tp].1.is_empty() { TracepointEnumerateStep::Source } else if !self.tracepoints[&tp].2.is_empty() { TracepointEnumerateStep::Action } else { TracepointEnumerateStep::Done }; Ok(ret) } fn tracepoint_enumerate_action( &mut self, tp: Tracepoint, step: u64, f: &mut dyn FnMut(&TracepointAction<'_, u32>), ) -> TargetResult, Self> { // Report our next action (f)(&self.tracepoints[&tp].2[step as usize]); let ret = if self.tracepoints[&tp].2.get((step as usize) + 1).is_some() { // Continue stepping TracepointEnumerateStep::Action } else if !self.tracepoints[&tp].1.is_empty() { // We're done with this tracepoint, report source TracepointEnumerateStep::Source } else { // No sources, move to the next tracepoint self.step_to_next_tracepoint(tp) }; Ok(ret) } #[inline(always)] fn support_tracepoint_source( &mut self, ) -> Option> { Some(self) } fn trace_buffer_configure(&mut self, _config: TraceBufferConfig) -> TargetResult<(), Self> { // we don't collect a "real" trace buffer, so just ignore configuration // attempts. Ok(()) } fn trace_buffer_request( &mut self, _offset: u64, _len: usize, _f: &mut dyn FnMut(&mut [u8]), ) -> TargetResult<(), Self> { // We don't have a "real" trace buffer, so just don't report any data Ok(()) } fn trace_experiment_status( &self, report: &mut dyn FnMut(ExperimentStatus<'_>), ) -> TargetResult<(), Self> { // For a bare-bones example, we don't provide in-depth status explanations. (report)(if self.tracing { ExperimentStatus::Running } else { ExperimentStatus::NotRunning }); Ok(()) } fn trace_experiment_info( &self, report: &mut dyn FnMut(ExperimentExplanation<'_>), ) -> TargetResult<(), Self> { (report)(ExperimentExplanation::Frames(self.traceframes.len())); Ok(()) } fn select_frame( &mut self, frame: FrameRequest, report: &mut dyn FnMut(FrameDescription), ) -> TargetResult<(), Self> { // For a bare-bones example, we only support `tfind ` and `tfind // tracepoint ` style frame selection and not the more // complicated ones. let found = match frame { FrameRequest::Select(n) => self.traceframes.get(n as usize).map(|frame| (n, frame)), FrameRequest::Hit(tp) => { let start = self .selected_frame .map(|selected| selected + 1) .unwrap_or(0); self.traceframes.get(start..).and_then(|frames| { frames .iter() .enumerate() .filter(|(_n, frame)| frame.number == tp) .map(|(n, frame)| ((start + n) as u64, frame)) .next() }) } _ => return Err(TargetError::NonFatal), }; if let Some((n, frame)) = found { (report)(FrameDescription::FrameNumber(n)); (report)(FrameDescription::Hit(frame.number)); self.selected_frame = Some(n as usize); } else { self.selected_frame = None; } Ok(()) } fn trace_experiment_start(&mut self) -> TargetResult<(), Self> { self.tracing = true; Ok(()) } fn trace_experiment_stop(&mut self) -> TargetResult<(), Self> { self.tracing = false; Ok(()) } } impl target::ext::tracepoints::TracepointSource for Emu { fn tracepoint_enumerate_source( &mut self, tp: Tracepoint, step: u64, f: &mut dyn FnMut(&SourceTracepoint<'_, u32>), ) -> TargetResult, Self> { // Report our next source item (f)(&self.tracepoints[&tp].1[step as usize]); let ret = if self.tracepoints[&tp].1.get((step as usize) + 1).is_some() { // Continue stepping TracepointEnumerateStep::Source } else { // Move to next tracepoint self.step_to_next_tracepoint(tp) }; Ok(ret) } fn tracepoint_attach_source( &mut self, src: SourceTracepoint<'_, u32>, ) -> TargetResult<(), Self> { self.tracepoints .get_mut(&src.number) .unwrap() .1 .push(src.get_owned()); Ok(()) } } ================================================ FILE: examples/armv4t/main.rs ================================================ //! An incredibly simple emulator to run elf binaries compiled with //! `arm-none-eabi-cc -march=armv4t`. It's not modeled after any real-world //! system. use gdbstub::common::Signal; 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::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>; const TEST_PROGRAM_ELF: &[u8] = include_bytes!("test_bin/test.elf"); mod emu; mod gdb; mod mem_sniffer; fn wait_for_tcp(port: u16) -> DynResult { let sockaddr = format!("127.0.0.1:{}", port); eprintln!("Waiting for a GDB connection on {:?}...", sockaddr); let sock = TcpListener::bind(sockaddr)?; let (stream, addr) = sock.accept()?; eprintln!("Debugger connected from {}", addr); Ok(stream) } #[cfg(unix)] fn wait_for_uds(path: &str) -> DynResult { match std::fs::remove_file(path) { Ok(_) => {} Err(e) => match e.kind() { std::io::ErrorKind::NotFound => {} _ => return Err(e.into()), }, } eprintln!("Waiting for a GDB connection on {}...", path); let sock = UnixListener::bind(path)?; let (stream, addr) = sock.accept()?; eprintln!("Debugger connected from {:?}", addr); Ok(stream) } enum EmuGdbEventLoop {} impl run_blocking::BlockingEventLoop for EmuGdbEventLoop { type Target = emu::Emu; type Connection = Box>; type StopReason = SingleThreadStopReason; #[allow(clippy::type_complexity)] fn wait_for_stop_reason( target: &mut emu::Emu, conn: &mut Self::Connection, ) -> Result< run_blocking::Event>, run_blocking::WaitForStopReasonError< ::Error, ::Error, >, > { // The `armv4t` example runs the emulator in the same thread as the GDB state // machine loop. As such, it uses a simple poll-based model to check for // interrupt events, whereby the emulator will check if there is any incoming // data over the connection, and pause execution with a synthetic // `RunEvent::IncomingData` event. // // In more complex integrations, the target will probably be running in a // separate thread, and instead of using a poll-based model to check for // incoming data, you'll want to use some kind of "select" based model to // simultaneously wait for incoming GDB data coming over the connection, along // with any target-reported stop events. // // The specifics of how this "select" mechanism work + how the target reports // stop events will entirely depend on your project's architecture. // // Some ideas on how to implement this `select` mechanism: // // - A mpsc channel // - epoll/kqueue // - Running the target + stopping every so often to peek the connection // - Driving `GdbStub` from various interrupt handlers let poll_incoming_data = || { // gdbstub takes ownership of the underlying connection, so the `borrow_conn` // method is used to borrow the underlying connection back from the stub to // check for incoming data. conn.peek().map(|b| b.is_some()).unwrap_or(true) }; match target.run(poll_incoming_data) { emu::RunEvent::IncomingData => { let byte = conn .read() .map_err(run_blocking::WaitForStopReasonError::Connection)?; Ok(run_blocking::Event::IncomingData(byte)) } emu::RunEvent::Event(event) => { use gdbstub::target::ext::breakpoints::WatchKind; // translate emulator stop reason into GDB stop reason let stop_reason = match event { emu::Event::DoneStep => SingleThreadStopReason::DoneStep, emu::Event::Halted => SingleThreadStopReason::Terminated(Signal::SIGSTOP), emu::Event::Break => SingleThreadStopReason::SwBreak(()), emu::Event::WatchWrite(addr) => SingleThreadStopReason::Watch { tid: (), kind: WatchKind::Write, addr, }, emu::Event::WatchRead(addr) => SingleThreadStopReason::Watch { tid: (), kind: WatchKind::Read, addr, }, }; Ok(run_blocking::Event::TargetStopped(stop_reason)) } } } fn on_interrupt( _target: &mut emu::Emu, ) -> Result>, ::Error> { // Because this emulator runs as part of the GDB stub loop, there isn't any // special action that needs to be taken to interrupt the underlying target. It // is implicitly paused whenever the stub isn't within the // `wait_for_stop_reason` callback. Ok(Some(SingleThreadStopReason::Signal(Signal::SIGINT))) } } fn main() -> DynResult<()> { pretty_env_logger::init(); let mut emu = emu::Emu::new(TEST_PROGRAM_ELF)?; let connection: Box> = { if std::env::args().nth(1) == Some("--uds".to_string()) { #[cfg(not(unix))] { return Err("Unix Domain Sockets can only be used on Unix".into()); } #[cfg(unix)] { Box::new(wait_for_uds("/tmp/armv4t_gdb")?) } } else { Box::new(wait_for_tcp(9001)?) } }; let gdb = GdbStub::new(connection); match gdb.run_blocking::(&mut emu) { Ok(disconnect_reason) => match disconnect_reason { DisconnectReason::Disconnect => { println!("GDB client has disconnected. Running to completion..."); while emu.step() != Some(emu::Event::Halted) {} } DisconnectReason::TargetExited(code) => { println!("Target exited with code {}!", code) } DisconnectReason::TargetTerminated(sig) => { println!("Target terminated with signal {}!", sig) } DisconnectReason::Kill => println!("GDB sent a kill command!"), }, 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) } } } let ret = emu.cpu.reg_get(armv4t_emu::Mode::User, 0); println!("Program completed. Return value: {}", ret); Ok(()) } ================================================ FILE: examples/armv4t/mem_sniffer.rs ================================================ use armv4t_emu::Memory; pub enum AccessKind { Read, Write, } pub struct Access { pub kind: AccessKind, pub addr: u32, // allow(dead_code) because the emulator is so simple that it doesn't matter #[allow(dead_code)] pub val: u32, #[allow(dead_code)] pub len: usize, } /// Wraps a `Memory` object, logging any accesses with the provided callback. #[derive(Debug)] pub struct MemSniffer<'a, M, F: FnMut(Access)> { mem: &'a mut M, addrs: &'a [u32], on_access: F, } impl<'a, M: Memory, F: FnMut(Access)> MemSniffer<'a, M, F> { pub fn new(mem: &'a mut M, addrs: &'a [u32], on_access: F) -> MemSniffer<'a, M, F> { MemSniffer { mem, addrs, on_access, } } } macro_rules! impl_memsniff_r { ($fn:ident, $ret:ty) => { fn $fn(&mut self, addr: u32) -> $ret { let ret = self.mem.$fn(addr); if self.addrs.contains(&addr) { (self.on_access)(Access { kind: AccessKind::Read, addr, val: ret as u32, len: ret.to_le_bytes().len(), }); } ret } }; } macro_rules! impl_memsniff_w { ($fn:ident, $val:ty) => { fn $fn(&mut self, addr: u32, val: $val) { self.mem.$fn(addr, val); if self.addrs.contains(&addr) { (self.on_access)(Access { kind: AccessKind::Write, addr, val: val as u32, len: val.to_le_bytes().len(), }); } } }; } impl Memory for MemSniffer<'_, M, F> { impl_memsniff_r!(r8, u8); impl_memsniff_r!(r16, u16); impl_memsniff_r!(r32, u32); impl_memsniff_w!(w8, u8); impl_memsniff_w!(w16, u16); impl_memsniff_w!(w32, u32); } ================================================ FILE: examples/armv4t/test_bin/.gdbinit ================================================ # set remote multiprocess-feature-packet off target extended-remote :9001 ================================================ FILE: examples/armv4t/test_bin/.gitignore ================================================ *.o .gdb_history ================================================ FILE: examples/armv4t/test_bin/compile_test.sh ================================================ arm-none-eabi-gcc -c test.c -march=armv4t -O0 -g -std=c11 -fdebug-prefix-map=$(pwd)=. arm-none-eabi-ld -static -Ttest.ld test.o -o test.elf ================================================ FILE: examples/armv4t/test_bin/test.c ================================================ int main() { int x = 4; int y = 3; x += 1; y += 3; // big, useless loop to test ctrl-c functionality for (int i = 0; i < 1024 * 32; i++) { x += 1; } return x; } ================================================ FILE: examples/armv4t/test_bin/test.ld ================================================ ENTRY(main) MEMORY { ram : ORIGIN = 0x55550000, LENGTH = 0x10000000 } SECTIONS { . = 0x55550000; .text : ALIGN(4) { __TEXT_START__ = .; *(.text*); . = ALIGN(4); __TEXT_END__ = .; } > ram .got : ALIGN(4) { *(.got*); } > ram .data : ALIGN(4) { __DATA_START__ = .; *(.data*); *(.rodata*); __DATA_END__ = .; } > ram .bss : ALIGN(4) { __BSS_START__ = .; *(.bss*); . = ALIGN(4); __BSS_END__ = .; end = __BSS_END__; } > ram /DISCARD/ : { *(.ARM.exidx*) /* index entries for section unwinding */ *(.ARM.extab*) /* exception unwinding information */ } } ================================================ FILE: examples/armv4t_multicore/README.md ================================================ # armv4t-multicore An incredibly simple emulator to run elf binaries compiled with `arm-none-eabi-cc -march=armv4t`. Uses a dual-core architecture to show off `gdbstub`'s multi-process support. It's not modeled after any real-world system. **Note:** The actual emulator's code is pretty sloppy, since it's just a contrived example to show off what `gdbstub` is capable of. Run `gdb-arm-none-eabi` (or alternatively, `gdb-multiarch`) from the `test_bin` directory to automatically connect to the emulator + load debug symbols for the emulated binary. This example can be run using: ```bash cargo run --example armv4t --features=std ``` **NOTE:** If debug symbols couldn't be loaded, try rebuilding `test.elf` locally (requires the `arm-none-eabi` toolchain to be installed), and recompiling the example. ## Memory Map The entire 32-bit address space is accessible as RAM. Reading from the magic memory location `0xffff_4200` returns `0xaa` if accessed by the CPU, and `0x55` if accessed by the COP. ## Unix Domain Sockets GDB versions since \~2018 support running a debugging session over Unix Domain Sockets (UDS). Debugging over UDS can feel much snappier than debugging over loopback TCP. Running the example with the `--uds` flag will bind the GdbStub to a socket at `/tmp/armv4t_gdb`. This feature is only supported on Unix-like systems. ================================================ FILE: examples/armv4t_multicore/emu.rs ================================================ //! ------------------------------------------------------------------------ !// //! ------------------------------ DISCLAIMER ------------------------------ !// //! ------------------------------------------------------------------------ !// //! //! This code is absolutely awful, and completely slapped together for the sake //! of example. The watchpoint implementation is particularly awful. //! //! While it technically "gets the job done" and provides a simple multicore //! 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 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; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum CpuId { Cpu, Cop, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Event { DoneStep, Halted, Break, WatchWrite(u32), WatchRead(u32), } #[derive(PartialEq)] pub enum ExecMode { Step, Continue, Stop, } /// incredibly barebones armv4t-based emulator pub struct Emu { pub(crate) cpu: Cpu, pub(crate) cop: Cpu, pub(crate) mem: ExampleMem, pub(crate) exec_mode: HashMap, pub(crate) watchpoints: Vec, /// (read, write) pub(crate) watchpoint_kind: HashMap, pub(crate) breakpoints: Vec, // GDB seems to get gets very confused if two threads are executing the exact same code at the // exact same time. Maybe this is a bug with `gdbstub`? stall_cop_cycles: usize, } impl Emu { pub fn new(program_elf: &[u8]) -> DynResult { // set up emulated system let mut cpu = Cpu::new(); let mut mem = ExampleMem::new(); // load ELF let elf_header = goblin::elf::Elf::parse(program_elf)?; // copy all in-memory sections from the ELF file into system RAM let sections = elf_header .section_headers .iter() .filter(|h| h.is_alloc() && h.sh_type != goblin::elf::section_header::SHT_NOBITS); for h in sections { eprintln!( "loading section {:?} into memory from [{:#010x?}..{:#010x?}]", elf_header.shdr_strtab.get_at(h.sh_name).unwrap(), h.sh_addr, h.sh_addr + h.sh_size, ); for (i, b) in program_elf[h.file_range().unwrap()].iter().enumerate() { mem.w8(h.sh_addr as u32 + i as u32, *b); } } // setup execution state eprintln!("Setting PC to {:#010x?}", elf_header.entry); cpu.reg_set(Mode::User, reg::SP, 0x10000000); cpu.reg_set(Mode::User, reg::LR, HLE_RETURN_ADDR); cpu.reg_set(Mode::User, reg::PC, elf_header.entry as u32); cpu.reg_set(Mode::User, reg::CPSR, 0x10); // user mode let cop = cpu; Ok(Emu { cpu, cop, mem, exec_mode: HashMap::new(), watchpoints: Vec::new(), watchpoint_kind: HashMap::new(), breakpoints: Vec::new(), stall_cop_cycles: 24, }) } pub fn step_core(&mut self, id: CpuId) -> Option { let cpu = match id { CpuId::Cop if self.stall_cop_cycles != 0 => { self.stall_cop_cycles -= 1; return None; } CpuId::Cop => &mut self.cop, CpuId::Cpu => &mut self.cpu, }; // set up magic memory location self.mem.w8( 0xffff_4200, match id { CpuId::Cpu => 0xaa, CpuId::Cop => 0x55, }, ); let mut hit_watchpoint = None; let mut sniffer = MemSniffer::new(&mut self.mem, &self.watchpoints, |access| { hit_watchpoint = Some(access) }); cpu.step(&mut sniffer); let pc = cpu.reg_get(Mode::User, reg::PC); if pc == HLE_RETURN_ADDR { match id { CpuId::Cpu => return Some(Event::Halted), CpuId::Cop => return Some(Event::Halted), } } if let Some(access) = hit_watchpoint { // NOTE: this isn't a particularly elegant way to do watchpoints! This works // fine for some example code, but don't use this as inspiration in your own // emulator! match access.kind { AccessKind::Read => { if *self .watchpoint_kind .get(&access.addr) .map(|(r, _w)| r) .unwrap_or(&false) { let fixup = if cpu.thumb_mode() { 2 } else { 4 }; cpu.reg_set(Mode::User, reg::PC, pc - fixup); return Some(Event::WatchRead(access.addr)); } } AccessKind::Write => { if *self .watchpoint_kind .get(&access.addr) .map(|(_r, w)| w) .unwrap_or(&false) { let fixup = if cpu.thumb_mode() { 2 } else { 4 }; cpu.reg_set(Mode::User, reg::PC, pc - fixup); return Some(Event::WatchWrite(access.addr)); } } } } if self.breakpoints.contains(&pc) { return Some(Event::Break); } None } pub fn step(&mut self) -> Option<(Event, CpuId)> { let mut evt = None; for id in [CpuId::Cpu, CpuId::Cop].iter().copied() { if matches!(self.exec_mode.get(&id), Some(ExecMode::Stop)) { continue; } if let Some(event) = self.step_core(id) { if evt.is_none() { evt = Some((event, id)); } } } evt } pub fn run(&mut self, mut poll_incoming_data: impl FnMut() -> bool) -> RunEvent { // The underlying armv4t_multicore emulator cycles all cores in lock-step. // // Inside `self.step()`, we iterate through all cores and only invoke // `step_core` if that core's `ExecMode` is not `Stop`. let should_single_step = self.exec_mode.values().any(|mode| mode == &ExecMode::Step); match should_single_step { true => match self.step() { Some((event, id)) => RunEvent::Event(event, id), None => { let stepping_core = self .exec_mode .iter() .find(|&(_, mode)| mode == &ExecMode::Step) .map(|(id, _)| *id) .unwrap_or(CpuId::Cpu); RunEvent::Event(Event::DoneStep, stepping_core) } }, false => { let mut cycles = 0; loop { if cycles % 1024 == 0 { // poll for incoming data if poll_incoming_data() { break RunEvent::IncomingData; } } cycles += 1; if let Some((event, id)) = self.step() { break RunEvent::Event(event, id); }; } } } } } pub enum RunEvent { Event(Event, CpuId), IncomingData, } ================================================ FILE: examples/armv4t_multicore/gdb.rs ================================================ 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; use gdbstub::target::ext::base::multithread::MultiThreadResume; use gdbstub::target::ext::breakpoints::WatchKind; use gdbstub::target::Target; use gdbstub::target::TargetError; use gdbstub::target::TargetResult; pub fn cpuid_to_tid(id: CpuId) -> Tid { match id { CpuId::Cpu => Tid::new(1).unwrap(), CpuId::Cop => Tid::new(2).unwrap(), } } fn tid_to_cpuid(tid: Tid) -> Result { match tid.get() { 1 => Ok(CpuId::Cpu), 2 => Ok(CpuId::Cop), _ => Err("specified invalid core"), } } impl Target for Emu { type Arch = gdbstub_arch::arm::Armv4t; type Error = &'static str; #[inline(always)] fn base_ops(&mut self) -> target::ext::base::BaseOps<'_, Self::Arch, Self::Error> { target::ext::base::BaseOps::MultiThread(self) } #[inline(always)] fn support_breakpoints( &mut self, ) -> Option> { Some(self) } } impl MultiThreadBase for Emu { fn read_registers( &mut self, regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs, tid: Tid, ) -> TargetResult<(), Self> { let cpu = match tid_to_cpuid(tid).map_err(TargetError::Fatal)? { CpuId::Cpu => &mut self.cpu, CpuId::Cop => &mut self.cop, }; let mode = cpu.mode(); for i in 0..13 { regs.r[i] = cpu.reg_get(mode, i as u8); } regs.sp = cpu.reg_get(mode, reg::SP); regs.lr = cpu.reg_get(mode, reg::LR); regs.pc = cpu.reg_get(mode, reg::PC); regs.cpsr = cpu.reg_get(mode, reg::CPSR); Ok(()) } fn write_registers( &mut self, regs: &gdbstub_arch::arm::reg::ArmCoreRegs, tid: Tid, ) -> TargetResult<(), Self> { let cpu = match tid_to_cpuid(tid).map_err(TargetError::Fatal)? { CpuId::Cpu => &mut self.cpu, CpuId::Cop => &mut self.cop, }; let mode = cpu.mode(); for i in 0..13 { cpu.reg_set(mode, i, regs.r[i as usize]); } cpu.reg_set(mode, reg::SP, regs.sp); cpu.reg_set(mode, reg::LR, regs.lr); cpu.reg_set(mode, reg::PC, regs.pc); cpu.reg_set(mode, reg::CPSR, regs.cpsr); Ok(()) } fn read_addrs( &mut self, start_addr: u32, data: &mut [u8], _tid: Tid, // same address space for each core ) -> TargetResult { for (addr, val) in (start_addr..).zip(data.iter_mut()) { *val = self.mem.r8(addr) } Ok(data.len()) } fn write_addrs( &mut self, start_addr: u32, data: &[u8], _tid: Tid, // same address space for each core ) -> TargetResult<(), Self> { for (addr, val) in (start_addr..).zip(data.iter().copied()) { self.mem.w8(addr, val) } Ok(()) } fn list_active_threads( &mut self, register_thread: &mut dyn FnMut(Tid), ) -> Result<(), Self::Error> { register_thread(cpuid_to_tid(CpuId::Cpu)); register_thread(cpuid_to_tid(CpuId::Cop)); Ok(()) } #[inline(always)] fn support_resume( &mut self, ) -> Option> { Some(self) } #[inline(always)] fn support_thread_extra_info( &mut self, ) -> Option> { Some(self) } } impl MultiThreadResume for Emu { fn resume(&mut self) -> Result<(), Self::Error> { // Upon returning from the `resume` method, the target being debugged should be // configured to run according to whatever resume actions the GDB client has // specified (as specified by `set_resume_action`, `set_resume_range_step`, // `set_reverse_{step, continue}`, etc...) // // In this basic `armv4t_multicore` example, the `resume` method is actually a // no-op, as the execution mode of the emulator's interpreter loop has already // been modified via the various `set_X` methods. // // In more complex implementations, it's likely that the target being debugged // will be running in another thread / process, and will require some kind of // external "orchestration" to set it's execution mode (e.g: modifying the // target's process state via platform specific debugging syscalls). Ok(()) } fn clear_resume_actions(&mut self) -> Result<(), Self::Error> { self.exec_mode.clear(); Ok(()) } #[inline(always)] fn support_single_step( &mut self, ) -> Option> { Some(self) } fn set_resume_action_continue( &mut self, tid: Tid, signal: Option, ) -> Result<(), Self::Error> { if signal.is_some() { return Err("no support for continuing with signal"); } self.exec_mode .insert(tid_to_cpuid(tid)?, ExecMode::Continue); Ok(()) } #[inline(always)] fn support_scheduler_locking( &mut self, ) -> Option> { Some(self) } } impl target::ext::base::multithread::MultiThreadSingleStep for Emu { fn set_resume_action_step( &mut self, tid: Tid, signal: Option, ) -> Result<(), Self::Error> { if signal.is_some() { return Err("no support for stepping with signal"); } self.exec_mode.insert(tid_to_cpuid(tid)?, ExecMode::Step); Ok(()) } } impl target::ext::breakpoints::Breakpoints for Emu { fn support_sw_breakpoint( &mut self, ) -> Option> { Some(self) } fn support_hw_watchpoint( &mut self, ) -> Option> { Some(self) } } impl target::ext::breakpoints::SwBreakpoint for Emu { fn add_sw_breakpoint( &mut self, addr: u32, _kind: gdbstub_arch::arm::ArmBreakpointKind, ) -> TargetResult { self.breakpoints.push(addr); Ok(true) } fn remove_sw_breakpoint( &mut self, addr: u32, _kind: gdbstub_arch::arm::ArmBreakpointKind, ) -> TargetResult { match self.breakpoints.iter().position(|x| *x == addr) { None => return Ok(false), Some(pos) => self.breakpoints.remove(pos), }; Ok(true) } } impl target::ext::breakpoints::HwWatchpoint for Emu { fn add_hw_watchpoint( &mut self, addr: u32, _len: u32, // TODO: properly handle `len` parameter kind: WatchKind, ) -> TargetResult { self.watchpoints.push(addr); let entry = self.watchpoint_kind.entry(addr).or_insert((false, false)); match kind { WatchKind::Write => entry.1 = true, WatchKind::Read => entry.0 = true, WatchKind::ReadWrite => entry.0 = true, // arbitrary }; Ok(true) } fn remove_hw_watchpoint( &mut self, addr: u32, _len: u32, // TODO: properly handle `len` parameter kind: WatchKind, ) -> TargetResult { let entry = self.watchpoint_kind.entry(addr).or_insert((false, false)); match kind { WatchKind::Write => entry.1 = false, WatchKind::Read => entry.0 = false, WatchKind::ReadWrite => entry.0 = false, // arbitrary }; if !self.watchpoint_kind.contains_key(&addr) { let pos = match self.watchpoints.iter().position(|x| *x == addr) { None => return Ok(false), Some(pos) => pos, }; self.watchpoints.remove(pos); } Ok(true) } } impl target::ext::thread_extra_info::ThreadExtraInfo for Emu { fn thread_extra_info(&self, tid: Tid, buf: &mut [u8]) -> Result { let cpu_id = tid_to_cpuid(tid)?; let info = format!("CPU {:?}", cpu_id); Ok(copy_to_buf(info.as_bytes(), buf)) } } impl target::ext::base::multithread::MultiThreadSchedulerLocking for Emu { fn set_resume_action_scheduler_lock(&mut self) -> Result<(), Self::Error> { for id in [CpuId::Cpu, CpuId::Cop] { self.exec_mode.entry(id).or_insert(ExecMode::Stop); } Ok(()) } } /// Copy all bytes of `data` to `buf`. /// Return the size of data copied. pub fn copy_to_buf(data: &[u8], buf: &mut [u8]) -> usize { let len = buf.len().min(data.len()); buf[..len].copy_from_slice(&data[..len]); len } ================================================ FILE: examples/armv4t_multicore/main.rs ================================================ //! An incredibly simple emulator to run elf binaries compiled with //! `arm-none-eabi-cc -march=armv4t`. Uses a dual-core architecture to show off //! `gdbstub`'s multi-process support. It's not modeled after any real-world //! system. use gdbstub::common::Signal; 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>; static TEST_PROGRAM_ELF: &[u8] = include_bytes!("test_bin/test.elf"); mod emu; mod gdb; mod mem_sniffer; fn wait_for_tcp(port: u16) -> DynResult { let sockaddr = format!("127.0.0.1:{}", port); eprintln!("Waiting for a GDB connection on {:?}...", sockaddr); let sock = TcpListener::bind(sockaddr)?; let (stream, addr) = sock.accept()?; eprintln!("Debugger connected from {}", addr); Ok(stream) } #[cfg(unix)] fn wait_for_uds(path: &str) -> DynResult { match std::fs::remove_file(path) { Ok(_) => {} Err(e) => match e.kind() { std::io::ErrorKind::NotFound => {} _ => return Err(e.into()), }, } eprintln!("Waiting for a GDB connection on {}...", path); let sock = UnixListener::bind(path)?; let (stream, addr) = sock.accept()?; eprintln!("Debugger connected from {:?}", addr); Ok(stream) } enum EmuGdbEventLoop {} impl run_blocking::BlockingEventLoop for EmuGdbEventLoop { type Target = emu::Emu; type Connection = Box>; type StopReason = MultiThreadStopReason; #[allow(clippy::type_complexity)] fn wait_for_stop_reason( target: &mut emu::Emu, conn: &mut Self::Connection, ) -> Result< run_blocking::Event, run_blocking::WaitForStopReasonError< ::Error, ::Error, >, > { // The `armv4t_multicore` example runs the emulator in the same thread as the // GDB state machine loop. As such, it uses a simple poll-based model to // check for interrupt events, whereby the emulator will check if there // is any incoming data over the connection, and pause execution with a // synthetic `RunEvent::IncomingData` event. // // In more complex integrations, the target will probably be running in a // separate thread, and instead of using a poll-based model to check for // incoming data, you'll want to use some kind of "select" based model to // simultaneously wait for incoming GDB data coming over the connection, along // with any target-reported stop events. // // The specifics of how this "select" mechanism work + how the target reports // stop events will entirely depend on your project's architecture. // // Some ideas on how to implement this `select` mechanism: // // - A mpsc channel // - epoll/kqueue // - Running the target + stopping every so often to peek the connection // - Driving `GdbStub` from various interrupt handlers let poll_incoming_data = || { // gdbstub takes ownership of the underlying connection, so the `borrow_conn` // method is used to borrow the underlying connection back from the stub to // check for incoming data. conn.peek().map(|b| b.is_some()).unwrap_or(true) }; match target.run(poll_incoming_data) { emu::RunEvent::IncomingData => { let byte = conn .read() .map_err(run_blocking::WaitForStopReasonError::Connection)?; Ok(run_blocking::Event::IncomingData(byte)) } emu::RunEvent::Event(event, cpuid) => { use gdbstub::target::ext::breakpoints::WatchKind; // translate emulator stop reason into GDB stop reason let tid = gdb::cpuid_to_tid(cpuid); let stop_reason = match event { emu::Event::DoneStep => MultiThreadStopReason::DoneStep, emu::Event::Halted => MultiThreadStopReason::Terminated(Signal::SIGSTOP), emu::Event::Break => MultiThreadStopReason::SwBreak(tid), emu::Event::WatchWrite(addr) => MultiThreadStopReason::Watch { tid, kind: WatchKind::Write, addr, }, emu::Event::WatchRead(addr) => MultiThreadStopReason::Watch { tid, kind: WatchKind::Read, addr, }, }; Ok(run_blocking::Event::TargetStopped(stop_reason)) } } } fn on_interrupt( _target: &mut emu::Emu, ) -> Result>, ::Error> { // Because this emulator runs as part of the GDB stub loop, there isn't any // special action that needs to be taken to interrupt the underlying target. It // is implicitly paused whenever the stub isn't within the // `wait_for_stop_reason` callback. Ok(Some(MultiThreadStopReason::Signal(Signal::SIGINT))) } } fn main() -> DynResult<()> { pretty_env_logger::init(); let mut emu = emu::Emu::new(TEST_PROGRAM_ELF)?; let connection: Box> = { if std::env::args().nth(1) == Some("--uds".to_string()) { #[cfg(not(unix))] { return Err("Unix Domain Sockets can only be used on Unix".into()); } #[cfg(unix)] { Box::new(wait_for_uds("/tmp/armv4t_gdb")?) } } else { Box::new(wait_for_tcp(9001)?) } }; let gdb = GdbStub::new(connection); match gdb.run_blocking::(&mut emu) { Ok(disconnect_reason) => match disconnect_reason { DisconnectReason::Disconnect => { println!("GDB client has disconnected. Running to completion..."); while emu.step() != Some((emu::Event::Halted, emu::CpuId::Cpu)) {} } DisconnectReason::TargetExited(code) => { println!("Target exited with code {}!", code) } DisconnectReason::TargetTerminated(sig) => { println!("Target terminated with signal {}!", sig) } DisconnectReason::Kill => println!("GDB sent a kill command!"), }, 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) } } } let ret = emu.cpu.reg_get(armv4t_emu::Mode::User, 0); println!("Program completed. Return value: {}", ret); Ok(()) } ================================================ FILE: examples/armv4t_multicore/mem_sniffer.rs ================================================ use armv4t_emu::Memory; #[derive(Debug)] pub enum AccessKind { Read, Write, } #[derive(Debug)] pub struct Access { pub kind: AccessKind, pub addr: u32, // allow(dead_code) because the emulator is so simple that it doesn't matter #[allow(dead_code)] pub val: u32, #[allow(dead_code)] pub len: usize, } /// Wraps a `Memory` object, logging any accesses with the provided callback. #[derive(Debug)] pub struct MemSniffer<'a, M, F: FnMut(Access)> { mem: &'a mut M, addrs: &'a [u32], on_access: F, } impl<'a, M: Memory, F: FnMut(Access)> MemSniffer<'a, M, F> { pub fn new(mem: &'a mut M, addrs: &'a [u32], on_access: F) -> MemSniffer<'a, M, F> { MemSniffer { mem, addrs, on_access, } } } macro_rules! impl_memsniff_r { ($fn:ident, $ret:ty) => { fn $fn(&mut self, addr: u32) -> $ret { let ret = self.mem.$fn(addr); if self.addrs.contains(&addr) { (self.on_access)(Access { kind: AccessKind::Read, addr, val: ret as u32, len: ret.to_le_bytes().len(), }); } ret } }; } macro_rules! impl_memsniff_w { ($fn:ident, $val:ty) => { fn $fn(&mut self, addr: u32, val: $val) { self.mem.$fn(addr, val); if self.addrs.contains(&addr) { (self.on_access)(Access { kind: AccessKind::Write, addr, val: val as u32, len: val.to_le_bytes().len(), }); } } }; } impl Memory for MemSniffer<'_, M, F> { impl_memsniff_r!(r8, u8); impl_memsniff_r!(r16, u16); impl_memsniff_r!(r32, u32); impl_memsniff_w!(w8, u8); impl_memsniff_w!(w16, u16); impl_memsniff_w!(w32, u32); } ================================================ FILE: examples/armv4t_multicore/test_bin/.gdbinit ================================================ file test.elf target remote :9001 ================================================ FILE: examples/armv4t_multicore/test_bin/.gitignore ================================================ *.o .gdb_history ================================================ FILE: examples/armv4t_multicore/test_bin/compile_test.sh ================================================ arm-none-eabi-gcc -c test.c -march=armv4t -O0 -g -std=c11 -fdebug-prefix-map=$(pwd)=. arm-none-eabi-ld -static -Ttest.ld test.o -o test.elf ================================================ FILE: examples/armv4t_multicore/test_bin/test.c ================================================ #define CPU_ID *((volatile unsigned char*)0xffff4200) int main() { // try switching between threads using `thread 1` and `thread 2`! int done = 0; int x = 0; // diverging paths on each CPU core if (CPU_ID == 0xaa) { while (!done) {} return x; } else { // big, useless loop to test ctrl-c functionality for (int i = 0; i < 1024 * 32; i++) { x += 1; } done = 1; // loop forever for (;;) {} } } ================================================ FILE: examples/armv4t_multicore/test_bin/test.ld ================================================ ENTRY(main) MEMORY { ram : ORIGIN = 0x55550000, LENGTH = 0x10000000 } SECTIONS { . = 0x55550000; .text : ALIGN(4) { __TEXT_START__ = .; *(.text*); . = ALIGN(4); __TEXT_END__ = .; } > ram .got : ALIGN(4) { *(.got*); } > ram .data : ALIGN(4) { __DATA_START__ = .; *(.data*); *(.rodata*); __DATA_END__ = .; } > ram .bss : ALIGN(4) { __BSS_START__ = .; *(.bss*); . = ALIGN(4); __BSS_END__ = .; end = __BSS_END__; } > ram /DISCARD/ : { *(.ARM.exidx*) /* index entries for section unwinding */ *(.ARM.extab*) /* exception unwinding information */ } } ================================================ FILE: gdbstub_arch/CHANGELOG.md ================================================ 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.3.3 - Add support for Wasm [\#192](https://github.com/daniel5151/gdbstub/pull/192) ([cfallin](https://github.com/cfallin)) # 0.3.2 - x86: Remove "padding" bytes from x86 register packet [\#171](https://github.com/daniel5151/gdbstub/pull/171) ([cadmic](https://github.com/cadmic)) # 0.3.1 - RISC-V: List all integer registers (for lldb compatibility) [\#149](https://github.com/daniel5151/gdbstub/pull/149) ([danlehmann](https://github.com/danlehmann)) # 0.3.0 #### Breaking Arch Changes - Entirely removed `SingleStepGdbBehavior` APIs # 0.2.4 - Add support for AArch64 [\#109](https://github.com/daniel5151/gdbstub/pull/109) ([ptosi](https://github.com/ptosi)) # 0.2.3 - Fix missing reg_id mapping in `Armv4t` (24 => Fps) # 0.2.2 - Remove faulty target.xml from mips64 arch # 0.2.1 - Removed all remaining instances of `SingleStepGdbBehavior::Unknown` [\#95](https://github.com/daniel5151/gdbstub/pull/95) ([bet4it](https://github.com/bet4it)) # 0.2.0 **Bumps required `gdbstub` version to 0.6.0**. #### Breaking Arch Changes - Improved support + fixes for `Msp430` [\#62](https://github.com/daniel5151/gdbstub/pull/62) ([mchesser](https://github.com/mchesser)) - `X86_64CoreRegId`: Change rip size to 8 [\#87](https://github.com/daniel5151/gdbstub/pull/87) ([gz](https://github.com/gz)) - Removed `RegId` template parameters from the following `Arch` implementations: - x86/x64 - MIPS - MSP-430 # 0.1.0 **Bumps required `gdbstub` version to 0.5.0**. - **`gdbstub::arch` has been moved into a separate `gdbstub_arch` crate** - _See [\#45](https://github.com/daniel5151/gdbstub/issues/45) for details on why this was done._ - (x86) Break GPRs & SRs into individual fields/variants [\#34](https://github.com/daniel5151/gdbstub/issues/34) ================================================ FILE: gdbstub_arch/Cargo.toml ================================================ [package] name = "gdbstub_arch" description = "Implementations of `gdbstub::arch::Arch` for various architectures." authors = ["Daniel Prilik "] version = "0.3.3" license = "MIT OR Apache-2.0" edition = "2018" readme = "README.md" documentation = "https://docs.rs/gdbstub_arch" homepage = "https://github.com/daniel5151/gdbstub" repository = "https://github.com/daniel5151/gdbstub" keywords = ["gdb", "emulation", "no_std", "debugging"] categories = ["development-tools::debugging", "embedded", "emulators", "no-std"] [dependencies] gdbstub = { path = "../", version = "0.7", default-features = false } num-traits = { version = "0.2", default-features = false } ================================================ FILE: gdbstub_arch/LICENSE ================================================ gdbstub_arch is dual-licensed under either * MIT License (../docs/LICENSE-MIT or http://opensource.org/licenses/MIT) * Apache License, Version 2.0 (../docs/LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) at your option. ================================================ FILE: gdbstub_arch/README.md ================================================ # gdbstub_arch [![](https://img.shields.io/crates/v/gdbstub_arch.svg)](https://crates.io/crates/gdbstub_arch) [![](https://docs.rs/gdbstub_arch/badge.svg)](https://docs.rs/gdbstub_arch) [![](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](./LICENSE) Community-contributed implementations of `gdbstub::arch::Arch` for various architectures. _Note:_ If an architecture is missing from this crate, that does _not_ mean that it can't be used with `gdbstub`! So-long as there's support for the target architecture in GDB, it should be fairly straightforward to implement `Arch` manually. Please consider upstreaming any missing `Arch` implementations you happen to implement yourself! Aside from the altruistic motive of improving `gdbstub`, upstreaming your `Arch` implementation will ensure that it's kept up-to-date with any future breaking API changes. **Disclaimer:** These implementations are all community contributions, and while they are tested (by the PR's author) and code-reviewed, it's not particularly feasible to write detailed tests for each architecture! If you spot a bug in any of the implementations, please file an issue / open a PR! ================================================ FILE: gdbstub_arch/src/aarch64/core.xml ================================================ ================================================ FILE: gdbstub_arch/src/aarch64/fpu.xml ================================================ ================================================ FILE: gdbstub_arch/src/aarch64/mod.rs ================================================ //! Implementation for the [AArch64](https://developer.arm.com/documentation/102374) //! ARM architecture. //! //! See PR [#109](https://github.com/daniel5151/gdbstub/pull/109) for more info. //! //! *Note*: doesn't support the AArch32 execution mode. //! *Note*: the target XML currently advertises all system registers to the GDB //! client. use gdbstub::arch::Arch; pub mod reg; /// Implements `Arch` for ARM AArch64. pub struct AArch64 {} impl Arch for AArch64 { type Usize = u64; type Registers = reg::AArch64CoreRegs; type RegId = reg::id::AArch64RegId; type BreakpointKind = usize; fn target_description_xml() -> Option<&'static str> { static DESCRIPTION_XML: &str = concat!( r#""#, "aarch64", include_str!("core.xml"), // feature "org.gnu.gdb.aarch64.core" include_str!("fpu.xml"), // feature "org.gnu.gdb.aarch64.fpu" include_str!("sysregs.xml"), "", ); Some(DESCRIPTION_XML) } } ================================================ FILE: gdbstub_arch/src/aarch64/reg/aarch64_core.rs ================================================ use core::convert::TryInto; use gdbstub::arch::Registers; /// AArch64 core registers. /// /// Registers from the `org.gnu.gdb.aarch64.core` and `org.gnu.gdb.aarch64.fpu` /// [AArch64 Standard GDB Target Features](https://sourceware.org/gdb/onlinedocs/gdb/AArch64-Features.html). #[derive(Debug, Default, Clone, Eq, PartialEq)] pub struct AArch64CoreRegs { /// General Purpose Registers (X0-X30) pub x: [u64; 31], /// Stack Pointer pub sp: u64, /// Program Counter pub pc: u64, /// Process State (GDB uses the AArch32 CPSR name) pub cpsr: u32, /// FP & SIMD Registers (V0-V31) pub v: [u128; 32], /// Floating-point Control Register pub fpcr: u32, /// Floating-point Status Register pub fpsr: u32, } impl Registers for AArch64CoreRegs { type ProgramCounter = u64; fn pc(&self) -> Self::ProgramCounter { self.pc } fn gdb_serialize(&self, mut write_byte: impl FnMut(Option)) { macro_rules! write_bytes { ($var: expr) => { for b in $var.to_le_bytes() { write_byte(Some(b)) } }; } for reg in self.x.iter() { write_bytes!(reg); } write_bytes!(self.sp); write_bytes!(self.pc); write_bytes!(self.cpsr); for reg in self.v.iter() { write_bytes!(reg); } write_bytes!(self.fpcr); write_bytes!(self.fpsr); } fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { const CPSR_OFF: usize = core::mem::size_of::() * 33; const FPSIMD_OFF: usize = CPSR_OFF + core::mem::size_of::(); const FPCR_OFF: usize = FPSIMD_OFF + core::mem::size_of::() * 32; const END: usize = FPCR_OFF + core::mem::size_of::() * 2; if bytes.len() < END { return Err(()); } let mut regs = bytes[0..CPSR_OFF] .chunks_exact(core::mem::size_of::()) .map(|c| u64::from_le_bytes(c.try_into().unwrap())); for reg in self.x.iter_mut() { *reg = regs.next().ok_or(())? } self.sp = regs.next().ok_or(())?; self.pc = regs.next().ok_or(())?; let mut regs = bytes[CPSR_OFF..FPSIMD_OFF] .chunks_exact(core::mem::size_of::()) .map(|c| u32::from_le_bytes(c.try_into().unwrap())); self.cpsr = regs.next().ok_or(())?; let mut regs = bytes[FPSIMD_OFF..FPCR_OFF] .chunks_exact(core::mem::size_of::()) .map(|c| u128::from_le_bytes(c.try_into().unwrap())); for reg in self.v.iter_mut() { *reg = regs.next().ok_or(())? } let mut regs = bytes[FPCR_OFF..] .chunks_exact(core::mem::size_of::()) .map(|c| u32::from_le_bytes(c.try_into().unwrap())); self.fpcr = regs.next().ok_or(())?; self.fpsr = regs.next().ok_or(())?; Ok(()) } } ================================================ FILE: gdbstub_arch/src/aarch64/reg/id.rs ================================================ use core::num::NonZeroUsize; use gdbstub::arch::RegId; /// AArch64 Architectural Registers. /// /// Represents architectural registers as /// /// - individual variants for those described in section B1.2. _Registers in /// AArch64 Execution state_ of the Architecture Reference Manual (DDI /// 0487H.a), accessed through their own respective subsets of instructions /// _e.g._ GPRs, FP & SIMD, ... /// - a generic variant for system registers, accessed through MSR/MRS /// instructions, based on their encoding as described in section C5.1. _The /// System instruction class encoding space_ when `op0` is `0b10` (_Debug and /// trace registers_) or `0b11` (_Non-debug System registers_ and /// _Special-purpose registers_), as `0b0x` do not encode registers; /// - a variant for the abstraction of process state information, `PSTATE` /// (section D1.4.), which should be preferred over field-specific /// special-purpose registers (`NZCV`, `DAIF`, ...) /// /// Provides `const` aliases for most system registers as syntactic sugar for /// the `System` variant. When those aren't available (_e.g._ for newly-added /// registers), the literal representation `System(0baa_bbb_xxxx_yyyy_cc)` may /// be used, similarly to the standard assembly symbol, /// `S____`. /// /// To future-proof and greatly simplify the implementation, the target's XML /// must encode system registers by using their 16-bit encoding as the `regnum` /// property; no clash with architectural registers is possible as the top bit /// of the 16-bit value is guaranteed to be set. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[non_exhaustive] pub enum AArch64RegId { /// General-purpose Register File (X0 - X30) X(u8), /// Stack Pointer Sp, /// Program Counter Pc, /// Process State (Pseudo-Register) Pstate, /// SIMD & FP Register File (V0 - V31) V(u8), /// System Registers encoded as (Op0:2, Op1:3, CRn:4, CRm:4, Op2:2) System(u16), } impl RegId for AArch64RegId { fn from_raw_id(id: usize) -> Option<(Self, Option)> { let reg = match id { 0..=30 => Self::X(id as u8), 31 => Self::Sp, 32 => Self::Pc, 33 => Self::Pstate, 34..=65 => Self::V((id - 34) as u8), 66 => Self::FPSR, 67 => Self::FPCR, #[allow(clippy::unusual_byte_groupings)] // We configure GDB to use regnums that correspond to the architectural u16 opcode // and avoid clashes with core registers thanks to op0==0b00 and op0==0b01 not being // allocated for system registers. 0b10_000_0000_0000_000..=0b11_111_1111_1111_111 => Self::System(id as u16), _ => return None, }; Some((reg, Some(NonZeroUsize::new(reg.len()?)?))) } } #[allow(clippy::unusual_byte_groupings)] impl AArch64RegId { #[allow(clippy::len_without_is_empty)] /// Gives the size of the register. pub fn len(&self) -> Option { match self { Self::Pstate => Some(core::mem::size_of::()), Self::X(_n @ 0..=30) => Some(core::mem::size_of::()), Self::V(_n @ 0..=31) => Some(core::mem::size_of::()), Self::Pc | Self::Sp | Self::System(_) => Some(core::mem::size_of::()), _ => None, } } /// Main ID Register pub const MIDR_EL1: Self = Self::System(0b11_000_0000_0000_000); /// Multiprocessor Affinity Register pub const MPIDR_EL1: Self = Self::System(0b11_000_0000_0000_101); /// Revision ID Register pub const REVIDR_EL1: Self = Self::System(0b11_000_0000_0000_110); /// AArch32 Processor Feature Register 0 pub const ID_PFR0_EL1: Self = Self::System(0b11_000_0000_0001_000); /// AArch32 Processor Feature Register 1 pub const ID_PFR1_EL1: Self = Self::System(0b11_000_0000_0001_001); /// AArch32 Debug Feature Register 0 pub const ID_DFR0_EL1: Self = Self::System(0b11_000_0000_0001_010); /// AArch32 Auxiliary Feature Register 0 pub const ID_AFR0_EL1: Self = Self::System(0b11_000_0000_0001_011); /// AArch32 Memory Model Feature Register 0 pub const ID_MMFR0_EL1: Self = Self::System(0b11_000_0000_0001_100); /// AArch32 Memory Model Feature Register 1 pub const ID_MMFR1_EL1: Self = Self::System(0b11_000_0000_0001_101); /// AArch32 Memory Model Feature Register 2 pub const ID_MMFR2_EL1: Self = Self::System(0b11_000_0000_0001_110); /// AArch32 Memory Model Feature Register 3 pub const ID_MMFR3_EL1: Self = Self::System(0b11_000_0000_0001_111); /// AArch32 Instruction Set Attribute Register 0 pub const ID_ISAR0_EL1: Self = Self::System(0b11_000_0000_0010_000); /// AArch32 Instruction Set Attribute Register 1 pub const ID_ISAR1_EL1: Self = Self::System(0b11_000_0000_0010_001); /// AArch32 Instruction Set Attribute Register 2 pub const ID_ISAR2_EL1: Self = Self::System(0b11_000_0000_0010_010); /// AArch32 Instruction Set Attribute Register 3 pub const ID_ISAR3_EL1: Self = Self::System(0b11_000_0000_0010_011); /// AArch32 Instruction Set Attribute Register 4 pub const ID_ISAR4_EL1: Self = Self::System(0b11_000_0000_0010_100); /// AArch32 Instruction Set Attribute Register 5 pub const ID_ISAR5_EL1: Self = Self::System(0b11_000_0000_0010_101); /// AArch32 Memory Model Feature Register 4 pub const ID_MMFR4_EL1: Self = Self::System(0b11_000_0000_0010_110); /// AArch32 Instruction Set Attribute Register 6 pub const ID_ISAR6_EL1: Self = Self::System(0b11_000_0000_0010_111); /// AArch32 Media And VFP Feature Register 0 pub const MVFR0_EL1: Self = Self::System(0b11_000_0000_0011_000); /// AArch32 Media And VFP Feature Register 1 pub const MVFR1_EL1: Self = Self::System(0b11_000_0000_0011_001); /// AArch32 Media And VFP Feature Register 2 pub const MVFR2_EL1: Self = Self::System(0b11_000_0000_0011_010); /// AArch32 Processor Feature Register 2 pub const ID_PFR2_EL1: Self = Self::System(0b11_000_0000_0011_100); /// Debug Feature Register 1 pub const ID_DFR1_EL1: Self = Self::System(0b11_000_0000_0011_101); /// AArch32 Memory Model Feature Register 5 pub const ID_MMFR5_EL1: Self = Self::System(0b11_000_0000_0011_110); /// AArch64 Processor Feature Register 0 pub const ID_AA64PFR0_EL1: Self = Self::System(0b11_000_0000_0100_000); /// AArch64 Processor Feature Register 1 pub const ID_AA64PFR1_EL1: Self = Self::System(0b11_000_0000_0100_001); /// SVE Feature ID Register 0 pub const ID_AA64ZFR0_EL1: Self = Self::System(0b11_000_0000_0100_100); /// SME Feature ID Register 0 pub const ID_AA64SMFR0_EL1: Self = Self::System(0b11_000_0000_0100_101); /// AArch64 Debug Feature Register 0 pub const ID_AA64DFR0_EL1: Self = Self::System(0b11_000_0000_0101_000); /// AArch64 Debug Feature Register 1 pub const ID_AA64DFR1_EL1: Self = Self::System(0b11_000_0000_0101_001); /// AArch64 Auxiliary Feature Register 0 pub const ID_AA64AFR0_EL1: Self = Self::System(0b11_000_0000_0101_100); /// AArch64 Auxiliary Feature Register 1 pub const ID_AA64AFR1_EL1: Self = Self::System(0b11_000_0000_0101_101); /// AArch64 Instruction Set Attribute Register 0 pub const ID_AA64ISAR0_EL1: Self = Self::System(0b11_000_0000_0110_000); /// AArch64 Instruction Set Attribute Register 1 pub const ID_AA64ISAR1_EL1: Self = Self::System(0b11_000_0000_0110_001); /// AArch64 Instruction Set Attribute Register 2 pub const ID_AA64ISAR2_EL1: Self = Self::System(0b11_000_0000_0110_010); /// AArch64 Memory Model Feature Register 0 pub const ID_AA64MMFR0_EL1: Self = Self::System(0b11_000_0000_0111_000); /// AArch64 Memory Model Feature Register 1 pub const ID_AA64MMFR1_EL1: Self = Self::System(0b11_000_0000_0111_001); /// AArch64 Memory Model Feature Register 2 pub const ID_AA64MMFR2_EL1: Self = Self::System(0b11_000_0000_0111_010); /// System Control Register (EL1) pub const SCTLR_EL1: Self = Self::System(0b11_000_0001_0000_000); /// Auxiliary Control Register (EL1) pub const ACTLR_EL1: Self = Self::System(0b11_000_0001_0000_001); /// Architectural Feature Access Control Register pub const CPACR_EL1: Self = Self::System(0b11_000_0001_0000_010); /// Random Allocation Tag Seed Register pub const RGSR_EL1: Self = Self::System(0b11_000_0001_0000_101); /// Tag Control Register pub const GCR_EL1: Self = Self::System(0b11_000_0001_0000_110); /// SVE Control Register (EL1) pub const ZCR_EL1: Self = Self::System(0b11_000_0001_0010_000); /// Trace Filter Control Register (EL1) pub const TRFCR_EL1: Self = Self::System(0b11_000_0001_0010_001); /// Streaming Mode Priority Register pub const SMPRI_EL1: Self = Self::System(0b11_000_0001_0010_100); /// SME Control Register (EL1) pub const SMCR_EL1: Self = Self::System(0b11_000_0001_0010_110); /// Translation Table Base Register 0 (EL1) pub const TTBR0_EL1: Self = Self::System(0b11_000_0010_0000_000); /// Translation Table Base Register 1 (EL1) pub const TTBR1_EL1: Self = Self::System(0b11_000_0010_0000_001); /// Translation Control Register (EL1) pub const TCR_EL1: Self = Self::System(0b11_000_0010_0000_010); /// Pointer Authentication Key A For Instruction (bits\[63:0]) pub const APIAKEYLO_EL1: Self = Self::System(0b11_000_0010_0001_000); /// Pointer Authentication Key A For Instruction (bits\[127:64]) pub const APIAKEYHI_EL1: Self = Self::System(0b11_000_0010_0001_001); /// Pointer Authentication Key B For Instruction (bits\[63:0]) pub const APIBKEYLO_EL1: Self = Self::System(0b11_000_0010_0001_010); /// Pointer Authentication Key B For Instruction (bits\[127:64]) pub const APIBKEYHI_EL1: Self = Self::System(0b11_000_0010_0001_011); /// Pointer Authentication Key A For Data (bits\[63:0]) pub const APDAKEYLO_EL1: Self = Self::System(0b11_000_0010_0010_000); /// Pointer Authentication Key A For Data (bits\[127:64]) pub const APDAKEYHI_EL1: Self = Self::System(0b11_000_0010_0010_001); /// Pointer Authentication Key B For Data (bits\[63:0]) pub const APDBKEYLO_EL1: Self = Self::System(0b11_000_0010_0010_010); /// Pointer Authentication Key B For Data (bits\[127:64]) pub const APDBKEYHI_EL1: Self = Self::System(0b11_000_0010_0010_011); /// Pointer Authentication Key A For Code (bits\[63:0]) pub const APGAKEYLO_EL1: Self = Self::System(0b11_000_0010_0011_000); /// Pointer Authentication Key A For Code (bits\[127:64]) pub const APGAKEYHI_EL1: Self = Self::System(0b11_000_0010_0011_001); /// Saved Program Status Register (EL1) pub const SPSR_EL1: Self = Self::System(0b11_000_0100_0000_000); /// Exception Link Register (EL1) pub const ELR_EL1: Self = Self::System(0b11_000_0100_0000_001); /// Stack Pointer (EL0) pub const SP_EL0: Self = Self::System(0b11_000_0100_0001_000); /// Interrupt Controller Interrupt Priority Mask Register pub const ICC_PMR_EL1: Self = Self::System(0b11_000_0100_0110_000); /// Interrupt Controller Virtual Interrupt Priority Mask Register pub const ICV_PMR_EL1: Self = Self::System(0b11_000_0100_0110_000); /// Auxiliary Fault Status Register 0 (EL1) pub const AFSR0_EL1: Self = Self::System(0b11_000_0101_0001_000); /// Auxiliary Fault Status Register 1 (EL1) pub const AFSR1_EL1: Self = Self::System(0b11_000_0101_0001_001); /// Exception Syndrome Register (EL1) pub const ESR_EL1: Self = Self::System(0b11_000_0101_0010_000); /// Error Record ID Register pub const ERRIDR_EL1: Self = Self::System(0b11_000_0101_0011_000); /// Error Record Select Register pub const ERRSELR_EL1: Self = Self::System(0b11_000_0101_0011_001); /// Selected Error Record Feature Register pub const ERXFR_EL1: Self = Self::System(0b11_000_0101_0100_000); /// Selected Error Record Control Register pub const ERXCTLR_EL1: Self = Self::System(0b11_000_0101_0100_001); /// Selected Error Record Primary Status Register pub const ERXSTATUS_EL1: Self = Self::System(0b11_000_0101_0100_010); /// Selected Error Record Address Register pub const ERXADDR_EL1: Self = Self::System(0b11_000_0101_0100_011); /// Selected Pseudo-fault Generation Feature Register pub const ERXPFGF_EL1: Self = Self::System(0b11_000_0101_0100_100); /// Selected Pseudo-fault Generation Control Register pub const ERXPFGCTL_EL1: Self = Self::System(0b11_000_0101_0100_101); /// Selected Pseudo-fault Generation Countdown Register pub const ERXPFGCDN_EL1: Self = Self::System(0b11_000_0101_0100_110); /// Selected Error Record Miscellaneous Register 0 pub const ERXMISC0_EL1: Self = Self::System(0b11_000_0101_0101_000); /// Selected Error Record Miscellaneous Register 1 pub const ERXMISC1_EL1: Self = Self::System(0b11_000_0101_0101_001); /// Selected Error Record Miscellaneous Register 2 pub const ERXMISC2_EL1: Self = Self::System(0b11_000_0101_0101_010); /// Selected Error Record Miscellaneous Register 3 pub const ERXMISC3_EL1: Self = Self::System(0b11_000_0101_0101_011); /// Tag Fault Status Register (EL1) pub const TFSR_EL1: Self = Self::System(0b11_000_0101_0110_000); /// Tag Fault Status Register (EL0) pub const TFSRE0_EL1: Self = Self::System(0b11_000_0101_0110_001); /// Fault Address Register (EL1) pub const FAR_EL1: Self = Self::System(0b11_000_0110_0000_000); /// Physical Address Register pub const PAR_EL1: Self = Self::System(0b11_000_0111_0100_000); /// Statistical Profiling Control Register (EL1) pub const PMSCR_EL1: Self = Self::System(0b11_000_1001_1001_000); /// Sampling Inverted Event Filter Register pub const PMSNEVFR_EL1: Self = Self::System(0b11_000_1001_1001_001); /// Sampling Interval Counter Register pub const PMSICR_EL1: Self = Self::System(0b11_000_1001_1001_010); /// Sampling Interval Reload Register pub const PMSIRR_EL1: Self = Self::System(0b11_000_1001_1001_011); /// Sampling Filter Control Register pub const PMSFCR_EL1: Self = Self::System(0b11_000_1001_1001_100); /// Sampling Event Filter Register pub const PMSEVFR_EL1: Self = Self::System(0b11_000_1001_1001_101); /// Sampling Latency Filter Register pub const PMSLATFR_EL1: Self = Self::System(0b11_000_1001_1001_110); /// Sampling Profiling ID Register pub const PMSIDR_EL1: Self = Self::System(0b11_000_1001_1001_111); /// Profiling Buffer Limit Address Register pub const PMBLIMITR_EL1: Self = Self::System(0b11_000_1001_1010_000); /// Profiling Buffer Write Pointer Register pub const PMBPTR_EL1: Self = Self::System(0b11_000_1001_1010_001); /// Profiling Buffer Status/syndrome Register pub const PMBSR_EL1: Self = Self::System(0b11_000_1001_1010_011); /// Profiling Buffer ID Register pub const PMBIDR_EL1: Self = Self::System(0b11_000_1001_1010_111); /// Trace Buffer Limit Address Register pub const TRBLIMITR_EL1: Self = Self::System(0b11_000_1001_1011_000); /// Trace Buffer Write Pointer Register pub const TRBPTR_EL1: Self = Self::System(0b11_000_1001_1011_001); /// Trace Buffer Base Address Register pub const TRBBASER_EL1: Self = Self::System(0b11_000_1001_1011_010); /// Trace Buffer Status/syndrome Register pub const TRBSR_EL1: Self = Self::System(0b11_000_1001_1011_011); /// Trace Buffer Memory Attribute Register pub const TRBMAR_EL1: Self = Self::System(0b11_000_1001_1011_100); /// Trace Buffer Trigger Counter Register pub const TRBTRG_EL1: Self = Self::System(0b11_000_1001_1011_110); /// Trace Buffer ID Register pub const TRBIDR_EL1: Self = Self::System(0b11_000_1001_1011_111); /// Performance Monitors Interrupt Enable Set Register pub const PMINTENSET_EL1: Self = Self::System(0b11_000_1001_1110_001); /// Performance Monitors Interrupt Enable Clear Register pub const PMINTENCLR_EL1: Self = Self::System(0b11_000_1001_1110_010); /// Performance Monitors Machine Identification Register pub const PMMIR_EL1: Self = Self::System(0b11_000_1001_1110_110); /// Memory Attribute Indirection Register (EL1) pub const MAIR_EL1: Self = Self::System(0b11_000_1010_0010_000); /// Auxiliary Memory Attribute Indirection Register (EL1) pub const AMAIR_EL1: Self = Self::System(0b11_000_1010_0011_000); /// LORegion Start Address (EL1) pub const LORSA_EL1: Self = Self::System(0b11_000_1010_0100_000); /// LORegion End Address (EL1) pub const LOREA_EL1: Self = Self::System(0b11_000_1010_0100_001); /// LORegion Number (EL1) pub const LORN_EL1: Self = Self::System(0b11_000_1010_0100_010); /// LORegion Control (EL1) pub const LORC_EL1: Self = Self::System(0b11_000_1010_0100_011); /// MPAM ID Register (EL1) pub const MPAMIDR_EL1: Self = Self::System(0b11_000_1010_0100_100); /// LORegionID (EL1) pub const LORID_EL1: Self = Self::System(0b11_000_1010_0100_111); /// MPAM1 Register (EL1) pub const MPAM1_EL1: Self = Self::System(0b11_000_1010_0101_000); /// MPAM0 Register (EL1) pub const MPAM0_EL1: Self = Self::System(0b11_000_1010_0101_001); /// MPAM Streaming Mode Register pub const MPAMSM_EL1: Self = Self::System(0b11_000_1010_0101_011); /// Vector Base Address Register (EL1) pub const VBAR_EL1: Self = Self::System(0b11_000_1100_0000_000); /// Reset Vector Base Address Register (if EL2 And EL3 Not Implemented) pub const RVBAR_EL1: Self = Self::System(0b11_000_1100_0000_001); /// Reset Management Register (EL1) pub const RMR_EL1: Self = Self::System(0b11_000_1100_0000_010); /// Interrupt Status Register pub const ISR_EL1: Self = Self::System(0b11_000_1100_0001_000); /// Deferred Interrupt Status Register pub const DISR_EL1: Self = Self::System(0b11_000_1100_0001_001); /// Interrupt Controller Interrupt Acknowledge Register 0 pub const ICC_IAR0_EL1: Self = Self::System(0b11_000_1100_1000_000); /// Interrupt Controller Virtual Interrupt Acknowledge Register 0 pub const ICV_IAR0_EL1: Self = Self::System(0b11_000_1100_1000_000); /// Interrupt Controller End Of Interrupt Register 0 pub const ICC_EOIR0_EL1: Self = Self::System(0b11_000_1100_1000_001); /// Interrupt Controller Virtual End Of Interrupt Register 0 pub const ICV_EOIR0_EL1: Self = Self::System(0b11_000_1100_1000_001); /// Interrupt Controller Highest Priority Pending Interrupt Register 0 pub const ICC_HPPIR0_EL1: Self = Self::System(0b11_000_1100_1000_010); /// Interrupt Controller Virtual Highest Priority Pending Interrupt Register /// 0 pub const ICV_HPPIR0_EL1: Self = Self::System(0b11_000_1100_1000_010); /// Interrupt Controller Binary Point Register 0 pub const ICC_BPR0_EL1: Self = Self::System(0b11_000_1100_1000_011); /// Interrupt Controller Virtual Binary Point Register 0 pub const ICV_BPR0_EL1: Self = Self::System(0b11_000_1100_1000_011); /// Interrupt Controller Active Priorities Group 0 Registers - 0 pub const ICC_AP0R0_EL1: Self = Self::System(0b11_000_1100_1000_100); /// Interrupt Controller Virtual Active Priorities Group 0 Registers - 0 pub const ICV_AP0R0_EL1: Self = Self::System(0b11_000_1100_1000_100); /// Interrupt Controller Active Priorities Group 0 Registers - 1 pub const ICC_AP0R1_EL1: Self = Self::System(0b11_000_1100_1000_101); /// Interrupt Controller Virtual Active Priorities Group 0 Registers - 1 pub const ICV_AP0R1_EL1: Self = Self::System(0b11_000_1100_1000_101); /// Interrupt Controller Active Priorities Group 0 Registers - 2 pub const ICC_AP0R2_EL1: Self = Self::System(0b11_000_1100_1000_110); /// Interrupt Controller Virtual Active Priorities Group 0 Registers - 2 pub const ICV_AP0R2_EL1: Self = Self::System(0b11_000_1100_1000_110); /// Interrupt Controller Active Priorities Group 0 Registers - 3 pub const ICC_AP0R3_EL1: Self = Self::System(0b11_000_1100_1000_111); /// Interrupt Controller Virtual Active Priorities Group 0 Registers - 3 pub const ICV_AP0R3_EL1: Self = Self::System(0b11_000_1100_1000_111); /// Interrupt Controller Active Priorities Group 1 Registers - 0 pub const ICC_AP1R0_EL1: Self = Self::System(0b11_000_1100_1001_000); /// Interrupt Controller Virtual Active Priorities Group 1 Registers - 0 pub const ICV_AP1R0_EL1: Self = Self::System(0b11_000_1100_1001_000); /// Interrupt Controller Active Priorities Group 1 Registers - 1 pub const ICC_AP1R1_EL1: Self = Self::System(0b11_000_1100_1001_001); /// Interrupt Controller Virtual Active Priorities Group 1 Registers - 1 pub const ICV_AP1R1_EL1: Self = Self::System(0b11_000_1100_1001_001); /// Interrupt Controller Active Priorities Group 1 Registers - 2 pub const ICC_AP1R2_EL1: Self = Self::System(0b11_000_1100_1001_010); /// Interrupt Controller Virtual Active Priorities Group 1 Registers - 2 pub const ICV_AP1R2_EL1: Self = Self::System(0b11_000_1100_1001_010); /// Interrupt Controller Active Priorities Group 1 Registers - 3 pub const ICC_AP1R3_EL1: Self = Self::System(0b11_000_1100_1001_011); /// Interrupt Controller Virtual Active Priorities Group 1 Registers - 3 pub const ICV_AP1R3_EL1: Self = Self::System(0b11_000_1100_1001_011); /// Interrupt Controller Non-maskable Interrupt Acknowledge Register 1 pub const ICC_NMIAR1_EL1: Self = Self::System(0b11_000_1100_1001_101); /// Interrupt Controller Virtual Non-maskable Interrupt Acknowledge Register /// 1 pub const ICV_NMIAR1_EL1: Self = Self::System(0b11_000_1100_1001_101); /// Interrupt Controller Deactivate Interrupt Register pub const ICC_DIR_EL1: Self = Self::System(0b11_000_1100_1011_001); /// Interrupt Controller Deactivate Virtual Interrupt Register pub const ICV_DIR_EL1: Self = Self::System(0b11_000_1100_1011_001); /// Interrupt Controller Running Priority Register pub const ICC_RPR_EL1: Self = Self::System(0b11_000_1100_1011_011); /// Interrupt Controller Virtual Running Priority Register pub const ICV_RPR_EL1: Self = Self::System(0b11_000_1100_1011_011); /// Interrupt Controller Software Generated Interrupt Group 1 Register pub const ICC_SGI1R_EL1: Self = Self::System(0b11_000_1100_1011_101); /// Interrupt Controller Alias Software Generated Interrupt Group 1 Register pub const ICC_ASGI1R_EL1: Self = Self::System(0b11_000_1100_1011_110); /// Interrupt Controller Software Generated Interrupt Group 0 Register pub const ICC_SGI0R_EL1: Self = Self::System(0b11_000_1100_1011_111); /// Interrupt Controller Interrupt Acknowledge Register 1 pub const ICC_IAR1_EL1: Self = Self::System(0b11_000_1100_1100_000); /// Interrupt Controller Virtual Interrupt Acknowledge Register 1 pub const ICV_IAR1_EL1: Self = Self::System(0b11_000_1100_1100_000); /// Interrupt Controller End Of Interrupt Register 1 pub const ICC_EOIR1_EL1: Self = Self::System(0b11_000_1100_1100_001); /// Interrupt Controller Virtual End Of Interrupt Register 1 pub const ICV_EOIR1_EL1: Self = Self::System(0b11_000_1100_1100_001); /// Interrupt Controller Highest Priority Pending Interrupt Register 1 pub const ICC_HPPIR1_EL1: Self = Self::System(0b11_000_1100_1100_010); /// Interrupt Controller Virtual Highest Priority Pending Interrupt Register /// 1 pub const ICV_HPPIR1_EL1: Self = Self::System(0b11_000_1100_1100_010); /// Interrupt Controller Binary Point Register 1 pub const ICC_BPR1_EL1: Self = Self::System(0b11_000_1100_1100_011); /// Interrupt Controller Virtual Binary Point Register 1 pub const ICV_BPR1_EL1: Self = Self::System(0b11_000_1100_1100_011); /// Interrupt Controller Control Register (EL1) pub const ICC_CTLR_EL1: Self = Self::System(0b11_000_1100_1100_100); /// Interrupt Controller Virtual Control Register pub const ICV_CTLR_EL1: Self = Self::System(0b11_000_1100_1100_100); /// Interrupt Controller System Register Enable Register (EL1) pub const ICC_SRE_EL1: Self = Self::System(0b11_000_1100_1100_101); /// Interrupt Controller Interrupt Group 0 Enable Register pub const ICC_IGRPEN0_EL1: Self = Self::System(0b11_000_1100_1100_110); /// Interrupt Controller Virtual Interrupt Group 0 Enable Register pub const ICV_IGRPEN0_EL1: Self = Self::System(0b11_000_1100_1100_110); /// Interrupt Controller Interrupt Group 1 Enable Register pub const ICC_IGRPEN1_EL1: Self = Self::System(0b11_000_1100_1100_111); /// Interrupt Controller Virtual Interrupt Group 1 Enable Register pub const ICV_IGRPEN1_EL1: Self = Self::System(0b11_000_1100_1100_111); /// Context ID Register (EL1) pub const CONTEXTIDR_EL1: Self = Self::System(0b11_000_1101_0000_001); /// EL1 Software Thread ID Register pub const TPIDR_EL1: Self = Self::System(0b11_000_1101_0000_100); /// Accelerator Data pub const ACCDATA_EL1: Self = Self::System(0b11_000_1101_0000_101); /// EL1 Read/Write Software Context Number pub const SCXTNUM_EL1: Self = Self::System(0b11_000_1101_0000_111); /// Counter-timer Kernel Control Register pub const CNTKCTL_EL1: Self = Self::System(0b11_000_1110_0001_000); /// Current Cache Size ID Register pub const CCSIDR_EL1: Self = Self::System(0b11_001_0000_0000_000); /// Cache Level ID Register pub const CLIDR_EL1: Self = Self::System(0b11_001_0000_0000_001); /// Current Cache Size ID Register 2 pub const CCSIDR2_EL1: Self = Self::System(0b11_001_0000_0000_010); /// Multiple Tag Transfer ID Register pub const GMID_EL1: Self = Self::System(0b11_001_0000_0000_100); /// Streaming Mode Identification Register pub const SMIDR_EL1: Self = Self::System(0b11_001_0000_0000_110); /// Auxiliary ID Register pub const AIDR_EL1: Self = Self::System(0b11_001_0000_0000_111); /// Cache Size Selection Register pub const CSSELR_EL1: Self = Self::System(0b11_010_0000_0000_000); /// Cache Type Register pub const CTR_EL0: Self = Self::System(0b11_011_0000_0000_001); /// Data Cache Zero ID Register pub const DCZID_EL0: Self = Self::System(0b11_011_0000_0000_111); /// Random Number pub const RNDR: Self = Self::System(0b11_011_0010_0100_000); /// Reseeded Random Number pub const RNDRRS: Self = Self::System(0b11_011_0010_0100_001); /// Streaming Vector Control Register pub const SVCR: Self = Self::System(0b11_011_0100_0010_010); /// Floating-point Control Register pub const FPCR: Self = Self::System(0b11_011_0100_0100_000); /// Floating-point Status Register pub const FPSR: Self = Self::System(0b11_011_0100_0100_001); /// Debug Saved Program Status Register pub const DSPSR_EL0: Self = Self::System(0b11_011_0100_0101_000); /// Debug Link Register pub const DLR_EL0: Self = Self::System(0b11_011_0100_0101_001); /// Performance Monitors Control Register pub const PMCR_EL0: Self = Self::System(0b11_011_1001_1100_000); /// Performance Monitors Count Enable Set Register pub const PMCNTENSET_EL0: Self = Self::System(0b11_011_1001_1100_001); /// Performance Monitors Count Enable Clear Register pub const PMCNTENCLR_EL0: Self = Self::System(0b11_011_1001_1100_010); /// Performance Monitors Overflow Flag Status Clear Register pub const PMOVSCLR_EL0: Self = Self::System(0b11_011_1001_1100_011); /// Performance Monitors Software Increment Register pub const PMSWINC_EL0: Self = Self::System(0b11_011_1001_1100_100); /// Performance Monitors Event Counter Selection Register pub const PMSELR_EL0: Self = Self::System(0b11_011_1001_1100_101); /// Performance Monitors Common Event Identification Register 0 pub const PMCEID0_EL0: Self = Self::System(0b11_011_1001_1100_110); /// Performance Monitors Common Event Identification Register 1 pub const PMCEID1_EL0: Self = Self::System(0b11_011_1001_1100_111); /// Performance Monitors Cycle Count Register pub const PMCCNTR_EL0: Self = Self::System(0b11_011_1001_1101_000); /// Performance Monitors Selected Event Type Register pub const PMXEVTYPER_EL0: Self = Self::System(0b11_011_1001_1101_001); /// Performance Monitors Selected Event Count Register pub const PMXEVCNTR_EL0: Self = Self::System(0b11_011_1001_1101_010); /// Performance Monitors User Enable Register pub const PMUSERENR_EL0: Self = Self::System(0b11_011_1001_1110_000); /// Performance Monitors Overflow Flag Status Set Register pub const PMOVSSET_EL0: Self = Self::System(0b11_011_1001_1110_011); /// EL0 Read/Write Software Thread ID Register pub const TPIDR_EL0: Self = Self::System(0b11_011_1101_0000_010); /// EL0 Read-Only Software Thread ID Register pub const TPIDRRO_EL0: Self = Self::System(0b11_011_1101_0000_011); /// EL0 Read/Write Software Thread ID Register 2 pub const TPIDR2_EL0: Self = Self::System(0b11_011_1101_0000_101); /// EL0 Read/Write Software Context Number pub const SCXTNUM_EL0: Self = Self::System(0b11_011_1101_0000_111); /// Activity Monitors Control Register pub const AMCR_EL0: Self = Self::System(0b11_011_1101_0010_000); /// Activity Monitors Configuration Register pub const AMCFGR_EL0: Self = Self::System(0b11_011_1101_0010_001); /// Activity Monitors Counter Group Configuration Register pub const AMCGCR_EL0: Self = Self::System(0b11_011_1101_0010_010); /// Activity Monitors User Enable Register pub const AMUSERENR_EL0: Self = Self::System(0b11_011_1101_0010_011); /// Activity Monitors Count Enable Clear Register 0 pub const AMCNTENCLR0_EL0: Self = Self::System(0b11_011_1101_0010_100); /// Activity Monitors Count Enable Set Register 0 pub const AMCNTENSET0_EL0: Self = Self::System(0b11_011_1101_0010_101); /// Activity Monitors Counter Group 1 Identification Register pub const AMCG1IDR_EL0: Self = Self::System(0b11_011_1101_0010_110); /// Activity Monitors Count Enable Clear Register 1 pub const AMCNTENCLR1_EL0: Self = Self::System(0b11_011_1101_0011_000); /// Activity Monitors Count Enable Set Register 1 pub const AMCNTENSET1_EL0: Self = Self::System(0b11_011_1101_0011_001); /// Activity Monitors Event Counter Registers 0 - 0 pub const AMEVCNTR00_EL0: Self = Self::System(0b11_011_1101_0100_000); /// Activity Monitors Event Counter Registers 0 - 1 pub const AMEVCNTR01_EL0: Self = Self::System(0b11_011_1101_0100_001); /// Activity Monitors Event Counter Registers 0 - 2 pub const AMEVCNTR02_EL0: Self = Self::System(0b11_011_1101_0100_010); /// Activity Monitors Event Counter Registers 0 - 3 pub const AMEVCNTR03_EL0: Self = Self::System(0b11_011_1101_0100_011); /// Activity Monitors Event Type Registers 0 - 0 pub const AMEVTYPER00_EL0: Self = Self::System(0b11_011_1101_0110_000); /// Activity Monitors Event Type Registers 0 - 1 pub const AMEVTYPER01_EL0: Self = Self::System(0b11_011_1101_0110_001); /// Activity Monitors Event Type Registers 0 - 2 pub const AMEVTYPER02_EL0: Self = Self::System(0b11_011_1101_0110_010); /// Activity Monitors Event Type Registers 0 - 3 pub const AMEVTYPER03_EL0: Self = Self::System(0b11_011_1101_0110_011); /// Activity Monitors Event Counter Registers 1 - 0 pub const AMEVCNTR10_EL0: Self = Self::System(0b11_011_1101_1100_000); /// Activity Monitors Event Counter Registers 1 - 1 pub const AMEVCNTR11_EL0: Self = Self::System(0b11_011_1101_1100_001); /// Activity Monitors Event Counter Registers 1 - 2 pub const AMEVCNTR12_EL0: Self = Self::System(0b11_011_1101_1100_010); /// Activity Monitors Event Counter Registers 1 - 3 pub const AMEVCNTR13_EL0: Self = Self::System(0b11_011_1101_1100_011); /// Activity Monitors Event Counter Registers 1 - 4 pub const AMEVCNTR14_EL0: Self = Self::System(0b11_011_1101_1100_100); /// Activity Monitors Event Counter Registers 1 - 5 pub const AMEVCNTR15_EL0: Self = Self::System(0b11_011_1101_1100_101); /// Activity Monitors Event Counter Registers 1 - 6 pub const AMEVCNTR16_EL0: Self = Self::System(0b11_011_1101_1100_110); /// Activity Monitors Event Counter Registers 1 - 7 pub const AMEVCNTR17_EL0: Self = Self::System(0b11_011_1101_1100_111); /// Activity Monitors Event Counter Registers 1 - 8 pub const AMEVCNTR18_EL0: Self = Self::System(0b11_011_1101_1101_000); /// Activity Monitors Event Counter Registers 1 - 9 pub const AMEVCNTR19_EL0: Self = Self::System(0b11_011_1101_1101_001); /// Activity Monitors Event Counter Registers 1 - 10 pub const AMEVCNTR110_EL0: Self = Self::System(0b11_011_1101_1101_010); /// Activity Monitors Event Counter Registers 1 - 11 pub const AMEVCNTR111_EL0: Self = Self::System(0b11_011_1101_1101_011); /// Activity Monitors Event Counter Registers 1 - 12 pub const AMEVCNTR112_EL0: Self = Self::System(0b11_011_1101_1101_100); /// Activity Monitors Event Counter Registers 1 - 13 pub const AMEVCNTR113_EL0: Self = Self::System(0b11_011_1101_1101_101); /// Activity Monitors Event Counter Registers 1 - 14 pub const AMEVCNTR114_EL0: Self = Self::System(0b11_011_1101_1101_110); /// Activity Monitors Event Counter Registers 1 - 15 pub const AMEVCNTR115_EL0: Self = Self::System(0b11_011_1101_1101_111); /// Activity Monitors Event Type Registers 1 - 0 pub const AMEVTYPER10_EL0: Self = Self::System(0b11_011_1101_1110_000); /// Activity Monitors Event Type Registers 1 - 1 pub const AMEVTYPER11_EL0: Self = Self::System(0b11_011_1101_1110_001); /// Activity Monitors Event Type Registers 1 - 2 pub const AMEVTYPER12_EL0: Self = Self::System(0b11_011_1101_1110_010); /// Activity Monitors Event Type Registers 1 - 3 pub const AMEVTYPER13_EL0: Self = Self::System(0b11_011_1101_1110_011); /// Activity Monitors Event Type Registers 1 - 4 pub const AMEVTYPER14_EL0: Self = Self::System(0b11_011_1101_1110_100); /// Activity Monitors Event Type Registers 1 - 5 pub const AMEVTYPER15_EL0: Self = Self::System(0b11_011_1101_1110_101); /// Activity Monitors Event Type Registers 1 - 6 pub const AMEVTYPER16_EL0: Self = Self::System(0b11_011_1101_1110_110); /// Activity Monitors Event Type Registers 1 - 7 pub const AMEVTYPER17_EL0: Self = Self::System(0b11_011_1101_1110_111); /// Activity Monitors Event Type Registers 1 - 8 pub const AMEVTYPER18_EL0: Self = Self::System(0b11_011_1101_1111_000); /// Activity Monitors Event Type Registers 1 - 9 pub const AMEVTYPER19_EL0: Self = Self::System(0b11_011_1101_1111_001); /// Activity Monitors Event Type Registers 1 - 10 pub const AMEVTYPER110_EL0: Self = Self::System(0b11_011_1101_1111_010); /// Activity Monitors Event Type Registers 1 - 11 pub const AMEVTYPER111_EL0: Self = Self::System(0b11_011_1101_1111_011); /// Activity Monitors Event Type Registers 1 - 12 pub const AMEVTYPER112_EL0: Self = Self::System(0b11_011_1101_1111_100); /// Activity Monitors Event Type Registers 1 - 13 pub const AMEVTYPER113_EL0: Self = Self::System(0b11_011_1101_1111_101); /// Activity Monitors Event Type Registers 1 - 14 pub const AMEVTYPER114_EL0: Self = Self::System(0b11_011_1101_1111_110); /// Activity Monitors Event Type Registers 1 - 15 pub const AMEVTYPER115_EL0: Self = Self::System(0b11_011_1101_1111_111); /// Counter-timer Frequency Register pub const CNTFRQ_EL0: Self = Self::System(0b11_011_1110_0000_000); /// Counter-timer Physical Count Register pub const CNTPCT_EL0: Self = Self::System(0b11_011_1110_0000_001); /// Counter-timer Virtual Count Register pub const CNTVCT_EL0: Self = Self::System(0b11_011_1110_0000_010); /// Counter-timer Self-Synchronized Physical Count Register pub const CNTPCTSS_EL0: Self = Self::System(0b11_011_1110_0000_101); /// Counter-timer Self-Synchronized Virtual Count Register pub const CNTVCTSS_EL0: Self = Self::System(0b11_011_1110_0000_110); /// Counter-timer Physical Timer TimerValue Register pub const CNTP_TVAL_EL0: Self = Self::System(0b11_011_1110_0010_000); /// Counter-timer Physical Timer Control Register pub const CNTP_CTL_EL0: Self = Self::System(0b11_011_1110_0010_001); /// Counter-timer Physical Timer CompareValue Register pub const CNTP_CVAL_EL0: Self = Self::System(0b11_011_1110_0010_010); /// Counter-timer Virtual Timer TimerValue Register pub const CNTV_TVAL_EL0: Self = Self::System(0b11_011_1110_0011_000); /// Counter-timer Virtual Timer Control Register pub const CNTV_CTL_EL0: Self = Self::System(0b11_011_1110_0011_001); /// Counter-timer Virtual Timer CompareValue Register pub const CNTV_CVAL_EL0: Self = Self::System(0b11_011_1110_0011_010); /// Performance Monitors Event Count Registers - 0 pub const PMEVCNTR0_EL0: Self = Self::System(0b11_011_1110_1000_000); /// Performance Monitors Event Count Registers - 1 pub const PMEVCNTR1_EL0: Self = Self::System(0b11_011_1110_1000_001); /// Performance Monitors Event Count Registers - 2 pub const PMEVCNTR2_EL0: Self = Self::System(0b11_011_1110_1000_010); /// Performance Monitors Event Count Registers - 3 pub const PMEVCNTR3_EL0: Self = Self::System(0b11_011_1110_1000_011); /// Performance Monitors Event Count Registers - 4 pub const PMEVCNTR4_EL0: Self = Self::System(0b11_011_1110_1000_100); /// Performance Monitors Event Count Registers - 5 pub const PMEVCNTR5_EL0: Self = Self::System(0b11_011_1110_1000_101); /// Performance Monitors Event Count Registers - 6 pub const PMEVCNTR6_EL0: Self = Self::System(0b11_011_1110_1000_110); /// Performance Monitors Event Count Registers - 7 pub const PMEVCNTR7_EL0: Self = Self::System(0b11_011_1110_1000_111); /// Performance Monitors Event Count Registers - 8 pub const PMEVCNTR8_EL0: Self = Self::System(0b11_011_1110_1001_000); /// Performance Monitors Event Count Registers - 9 pub const PMEVCNTR9_EL0: Self = Self::System(0b11_011_1110_1001_001); /// Performance Monitors Event Count Registers - 10 pub const PMEVCNTR10_EL0: Self = Self::System(0b11_011_1110_1001_010); /// Performance Monitors Event Count Registers - 11 pub const PMEVCNTR11_EL0: Self = Self::System(0b11_011_1110_1001_011); /// Performance Monitors Event Count Registers - 12 pub const PMEVCNTR12_EL0: Self = Self::System(0b11_011_1110_1001_100); /// Performance Monitors Event Count Registers - 13 pub const PMEVCNTR13_EL0: Self = Self::System(0b11_011_1110_1001_101); /// Performance Monitors Event Count Registers - 14 pub const PMEVCNTR14_EL0: Self = Self::System(0b11_011_1110_1001_110); /// Performance Monitors Event Count Registers - 15 pub const PMEVCNTR15_EL0: Self = Self::System(0b11_011_1110_1001_111); /// Performance Monitors Event Count Registers - 16 pub const PMEVCNTR16_EL0: Self = Self::System(0b11_011_1110_1010_000); /// Performance Monitors Event Count Registers - 17 pub const PMEVCNTR17_EL0: Self = Self::System(0b11_011_1110_1010_001); /// Performance Monitors Event Count Registers - 18 pub const PMEVCNTR18_EL0: Self = Self::System(0b11_011_1110_1010_010); /// Performance Monitors Event Count Registers - 19 pub const PMEVCNTR19_EL0: Self = Self::System(0b11_011_1110_1010_011); /// Performance Monitors Event Count Registers - 20 pub const PMEVCNTR20_EL0: Self = Self::System(0b11_011_1110_1010_100); /// Performance Monitors Event Count Registers - 21 pub const PMEVCNTR21_EL0: Self = Self::System(0b11_011_1110_1010_101); /// Performance Monitors Event Count Registers - 22 pub const PMEVCNTR22_EL0: Self = Self::System(0b11_011_1110_1010_110); /// Performance Monitors Event Count Registers - 23 pub const PMEVCNTR23_EL0: Self = Self::System(0b11_011_1110_1010_111); /// Performance Monitors Event Count Registers - 24 pub const PMEVCNTR24_EL0: Self = Self::System(0b11_011_1110_1011_000); /// Performance Monitors Event Count Registers - 25 pub const PMEVCNTR25_EL0: Self = Self::System(0b11_011_1110_1011_001); /// Performance Monitors Event Count Registers - 26 pub const PMEVCNTR26_EL0: Self = Self::System(0b11_011_1110_1011_010); /// Performance Monitors Event Count Registers - 27 pub const PMEVCNTR27_EL0: Self = Self::System(0b11_011_1110_1011_011); /// Performance Monitors Event Count Registers - 28 pub const PMEVCNTR28_EL0: Self = Self::System(0b11_011_1110_1011_100); /// Performance Monitors Event Count Registers - 29 pub const PMEVCNTR29_EL0: Self = Self::System(0b11_011_1110_1011_101); /// Performance Monitors Event Count Registers - 30 pub const PMEVCNTR30_EL0: Self = Self::System(0b11_011_1110_1011_110); /// Performance Monitors Event Type Registers - 0 pub const PMEVTYPER0_EL0: Self = Self::System(0b11_011_1110_1100_000); /// Performance Monitors Event Type Registers - 1 pub const PMEVTYPER1_EL0: Self = Self::System(0b11_011_1110_1100_001); /// Performance Monitors Event Type Registers - 2 pub const PMEVTYPER2_EL0: Self = Self::System(0b11_011_1110_1100_010); /// Performance Monitors Event Type Registers - 3 pub const PMEVTYPER3_EL0: Self = Self::System(0b11_011_1110_1100_011); /// Performance Monitors Event Type Registers - 4 pub const PMEVTYPER4_EL0: Self = Self::System(0b11_011_1110_1100_100); /// Performance Monitors Event Type Registers - 5 pub const PMEVTYPER5_EL0: Self = Self::System(0b11_011_1110_1100_101); /// Performance Monitors Event Type Registers - 6 pub const PMEVTYPER6_EL0: Self = Self::System(0b11_011_1110_1100_110); /// Performance Monitors Event Type Registers - 7 pub const PMEVTYPER7_EL0: Self = Self::System(0b11_011_1110_1100_111); /// Performance Monitors Event Type Registers - 8 pub const PMEVTYPER8_EL0: Self = Self::System(0b11_011_1110_1101_000); /// Performance Monitors Event Type Registers - 9 pub const PMEVTYPER9_EL0: Self = Self::System(0b11_011_1110_1101_001); /// Performance Monitors Event Type Registers - 10 pub const PMEVTYPER10_EL0: Self = Self::System(0b11_011_1110_1101_010); /// Performance Monitors Event Type Registers - 11 pub const PMEVTYPER11_EL0: Self = Self::System(0b11_011_1110_1101_011); /// Performance Monitors Event Type Registers - 12 pub const PMEVTYPER12_EL0: Self = Self::System(0b11_011_1110_1101_100); /// Performance Monitors Event Type Registers - 13 pub const PMEVTYPER13_EL0: Self = Self::System(0b11_011_1110_1101_101); /// Performance Monitors Event Type Registers - 14 pub const PMEVTYPER14_EL0: Self = Self::System(0b11_011_1110_1101_110); /// Performance Monitors Event Type Registers - 15 pub const PMEVTYPER15_EL0: Self = Self::System(0b11_011_1110_1101_111); /// Performance Monitors Event Type Registers - 16 pub const PMEVTYPER16_EL0: Self = Self::System(0b11_011_1110_1110_000); /// Performance Monitors Event Type Registers - 17 pub const PMEVTYPER17_EL0: Self = Self::System(0b11_011_1110_1110_001); /// Performance Monitors Event Type Registers - 18 pub const PMEVTYPER18_EL0: Self = Self::System(0b11_011_1110_1110_010); /// Performance Monitors Event Type Registers - 19 pub const PMEVTYPER19_EL0: Self = Self::System(0b11_011_1110_1110_011); /// Performance Monitors Event Type Registers - 20 pub const PMEVTYPER20_EL0: Self = Self::System(0b11_011_1110_1110_100); /// Performance Monitors Event Type Registers - 21 pub const PMEVTYPER21_EL0: Self = Self::System(0b11_011_1110_1110_101); /// Performance Monitors Event Type Registers - 22 pub const PMEVTYPER22_EL0: Self = Self::System(0b11_011_1110_1110_110); /// Performance Monitors Event Type Registers - 23 pub const PMEVTYPER23_EL0: Self = Self::System(0b11_011_1110_1110_111); /// Performance Monitors Event Type Registers - 24 pub const PMEVTYPER24_EL0: Self = Self::System(0b11_011_1110_1111_000); /// Performance Monitors Event Type Registers - 25 pub const PMEVTYPER25_EL0: Self = Self::System(0b11_011_1110_1111_001); /// Performance Monitors Event Type Registers - 26 pub const PMEVTYPER26_EL0: Self = Self::System(0b11_011_1110_1111_010); /// Performance Monitors Event Type Registers - 27 pub const PMEVTYPER27_EL0: Self = Self::System(0b11_011_1110_1111_011); /// Performance Monitors Event Type Registers - 28 pub const PMEVTYPER28_EL0: Self = Self::System(0b11_011_1110_1111_100); /// Performance Monitors Event Type Registers - 29 pub const PMEVTYPER29_EL0: Self = Self::System(0b11_011_1110_1111_101); /// Performance Monitors Event Type Registers - 30 pub const PMEVTYPER30_EL0: Self = Self::System(0b11_011_1110_1111_110); /// Performance Monitors Cycle Count Filter Register pub const PMCCFILTR_EL0: Self = Self::System(0b11_011_1110_1111_111); /// Virtualization Processor ID Register pub const VPIDR_EL2: Self = Self::System(0b11_100_0000_0000_000); /// Virtualization Multiprocessor ID Register pub const VMPIDR_EL2: Self = Self::System(0b11_100_0000_0000_101); /// System Control Register (EL2) pub const SCTLR_EL2: Self = Self::System(0b11_100_0001_0000_000); /// Auxiliary Control Register (EL2) pub const ACTLR_EL2: Self = Self::System(0b11_100_0001_0000_001); /// Hypervisor Configuration Register pub const HCR_EL2: Self = Self::System(0b11_100_0001_0001_000); /// Monitor Debug Configuration Register (EL2) pub const MDCR_EL2: Self = Self::System(0b11_100_0001_0001_001); /// Architectural Feature Trap Register (EL2) pub const CPTR_EL2: Self = Self::System(0b11_100_0001_0001_010); /// Hypervisor System Trap Register pub const HSTR_EL2: Self = Self::System(0b11_100_0001_0001_011); /// Hypervisor Fine-Grained Read Trap Register pub const HFGRTR_EL2: Self = Self::System(0b11_100_0001_0001_100); /// Hypervisor Fine-Grained Write Trap Register pub const HFGWTR_EL2: Self = Self::System(0b11_100_0001_0001_101); /// Hypervisor Fine-Grained Instruction Trap Register pub const HFGITR_EL2: Self = Self::System(0b11_100_0001_0001_110); /// Hypervisor Auxiliary Control Register pub const HACR_EL2: Self = Self::System(0b11_100_0001_0001_111); /// SVE Control Register (EL2) pub const ZCR_EL2: Self = Self::System(0b11_100_0001_0010_000); /// Trace Filter Control Register (EL2) pub const TRFCR_EL2: Self = Self::System(0b11_100_0001_0010_001); /// Extended Hypervisor Configuration Register pub const HCRX_EL2: Self = Self::System(0b11_100_0001_0010_010); /// Streaming Mode Priority Mapping Register pub const SMPRIMAP_EL2: Self = Self::System(0b11_100_0001_0010_101); /// SME Control Register (EL2) pub const SMCR_EL2: Self = Self::System(0b11_100_0001_0010_110); /// AArch32 Secure Debug Enable Register pub const SDER32_EL2: Self = Self::System(0b11_100_0001_0011_001); /// Translation Table Base Register 0 (EL2) pub const TTBR0_EL2: Self = Self::System(0b11_100_0010_0000_000); /// Translation Table Base Register 1 (EL2) pub const TTBR1_EL2: Self = Self::System(0b11_100_0010_0000_001); /// Translation Control Register (EL2) pub const TCR_EL2: Self = Self::System(0b11_100_0010_0000_010); /// Virtualization Translation Table Base Register pub const VTTBR_EL2: Self = Self::System(0b11_100_0010_0001_000); /// Virtualization Translation Control Register pub const VTCR_EL2: Self = Self::System(0b11_100_0010_0001_010); /// Virtual Nested Control Register pub const VNCR_EL2: Self = Self::System(0b11_100_0010_0010_000); /// Virtualization Secure Translation Table Base Register pub const VSTTBR_EL2: Self = Self::System(0b11_100_0010_0110_000); /// Virtualization Secure Translation Control Register pub const VSTCR_EL2: Self = Self::System(0b11_100_0010_0110_010); /// Domain Access Control Register pub const DACR32_EL2: Self = Self::System(0b11_100_0011_0000_000); /// Hypervisor Debug Fine-Grained Read Trap Register pub const HDFGRTR_EL2: Self = Self::System(0b11_100_0011_0001_100); /// Hypervisor Debug Fine-Grained Write Trap Register pub const HDFGWTR_EL2: Self = Self::System(0b11_100_0011_0001_101); /// Hypervisor Activity Monitors Fine-Grained Read Trap Register pub const HAFGRTR_EL2: Self = Self::System(0b11_100_0011_0001_110); /// Saved Program Status Register (EL2) pub const SPSR_EL2: Self = Self::System(0b11_100_0100_0000_000); /// Exception Link Register (EL2) pub const ELR_EL2: Self = Self::System(0b11_100_0100_0000_001); /// Stack Pointer (EL1) pub const SP_EL1: Self = Self::System(0b11_100_0100_0001_000); /// Saved Program Status Register (IRQ Mode) pub const SPSR_IRQ: Self = Self::System(0b11_100_0100_0011_000); /// Saved Program Status Register (Abort Mode) pub const SPSR_ABT: Self = Self::System(0b11_100_0100_0011_001); /// Saved Program Status Register (Undefined Mode) pub const SPSR_UND: Self = Self::System(0b11_100_0100_0011_010); /// Saved Program Status Register (FIQ Mode) pub const SPSR_FIQ: Self = Self::System(0b11_100_0100_0011_011); /// Instruction Fault Status Register (EL2) pub const IFSR32_EL2: Self = Self::System(0b11_100_0101_0000_001); /// Auxiliary Fault Status Register 0 (EL2) pub const AFSR0_EL2: Self = Self::System(0b11_100_0101_0001_000); /// Auxiliary Fault Status Register 1 (EL2) pub const AFSR1_EL2: Self = Self::System(0b11_100_0101_0001_001); /// Exception Syndrome Register (EL2) pub const ESR_EL2: Self = Self::System(0b11_100_0101_0010_000); /// Virtual SError Exception Syndrome Register pub const VSESR_EL2: Self = Self::System(0b11_100_0101_0010_011); /// Floating-Point Exception Control Register pub const FPEXC32_EL2: Self = Self::System(0b11_100_0101_0011_000); /// Tag Fault Status Register (EL2) pub const TFSR_EL2: Self = Self::System(0b11_100_0101_0110_000); /// Fault Address Register (EL2) pub const FAR_EL2: Self = Self::System(0b11_100_0110_0000_000); /// Hypervisor IPA Fault Address Register pub const HPFAR_EL2: Self = Self::System(0b11_100_0110_0000_100); /// Statistical Profiling Control Register (EL2) pub const PMSCR_EL2: Self = Self::System(0b11_100_1001_1001_000); /// Memory Attribute Indirection Register (EL2) pub const MAIR_EL2: Self = Self::System(0b11_100_1010_0010_000); /// Auxiliary Memory Attribute Indirection Register (EL2) pub const AMAIR_EL2: Self = Self::System(0b11_100_1010_0011_000); /// MPAM Hypervisor Control Register (EL2) pub const MPAMHCR_EL2: Self = Self::System(0b11_100_1010_0100_000); /// MPAM Virtual Partition Mapping Valid Register pub const MPAMVPMV_EL2: Self = Self::System(0b11_100_1010_0100_001); /// MPAM2 Register (EL2) pub const MPAM2_EL2: Self = Self::System(0b11_100_1010_0101_000); /// MPAM Virtual PARTID Mapping Register 0 pub const MPAMVPM0_EL2: Self = Self::System(0b11_100_1010_0110_000); /// MPAM Virtual PARTID Mapping Register 1 pub const MPAMVPM1_EL2: Self = Self::System(0b11_100_1010_0110_001); /// MPAM Virtual PARTID Mapping Register 2 pub const MPAMVPM2_EL2: Self = Self::System(0b11_100_1010_0110_010); /// MPAM Virtual PARTID Mapping Register 3 pub const MPAMVPM3_EL2: Self = Self::System(0b11_100_1010_0110_011); /// MPAM Virtual PARTID Mapping Register 4 pub const MPAMVPM4_EL2: Self = Self::System(0b11_100_1010_0110_100); /// MPAM Virtual PARTID Mapping Register 5 pub const MPAMVPM5_EL2: Self = Self::System(0b11_100_1010_0110_101); /// MPAM Virtual PARTID Mapping Register 6 pub const MPAMVPM6_EL2: Self = Self::System(0b11_100_1010_0110_110); /// MPAM Virtual PARTID Mapping Register 7 pub const MPAMVPM7_EL2: Self = Self::System(0b11_100_1010_0110_111); /// Vector Base Address Register (EL2) pub const VBAR_EL2: Self = Self::System(0b11_100_1100_0000_000); /// Reset Vector Base Address Register (if EL3 Not Implemented) pub const RVBAR_EL2: Self = Self::System(0b11_100_1100_0000_001); /// Reset Management Register (EL2) pub const RMR_EL2: Self = Self::System(0b11_100_1100_0000_010); /// Virtual Deferred Interrupt Status Register pub const VDISR_EL2: Self = Self::System(0b11_100_1100_0001_001); /// Interrupt Controller Hyp Active Priorities Group 0 Registers - 0 pub const ICH_AP0R0_EL2: Self = Self::System(0b11_100_1100_1000_000); /// Interrupt Controller Hyp Active Priorities Group 0 Registers - 1 pub const ICH_AP0R1_EL2: Self = Self::System(0b11_100_1100_1000_001); /// Interrupt Controller Hyp Active Priorities Group 0 Registers - 2 pub const ICH_AP0R2_EL2: Self = Self::System(0b11_100_1100_1000_010); /// Interrupt Controller Hyp Active Priorities Group 0 Registers - 3 pub const ICH_AP0R3_EL2: Self = Self::System(0b11_100_1100_1000_011); /// Interrupt Controller Hyp Active Priorities Group 1 Registers - 0 pub const ICH_AP1R0_EL2: Self = Self::System(0b11_100_1100_1001_000); /// Interrupt Controller Hyp Active Priorities Group 1 Registers - 1 pub const ICH_AP1R1_EL2: Self = Self::System(0b11_100_1100_1001_001); /// Interrupt Controller Hyp Active Priorities Group 1 Registers - 2 pub const ICH_AP1R2_EL2: Self = Self::System(0b11_100_1100_1001_010); /// Interrupt Controller Hyp Active Priorities Group 1 Registers - 3 pub const ICH_AP1R3_EL2: Self = Self::System(0b11_100_1100_1001_011); /// Interrupt Controller System Register Enable Register (EL2) pub const ICC_SRE_EL2: Self = Self::System(0b11_100_1100_1001_101); /// Interrupt Controller Hyp Control Register pub const ICH_HCR_EL2: Self = Self::System(0b11_100_1100_1011_000); /// Interrupt Controller VGIC Type Register pub const ICH_VTR_EL2: Self = Self::System(0b11_100_1100_1011_001); /// Interrupt Controller Maintenance Interrupt State Register pub const ICH_MISR_EL2: Self = Self::System(0b11_100_1100_1011_010); /// Interrupt Controller End Of Interrupt Status Register pub const ICH_EISR_EL2: Self = Self::System(0b11_100_1100_1011_011); /// Interrupt Controller Empty List Register Status Register pub const ICH_ELRSR_EL2: Self = Self::System(0b11_100_1100_1011_101); /// Interrupt Controller Virtual Machine Control Register pub const ICH_VMCR_EL2: Self = Self::System(0b11_100_1100_1011_111); /// Interrupt Controller List Registers - 0 pub const ICH_LR0_EL2: Self = Self::System(0b11_100_1100_1100_000); /// Interrupt Controller List Registers - 1 pub const ICH_LR1_EL2: Self = Self::System(0b11_100_1100_1100_001); /// Interrupt Controller List Registers - 2 pub const ICH_LR2_EL2: Self = Self::System(0b11_100_1100_1100_010); /// Interrupt Controller List Registers - 3 pub const ICH_LR3_EL2: Self = Self::System(0b11_100_1100_1100_011); /// Interrupt Controller List Registers - 4 pub const ICH_LR4_EL2: Self = Self::System(0b11_100_1100_1100_100); /// Interrupt Controller List Registers - 5 pub const ICH_LR5_EL2: Self = Self::System(0b11_100_1100_1100_101); /// Interrupt Controller List Registers - 6 pub const ICH_LR6_EL2: Self = Self::System(0b11_100_1100_1100_110); /// Interrupt Controller List Registers - 7 pub const ICH_LR7_EL2: Self = Self::System(0b11_100_1100_1100_111); /// Interrupt Controller List Registers - 8 pub const ICH_LR8_EL2: Self = Self::System(0b11_100_1100_1101_000); /// Interrupt Controller List Registers - 9 pub const ICH_LR9_EL2: Self = Self::System(0b11_100_1100_1101_001); /// Interrupt Controller List Registers - 10 pub const ICH_LR10_EL2: Self = Self::System(0b11_100_1100_1101_010); /// Interrupt Controller List Registers - 11 pub const ICH_LR11_EL2: Self = Self::System(0b11_100_1100_1101_011); /// Interrupt Controller List Registers - 12 pub const ICH_LR12_EL2: Self = Self::System(0b11_100_1100_1101_100); /// Interrupt Controller List Registers - 13 pub const ICH_LR13_EL2: Self = Self::System(0b11_100_1100_1101_101); /// Interrupt Controller List Registers - 14 pub const ICH_LR14_EL2: Self = Self::System(0b11_100_1100_1101_110); /// Interrupt Controller List Registers - 15 pub const ICH_LR15_EL2: Self = Self::System(0b11_100_1100_1101_111); /// Context ID Register (EL2) pub const CONTEXTIDR_EL2: Self = Self::System(0b11_100_1101_0000_001); /// EL2 Software Thread ID Register pub const TPIDR_EL2: Self = Self::System(0b11_100_1101_0000_010); /// EL2 Read/Write Software Context Number pub const SCXTNUM_EL2: Self = Self::System(0b11_100_1101_0000_111); /// Activity Monitors Event Counter Virtual Offset Registers 0 - 0 pub const AMEVCNTVOFF00_EL2: Self = Self::System(0b11_100_1101_1000_000); /// Activity Monitors Event Counter Virtual Offset Registers 0 - 1 pub const AMEVCNTVOFF01_EL2: Self = Self::System(0b11_100_1101_1000_001); /// Activity Monitors Event Counter Virtual Offset Registers 0 - 2 pub const AMEVCNTVOFF02_EL2: Self = Self::System(0b11_100_1101_1000_010); /// Activity Monitors Event Counter Virtual Offset Registers 0 - 3 pub const AMEVCNTVOFF03_EL2: Self = Self::System(0b11_100_1101_1000_011); /// Activity Monitors Event Counter Virtual Offset Registers 0 - 4 pub const AMEVCNTVOFF04_EL2: Self = Self::System(0b11_100_1101_1000_100); /// Activity Monitors Event Counter Virtual Offset Registers 0 - 5 pub const AMEVCNTVOFF05_EL2: Self = Self::System(0b11_100_1101_1000_101); /// Activity Monitors Event Counter Virtual Offset Registers 0 - 6 pub const AMEVCNTVOFF06_EL2: Self = Self::System(0b11_100_1101_1000_110); /// Activity Monitors Event Counter Virtual Offset Registers 0 - 7 pub const AMEVCNTVOFF07_EL2: Self = Self::System(0b11_100_1101_1000_111); /// Activity Monitors Event Counter Virtual Offset Registers 0 - 8 pub const AMEVCNTVOFF08_EL2: Self = Self::System(0b11_100_1101_1001_000); /// Activity Monitors Event Counter Virtual Offset Registers 0 - 9 pub const AMEVCNTVOFF09_EL2: Self = Self::System(0b11_100_1101_1001_001); /// Activity Monitors Event Counter Virtual Offset Registers 0 - 10 pub const AMEVCNTVOFF010_EL2: Self = Self::System(0b11_100_1101_1001_010); /// Activity Monitors Event Counter Virtual Offset Registers 0 - 11 pub const AMEVCNTVOFF011_EL2: Self = Self::System(0b11_100_1101_1001_011); /// Activity Monitors Event Counter Virtual Offset Registers 0 - 12 pub const AMEVCNTVOFF012_EL2: Self = Self::System(0b11_100_1101_1001_100); /// Activity Monitors Event Counter Virtual Offset Registers 0 - 13 pub const AMEVCNTVOFF013_EL2: Self = Self::System(0b11_100_1101_1001_101); /// Activity Monitors Event Counter Virtual Offset Registers 0 - 14 pub const AMEVCNTVOFF014_EL2: Self = Self::System(0b11_100_1101_1001_110); /// Activity Monitors Event Counter Virtual Offset Registers 0 - 15 pub const AMEVCNTVOFF015_EL2: Self = Self::System(0b11_100_1101_1001_111); /// Activity Monitors Event Counter Virtual Offset Registers 1 - 0 pub const AMEVCNTVOFF10_EL2: Self = Self::System(0b11_100_1101_1010_000); /// Activity Monitors Event Counter Virtual Offset Registers 1 - 1 pub const AMEVCNTVOFF11_EL2: Self = Self::System(0b11_100_1101_1010_001); /// Activity Monitors Event Counter Virtual Offset Registers 1 - 2 pub const AMEVCNTVOFF12_EL2: Self = Self::System(0b11_100_1101_1010_010); /// Activity Monitors Event Counter Virtual Offset Registers 1 - 3 pub const AMEVCNTVOFF13_EL2: Self = Self::System(0b11_100_1101_1010_011); /// Activity Monitors Event Counter Virtual Offset Registers 1 - 4 pub const AMEVCNTVOFF14_EL2: Self = Self::System(0b11_100_1101_1010_100); /// Activity Monitors Event Counter Virtual Offset Registers 1 - 5 pub const AMEVCNTVOFF15_EL2: Self = Self::System(0b11_100_1101_1010_101); /// Activity Monitors Event Counter Virtual Offset Registers 1 - 6 pub const AMEVCNTVOFF16_EL2: Self = Self::System(0b11_100_1101_1010_110); /// Activity Monitors Event Counter Virtual Offset Registers 1 - 7 pub const AMEVCNTVOFF17_EL2: Self = Self::System(0b11_100_1101_1010_111); /// Activity Monitors Event Counter Virtual Offset Registers 1 - 8 pub const AMEVCNTVOFF18_EL2: Self = Self::System(0b11_100_1101_1011_000); /// Activity Monitors Event Counter Virtual Offset Registers 1 - 9 pub const AMEVCNTVOFF19_EL2: Self = Self::System(0b11_100_1101_1011_001); /// Activity Monitors Event Counter Virtual Offset Registers 1 - 10 pub const AMEVCNTVOFF110_EL2: Self = Self::System(0b11_100_1101_1011_010); /// Activity Monitors Event Counter Virtual Offset Registers 1 - 11 pub const AMEVCNTVOFF111_EL2: Self = Self::System(0b11_100_1101_1011_011); /// Activity Monitors Event Counter Virtual Offset Registers 1 - 12 pub const AMEVCNTVOFF112_EL2: Self = Self::System(0b11_100_1101_1011_100); /// Activity Monitors Event Counter Virtual Offset Registers 1 - 13 pub const AMEVCNTVOFF113_EL2: Self = Self::System(0b11_100_1101_1011_101); /// Activity Monitors Event Counter Virtual Offset Registers 1 - 14 pub const AMEVCNTVOFF114_EL2: Self = Self::System(0b11_100_1101_1011_110); /// Activity Monitors Event Counter Virtual Offset Registers 1 - 15 pub const AMEVCNTVOFF115_EL2: Self = Self::System(0b11_100_1101_1011_111); /// Counter-timer Virtual Offset Register pub const CNTVOFF_EL2: Self = Self::System(0b11_100_1110_0000_011); /// Counter-timer Physical Offset Register pub const CNTPOFF_EL2: Self = Self::System(0b11_100_1110_0000_110); /// Counter-timer Hypervisor Control Register pub const CNTHCTL_EL2: Self = Self::System(0b11_100_1110_0001_000); /// Counter-timer Physical Timer TimerValue Register (EL2) pub const CNTHP_TVAL_EL2: Self = Self::System(0b11_100_1110_0010_000); /// Counter-timer Hypervisor Physical Timer Control Register pub const CNTHP_CTL_EL2: Self = Self::System(0b11_100_1110_0010_001); /// Counter-timer Physical Timer CompareValue Register (EL2) pub const CNTHP_CVAL_EL2: Self = Self::System(0b11_100_1110_0010_010); /// Counter-timer Virtual Timer TimerValue Register (EL2) pub const CNTHV_TVAL_EL2: Self = Self::System(0b11_100_1110_0011_000); /// Counter-timer Virtual Timer Control Register (EL2) pub const CNTHV_CTL_EL2: Self = Self::System(0b11_100_1110_0011_001); /// Counter-timer Virtual Timer CompareValue Register (EL2) pub const CNTHV_CVAL_EL2: Self = Self::System(0b11_100_1110_0011_010); /// Counter-timer Secure Virtual Timer TimerValue Register (EL2) pub const CNTHVS_TVAL_EL2: Self = Self::System(0b11_100_1110_0100_000); /// Counter-timer Secure Virtual Timer Control Register (EL2) pub const CNTHVS_CTL_EL2: Self = Self::System(0b11_100_1110_0100_001); /// Counter-timer Secure Virtual Timer CompareValue Register (EL2) pub const CNTHVS_CVAL_EL2: Self = Self::System(0b11_100_1110_0100_010); /// Counter-timer Secure Physical Timer TimerValue Register (EL2) pub const CNTHPS_TVAL_EL2: Self = Self::System(0b11_100_1110_0101_000); /// Counter-timer Secure Physical Timer Control Register (EL2) pub const CNTHPS_CTL_EL2: Self = Self::System(0b11_100_1110_0101_001); /// Counter-timer Secure Physical Timer CompareValue Register (EL2) pub const CNTHPS_CVAL_EL2: Self = Self::System(0b11_100_1110_0101_010); /// System Control Register (EL3) pub const SCTLR_EL3: Self = Self::System(0b11_110_0001_0000_000); /// Auxiliary Control Register (EL3) pub const ACTLR_EL3: Self = Self::System(0b11_110_0001_0000_001); /// Secure Configuration Register pub const SCR_EL3: Self = Self::System(0b11_110_0001_0001_000); /// AArch32 Secure Debug Enable Register pub const SDER32_EL3: Self = Self::System(0b11_110_0001_0001_001); /// Architectural Feature Trap Register (EL3) pub const CPTR_EL3: Self = Self::System(0b11_110_0001_0001_010); /// SVE Control Register (EL3) pub const ZCR_EL3: Self = Self::System(0b11_110_0001_0010_000); /// SME Control Register (EL3) pub const SMCR_EL3: Self = Self::System(0b11_110_0001_0010_110); /// Monitor Debug Configuration Register (EL3) pub const MDCR_EL3: Self = Self::System(0b11_110_0001_0011_001); /// Translation Table Base Register 0 (EL3) pub const TTBR0_EL3: Self = Self::System(0b11_110_0010_0000_000); /// Translation Control Register (EL3) pub const TCR_EL3: Self = Self::System(0b11_110_0010_0000_010); /// Granule Protection Table Base Register pub const GPTBR_EL3: Self = Self::System(0b11_110_0010_0001_100); /// Granule Protection Check Control Register (EL3) pub const GPCCR_EL3: Self = Self::System(0b11_110_0010_0001_110); /// Saved Program Status Register (EL3) pub const SPSR_EL3: Self = Self::System(0b11_110_0100_0000_000); /// Exception Link Register (EL3) pub const ELR_EL3: Self = Self::System(0b11_110_0100_0000_001); /// Stack Pointer (EL2) pub const SP_EL2: Self = Self::System(0b11_110_0100_0001_000); /// Auxiliary Fault Status Register 0 (EL3) pub const AFSR0_EL3: Self = Self::System(0b11_110_0101_0001_000); /// Auxiliary Fault Status Register 1 (EL3) pub const AFSR1_EL3: Self = Self::System(0b11_110_0101_0001_001); /// Exception Syndrome Register (EL3) pub const ESR_EL3: Self = Self::System(0b11_110_0101_0010_000); /// Tag Fault Status Register (EL3) pub const TFSR_EL3: Self = Self::System(0b11_110_0101_0110_000); /// Fault Address Register (EL3) pub const FAR_EL3: Self = Self::System(0b11_110_0110_0000_000); /// PA Fault Address Register pub const MFAR_EL3: Self = Self::System(0b11_110_0110_0000_101); /// Memory Attribute Indirection Register (EL3) pub const MAIR_EL3: Self = Self::System(0b11_110_1010_0010_000); /// Auxiliary Memory Attribute Indirection Register (EL3) pub const AMAIR_EL3: Self = Self::System(0b11_110_1010_0011_000); /// MPAM3 Register (EL3) pub const MPAM3_EL3: Self = Self::System(0b11_110_1010_0101_000); /// Vector Base Address Register (EL3) pub const VBAR_EL3: Self = Self::System(0b11_110_1100_0000_000); /// Reset Vector Base Address Register (if EL3 Implemented) pub const RVBAR_EL3: Self = Self::System(0b11_110_1100_0000_001); /// Reset Management Register (EL3) pub const RMR_EL3: Self = Self::System(0b11_110_1100_0000_010); /// Interrupt Controller Control Register (EL3) pub const ICC_CTLR_EL3: Self = Self::System(0b11_110_1100_1100_100); /// Interrupt Controller System Register Enable Register (EL3) pub const ICC_SRE_EL3: Self = Self::System(0b11_110_1100_1100_101); /// Interrupt Controller Interrupt Group 1 Enable Register (EL3) pub const ICC_IGRPEN1_EL3: Self = Self::System(0b11_110_1100_1100_111); /// EL3 Software Thread ID Register pub const TPIDR_EL3: Self = Self::System(0b11_110_1101_0000_010); /// EL3 Read/Write Software Context Number pub const SCXTNUM_EL3: Self = Self::System(0b11_110_1101_0000_111); /// Counter-timer Physical Secure Timer TimerValue Register pub const CNTPS_TVAL_EL1: Self = Self::System(0b11_111_1110_0010_000); /// Counter-timer Physical Secure Timer Control Register pub const CNTPS_CTL_EL1: Self = Self::System(0b11_111_1110_0010_001); /// Counter-timer Physical Secure Timer CompareValue Register pub const CNTPS_CVAL_EL1: Self = Self::System(0b11_111_1110_0010_010); /// OS Lock Data Transfer Register, Receive pub const OSDTRRX_EL1: Self = Self::System(0b10_000_0000_0000_010); /// Debug Breakpoint Value Registers - 0 pub const DBGBVR0_EL1: Self = Self::System(0b10_000_0000_0000_100); /// Debug Breakpoint Control Registers - 0 pub const DBGBCR0_EL1: Self = Self::System(0b10_000_0000_0000_101); /// Debug Watchpoint Value Registers - 0 pub const DBGWVR0_EL1: Self = Self::System(0b10_000_0000_0000_110); /// Debug Watchpoint Control Registers - 0 pub const DBGWCR0_EL1: Self = Self::System(0b10_000_0000_0000_111); /// Debug Breakpoint Value Registers - 1 pub const DBGBVR1_EL1: Self = Self::System(0b10_000_0000_0001_100); /// Debug Breakpoint Control Registers - 1 pub const DBGBCR1_EL1: Self = Self::System(0b10_000_0000_0001_101); /// Debug Watchpoint Value Registers - 1 pub const DBGWVR1_EL1: Self = Self::System(0b10_000_0000_0001_110); /// Debug Watchpoint Control Registers - 1 pub const DBGWCR1_EL1: Self = Self::System(0b10_000_0000_0001_111); /// Monitor DCC Interrupt Enable Register pub const MDCCINT_EL1: Self = Self::System(0b10_000_0000_0010_000); /// Monitor Debug System Control Register pub const MDSCR_EL1: Self = Self::System(0b10_000_0000_0010_010); /// Debug Breakpoint Value Registers - 2 pub const DBGBVR2_EL1: Self = Self::System(0b10_000_0000_0010_100); /// Debug Breakpoint Control Registers - 2 pub const DBGBCR2_EL1: Self = Self::System(0b10_000_0000_0010_101); /// Debug Watchpoint Value Registers - 2 pub const DBGWVR2_EL1: Self = Self::System(0b10_000_0000_0010_110); /// Debug Watchpoint Control Registers - 2 pub const DBGWCR2_EL1: Self = Self::System(0b10_000_0000_0010_111); /// OS Lock Data Transfer Register, Transmit pub const OSDTRTX_EL1: Self = Self::System(0b10_000_0000_0011_010); /// Debug Breakpoint Value Registers - 3 pub const DBGBVR3_EL1: Self = Self::System(0b10_000_0000_0011_100); /// Debug Breakpoint Control Registers - 3 pub const DBGBCR3_EL1: Self = Self::System(0b10_000_0000_0011_101); /// Debug Watchpoint Value Registers - 3 pub const DBGWVR3_EL1: Self = Self::System(0b10_000_0000_0011_110); /// Debug Watchpoint Control Registers - 3 pub const DBGWCR3_EL1: Self = Self::System(0b10_000_0000_0011_111); /// Debug Breakpoint Value Registers - 4 pub const DBGBVR4_EL1: Self = Self::System(0b10_000_0000_0100_100); /// Debug Breakpoint Control Registers - 4 pub const DBGBCR4_EL1: Self = Self::System(0b10_000_0000_0100_101); /// Debug Watchpoint Value Registers - 4 pub const DBGWVR4_EL1: Self = Self::System(0b10_000_0000_0100_110); /// Debug Watchpoint Control Registers - 4 pub const DBGWCR4_EL1: Self = Self::System(0b10_000_0000_0100_111); /// Debug Breakpoint Value Registers - 5 pub const DBGBVR5_EL1: Self = Self::System(0b10_000_0000_0101_100); /// Debug Breakpoint Control Registers - 5 pub const DBGBCR5_EL1: Self = Self::System(0b10_000_0000_0101_101); /// Debug Watchpoint Value Registers - 5 pub const DBGWVR5_EL1: Self = Self::System(0b10_000_0000_0101_110); /// Debug Watchpoint Control Registers - 5 pub const DBGWCR5_EL1: Self = Self::System(0b10_000_0000_0101_111); /// OS Lock Exception Catch Control Register pub const OSECCR_EL1: Self = Self::System(0b10_000_0000_0110_010); /// Debug Breakpoint Value Registers - 6 pub const DBGBVR6_EL1: Self = Self::System(0b10_000_0000_0110_100); /// Debug Breakpoint Control Registers - 6 pub const DBGBCR6_EL1: Self = Self::System(0b10_000_0000_0110_101); /// Debug Watchpoint Value Registers - 6 pub const DBGWVR6_EL1: Self = Self::System(0b10_000_0000_0110_110); /// Debug Watchpoint Control Registers - 6 pub const DBGWCR6_EL1: Self = Self::System(0b10_000_0000_0110_111); /// Debug Breakpoint Value Registers - 7 pub const DBGBVR7_EL1: Self = Self::System(0b10_000_0000_0111_100); /// Debug Breakpoint Control Registers - 7 pub const DBGBCR7_EL1: Self = Self::System(0b10_000_0000_0111_101); /// Debug Watchpoint Value Registers - 7 pub const DBGWVR7_EL1: Self = Self::System(0b10_000_0000_0111_110); /// Debug Watchpoint Control Registers - 7 pub const DBGWCR7_EL1: Self = Self::System(0b10_000_0000_0111_111); /// Debug Breakpoint Value Registers - 8 pub const DBGBVR8_EL1: Self = Self::System(0b10_000_0000_1000_100); /// Debug Breakpoint Control Registers - 8 pub const DBGBCR8_EL1: Self = Self::System(0b10_000_0000_1000_101); /// Debug Watchpoint Value Registers - 8 pub const DBGWVR8_EL1: Self = Self::System(0b10_000_0000_1000_110); /// Debug Watchpoint Control Registers - 8 pub const DBGWCR8_EL1: Self = Self::System(0b10_000_0000_1000_111); /// Debug Breakpoint Value Registers - 9 pub const DBGBVR9_EL1: Self = Self::System(0b10_000_0000_1001_100); /// Debug Breakpoint Control Registers - 9 pub const DBGBCR9_EL1: Self = Self::System(0b10_000_0000_1001_101); /// Debug Watchpoint Value Registers - 9 pub const DBGWVR9_EL1: Self = Self::System(0b10_000_0000_1001_110); /// Debug Watchpoint Control Registers - 9 pub const DBGWCR9_EL1: Self = Self::System(0b10_000_0000_1001_111); /// Debug Breakpoint Value Registers - 10 pub const DBGBVR10_EL1: Self = Self::System(0b10_000_0000_1010_100); /// Debug Breakpoint Control Registers - 10 pub const DBGBCR10_EL1: Self = Self::System(0b10_000_0000_1010_101); /// Debug Watchpoint Value Registers - 10 pub const DBGWVR10_EL1: Self = Self::System(0b10_000_0000_1010_110); /// Debug Watchpoint Control Registers - 10 pub const DBGWCR10_EL1: Self = Self::System(0b10_000_0000_1010_111); /// Debug Breakpoint Value Registers - 11 pub const DBGBVR11_EL1: Self = Self::System(0b10_000_0000_1011_100); /// Debug Breakpoint Control Registers - 11 pub const DBGBCR11_EL1: Self = Self::System(0b10_000_0000_1011_101); /// Debug Watchpoint Value Registers - 11 pub const DBGWVR11_EL1: Self = Self::System(0b10_000_0000_1011_110); /// Debug Watchpoint Control Registers - 11 pub const DBGWCR11_EL1: Self = Self::System(0b10_000_0000_1011_111); /// Debug Breakpoint Value Registers - 12 pub const DBGBVR12_EL1: Self = Self::System(0b10_000_0000_1100_100); /// Debug Breakpoint Control Registers - 12 pub const DBGBCR12_EL1: Self = Self::System(0b10_000_0000_1100_101); /// Debug Watchpoint Value Registers - 12 pub const DBGWVR12_EL1: Self = Self::System(0b10_000_0000_1100_110); /// Debug Watchpoint Control Registers - 12 pub const DBGWCR12_EL1: Self = Self::System(0b10_000_0000_1100_111); /// Debug Breakpoint Value Registers - 13 pub const DBGBVR13_EL1: Self = Self::System(0b10_000_0000_1101_100); /// Debug Breakpoint Control Registers - 13 pub const DBGBCR13_EL1: Self = Self::System(0b10_000_0000_1101_101); /// Debug Watchpoint Value Registers - 13 pub const DBGWVR13_EL1: Self = Self::System(0b10_000_0000_1101_110); /// Debug Watchpoint Control Registers - 13 pub const DBGWCR13_EL1: Self = Self::System(0b10_000_0000_1101_111); /// Debug Breakpoint Value Registers - 14 pub const DBGBVR14_EL1: Self = Self::System(0b10_000_0000_1110_100); /// Debug Breakpoint Control Registers - 14 pub const DBGBCR14_EL1: Self = Self::System(0b10_000_0000_1110_101); /// Debug Watchpoint Value Registers - 14 pub const DBGWVR14_EL1: Self = Self::System(0b10_000_0000_1110_110); /// Debug Watchpoint Control Registers - 14 pub const DBGWCR14_EL1: Self = Self::System(0b10_000_0000_1110_111); /// Debug Breakpoint Value Registers - 15 pub const DBGBVR15_EL1: Self = Self::System(0b10_000_0000_1111_100); /// Debug Breakpoint Control Registers - 15 pub const DBGBCR15_EL1: Self = Self::System(0b10_000_0000_1111_101); /// Debug Watchpoint Value Registers - 15 pub const DBGWVR15_EL1: Self = Self::System(0b10_000_0000_1111_110); /// Debug Watchpoint Control Registers - 15 pub const DBGWCR15_EL1: Self = Self::System(0b10_000_0000_1111_111); /// Monitor Debug ROM Address Register pub const MDRAR_EL1: Self = Self::System(0b10_000_0001_0000_000); /// OS Lock Access Register pub const OSLAR_EL1: Self = Self::System(0b10_000_0001_0000_100); /// OS Lock Status Register pub const OSLSR_EL1: Self = Self::System(0b10_000_0001_0001_100); /// OS Double Lock Register pub const OSDLR_EL1: Self = Self::System(0b10_000_0001_0011_100); /// Debug Power Control Register pub const DBGPRCR_EL1: Self = Self::System(0b10_000_0001_0100_100); /// Debug CLAIM Tag Set Register pub const DBGCLAIMSET_EL1: Self = Self::System(0b10_000_0111_1000_110); /// Debug CLAIM Tag Clear Register pub const DBGCLAIMCLR_EL1: Self = Self::System(0b10_000_0111_1001_110); /// Debug Authentication Status Register pub const DBGAUTHSTATUS_EL1: Self = Self::System(0b10_000_0111_1110_110); /// Trace ID Register pub const TRCTRACEIDR: Self = Self::System(0b10_001_0000_0000_001); /// ViewInst Main Control Register pub const TRCVICTLR: Self = Self::System(0b10_001_0000_0000_010); /// Sequencer State Transition Control Register 0 pub const TRCSEQEVR0: Self = Self::System(0b10_001_0000_0000_100); /// Counter Reload Value Register 0 pub const TRCCNTRLDVR0: Self = Self::System(0b10_001_0000_0000_101); /// ID Register 8 pub const TRCIDR8: Self = Self::System(0b10_001_0000_0000_110); /// IMP DEF Register 0 pub const TRCIMSPEC0: Self = Self::System(0b10_001_0000_0000_111); /// Programming Control Register pub const TRCPRGCTLR: Self = Self::System(0b10_001_0000_0001_000); /// Q Element Control Register pub const TRCQCTLR: Self = Self::System(0b10_001_0000_0001_001); /// ViewInst Include/Exclude Control Register pub const TRCVIIECTLR: Self = Self::System(0b10_001_0000_0001_010); /// Sequencer State Transition Control Register 1 pub const TRCSEQEVR1: Self = Self::System(0b10_001_0000_0001_100); /// Counter Reload Value Register 1 pub const TRCCNTRLDVR1: Self = Self::System(0b10_001_0000_0001_101); /// ID Register 9 pub const TRCIDR9: Self = Self::System(0b10_001_0000_0001_110); /// IMP DEF Register 1 pub const TRCIMSPEC1: Self = Self::System(0b10_001_0000_0001_111); /// ViewInst Start/Stop Control Register pub const TRCVISSCTLR: Self = Self::System(0b10_001_0000_0010_010); /// Sequencer State Transition Control Register 2 pub const TRCSEQEVR2: Self = Self::System(0b10_001_0000_0010_100); /// Counter Reload Value Register 2 pub const TRCCNTRLDVR2: Self = Self::System(0b10_001_0000_0010_101); /// ID Register 10 pub const TRCIDR10: Self = Self::System(0b10_001_0000_0010_110); /// IMP DEF Register 2 pub const TRCIMSPEC2: Self = Self::System(0b10_001_0000_0010_111); /// Trace Status Register pub const TRCSTATR: Self = Self::System(0b10_001_0000_0011_000); /// ViewInst Start/Stop PE Comparator Control Register pub const TRCVIPCSSCTLR: Self = Self::System(0b10_001_0000_0011_010); /// Counter Reload Value Register 3 pub const TRCCNTRLDVR3: Self = Self::System(0b10_001_0000_0011_101); /// ID Register 11 pub const TRCIDR11: Self = Self::System(0b10_001_0000_0011_110); /// IMP DEF Register 3 pub const TRCIMSPEC3: Self = Self::System(0b10_001_0000_0011_111); /// Trace Configuration Register pub const TRCCONFIGR: Self = Self::System(0b10_001_0000_0100_000); /// Counter Control Register 0 pub const TRCCNTCTLR0: Self = Self::System(0b10_001_0000_0100_101); /// ID Register 12 pub const TRCIDR12: Self = Self::System(0b10_001_0000_0100_110); /// IMP DEF Register 4 pub const TRCIMSPEC4: Self = Self::System(0b10_001_0000_0100_111); /// Counter Control Register 1 pub const TRCCNTCTLR1: Self = Self::System(0b10_001_0000_0101_101); /// ID Register 13 pub const TRCIDR13: Self = Self::System(0b10_001_0000_0101_110); /// IMP DEF Register 5 pub const TRCIMSPEC5: Self = Self::System(0b10_001_0000_0101_111); /// Auxiliary Control Register pub const TRCAUXCTLR: Self = Self::System(0b10_001_0000_0110_000); /// Sequencer Reset Control Register pub const TRCSEQRSTEVR: Self = Self::System(0b10_001_0000_0110_100); /// Counter Control Register 2 pub const TRCCNTCTLR2: Self = Self::System(0b10_001_0000_0110_101); /// IMP DEF Register 6 pub const TRCIMSPEC6: Self = Self::System(0b10_001_0000_0110_111); /// Sequencer State Register pub const TRCSEQSTR: Self = Self::System(0b10_001_0000_0111_100); /// Counter Control Register 3 pub const TRCCNTCTLR3: Self = Self::System(0b10_001_0000_0111_101); /// IMP DEF Register 7 pub const TRCIMSPEC7: Self = Self::System(0b10_001_0000_0111_111); /// Event Control 0 Register pub const TRCEVENTCTL0R: Self = Self::System(0b10_001_0000_1000_000); /// External Input Select Register 0 pub const TRCEXTINSELR0: Self = Self::System(0b10_001_0000_1000_100); /// Counter Value Register 0 pub const TRCCNTVR0: Self = Self::System(0b10_001_0000_1000_101); /// ID Register 0 pub const TRCIDR0: Self = Self::System(0b10_001_0000_1000_111); /// Event Control 1 Register pub const TRCEVENTCTL1R: Self = Self::System(0b10_001_0000_1001_000); /// External Input Select Register 1 pub const TRCEXTINSELR1: Self = Self::System(0b10_001_0000_1001_100); /// Counter Value Register 1 pub const TRCCNTVR1: Self = Self::System(0b10_001_0000_1001_101); /// ID Register 1 pub const TRCIDR1: Self = Self::System(0b10_001_0000_1001_111); /// Resources Status Register pub const TRCRSR: Self = Self::System(0b10_001_0000_1010_000); /// External Input Select Register 2 pub const TRCEXTINSELR2: Self = Self::System(0b10_001_0000_1010_100); /// Counter Value Register 2 pub const TRCCNTVR2: Self = Self::System(0b10_001_0000_1010_101); /// ID Register 2 pub const TRCIDR2: Self = Self::System(0b10_001_0000_1010_111); /// Stall Control Register pub const TRCSTALLCTLR: Self = Self::System(0b10_001_0000_1011_000); /// External Input Select Register 3 pub const TRCEXTINSELR3: Self = Self::System(0b10_001_0000_1011_100); /// Counter Value Register 3 pub const TRCCNTVR3: Self = Self::System(0b10_001_0000_1011_101); /// ID Register 3 pub const TRCIDR3: Self = Self::System(0b10_001_0000_1011_111); /// Timestamp Control Register pub const TRCTSCTLR: Self = Self::System(0b10_001_0000_1100_000); /// ID Register 4 pub const TRCIDR4: Self = Self::System(0b10_001_0000_1100_111); /// Synchronization Period Register pub const TRCSYNCPR: Self = Self::System(0b10_001_0000_1101_000); /// ID Register 5 pub const TRCIDR5: Self = Self::System(0b10_001_0000_1101_111); /// Cycle Count Control Register pub const TRCCCCTLR: Self = Self::System(0b10_001_0000_1110_000); /// ID Register 6 pub const TRCIDR6: Self = Self::System(0b10_001_0000_1110_111); /// Branch Broadcast Control Register pub const TRCBBCTLR: Self = Self::System(0b10_001_0000_1111_000); /// ID Register 7 pub const TRCIDR7: Self = Self::System(0b10_001_0000_1111_111); /// Resource Selection Control Register 16 pub const TRCRSCTLR16: Self = Self::System(0b10_001_0001_0000_001); /// Single-shot Comparator Control Register 0 pub const TRCSSCCR0: Self = Self::System(0b10_001_0001_0000_010); /// Single-shot Processing Element Comparator Input Control Register 0 pub const TRCSSPCICR0: Self = Self::System(0b10_001_0001_0000_011); /// Resource Selection Control Register 17 pub const TRCRSCTLR17: Self = Self::System(0b10_001_0001_0001_001); /// Single-shot Comparator Control Register 1 pub const TRCSSCCR1: Self = Self::System(0b10_001_0001_0001_010); /// Single-shot Processing Element Comparator Input Control Register 1 pub const TRCSSPCICR1: Self = Self::System(0b10_001_0001_0001_011); /// Trace OS Lock Status Register pub const TRCOSLSR: Self = Self::System(0b10_001_0001_0001_100); /// Resource Selection Control Register 2 pub const TRCRSCTLR2: Self = Self::System(0b10_001_0001_0010_000); /// Resource Selection Control Register 18 pub const TRCRSCTLR18: Self = Self::System(0b10_001_0001_0010_001); /// Single-shot Comparator Control Register 2 pub const TRCSSCCR2: Self = Self::System(0b10_001_0001_0010_010); /// Single-shot Processing Element Comparator Input Control Register 2 pub const TRCSSPCICR2: Self = Self::System(0b10_001_0001_0010_011); /// Resource Selection Control Register 3 pub const TRCRSCTLR3: Self = Self::System(0b10_001_0001_0011_000); /// Resource Selection Control Register 19 pub const TRCRSCTLR19: Self = Self::System(0b10_001_0001_0011_001); /// Single-shot Comparator Control Register 3 pub const TRCSSCCR3: Self = Self::System(0b10_001_0001_0011_010); /// Single-shot Processing Element Comparator Input Control Register 3 pub const TRCSSPCICR3: Self = Self::System(0b10_001_0001_0011_011); /// Resource Selection Control Register 4 pub const TRCRSCTLR4: Self = Self::System(0b10_001_0001_0100_000); /// Resource Selection Control Register 20 pub const TRCRSCTLR20: Self = Self::System(0b10_001_0001_0100_001); /// Single-shot Comparator Control Register 4 pub const TRCSSCCR4: Self = Self::System(0b10_001_0001_0100_010); /// Single-shot Processing Element Comparator Input Control Register 4 pub const TRCSSPCICR4: Self = Self::System(0b10_001_0001_0100_011); /// Resource Selection Control Register 5 pub const TRCRSCTLR5: Self = Self::System(0b10_001_0001_0101_000); /// Resource Selection Control Register 21 pub const TRCRSCTLR21: Self = Self::System(0b10_001_0001_0101_001); /// Single-shot Comparator Control Register 5 pub const TRCSSCCR5: Self = Self::System(0b10_001_0001_0101_010); /// Single-shot Processing Element Comparator Input Control Register 5 pub const TRCSSPCICR5: Self = Self::System(0b10_001_0001_0101_011); /// Resource Selection Control Register 6 pub const TRCRSCTLR6: Self = Self::System(0b10_001_0001_0110_000); /// Resource Selection Control Register 22 pub const TRCRSCTLR22: Self = Self::System(0b10_001_0001_0110_001); /// Single-shot Comparator Control Register 6 pub const TRCSSCCR6: Self = Self::System(0b10_001_0001_0110_010); /// Single-shot Processing Element Comparator Input Control Register 6 pub const TRCSSPCICR6: Self = Self::System(0b10_001_0001_0110_011); /// Resource Selection Control Register 7 pub const TRCRSCTLR7: Self = Self::System(0b10_001_0001_0111_000); /// Resource Selection Control Register 23 pub const TRCRSCTLR23: Self = Self::System(0b10_001_0001_0111_001); /// Single-shot Comparator Control Register 7 pub const TRCSSCCR7: Self = Self::System(0b10_001_0001_0111_010); /// Single-shot Processing Element Comparator Input Control Register 7 pub const TRCSSPCICR7: Self = Self::System(0b10_001_0001_0111_011); /// Resource Selection Control Register 8 pub const TRCRSCTLR8: Self = Self::System(0b10_001_0001_1000_000); /// Resource Selection Control Register 24 pub const TRCRSCTLR24: Self = Self::System(0b10_001_0001_1000_001); /// Single-shot Comparator Control Status Register 0 pub const TRCSSCSR0: Self = Self::System(0b10_001_0001_1000_010); /// Resource Selection Control Register 9 pub const TRCRSCTLR9: Self = Self::System(0b10_001_0001_1001_000); /// Resource Selection Control Register 25 pub const TRCRSCTLR25: Self = Self::System(0b10_001_0001_1001_001); /// Single-shot Comparator Control Status Register 1 pub const TRCSSCSR1: Self = Self::System(0b10_001_0001_1001_010); /// Resource Selection Control Register 10 pub const TRCRSCTLR10: Self = Self::System(0b10_001_0001_1010_000); /// Resource Selection Control Register 26 pub const TRCRSCTLR26: Self = Self::System(0b10_001_0001_1010_001); /// Single-shot Comparator Control Status Register 2 pub const TRCSSCSR2: Self = Self::System(0b10_001_0001_1010_010); /// Resource Selection Control Register 11 pub const TRCRSCTLR11: Self = Self::System(0b10_001_0001_1011_000); /// Resource Selection Control Register 27 pub const TRCRSCTLR27: Self = Self::System(0b10_001_0001_1011_001); /// Single-shot Comparator Control Status Register 3 pub const TRCSSCSR3: Self = Self::System(0b10_001_0001_1011_010); /// Resource Selection Control Register 12 pub const TRCRSCTLR12: Self = Self::System(0b10_001_0001_1100_000); /// Resource Selection Control Register 28 pub const TRCRSCTLR28: Self = Self::System(0b10_001_0001_1100_001); /// Single-shot Comparator Control Status Register 4 pub const TRCSSCSR4: Self = Self::System(0b10_001_0001_1100_010); /// Resource Selection Control Register 13 pub const TRCRSCTLR13: Self = Self::System(0b10_001_0001_1101_000); /// Resource Selection Control Register 29 pub const TRCRSCTLR29: Self = Self::System(0b10_001_0001_1101_001); /// Single-shot Comparator Control Status Register 5 pub const TRCSSCSR5: Self = Self::System(0b10_001_0001_1101_010); /// Resource Selection Control Register 14 pub const TRCRSCTLR14: Self = Self::System(0b10_001_0001_1110_000); /// Resource Selection Control Register 30 pub const TRCRSCTLR30: Self = Self::System(0b10_001_0001_1110_001); /// Single-shot Comparator Control Status Register 6 pub const TRCSSCSR6: Self = Self::System(0b10_001_0001_1110_010); /// Resource Selection Control Register 15 pub const TRCRSCTLR15: Self = Self::System(0b10_001_0001_1111_000); /// Resource Selection Control Register 31 pub const TRCRSCTLR31: Self = Self::System(0b10_001_0001_1111_001); /// Single-shot Comparator Control Status Register 7 pub const TRCSSCSR7: Self = Self::System(0b10_001_0001_1111_010); /// Address Comparator Value Register 0 pub const TRCACVR0: Self = Self::System(0b10_001_0010_0000_000); /// Address Comparator Value Register 8 pub const TRCACVR8: Self = Self::System(0b10_001_0010_0000_001); /// Address Comparator Access Type Register 0 pub const TRCACATR0: Self = Self::System(0b10_001_0010_0000_010); /// Address Comparator Access Type Register 8 pub const TRCACATR8: Self = Self::System(0b10_001_0010_0000_011); /// Address Comparator Value Register 1 pub const TRCACVR1: Self = Self::System(0b10_001_0010_0010_000); /// Address Comparator Value Register 9 pub const TRCACVR9: Self = Self::System(0b10_001_0010_0010_001); /// Address Comparator Access Type Register 1 pub const TRCACATR1: Self = Self::System(0b10_001_0010_0010_010); /// Address Comparator Access Type Register 9 pub const TRCACATR9: Self = Self::System(0b10_001_0010_0010_011); /// Address Comparator Value Register 2 pub const TRCACVR2: Self = Self::System(0b10_001_0010_0100_000); /// Address Comparator Value Register 10 pub const TRCACVR10: Self = Self::System(0b10_001_0010_0100_001); /// Address Comparator Access Type Register 2 pub const TRCACATR2: Self = Self::System(0b10_001_0010_0100_010); /// Address Comparator Access Type Register 10 pub const TRCACATR10: Self = Self::System(0b10_001_0010_0100_011); /// Address Comparator Value Register 3 pub const TRCACVR3: Self = Self::System(0b10_001_0010_0110_000); /// Address Comparator Value Register 11 pub const TRCACVR11: Self = Self::System(0b10_001_0010_0110_001); /// Address Comparator Access Type Register 3 pub const TRCACATR3: Self = Self::System(0b10_001_0010_0110_010); /// Address Comparator Access Type Register 11 pub const TRCACATR11: Self = Self::System(0b10_001_0010_0110_011); /// Address Comparator Value Register 4 pub const TRCACVR4: Self = Self::System(0b10_001_0010_1000_000); /// Address Comparator Value Register 12 pub const TRCACVR12: Self = Self::System(0b10_001_0010_1000_001); /// Address Comparator Access Type Register 4 pub const TRCACATR4: Self = Self::System(0b10_001_0010_1000_010); /// Address Comparator Access Type Register 12 pub const TRCACATR12: Self = Self::System(0b10_001_0010_1000_011); /// Address Comparator Value Register 5 pub const TRCACVR5: Self = Self::System(0b10_001_0010_1010_000); /// Address Comparator Value Register 13 pub const TRCACVR13: Self = Self::System(0b10_001_0010_1010_001); /// Address Comparator Access Type Register 5 pub const TRCACATR5: Self = Self::System(0b10_001_0010_1010_010); /// Address Comparator Access Type Register 13 pub const TRCACATR13: Self = Self::System(0b10_001_0010_1010_011); /// Address Comparator Value Register 6 pub const TRCACVR6: Self = Self::System(0b10_001_0010_1100_000); /// Address Comparator Value Register 14 pub const TRCACVR14: Self = Self::System(0b10_001_0010_1100_001); /// Address Comparator Access Type Register 6 pub const TRCACATR6: Self = Self::System(0b10_001_0010_1100_010); /// Address Comparator Access Type Register 14 pub const TRCACATR14: Self = Self::System(0b10_001_0010_1100_011); /// Address Comparator Value Register 7 pub const TRCACVR7: Self = Self::System(0b10_001_0010_1110_000); /// Address Comparator Value Register 15 pub const TRCACVR15: Self = Self::System(0b10_001_0010_1110_001); /// Address Comparator Access Type Register 7 pub const TRCACATR7: Self = Self::System(0b10_001_0010_1110_010); /// Address Comparator Access Type Register 15 pub const TRCACATR15: Self = Self::System(0b10_001_0010_1110_011); /// Context Identifier Comparator Value Registers 0 pub const TRCCIDCVR0: Self = Self::System(0b10_001_0011_0000_000); /// Virtual Context Identifier Comparator Value Register 0 pub const TRCVMIDCVR0: Self = Self::System(0b10_001_0011_0000_001); /// Context Identifier Comparator Control Register 0 pub const TRCCIDCCTLR0: Self = Self::System(0b10_001_0011_0000_010); /// Context Identifier Comparator Control Register 1 pub const TRCCIDCCTLR1: Self = Self::System(0b10_001_0011_0001_010); /// Context Identifier Comparator Value Registers 1 pub const TRCCIDCVR1: Self = Self::System(0b10_001_0011_0010_000); /// Virtual Context Identifier Comparator Value Register 1 pub const TRCVMIDCVR1: Self = Self::System(0b10_001_0011_0010_001); /// Virtual Context Identifier Comparator Control Register 0 pub const TRCVMIDCCTLR0: Self = Self::System(0b10_001_0011_0010_010); /// Virtual Context Identifier Comparator Control Register 1 pub const TRCVMIDCCTLR1: Self = Self::System(0b10_001_0011_0011_010); /// Context Identifier Comparator Value Registers 2 pub const TRCCIDCVR2: Self = Self::System(0b10_001_0011_0100_000); /// Virtual Context Identifier Comparator Value Register 2 pub const TRCVMIDCVR2: Self = Self::System(0b10_001_0011_0100_001); /// Context Identifier Comparator Value Registers 3 pub const TRCCIDCVR3: Self = Self::System(0b10_001_0011_0110_000); /// Virtual Context Identifier Comparator Value Register 3 pub const TRCVMIDCVR3: Self = Self::System(0b10_001_0011_0110_001); /// Context Identifier Comparator Value Registers 4 pub const TRCCIDCVR4: Self = Self::System(0b10_001_0011_1000_000); /// Virtual Context Identifier Comparator Value Register 4 pub const TRCVMIDCVR4: Self = Self::System(0b10_001_0011_1000_001); /// Context Identifier Comparator Value Registers 5 pub const TRCCIDCVR5: Self = Self::System(0b10_001_0011_1010_000); /// Virtual Context Identifier Comparator Value Register 5 pub const TRCVMIDCVR5: Self = Self::System(0b10_001_0011_1010_001); /// Context Identifier Comparator Value Registers 6 pub const TRCCIDCVR6: Self = Self::System(0b10_001_0011_1100_000); /// Virtual Context Identifier Comparator Value Register 6 pub const TRCVMIDCVR6: Self = Self::System(0b10_001_0011_1100_001); /// Context Identifier Comparator Value Registers 7 pub const TRCCIDCVR7: Self = Self::System(0b10_001_0011_1110_000); /// Virtual Context Identifier Comparator Value Register 7 pub const TRCVMIDCVR7: Self = Self::System(0b10_001_0011_1110_001); /// Device Configuration Register pub const TRCDEVID: Self = Self::System(0b10_001_0111_0010_111); /// Claim Tag Set Register pub const TRCCLAIMSET: Self = Self::System(0b10_001_0111_1000_110); /// Claim Tag Clear Register pub const TRCCLAIMCLR: Self = Self::System(0b10_001_0111_1001_110); /// Authentication Status Register pub const TRCAUTHSTATUS: Self = Self::System(0b10_001_0111_1110_110); /// Device Architecture Register pub const TRCDEVARCH: Self = Self::System(0b10_001_0111_1111_110); /// Branch Record Buffer Information Register 0 pub const BRBINF0_EL1: Self = Self::System(0b10_001_1000_0000_000); /// Branch Record Buffer Source Address Register 0 pub const BRBSRC0_EL1: Self = Self::System(0b10_001_1000_0000_001); /// Branch Record Buffer Target Address Register 0 pub const BRBTGT0_EL1: Self = Self::System(0b10_001_1000_0000_010); /// Branch Record Buffer Information Register 16 pub const BRBINF16_EL1: Self = Self::System(0b10_001_1000_0000_100); /// Branch Record Buffer Source Address Register 16 pub const BRBSRC16_EL1: Self = Self::System(0b10_001_1000_0000_101); /// Branch Record Buffer Target Address Register 16 pub const BRBTGT16_EL1: Self = Self::System(0b10_001_1000_0000_110); /// Branch Record Buffer Information Register 1 pub const BRBINF1_EL1: Self = Self::System(0b10_001_1000_0001_000); /// Branch Record Buffer Source Address Register 1 pub const BRBSRC1_EL1: Self = Self::System(0b10_001_1000_0001_001); /// Branch Record Buffer Target Address Register 1 pub const BRBTGT1_EL1: Self = Self::System(0b10_001_1000_0001_010); /// Branch Record Buffer Information Register 17 pub const BRBINF17_EL1: Self = Self::System(0b10_001_1000_0001_100); /// Branch Record Buffer Source Address Register 17 pub const BRBSRC17_EL1: Self = Self::System(0b10_001_1000_0001_101); /// Branch Record Buffer Target Address Register 17 pub const BRBTGT17_EL1: Self = Self::System(0b10_001_1000_0001_110); /// Branch Record Buffer Information Register 2 pub const BRBINF2_EL1: Self = Self::System(0b10_001_1000_0010_000); /// Branch Record Buffer Source Address Register 2 pub const BRBSRC2_EL1: Self = Self::System(0b10_001_1000_0010_001); /// Branch Record Buffer Target Address Register 2 pub const BRBTGT2_EL1: Self = Self::System(0b10_001_1000_0010_010); /// Branch Record Buffer Information Register 18 pub const BRBINF18_EL1: Self = Self::System(0b10_001_1000_0010_100); /// Branch Record Buffer Source Address Register 18 pub const BRBSRC18_EL1: Self = Self::System(0b10_001_1000_0010_101); /// Branch Record Buffer Target Address Register 18 pub const BRBTGT18_EL1: Self = Self::System(0b10_001_1000_0010_110); /// Branch Record Buffer Information Register 3 pub const BRBINF3_EL1: Self = Self::System(0b10_001_1000_0011_000); /// Branch Record Buffer Source Address Register 3 pub const BRBSRC3_EL1: Self = Self::System(0b10_001_1000_0011_001); /// Branch Record Buffer Target Address Register 3 pub const BRBTGT3_EL1: Self = Self::System(0b10_001_1000_0011_010); /// Branch Record Buffer Information Register 19 pub const BRBINF19_EL1: Self = Self::System(0b10_001_1000_0011_100); /// Branch Record Buffer Source Address Register 19 pub const BRBSRC19_EL1: Self = Self::System(0b10_001_1000_0011_101); /// Branch Record Buffer Target Address Register 19 pub const BRBTGT19_EL1: Self = Self::System(0b10_001_1000_0011_110); /// Branch Record Buffer Information Register 4 pub const BRBINF4_EL1: Self = Self::System(0b10_001_1000_0100_000); /// Branch Record Buffer Source Address Register 4 pub const BRBSRC4_EL1: Self = Self::System(0b10_001_1000_0100_001); /// Branch Record Buffer Target Address Register 4 pub const BRBTGT4_EL1: Self = Self::System(0b10_001_1000_0100_010); /// Branch Record Buffer Information Register 20 pub const BRBINF20_EL1: Self = Self::System(0b10_001_1000_0100_100); /// Branch Record Buffer Source Address Register 20 pub const BRBSRC20_EL1: Self = Self::System(0b10_001_1000_0100_101); /// Branch Record Buffer Target Address Register 20 pub const BRBTGT20_EL1: Self = Self::System(0b10_001_1000_0100_110); /// Branch Record Buffer Information Register 5 pub const BRBINF5_EL1: Self = Self::System(0b10_001_1000_0101_000); /// Branch Record Buffer Source Address Register 5 pub const BRBSRC5_EL1: Self = Self::System(0b10_001_1000_0101_001); /// Branch Record Buffer Target Address Register 5 pub const BRBTGT5_EL1: Self = Self::System(0b10_001_1000_0101_010); /// Branch Record Buffer Information Register 21 pub const BRBINF21_EL1: Self = Self::System(0b10_001_1000_0101_100); /// Branch Record Buffer Source Address Register 21 pub const BRBSRC21_EL1: Self = Self::System(0b10_001_1000_0101_101); /// Branch Record Buffer Target Address Register 21 pub const BRBTGT21_EL1: Self = Self::System(0b10_001_1000_0101_110); /// Branch Record Buffer Information Register 6 pub const BRBINF6_EL1: Self = Self::System(0b10_001_1000_0110_000); /// Branch Record Buffer Source Address Register 6 pub const BRBSRC6_EL1: Self = Self::System(0b10_001_1000_0110_001); /// Branch Record Buffer Target Address Register 6 pub const BRBTGT6_EL1: Self = Self::System(0b10_001_1000_0110_010); /// Branch Record Buffer Information Register 22 pub const BRBINF22_EL1: Self = Self::System(0b10_001_1000_0110_100); /// Branch Record Buffer Source Address Register 22 pub const BRBSRC22_EL1: Self = Self::System(0b10_001_1000_0110_101); /// Branch Record Buffer Target Address Register 22 pub const BRBTGT22_EL1: Self = Self::System(0b10_001_1000_0110_110); /// Branch Record Buffer Information Register 7 pub const BRBINF7_EL1: Self = Self::System(0b10_001_1000_0111_000); /// Branch Record Buffer Source Address Register 7 pub const BRBSRC7_EL1: Self = Self::System(0b10_001_1000_0111_001); /// Branch Record Buffer Target Address Register 7 pub const BRBTGT7_EL1: Self = Self::System(0b10_001_1000_0111_010); /// Branch Record Buffer Information Register 23 pub const BRBINF23_EL1: Self = Self::System(0b10_001_1000_0111_100); /// Branch Record Buffer Source Address Register 23 pub const BRBSRC23_EL1: Self = Self::System(0b10_001_1000_0111_101); /// Branch Record Buffer Target Address Register 23 pub const BRBTGT23_EL1: Self = Self::System(0b10_001_1000_0111_110); /// Branch Record Buffer Information Register 8 pub const BRBINF8_EL1: Self = Self::System(0b10_001_1000_1000_000); /// Branch Record Buffer Source Address Register 8 pub const BRBSRC8_EL1: Self = Self::System(0b10_001_1000_1000_001); /// Branch Record Buffer Target Address Register 8 pub const BRBTGT8_EL1: Self = Self::System(0b10_001_1000_1000_010); /// Branch Record Buffer Information Register 24 pub const BRBINF24_EL1: Self = Self::System(0b10_001_1000_1000_100); /// Branch Record Buffer Source Address Register 24 pub const BRBSRC24_EL1: Self = Self::System(0b10_001_1000_1000_101); /// Branch Record Buffer Target Address Register 24 pub const BRBTGT24_EL1: Self = Self::System(0b10_001_1000_1000_110); /// Branch Record Buffer Information Register 9 pub const BRBINF9_EL1: Self = Self::System(0b10_001_1000_1001_000); /// Branch Record Buffer Source Address Register 9 pub const BRBSRC9_EL1: Self = Self::System(0b10_001_1000_1001_001); /// Branch Record Buffer Target Address Register 9 pub const BRBTGT9_EL1: Self = Self::System(0b10_001_1000_1001_010); /// Branch Record Buffer Information Register 25 pub const BRBINF25_EL1: Self = Self::System(0b10_001_1000_1001_100); /// Branch Record Buffer Source Address Register 25 pub const BRBSRC25_EL1: Self = Self::System(0b10_001_1000_1001_101); /// Branch Record Buffer Target Address Register 25 pub const BRBTGT25_EL1: Self = Self::System(0b10_001_1000_1001_110); /// Branch Record Buffer Information Register 10 pub const BRBINF10_EL1: Self = Self::System(0b10_001_1000_1010_000); /// Branch Record Buffer Source Address Register 10 pub const BRBSRC10_EL1: Self = Self::System(0b10_001_1000_1010_001); /// Branch Record Buffer Target Address Register 10 pub const BRBTGT10_EL1: Self = Self::System(0b10_001_1000_1010_010); /// Branch Record Buffer Information Register 26 pub const BRBINF26_EL1: Self = Self::System(0b10_001_1000_1010_100); /// Branch Record Buffer Source Address Register 26 pub const BRBSRC26_EL1: Self = Self::System(0b10_001_1000_1010_101); /// Branch Record Buffer Target Address Register 26 pub const BRBTGT26_EL1: Self = Self::System(0b10_001_1000_1010_110); /// Branch Record Buffer Information Register 11 pub const BRBINF11_EL1: Self = Self::System(0b10_001_1000_1011_000); /// Branch Record Buffer Source Address Register 11 pub const BRBSRC11_EL1: Self = Self::System(0b10_001_1000_1011_001); /// Branch Record Buffer Target Address Register 11 pub const BRBTGT11_EL1: Self = Self::System(0b10_001_1000_1011_010); /// Branch Record Buffer Information Register 27 pub const BRBINF27_EL1: Self = Self::System(0b10_001_1000_1011_100); /// Branch Record Buffer Source Address Register 27 pub const BRBSRC27_EL1: Self = Self::System(0b10_001_1000_1011_101); /// Branch Record Buffer Target Address Register 27 pub const BRBTGT27_EL1: Self = Self::System(0b10_001_1000_1011_110); /// Branch Record Buffer Information Register 12 pub const BRBINF12_EL1: Self = Self::System(0b10_001_1000_1100_000); /// Branch Record Buffer Source Address Register 12 pub const BRBSRC12_EL1: Self = Self::System(0b10_001_1000_1100_001); /// Branch Record Buffer Target Address Register 12 pub const BRBTGT12_EL1: Self = Self::System(0b10_001_1000_1100_010); /// Branch Record Buffer Information Register 28 pub const BRBINF28_EL1: Self = Self::System(0b10_001_1000_1100_100); /// Branch Record Buffer Source Address Register 28 pub const BRBSRC28_EL1: Self = Self::System(0b10_001_1000_1100_101); /// Branch Record Buffer Target Address Register 28 pub const BRBTGT28_EL1: Self = Self::System(0b10_001_1000_1100_110); /// Branch Record Buffer Information Register 13 pub const BRBINF13_EL1: Self = Self::System(0b10_001_1000_1101_000); /// Branch Record Buffer Source Address Register 13 pub const BRBSRC13_EL1: Self = Self::System(0b10_001_1000_1101_001); /// Branch Record Buffer Target Address Register 13 pub const BRBTGT13_EL1: Self = Self::System(0b10_001_1000_1101_010); /// Branch Record Buffer Information Register 29 pub const BRBINF29_EL1: Self = Self::System(0b10_001_1000_1101_100); /// Branch Record Buffer Source Address Register 29 pub const BRBSRC29_EL1: Self = Self::System(0b10_001_1000_1101_101); /// Branch Record Buffer Target Address Register 29 pub const BRBTGT29_EL1: Self = Self::System(0b10_001_1000_1101_110); /// Branch Record Buffer Information Register 14 pub const BRBINF14_EL1: Self = Self::System(0b10_001_1000_1110_000); /// Branch Record Buffer Source Address Register 14 pub const BRBSRC14_EL1: Self = Self::System(0b10_001_1000_1110_001); /// Branch Record Buffer Target Address Register 14 pub const BRBTGT14_EL1: Self = Self::System(0b10_001_1000_1110_010); /// Branch Record Buffer Information Register 30 pub const BRBINF30_EL1: Self = Self::System(0b10_001_1000_1110_100); /// Branch Record Buffer Source Address Register 30 pub const BRBSRC30_EL1: Self = Self::System(0b10_001_1000_1110_101); /// Branch Record Buffer Target Address Register 30 pub const BRBTGT30_EL1: Self = Self::System(0b10_001_1000_1110_110); /// Branch Record Buffer Information Register 15 pub const BRBINF15_EL1: Self = Self::System(0b10_001_1000_1111_000); /// Branch Record Buffer Source Address Register 15 pub const BRBSRC15_EL1: Self = Self::System(0b10_001_1000_1111_001); /// Branch Record Buffer Target Address Register 15 pub const BRBTGT15_EL1: Self = Self::System(0b10_001_1000_1111_010); /// Branch Record Buffer Information Register 31 pub const BRBINF31_EL1: Self = Self::System(0b10_001_1000_1111_100); /// Branch Record Buffer Source Address Register 31 pub const BRBSRC31_EL1: Self = Self::System(0b10_001_1000_1111_101); /// Branch Record Buffer Target Address Register 31 pub const BRBTGT31_EL1: Self = Self::System(0b10_001_1000_1111_110); /// Branch Record Buffer Control Register (EL1) pub const BRBCR_EL1: Self = Self::System(0b10_001_1001_0000_000); /// Branch Record Buffer Control Register (EL2) pub const BRBCR_EL2: Self = Self::System(0b10_001_1001_0000_000); /// Branch Record Buffer Function Control Register pub const BRBFCR_EL1: Self = Self::System(0b10_001_1001_0000_001); /// Branch Record Buffer Timestamp Register pub const BRBTS_EL1: Self = Self::System(0b10_001_1001_0000_010); /// Branch Record Buffer Information Injection Register pub const BRBINFINJ_EL1: Self = Self::System(0b10_001_1001_0001_000); /// Branch Record Buffer Source Address Injection Register pub const BRBSRCINJ_EL1: Self = Self::System(0b10_001_1001_0001_001); /// Branch Record Buffer Target Address Injection Register pub const BRBTGTINJ_EL1: Self = Self::System(0b10_001_1001_0001_010); /// Branch Record Buffer ID0 Register pub const BRBIDR0_EL1: Self = Self::System(0b10_001_1001_0010_000); /// Monitor DCC Status Register pub const MDCCSR_EL0: Self = Self::System(0b10_011_0000_0001_000); /// Debug Data Transfer Register, Half-duplex pub const DBGDTR_EL0: Self = Self::System(0b10_011_0000_0100_000); /// Debug Data Transfer Register, Receive pub const DBGDTRRX_EL0: Self = Self::System(0b10_011_0000_0101_000); /// Debug Data Transfer Register, Transmit pub const DBGDTRTX_EL0: Self = Self::System(0b10_011_0000_0101_000); /// Debug Vector Catch Register pub const DBGVCR32_EL2: Self = Self::System(0b10_100_0000_0111_000); } ================================================ FILE: gdbstub_arch/src/aarch64/reg/mod.rs ================================================ //! `Register` structs for the AArch64 ARM architecture. /// `RegId` definitions for the ARM AArch64 Architecture. pub mod id; mod aarch64_core; pub use aarch64_core::AArch64CoreRegs; ================================================ FILE: gdbstub_arch/src/aarch64/sysregs.xml ================================================ ================================================ FILE: gdbstub_arch/src/arm/mod.rs ================================================ //! Implementations for various ARM architectures. use gdbstub::arch::Arch; pub mod reg; /// ARM-specific breakpoint kinds. /// /// Extracted from the GDB documentation at /// [E.5.1.1 ARM Breakpoint Kinds](https://sourceware.org/gdb/current/onlinedocs/gdb/ARM-Breakpoint-Kinds.html#ARM-Breakpoint-Kinds) #[derive(Debug)] pub enum ArmBreakpointKind { /// 16-bit Thumb mode breakpoint. Thumb16, /// 32-bit Thumb mode (Thumb-2) breakpoint. Thumb32, /// 32-bit ARM mode breakpoint. Arm32, } impl gdbstub::arch::BreakpointKind for ArmBreakpointKind { fn from_usize(kind: usize) -> Option { let kind = match kind { 2 => ArmBreakpointKind::Thumb16, 3 => ArmBreakpointKind::Thumb32, 4 => ArmBreakpointKind::Arm32, _ => return None, }; Some(kind) } } /// Implements `Arch` for the ARMv4T architecture pub enum Armv4t {} impl Arch for Armv4t { type Usize = u32; type Registers = reg::ArmCoreRegs; type RegId = reg::id::ArmCoreRegId; type BreakpointKind = ArmBreakpointKind; fn target_description_xml() -> Option<&'static str> { Some(r#"armv4t"#) } } ================================================ FILE: gdbstub_arch/src/arm/reg/arm_core.rs ================================================ use gdbstub::arch::Registers; /// 32-bit ARM core registers. /// /// Source: #[derive(Debug, Default, Clone, Eq, PartialEq)] pub struct ArmCoreRegs { /// General purpose registers (R0-R12) pub r: [u32; 13], /// Stack Pointer (R13) pub sp: u32, /// Link Register (R14) pub lr: u32, /// Program Counter (R15) pub pc: u32, /// Current Program Status Register (cpsr) pub cpsr: u32, } impl Registers for ArmCoreRegs { type ProgramCounter = u32; fn pc(&self) -> Self::ProgramCounter { self.pc } fn gdb_serialize(&self, mut write_byte: impl FnMut(Option)) { macro_rules! write_bytes { ($bytes:expr) => { for b in $bytes { write_byte(Some(*b)) } }; } for reg in self.r.iter() { write_bytes!(®.to_le_bytes()); } write_bytes!(&self.sp.to_le_bytes()); write_bytes!(&self.lr.to_le_bytes()); write_bytes!(&self.pc.to_le_bytes()); // Floating point registers (unused) for _ in 0..25 { (0..4).for_each(|_| write_byte(None)) } write_bytes!(&self.cpsr.to_le_bytes()); } fn gdb_deserialize(&mut self, mut bytes: &[u8]) -> Result<(), ()> { if bytes.len() != (17 + 25) * 4 { return Err(()); } let mut next_reg = || { if bytes.len() < 4 { Err(()) } else { use core::convert::TryInto; let (next, rest) = bytes.split_at(4); bytes = rest; Ok(u32::from_le_bytes(next.try_into().unwrap())) } }; for reg in self.r.iter_mut() { *reg = next_reg()? } self.sp = next_reg()?; self.lr = next_reg()?; self.pc = next_reg()?; // Floating point registers (unused) for _ in 0..25 { next_reg()?; } self.cpsr = next_reg()?; if next_reg().is_ok() { return Err(()); } Ok(()) } } ================================================ FILE: gdbstub_arch/src/arm/reg/id.rs ================================================ use core::num::NonZeroUsize; use gdbstub::arch::RegId; /// 32-bit ARM core register identifier. #[derive(Debug, Clone, Copy)] #[non_exhaustive] pub enum ArmCoreRegId { /// General purpose registers (R0-R12) Gpr(u8), /// Stack Pointer (R13) Sp, /// Link Register (R14) Lr, /// Program Counter (R15) Pc, /// Floating point registers (F0-F7) Fpr(u8), /// Floating point status Fps, /// Current Program Status Register (cpsr) Cpsr, } impl RegId for ArmCoreRegId { fn from_raw_id(id: usize) -> Option<(Self, Option)> { let reg = match id { 0..=12 => Self::Gpr(id as u8), 13 => Self::Sp, 14 => Self::Lr, 15 => Self::Pc, 16..=23 => Self::Fpr((id as u8) - 16), 24 => Self::Fps, 25 => Self::Cpsr, _ => return None, }; Some((reg, Some(NonZeroUsize::new(4)?))) } } ================================================ FILE: gdbstub_arch/src/arm/reg/mod.rs ================================================ //! `Register` structs for various ARM architectures. /// `RegId` definitions for ARM architectures. pub mod id; mod arm_core; pub use arm_core::ArmCoreRegs; ================================================ FILE: gdbstub_arch/src/lib.rs ================================================ //! Community-created implementations of [`gdbstub::arch::Arch`] for various //! architectures. //! //! _Note:_ If an architecture is missing from this crate, that does _not_ mean //! that it can't be used with `gdbstub`! So-long as there's support for the //! target architecture in GDB, it should be fairly straightforward to implement //! `Arch` manually. //! //! Please consider upstreaming any missing `Arch` implementations you happen to //! implement yourself! Aside from the altruistic motive of improving `gdbstub`, //! upstreaming your `Arch` implementation will ensure that it's kept up-to-date //! with any future breaking API changes. //! //! **Disclaimer:** These implementations are all community contributions, and //! while they are tested (by the PR's author) and code-reviewed, it's not //! particularly feasible to write detailed tests for each architecture! If you //! spot a bug in any of the implementations, please file an issue / open a PR! //! //! # What's with `RegIdImpl`? //! //! Supporting the `Target::read/write_register` API required introducing a new //! [`RegId`] trait + [`Arch::RegId`] associated type. `RegId` is used by //! `gdbstub` to translate raw GDB register ids (a protocol level arch-dependent //! `usize`) into human-readable enum variants. //! //! Unfortunately, this API was added after several contributors had already //! upstreamed their `Arch` implementations, and as a result, there are several //! built-in arch implementations which are missing proper `RegId` enums //! (tracked under [issue #29](https://github.com/daniel5151/gdbstub/issues/29)). //! //! As a stop-gap measure, affected `Arch` implementations have been modified to //! accept a `RegIdImpl` type parameter, which requires users to manually //! specify a `RegId` implementation. //! //! If you're not interested in implementing the `Target::read/write_register` //! methods and just want to get up-and-running with `gdbstub`, it's fine to //! set `RegIdImpl` to `()` and use the built-in stubbed `impl RegId for ()`. //! //! A better approach would be to implement (and hopefully upstream!) a proper //! `RegId` enum. While this will require doing a bit of digging through the GDB //! docs + [architecture XML definitions](https://github.com/bminor/binutils-gdb/tree/master/gdb/features/), //! it's not too tricky to get a working implementation up and running, and //! makes it possible to safely and efficiently implement the //! `Target::read/write_register` API. As an example, check out //! [`ArmCoreRegId`](arm::reg::id::ArmCoreRegId#impl-RegId). //! //! Whenever a `RegId` enum is upstreamed, the associated `Arch`'s `RegIdImpl` //! parameter will be defaulted to the newly added enum. This will simplify the //! API without requiring an explicit breaking API change. Once all `RegIdImpl` //! have a default implementation, only a single breaking API change will be //! required to remove `RegIdImpl` entirely (along with this documentation). #![cfg_attr(not(test), no_std)] #![deny(missing_docs)] pub mod aarch64; pub mod arm; pub mod mips; pub mod msp430; pub mod ppc; pub mod riscv; pub mod wasm; pub mod x86; // used as part of intra-doc link #[allow(unused_imports)] use gdbstub::arch::*; ================================================ FILE: gdbstub_arch/src/mips/mod.rs ================================================ //! Implementations for the MIPS architecture. use gdbstub::arch::Arch; pub mod reg; /// MIPS-specific breakpoint kinds. /// /// Extracted from the GDB documentation at /// [E.5.1.1 MIPS Breakpoint Kinds](https://sourceware.org/gdb/current/onlinedocs/gdb/MIPS-Breakpoint-Kinds.html#MIPS-Breakpoint-Kinds) #[derive(Debug)] pub enum MipsBreakpointKind { /// 16-bit MIPS16 mode breakpoint. Mips16, /// 16-bit microMIPS mode breakpoint. MicroMips16, /// 32-bit standard MIPS mode breakpoint. Mips32, /// 32-bit microMIPS mode breakpoint. MicroMips32, } impl gdbstub::arch::BreakpointKind for MipsBreakpointKind { fn from_usize(kind: usize) -> Option { let kind = match kind { 2 => MipsBreakpointKind::Mips16, 3 => MipsBreakpointKind::MicroMips16, 4 => MipsBreakpointKind::Mips32, 5 => MipsBreakpointKind::MicroMips32, _ => return None, }; Some(kind) } } /// Implements `Arch` for 32-bit MIPS. pub enum Mips {} /// Implements `Arch` for 32-bit MIPS, with the DSP feature enabled. pub enum MipsWithDsp {} /// Implements `Arch` for 64-bit MIPS. /// /// **NOTE:** Due to GDB client behavior, this arch does _not_ include a /// built-in `target.xml` implementation. Consider manually implementing /// [`TargetDescriptionXmlOverride`]. /// /// See [daniel5151/gdbstub#97](https://github.com/daniel5151/gdbstub/issues/97). /// /// [`TargetDescriptionXmlOverride`]: gdbstub::target::ext::target_description_xml_override::TargetDescriptionXmlOverride pub enum Mips64 {} /// Implements `Arch` for 64-bit MIPS, with the DSP feature enabled. /// /// **NOTE:** Due to GDB client behavior, this arch does _not_ include a /// built-in `target.xml` implementation. Consider manually implementing /// [`TargetDescriptionXmlOverride`]. /// /// See [daniel5151/gdbstub#97](https://github.com/daniel5151/gdbstub/issues/97). /// /// [`TargetDescriptionXmlOverride`]: gdbstub::target::ext::target_description_xml_override::TargetDescriptionXmlOverride pub enum Mips64WithDsp {} impl Arch for Mips { type Usize = u32; type Registers = reg::MipsCoreRegs; type RegId = reg::id::MipsRegId; type BreakpointKind = MipsBreakpointKind; fn target_description_xml() -> Option<&'static str> { Some(r#"mips"#) } } impl Arch for MipsWithDsp { type Usize = u32; type Registers = reg::MipsCoreRegsWithDsp; type RegId = reg::id::MipsRegId; type BreakpointKind = MipsBreakpointKind; fn target_description_xml() -> Option<&'static str> { Some( r#"mips"#, ) } } #[allow(deprecated)] impl Arch for Mips64 { type Usize = u64; type Registers = reg::MipsCoreRegs; type RegId = reg::id::MipsRegId; type BreakpointKind = MipsBreakpointKind; fn target_description_xml() -> Option<&'static str> { None } } #[allow(deprecated)] impl Arch for Mips64WithDsp { type Usize = u64; type Registers = reg::MipsCoreRegsWithDsp; type RegId = reg::id::MipsRegId; type BreakpointKind = MipsBreakpointKind; fn target_description_xml() -> Option<&'static str> { None } } ================================================ FILE: gdbstub_arch/src/mips/reg/id.rs ================================================ use core::num::NonZeroUsize; use gdbstub::arch::RegId; /// MIPS register identifier. #[derive(Debug, Clone, Copy)] #[non_exhaustive] pub enum MipsRegId { /// General purpose registers (R0-R31) Gpr(u8), /// Status register Status, /// Low register Lo, /// High register Hi, /// Bad Virtual Address register Badvaddr, /// Exception Cause register Cause, /// Program Counter Pc, /// Floating point registers (F0-F31) Fpr(u8), /// Floating-point Control Status register Fcsr, /// Floating-point Implementation Register Fir, /// High 1 register Hi1, /// Low 1 register Lo1, /// High 2 register Hi2, /// Low 2 register Lo2, /// High 3 register Hi3, /// Low 3 register Lo3, /// DSP Control register Dspctl, /// Restart register Restart, #[doc(hidden)] _Size(core::marker::PhantomData), } fn from_raw_id(id: usize) -> Option<(MipsRegId, Option)> { let reg = match id { 0..=31 => MipsRegId::Gpr(id as u8), 32 => MipsRegId::Status, 33 => MipsRegId::Lo, 34 => MipsRegId::Hi, 35 => MipsRegId::Badvaddr, 36 => MipsRegId::Cause, 37 => MipsRegId::Pc, 38..=69 => MipsRegId::Fpr((id as u8) - 38), 70 => MipsRegId::Fcsr, 71 => MipsRegId::Fir, 72 => MipsRegId::Hi1, 73 => MipsRegId::Lo1, 74 => MipsRegId::Hi2, 75 => MipsRegId::Lo2, 76 => MipsRegId::Hi3, 77 => MipsRegId::Lo3, // `MipsRegId::Dspctl` is the only register that will always be 4 bytes wide 78 => return Some((MipsRegId::Dspctl, Some(NonZeroUsize::new(4)?))), 79 => MipsRegId::Restart, _ => return None, }; let ptrsize = core::mem::size_of::(); Some((reg, Some(NonZeroUsize::new(ptrsize)?))) } impl RegId for MipsRegId { fn from_raw_id(id: usize) -> Option<(Self, Option)> { from_raw_id::(id) } } impl RegId for MipsRegId { fn from_raw_id(id: usize) -> Option<(Self, Option)> { from_raw_id::(id) } } #[cfg(test)] mod tests { use gdbstub::arch::RegId; use gdbstub::arch::Registers; fn test() { // Obtain the data length written by `gdb_serialize` by passing a custom // closure. let mut serialized_data_len = 0; let counter = |b: Option| { if b.is_some() { serialized_data_len += 1; } }; Rs::default().gdb_serialize(counter); // Accumulate register sizes returned by `from_raw_id`. let mut i = 0; let mut sum_reg_sizes = 0; while let Some((_, size)) = RId::from_raw_id(i) { sum_reg_sizes += size.unwrap().get(); i += 1; } assert_eq!(serialized_data_len, sum_reg_sizes); } #[test] fn test_mips32() { test::, crate::mips::reg::id::MipsRegId>() } #[test] fn test_mips64() { test::, crate::mips::reg::id::MipsRegId>() } } ================================================ FILE: gdbstub_arch/src/mips/reg/mips.rs ================================================ use core::convert::TryInto; use gdbstub::arch::Registers; use gdbstub::internal::LeBytes; use num_traits::PrimInt; /// MIPS registers. /// /// The register width is set to `u32` or `u64` based on the `` type. /// /// Source: #[derive(Debug, Default, Clone, Eq, PartialEq)] pub struct MipsCoreRegs { /// General purpose registers (R0-R31) pub r: [U; 32], /// Low register (regnum 33) pub lo: U, /// High register (regnum 34) pub hi: U, /// Program Counter (regnum 37) pub pc: U, /// CP0 registers pub cp0: MipsCp0Regs, /// FPU registers pub fpu: MipsFpuRegs, } /// MIPS CP0 (coprocessor 0) registers. /// /// Source: #[derive(Debug, Default, Clone, Eq, PartialEq)] pub struct MipsCp0Regs { /// Status register (regnum 32) pub status: U, /// Bad Virtual Address register (regnum 35) pub badvaddr: U, /// Exception Cause register (regnum 36) pub cause: U, } /// MIPS FPU registers. /// /// Source: #[derive(Debug, Default, Clone, Eq, PartialEq)] pub struct MipsFpuRegs { /// FP registers (F0-F31) starting at regnum 38 pub r: [U; 32], /// Floating-point Control Status register pub fcsr: U, /// Floating-point Implementation Register pub fir: U, } /// MIPS DSP registers. /// /// Source: #[derive(Debug, Default, Clone, Eq, PartialEq)] pub struct MipsDspRegs { /// High 1 register (regnum 72) pub hi1: U, /// Low 1 register (regnum 73) pub lo1: U, /// High 2 register (regnum 74) pub hi2: U, /// Low 2 register (regnum 75) pub lo2: U, /// High 3 register (regnum 76) pub hi3: U, /// Low 3 register (regnum 77) pub lo3: U, /// DSP Control register (regnum 78) /// Note: This register will always be 32-bit regardless of the target /// pub dspctl: u32, /// Restart register (regnum 79) pub restart: U, } /// MIPS core and DSP registers. /// /// Source: #[derive(Debug, Default, Clone, Eq, PartialEq)] pub struct MipsCoreRegsWithDsp { /// Core registers pub core: MipsCoreRegs, /// DSP registers pub dsp: MipsDspRegs, } impl Registers for MipsCoreRegs where U: PrimInt + LeBytes + Default + core::fmt::Debug, { type ProgramCounter = U; fn pc(&self) -> Self::ProgramCounter { self.pc } fn gdb_serialize(&self, mut write_byte: impl FnMut(Option)) { macro_rules! write_le_bytes { ($value:expr) => { let mut buf = [0; 16]; // infallible (unless digit is a >128 bit number) let len = $value.to_le_bytes(&mut buf).unwrap(); let buf = &buf[..len]; for b in buf { write_byte(Some(*b)); } }; } // Write GPRs for reg in self.r.iter() { write_le_bytes!(reg); } // Status register is regnum 32 write_le_bytes!(&self.cp0.status); // Low and high registers are regnums 33 and 34 write_le_bytes!(&self.lo); write_le_bytes!(&self.hi); // Badvaddr and Cause registers are regnums 35 and 36 write_le_bytes!(&self.cp0.badvaddr); write_le_bytes!(&self.cp0.cause); // Program Counter is regnum 37 write_le_bytes!(&self.pc); // Write FPRs for reg in self.fpu.r.iter() { write_le_bytes!(reg); } // Write FCSR and FIR registers write_le_bytes!(&self.fpu.fcsr); write_le_bytes!(&self.fpu.fir); } fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { let ptrsize = core::mem::size_of::(); // Ensure bytes contains enough data for all 72 registers if bytes.len() < ptrsize * 72 { return Err(()); } // All core registers are the same size let mut regs = bytes .chunks_exact(ptrsize) .map(|c| U::from_le_bytes(c).unwrap()); // Read GPRs for reg in self.r.iter_mut() { *reg = regs.next().ok_or(())? } // Read Status register self.cp0.status = regs.next().ok_or(())?; // Read Low and High registers self.lo = regs.next().ok_or(())?; self.hi = regs.next().ok_or(())?; // Read Badvaddr and Cause registers self.cp0.badvaddr = regs.next().ok_or(())?; self.cp0.cause = regs.next().ok_or(())?; // Read the Program Counter self.pc = regs.next().ok_or(())?; // Read FPRs for reg in self.fpu.r.iter_mut() { *reg = regs.next().ok_or(())? } // Read FCSR and FIR registers self.fpu.fcsr = regs.next().ok_or(())?; self.fpu.fir = regs.next().ok_or(())?; Ok(()) } } impl Registers for MipsCoreRegsWithDsp where U: PrimInt + LeBytes + Default + core::fmt::Debug, { type ProgramCounter = U; fn pc(&self) -> Self::ProgramCounter { self.core.pc } fn gdb_serialize(&self, mut write_byte: impl FnMut(Option)) { macro_rules! write_le_bytes { ($value:expr) => { let mut buf = [0; 16]; // infallible (unless digit is a >128 bit number) let len = $value.to_le_bytes(&mut buf).unwrap(); let buf = &buf[..len]; for b in buf { write_byte(Some(*b)); } }; } // Serialize the core registers first self.core.gdb_serialize(&mut write_byte); // Write the DSP registers write_le_bytes!(&self.dsp.hi1); write_le_bytes!(&self.dsp.lo1); write_le_bytes!(&self.dsp.hi2); write_le_bytes!(&self.dsp.lo2); write_le_bytes!(&self.dsp.hi3); write_le_bytes!(&self.dsp.lo3); for b in &self.dsp.dspctl.to_le_bytes() { write_byte(Some(*b)); } write_le_bytes!(&self.dsp.restart); } fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { // Deserialize the core registers first self.core.gdb_deserialize(bytes)?; // Ensure bytes contains enough data for all 79 registers of target-width // and the dspctl register which is always 4 bytes let ptrsize = core::mem::size_of::(); if bytes.len() < (ptrsize * 79) + 4 { return Err(()); } // Calculate the offsets to the DSP registers based on the ptrsize let dspregs_start = ptrsize * 72; let dspctl_start = ptrsize * 78; // Read up until the dspctl register let mut regs = bytes[dspregs_start..dspctl_start] .chunks_exact(ptrsize) .map(|c| U::from_le_bytes(c).unwrap()); self.dsp.hi1 = regs.next().ok_or(())?; self.dsp.lo1 = regs.next().ok_or(())?; self.dsp.hi2 = regs.next().ok_or(())?; self.dsp.lo2 = regs.next().ok_or(())?; self.dsp.hi3 = regs.next().ok_or(())?; self.dsp.lo3 = regs.next().ok_or(())?; // Dspctl will always be a u32 self.dsp.dspctl = u32::from_le_bytes(bytes[dspctl_start..dspctl_start + 4].try_into().unwrap()); // Only 4 or 8 bytes should remain to be read self.dsp.restart = U::from_le_bytes( bytes[dspctl_start + 4..] .chunks_exact(ptrsize) .next() .ok_or(())?, ) .unwrap(); Ok(()) } } ================================================ FILE: gdbstub_arch/src/mips/reg/mod.rs ================================================ //! `Register` structs for MIPS architectures. /// `RegId` definitions for MIPS architectures. pub mod id; mod mips; pub use mips::MipsCoreRegs; pub use mips::MipsCoreRegsWithDsp; pub use mips::MipsCp0Regs; pub use mips::MipsFpuRegs; ================================================ FILE: gdbstub_arch/src/msp430/mod.rs ================================================ //! Implementations for the TI-MSP430 family of MCUs. use gdbstub::arch::Arch; pub mod reg; /// Implements `Arch` for standard 16-bit TI-MSP430 MCUs. pub struct Msp430 {} impl Arch for Msp430 { type Usize = u16; type Registers = reg::Msp430Regs; type RegId = reg::id::Msp430RegId; type BreakpointKind = usize; fn target_description_xml() -> Option<&'static str> { Some(r#"msp430"#) } } /// Implements `Arch` for 20-bit TI-MSP430 MCUs (CPUX). pub struct Msp430X {} impl Arch for Msp430X { type Usize = u32; type Registers = reg::Msp430Regs; type RegId = reg::id::Msp430RegId; type BreakpointKind = usize; fn target_description_xml() -> Option<&'static str> { Some(r#"msp430x"#) } } ================================================ FILE: gdbstub_arch/src/msp430/reg/id.rs ================================================ use core::num::NonZeroUsize; use gdbstub::arch::RegId; /// TI-MSP430 register identifier. /// /// GDB does not provide a XML file for the MSP430. /// The best file to reference is [msp430-tdep.c](https://github.com/bminor/binutils-gdb/blob/master/gdb/msp430-tdep.c). #[derive(Debug, Clone, Copy)] #[non_exhaustive] pub enum Msp430RegId { /// Program Counter (R0) Pc, /// Stack Pointer (R1) Sp, /// Status Register (R2) Sr, /// Constant Generator (R3) Cg, /// General Purpose Registers (R4-R15) Gpr(u8), #[doc(hidden)] _Size(core::marker::PhantomData), } fn from_raw_id(id: usize) -> Option<(Msp430RegId, Option)> { let reg = match id { 0 => Msp430RegId::Pc, 1 => Msp430RegId::Sp, 2 => Msp430RegId::Sr, 3 => Msp430RegId::Cg, 4..=15 => Msp430RegId::Gpr((id as u8) - 4), _ => return None, }; let ptrsize = core::mem::size_of::(); Some((reg, Some(NonZeroUsize::new(ptrsize)?))) } impl RegId for Msp430RegId { fn from_raw_id(id: usize) -> Option<(Self, Option)> { from_raw_id::(id) } } impl RegId for Msp430RegId { fn from_raw_id(id: usize) -> Option<(Self, Option)> { from_raw_id::(id) } } #[cfg(test)] mod tests { use gdbstub::arch::RegId; use gdbstub::arch::Registers; fn test() { // Obtain the data length written by `gdb_serialize` by passing a custom // closure. let mut serialized_data_len = 0; let counter = |b: Option| { if b.is_some() { serialized_data_len += 1; } }; Rs::default().gdb_serialize(counter); // The `Msp430Regs` implementation does not increment the size for // the CG register since it will always be the constant zero. serialized_data_len += RId::from_raw_id(3).unwrap().1.unwrap().get(); // Accumulate register sizes returned by `from_raw_id`. let mut i = 0; let mut sum_reg_sizes = 0; while let Some((_, size)) = RId::from_raw_id(i) { sum_reg_sizes += size.unwrap().get(); i += 1; } assert_eq!(serialized_data_len, sum_reg_sizes); } #[test] fn test_msp430() { test::, crate::msp430::reg::id::Msp430RegId>() } #[test] fn test_msp430x() { test::, crate::msp430::reg::id::Msp430RegId>() } } ================================================ FILE: gdbstub_arch/src/msp430/reg/mod.rs ================================================ //! `Register` structs for various TI-MSP430 CPUs. /// `RegId` definitions for various TI-MSP430 CPUs. pub mod id; mod msp430; pub use msp430::Msp430Regs; ================================================ FILE: gdbstub_arch/src/msp430/reg/msp430.rs ================================================ use gdbstub::arch::Registers; use gdbstub::internal::LeBytes; use num_traits::PrimInt; /// TI-MSP430 registers. /// /// The register width is set based on the `` type. For 16-bit MSP430 CPUs /// this should be `u16` and for 20-bit MSP430 CPUs (CPUX) this should be `u32`. #[derive(Debug, Default, Clone, Eq, PartialEq)] pub struct Msp430Regs { /// Program Counter (R0) pub pc: U, /// Stack Pointer (R1) pub sp: U, /// Status Register (R2) pub sr: U, /// General Purpose Registers (R4-R15) pub r: [U; 12], } impl Registers for Msp430Regs where U: PrimInt + LeBytes + Default + core::fmt::Debug, { type ProgramCounter = U; fn pc(&self) -> Self::ProgramCounter { self.pc } fn gdb_serialize(&self, mut write_byte: impl FnMut(Option)) { macro_rules! write_le_bytes { ($value:expr) => { let mut buf = [0; 4]; // infallible (register size a maximum of 32-bits) let len = $value.to_le_bytes(&mut buf).unwrap(); let buf = &buf[..len]; for b in buf { write_byte(Some(*b)); } }; } write_le_bytes!(&self.pc); write_le_bytes!(&self.sp); write_le_bytes!(&self.sr); (0..core::mem::size_of::()).for_each(|_| write_byte(None)); // Constant Generator (CG/R3) for reg in self.r.iter() { write_le_bytes!(reg); } } fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { let ptrsize = core::mem::size_of::(); // Ensure bytes contains enough data for all 16 registers if bytes.len() < ptrsize * 16 { return Err(()); } let mut regs = bytes .chunks_exact(ptrsize) .map(|c| U::from_le_bytes(c).unwrap()); self.pc = regs.next().ok_or(())?; self.sp = regs.next().ok_or(())?; self.sr = regs.next().ok_or(())?; // Constant Generator (CG/R3) should always be 0 if regs.next().ok_or(())? != U::zero() { return Err(()); } for reg in self.r.iter_mut() { *reg = regs.next().ok_or(())? } if regs.next().is_some() { return Err(()); } Ok(()) } } ================================================ FILE: gdbstub_arch/src/ppc/mod.rs ================================================ //! Implementations for various PowerPC architectures. use gdbstub::arch::Arch; use gdbstub::arch::RegId; pub mod reg; /// Implements `Arch` for 32-bit PowerPC + AltiVec SIMD. /// /// Check out the [module level docs](gdbstub::arch#whats-with-regidimpl) for /// more info about the `RegIdImpl` type parameter. pub enum PowerPcAltivec32 { #[doc(hidden)] _Marker(core::marker::PhantomData), } impl Arch for PowerPcAltivec32 { type Usize = u32; type Registers = reg::PowerPcCommonRegs; type RegId = RegIdImpl; type BreakpointKind = usize; fn target_description_xml() -> Option<&'static str> { Some( r#"powerpc:common"#, ) } } ================================================ FILE: gdbstub_arch/src/ppc/reg/common.rs ================================================ use super::PpcVector; use core::convert::TryInto; use gdbstub::arch::Registers; /// 32-bit PowerPC core registers, FPU registers, and AltiVec SIMD registers. /// /// Sources: /// * /// * /// * /// * #[derive(Debug, Default, Clone, PartialEq)] pub struct PowerPcCommonRegs { /// General purpose registers pub r: [u32; 32], /// Floating Point registers pub f: [f64; 32], /// Program counter pub pc: u32, /// Machine state pub msr: u32, /// Condition register pub cr: u32, /// Link register pub lr: u32, /// Count register pub ctr: u32, /// Integer exception register pub xer: u32, /// Floating-point status and control register pub fpscr: u32, /// Vector registers pub vr: [PpcVector; 32], /// Vector status and control register pub vscr: u32, /// Vector context save register pub vrsave: u32, } impl Registers for PowerPcCommonRegs { type ProgramCounter = u32; fn pc(&self) -> Self::ProgramCounter { self.pc } fn gdb_serialize(&self, mut write_byte: impl FnMut(Option)) { macro_rules! write_bytes { ($bytes:expr) => { for b in $bytes { write_byte(Some(*b)) } }; } macro_rules! write_regs { ($($reg:ident),*) => { $( write_bytes!(&self.$reg.to_be_bytes()); )* } } for reg in &self.r { write_bytes!(®.to_be_bytes()); } for reg in &self.f { write_bytes!(®.to_be_bytes()); } write_regs!(pc, msr, cr, lr, ctr, xer, fpscr); for ® in &self.vr { let reg: u128 = reg; write_bytes!(®.to_be_bytes()); } write_regs!(vscr, vrsave); } fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { if bytes.len() < 0x3a4 { return Err(()); } let mut regs = bytes[0..0x80] .chunks_exact(4) .map(|x| u32::from_be_bytes(x.try_into().unwrap())); for reg in &mut self.r { *reg = regs.next().ok_or(())?; } let mut regs = bytes[0x80..0x180] .chunks_exact(8) .map(|x| f64::from_be_bytes(x.try_into().unwrap())); for reg in &mut self.f { *reg = regs.next().ok_or(())?; } macro_rules! parse_regs { ($start:literal..$end:literal, $($reg:ident),*) => { let mut regs = bytes[$start..$end] .chunks_exact(4) .map(|x| u32::from_be_bytes(x.try_into().unwrap())); $( self.$reg = regs.next().ok_or(())?; )* } } parse_regs!(0x180..0x19c, pc, msr, cr, lr, ctr, xer, fpscr); let mut regs = bytes[0x19c..0x39c] .chunks_exact(0x10) .map(|x| u128::from_be_bytes(x.try_into().unwrap())); for reg in &mut self.vr { *reg = regs.next().ok_or(())?; } parse_regs!(0x39c..0x3a4, vscr, vrsave); Ok(()) } } #[cfg(test)] mod tests { use super::*; #[test] fn ppc_core_round_trip() { let regs_before = PowerPcCommonRegs { r: [1; 32], pc: 2, msr: 3, cr: 4, lr: 5, ctr: 6, xer: 7, fpscr: 8, f: [9.0; 32], vr: [52; 32], vrsave: 10, vscr: 11, }; let mut data = vec![]; regs_before.gdb_serialize(|x| { data.push(x.unwrap_or(b'x')); }); assert_eq!(data.len(), 0x3a4); let mut regs_after = PowerPcCommonRegs::default(); regs_after.gdb_deserialize(&data).unwrap(); assert_eq!(regs_before, regs_after); } } ================================================ FILE: gdbstub_arch/src/ppc/reg/id.rs ================================================ // TODO: Add proper `RegId` implementation. See [issue #29](https://github.com/daniel5151/gdbstub/issues/29) // pub enum PowerPc32RegId {} ================================================ FILE: gdbstub_arch/src/ppc/reg/mod.rs ================================================ //! `Register` structs for PowerPC architectures /// `RegId` definitions for PowerPC architectures. pub mod id; mod common; pub use common::PowerPcCommonRegs; type PpcVector = u128; ================================================ FILE: gdbstub_arch/src/riscv/mod.rs ================================================ //! Implementations for the [RISC-V](https://riscv.org/) architecture. //! //! *Note*: currently only supports integer versions of the ISA. use gdbstub::arch::Arch; pub mod reg; /// Implements `Arch` for 32-bit RISC-V. pub enum Riscv32 {} /// Implements `Arch` for 64-bit RISC-V. pub enum Riscv64 {} impl Arch for Riscv32 { type Usize = u32; type Registers = reg::RiscvCoreRegs; type BreakpointKind = usize; type RegId = reg::id::RiscvRegId; fn target_description_xml() -> Option<&'static str> { Some(include_str!("rv32i.xml")) } } impl Arch for Riscv64 { type Usize = u64; type Registers = reg::RiscvCoreRegs; type BreakpointKind = usize; type RegId = reg::id::RiscvRegId; fn target_description_xml() -> Option<&'static str> { Some(include_str!("rv64i.xml")) } } ================================================ FILE: gdbstub_arch/src/riscv/reg/id.rs ================================================ use core::num::NonZeroUsize; use gdbstub::arch::RegId; /// RISC-V Register identifier. #[derive(Debug, Clone, Copy)] #[non_exhaustive] pub enum RiscvRegId { /// General Purpose Register (x0-x31). Gpr(u8), /// Floating Point Register (f0-f31). Fpr(u8), /// Program Counter. Pc, /// Control and Status Register. Csr(u16), /// Privilege level. Priv, #[doc(hidden)] _Marker(core::marker::PhantomData), } macro_rules! impl_riscv_reg_id { ($usize:ty) => { impl RegId for RiscvRegId<$usize> { fn from_raw_id(id: usize) -> Option<(Self, Option)> { const USIZE: usize = core::mem::size_of::<$usize>(); let (id, size) = match id { 0..=31 => (Self::Gpr(id as u8), USIZE), 32 => (Self::Pc, USIZE), 33..=64 => (Self::Fpr((id - 33) as u8), USIZE), 65..=4160 => (Self::Csr((id - 65) as u16), USIZE), 4161 => (Self::Priv, 1), _ => return None, }; Some((id, Some(NonZeroUsize::new(size)?))) } } }; } impl_riscv_reg_id!(u32); impl_riscv_reg_id!(u64); ================================================ FILE: gdbstub_arch/src/riscv/reg/mod.rs ================================================ //! `Register` structs for RISC-V architectures. /// `RegId` definitions for RISC-V architectures. pub mod id; mod riscv; pub use riscv::RiscvCoreRegs; ================================================ FILE: gdbstub_arch/src/riscv/reg/riscv.rs ================================================ use gdbstub::arch::Registers; use gdbstub::internal::LeBytes; use num_traits::PrimInt; /// RISC-V Integer registers. /// /// The register width is set to `u32` or `u64` based on the `` type. /// /// Useful links: /// * [GNU binutils-gdb XML descriptions](https://github.com/bminor/binutils-gdb/blob/master/gdb/features/riscv) /// * [riscv-tdep.h](https://github.com/bminor/binutils-gdb/blob/master/gdb/riscv-tdep.h) #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct RiscvCoreRegs { /// General purpose registers (x0-x31) pub x: [U; 32], /// Program counter pub pc: U, } impl Registers for RiscvCoreRegs where U: PrimInt + LeBytes + Default + core::fmt::Debug, { type ProgramCounter = U; fn pc(&self) -> Self::ProgramCounter { self.pc } fn gdb_serialize(&self, mut write_byte: impl FnMut(Option)) { macro_rules! write_le_bytes { ($value:expr) => { let mut buf = [0; 16]; // infallible (unless digit is a >128 bit number) let len = $value.to_le_bytes(&mut buf).unwrap(); let buf = &buf[..len]; for b in buf { write_byte(Some(*b)); } }; } // Write GPRs for reg in self.x.iter() { write_le_bytes!(reg); } // Program Counter is regnum 33 write_le_bytes!(&self.pc); } fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { let ptrsize = core::mem::size_of::(); // ensure bytes.chunks_exact(ptrsize) won't panic if !bytes.len().is_multiple_of(ptrsize) { return Err(()); } let mut regs = bytes .chunks_exact(ptrsize) .map(|c| U::from_le_bytes(c).unwrap()); // Read GPRs for reg in self.x.iter_mut() { *reg = regs.next().ok_or(())? } self.pc = regs.next().ok_or(())?; if regs.next().is_some() { return Err(()); } Ok(()) } } ================================================ FILE: gdbstub_arch/src/riscv/rv32i.xml ================================================ ================================================ FILE: gdbstub_arch/src/riscv/rv64i.xml ================================================ ================================================ FILE: gdbstub_arch/src/wasm/addr.rs ================================================ //! Synthetic 64-bit Wasm address space expected by the LLDB Wasm extensions. //! //! WebAssembly is natively *multi-memory* and *multi-address-space*: //! //! - It supports zero or more "linear memories", and they have no canonical //! mapping into a single global address space for pointers; rather, each load //! and store instruction names which memory it accesses statically. //! - It supports one or more "modules" containing first-class functions, and //! they have no canonical mapping into a single global code space; rather, //! control flow is structured, and calls between functions in different //! modules only occur via explicit strongly-typed function imports and //! exports. //! //! Wasm implementations typically represent these concepts directly rather than //! attempt to map to a more conventional ISA model of a single flat address //! space with machine code and data. However, the GDB RSP assumes the latter: //! all of its commands, such as memory reads/writes, breakpoint updates, and //! the like, use integers as pointers in a single address space. //! //! The LLDB Wasm extensions to the GDB RSP thus define a canonical mapping //! between the multi-address-space world and a flat 64-bit address space for //! the purposes of the protocol only. Note that this is 64-bit even when Wasm //! natively has 32-bit memory offsets (the "wasm32" architecture), because the //! definition adds additional information above the 32-bit offset. //! //! The [ProcessWasm.h] header file in the LLDB source contains definitions that //! are as close to documentation as we can find: see the `WasmAddressType` and //! `wasm_addr_t` definitions. //! //! An address consists of three parts: //! //! - The type: code or data. Wasm has separate "address spaces" for these, so //! they are mapped to different regions of the 64-bit synthetic space.\* //! //! - The module/memory index. The engine decides an arbitrary index ordering //! for all of the Wasm modules and Wasm linear memories present in a given //! execution. //! //! - The offset within that Wasm module bytecode or linear memory. //! //! \*Note that this implies that the original bytecode (the full image of the //! Wasm module, starting with its magic number) is present in this synthetic //! address space. An engine that implements debugging for Wasm should keep //! around the original bytecode, even if it does ahead-of-time compilation or //! other processing, so that the debugger can use it: LLDB will read the //! module bytecode from the synthetic address space, including its debug //! sections, rather than find the image elsewhere. //! //! [ProcessWasm.h]: //! https://github.com/llvm/llvm-project/blob/074653a/lldb/source/Plugins/Process/wasm/ProcessWasm.h /// The type of an address in the synthetic address space used by the /// Wasm target. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum WasmAddrType { /// Address in a 32-bit linear memory. Memory, /// Address in a `.wasm` module image. /// /// Used both for memory-read commands to fetch the Wasm binary /// from the gdbstub host, and software-breakpoint commands. Object, } /// An address in the synthetic address space used by the Wasm target. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct WasmAddr(u64); impl WasmAddr { const TYPE_BITS: u32 = 2; const MODULE_BITS: u32 = 30; const OFFSET_BITS: u32 = 32; const MODULE_SHIFT: u32 = Self::OFFSET_BITS; const TYPE_SHIFT: u32 = Self::OFFSET_BITS + Self::MODULE_BITS; const TYPE_MASK: u64 = (1u64 << Self::TYPE_BITS) - 1; const MODULE_MASK: u64 = (1u64 << Self::MODULE_BITS) - 1; const OFFSET_MASK: u64 = (1u64 << Self::OFFSET_BITS) - 1; /// Construct a `WasmAddr` from a raw 64-bit encoded address. /// /// Returns `None` if the encoding is invalid. pub fn from_raw(raw: u64) -> Option { let type_bits = (raw >> Self::TYPE_SHIFT) & Self::TYPE_MASK; if type_bits > 1 { return None; } Some(WasmAddr(raw)) } /// Provide the raw 64-bit encoding of this `WasmAddr`. pub fn as_raw(self) -> u64 { self.0 } /// Construct a `WasmAddr` from its constituent parts. /// /// Returns `None` if the `module_index` is out-of-range: it only supports /// up to 2^30 modules. pub fn new(addr_type: WasmAddrType, module_index: u32, offset: u32) -> Option { // There are fewer than 32 bits in the encoding for the module // index. if module_index >> Self::MODULE_BITS != 0 { return None; } let type_bits: u64 = match addr_type { WasmAddrType::Memory => 0, WasmAddrType::Object => 1, }; Some(WasmAddr( (type_bits << Self::TYPE_SHIFT) | ((u64::from(module_index)) << Self::MODULE_SHIFT) | (u64::from(offset)), )) } /// Get the type of this address. pub fn addr_type(self) -> WasmAddrType { match (self.0 >> Self::TYPE_SHIFT) & Self::TYPE_MASK { 0 => WasmAddrType::Memory, 1 => WasmAddrType::Object, // We never set other type-bits and the raw bits are fully // encapsulated and checked in `from_raw`, so this is // unreachable in practice and should never panic. // We silence clippy's warning about this. #[allow(clippy::unreachable)] _ => unreachable!(), } } /// Get the index of the module or memory referenced by this /// address. pub fn module_index(self) -> u32 { ((self.0 >> Self::MODULE_SHIFT) & Self::MODULE_MASK) as u32 } /// Get the offset within the module or memory referenced by this /// address. pub fn offset(self) -> u32 { (self.0 & Self::OFFSET_MASK) as u32 } } impl core::fmt::Display for WasmAddr { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let type_str = match self.addr_type() { WasmAddrType::Memory => "Memory", WasmAddrType::Object => "Object", }; write!( f, "{}(module={}, offset={:#x})", type_str, self.module_index(), self.offset() ) } } impl core::fmt::Debug for WasmAddr { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "WasmAddr({self})") } } ================================================ FILE: gdbstub_arch/src/wasm/mod.rs ================================================ //! Implementation for the WebAssembly architecture. //! //! This implementation follows the [LLDB-specific Wasm extensions] to the GDB //! RSP, which define a mapping from Wasm concepts to more classical ISA //! concepts. //! //! WebAssembly is somewhat *unlike* most ISAs in many of its details: for //! example, it uses an operand stack rather than classical registers, and has //! explicit concepts of function locals, of globals, and of first-class //! functions and a callstack, rather than a flat address space of bytes that //! are used to build up machine code, a stack, and storage as in most other //! ISAs. //! //! As such - you'll need to implement the [`Wasm`] extension trait in your //! `Target` implementation in order to provide LLDB access to these Wasm-native //! concepts. //! //! As a particularly important detail, note that the natively //! multi-address-space Wasm world, where multiple code modules exist without a //! native concept of a global PC space, and multiple linear memories exist with //! every load/store qualified by the memory it accesses, is mapped into a //! single synthesized 64-bit address space by definition of the protocol //! extensions. See the [`self::addr`] submodule for utilities to encode and //! decode these synthesized addresses. //! //! To use `gdbstub` with the LLDB Wasm GDB RSP extensions: //! //! 1. Implement the `Target` trait and the [`Wasm`], [`HostInfo`] and //! [`ProcessInfo`] traits on the target implementation for your Wasm //! execution engine/target. //! 2. Make use of this `Arch` implementation in your target. //! 3. Make use of the [`report_stop_with_regs`] API to report the Wasm PC with //! every stop packet. //! - _Note_: It seems likely that this requirement stems from a LLDB bug, as //! "expedited registers" are not typically mandated by the GDB RSP, and //! generally serve as an optional optimization to reduce roundtrips. //! 4. Ensure that you have a build of LLDB with the Wasm target enabled. (A //! binary distribution of LLDB with your operating system may not have this, //! but a build from LLVM source will, by default. Once a release of //! [`wasi-sdk`] with [this PR] is made, `wasi-sdk` will distribute such a //! build for all major platforms.) //! 5. Start up LLDB and attach it to an endpoint served by `gdbstub` with this //! target: //! //! ```text //! $ .../bin/lldb //! (lldb) process connect --plugin wasm connect://localhost:1234 //! ``` //! //! then ordinary debugging with breakpoints, step/continue, and state //! examination should work. //! //! See [Wasmtime] for an example of the use of this crate. //! //! [LLDB-specific Wasm extensions]: //! https://lldb.llvm.org/resources/lldbgdbremote.html#wasm-packets //! [`Wasm`]: gdbstub::target::ext::wasm::Wasm //! [`HostInfo`]: gdbstub::target::ext::host_info::HostInfo //! [`ProcessInfo`]: gdbstub::target::ext::process_info::ProcessInfo //! [`report_stop_with_regs`]: //! gdbstub::stub::state_machine::GdbStubStateMachineInner::report_stop_with_regs //! [`wasi-sdk`]: https://github.com/WebAssembly/wasi-sdk //! [this PR]: https://github.com/WebAssembly/wasi-sdk/pull/596 //! [Wasmtime]: https://github.com/bytecodealliance/wasmtime use gdbstub::arch::Arch; pub mod addr; pub mod reg; /// Implements `Arch` for the WebAssembly architecture. pub enum Wasm {} impl Arch for Wasm { /// Even though Wasm is nominally a 32-bit platform, LLDB's GDB RSP /// extensions for Wasm uses a 64-bit address word to multiplex module /// bytecode regions and linear memory regions into a single address space. type Usize = u64; type Registers = reg::WasmRegisters; type RegId = reg::id::WasmRegId; type BreakpointKind = usize; } ================================================ FILE: gdbstub_arch/src/wasm/reg/id.rs ================================================ use core::num::NonZeroUsize; use gdbstub::arch::RegId; /// The only register exposed to GDB: `pc` (register index 0). #[derive(Debug, Clone, Copy)] #[non_exhaustive] pub enum WasmRegId { /// Program Counter. Pc, } impl RegId for WasmRegId { fn from_raw_id(id: usize) -> Option<(Self, Option)> { match id { 0 => Some((WasmRegId::Pc, NonZeroUsize::new(8))), _ => None, } } fn to_raw_id(&self) -> Option { match self { WasmRegId::Pc => Some(0), } } } ================================================ FILE: gdbstub_arch/src/wasm/reg/mod.rs ================================================ //! `Register` structs for the WebAssembly architecture. //! //! Because Wasm is mostly stack-based, it only has one "register": //! the program counter (PC) according to the gdbstub mappings for //! this architecture. /// `RegId` definitions for WebAssembly. pub mod id; mod wasm_regs; pub use wasm_regs::WasmRegisters; ================================================ FILE: gdbstub_arch/src/wasm/reg/wasm_regs.rs ================================================ use core::convert::TryInto; use gdbstub::arch::Registers; /// The register state for WebAssembly. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct WasmRegisters { /// Program Counter. See [`crate::wasm::addr`] for the 64-bit /// synthetic address space in which this PC exists. pub pc: u64, } impl Registers for WasmRegisters { type ProgramCounter = u64; fn pc(&self) -> u64 { self.pc } fn gdb_serialize(&self, mut write_byte: impl FnMut(Option)) { for byte in self.pc.to_le_bytes() { write_byte(Some(byte)); } } fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { if bytes.len() < 8 { return Err(()); } self.pc = u64::from_le_bytes(bytes[..8].try_into().unwrap()); Ok(()) } } ================================================ FILE: gdbstub_arch/src/x86/mod.rs ================================================ //! Implementations for various x86 architectures. use gdbstub::arch::Arch; pub mod reg; /// Implements `Arch` for 64-bit x86 + SSE Extensions. #[allow(non_camel_case_types, clippy::upper_case_acronyms)] pub enum X86_64_SSE {} impl Arch for X86_64_SSE { type Usize = u64; type Registers = reg::X86_64CoreRegs; type RegId = reg::id::X86_64CoreRegId; type BreakpointKind = usize; fn target_description_xml() -> Option<&'static str> { Some( r#"i386:x86-64"#, ) } } /// Implements `Arch` for 32-bit x86 + SSE Extensions. #[allow(non_camel_case_types, clippy::upper_case_acronyms)] pub enum X86_SSE {} impl Arch for X86_SSE { type Usize = u32; type Registers = reg::X86CoreRegs; type RegId = reg::id::X86CoreRegId; type BreakpointKind = usize; fn target_description_xml() -> Option<&'static str> { Some( r#"i386:intel"#, ) } } ================================================ FILE: gdbstub_arch/src/x86/reg/core32.rs ================================================ use super::X86SegmentRegs; use super::X87FpuInternalRegs; use super::F80; use core::convert::TryInto; use gdbstub::arch::Registers; /// 32-bit x86 core registers (+ SSE extensions). /// /// Source: /// Additionally: #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct X86CoreRegs { /// Accumulator pub eax: u32, /// Count register pub ecx: u32, /// Data register pub edx: u32, /// Base register pub ebx: u32, /// Stack pointer pub esp: u32, /// Base pointer pub ebp: u32, /// Source index pub esi: u32, /// Destination index pub edi: u32, /// Instruction pointer pub eip: u32, /// Status register pub eflags: u32, /// Segment registers: CS, SS, DS, ES, FS, GS pub segments: X86SegmentRegs, /// FPU registers: ST0 through ST7 pub st: [F80; 8], /// FPU internal registers pub fpu: X87FpuInternalRegs, /// SIMD Registers: XMM0 through XMM7 pub xmm: [u128; 8], /// SSE Status/Control Register pub mxcsr: u32, } impl Registers for X86CoreRegs { type ProgramCounter = u32; fn pc(&self) -> Self::ProgramCounter { self.eip } fn gdb_serialize(&self, mut write_byte: impl FnMut(Option)) { macro_rules! write_bytes { ($bytes:expr) => { for b in $bytes { write_byte(Some(*b)) } }; } macro_rules! write_regs { ($($reg:ident),*) => { $( write_bytes!(&self.$reg.to_le_bytes()); )* } } write_regs!(eax, ecx, edx, ebx, esp, ebp, esi, edi, eip, eflags); self.segments.gdb_serialize(&mut write_byte); // st0 to st7 for st_reg in &self.st { write_bytes!(st_reg); } self.fpu.gdb_serialize(&mut write_byte); // xmm0 to xmm15 for xmm_reg in &self.xmm { write_bytes!(&xmm_reg.to_le_bytes()); } // mxcsr write_bytes!(&self.mxcsr.to_le_bytes()); } fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { if bytes.len() < 0x134 { return Err(()); } macro_rules! parse_regs { ($($reg:ident),*) => { let mut regs = bytes[0..0x28] .chunks_exact(4) .map(|x| u32::from_le_bytes(x.try_into().unwrap())); $( self.$reg = regs.next().ok_or(())?; )* } } parse_regs!(eax, ecx, edx, ebx, esp, ebp, esi, edi, eip, eflags); self.segments.gdb_deserialize(&bytes[0x28..0x40])?; let mut regs = bytes[0x40..0x90].chunks_exact(10).map(TryInto::try_into); for reg in self.st.iter_mut() { *reg = regs.next().ok_or(())?.map_err(|_| ())?; } self.fpu.gdb_deserialize(&bytes[0x90..0xb0])?; let mut regs = bytes[0xb0..0x130] .chunks_exact(0x10) .map(|x| u128::from_le_bytes(x.try_into().unwrap())); for reg in self.xmm.iter_mut() { *reg = regs.next().ok_or(())?; } self.mxcsr = u32::from_le_bytes(bytes[0x130..0x134].try_into().unwrap()); Ok(()) } } #[cfg(test)] mod tests { use super::*; #[test] fn x86_core_round_trip() { let regs_before = X86CoreRegs { eax: 1, ecx: 2, edx: 3, ebx: 4, esp: 5, ebp: 6, esi: 7, edi: 8, eip: 9, eflags: 10, segments: X86SegmentRegs { cs: 11, ss: 12, ds: 13, es: 14, fs: 15, gs: 16, }, st: Default::default(), fpu: X87FpuInternalRegs { fctrl: 17, fstat: 18, ftag: 19, fiseg: 20, fioff: 21, foseg: 22, fooff: 23, fop: 24, }, xmm: Default::default(), mxcsr: 99, }; let mut data = vec![]; regs_before.gdb_serialize(|x| { data.push(x.unwrap_or(b'x')); }); let mut regs_after = X86CoreRegs::default(); regs_after.gdb_deserialize(&data).unwrap(); assert_eq!(regs_before, regs_after); } } ================================================ FILE: gdbstub_arch/src/x86/reg/core64.rs ================================================ use super::X86SegmentRegs; use super::X87FpuInternalRegs; use super::F80; use core::convert::TryInto; use gdbstub::arch::Registers; /// 64-bit x86 core registers (+ SSE extensions). /// /// Source: /// Additionally: #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct X86_64CoreRegs { /// RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, r8-r15 pub regs: [u64; 16], /// Status register pub eflags: u32, /// Instruction pointer pub rip: u64, /// Segment registers: CS, SS, DS, ES, FS, GS pub segments: X86SegmentRegs, /// FPU registers: ST0 through ST7 pub st: [F80; 8], /// FPU internal registers pub fpu: X87FpuInternalRegs, /// SIMD Registers: XMM0 through XMM15 pub xmm: [u128; 0x10], /// SSE Status/Control Register pub mxcsr: u32, } impl Registers for X86_64CoreRegs { type ProgramCounter = u64; fn pc(&self) -> Self::ProgramCounter { self.rip } fn gdb_serialize(&self, mut write_byte: impl FnMut(Option)) { macro_rules! write_bytes { ($bytes:expr) => { for b in $bytes { write_byte(Some(*b)) } }; } // rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, r8-r15 for reg in &self.regs { write_bytes!(®.to_le_bytes()); } // rip write_bytes!(&self.rip.to_le_bytes()); // eflags write_bytes!(&self.eflags.to_le_bytes()); self.segments.gdb_serialize(&mut write_byte); // st0 to st7 for st_reg in &self.st { write_bytes!(st_reg); } self.fpu.gdb_serialize(&mut write_byte); // xmm0 to xmm15 for xmm_reg in &self.xmm { write_bytes!(&xmm_reg.to_le_bytes()); } // mxcsr write_bytes!(&self.mxcsr.to_le_bytes()); } fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { if bytes.len() < 0x218 { return Err(()); } let mut regs = bytes[0..0x80] .chunks_exact(8) .map(|x| u64::from_le_bytes(x.try_into().unwrap())); for reg in self.regs.iter_mut() { *reg = regs.next().ok_or(())?; } self.rip = u64::from_le_bytes(bytes[0x80..0x88].try_into().unwrap()); self.eflags = u32::from_le_bytes(bytes[0x88..0x8C].try_into().unwrap()); self.segments.gdb_deserialize(&bytes[0x8C..0xA4])?; let mut regs = bytes[0xA4..0xF4].chunks_exact(10).map(TryInto::try_into); for reg in self.st.iter_mut() { *reg = regs.next().ok_or(())?.map_err(|_| ())?; } self.fpu.gdb_deserialize(&bytes[0xF4..0x114])?; let mut regs = bytes[0x114..0x214] .chunks_exact(0x10) .map(|x| u128::from_le_bytes(x.try_into().unwrap())); for reg in self.xmm.iter_mut() { *reg = regs.next().ok_or(())?; } self.mxcsr = u32::from_le_bytes(bytes[0x214..0x218].try_into().unwrap()); Ok(()) } } #[cfg(test)] mod tests { use super::*; #[test] fn x86_64_core_round_trip() { let regs_before = X86_64CoreRegs { regs: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], eflags: 17, rip: 18, segments: X86SegmentRegs { cs: 19, ss: 20, ds: 21, es: 22, fs: 23, gs: 24, }, st: Default::default(), fpu: X87FpuInternalRegs { fctrl: 25, fstat: 26, ftag: 27, fiseg: 28, fioff: 29, foseg: 30, fooff: 31, fop: 32, }, xmm: Default::default(), mxcsr: 99, }; let mut data = vec![]; regs_before.gdb_serialize(|x| { data.push(x.unwrap_or(b'x')); }); let mut regs_after = X86_64CoreRegs::default(); regs_after.gdb_deserialize(&data).unwrap(); assert_eq!(regs_before, regs_after); } } ================================================ FILE: gdbstub_arch/src/x86/reg/id.rs ================================================ use core::num::NonZeroUsize; use gdbstub::arch::RegId; /// FPU register identifier. #[derive(Debug, Clone, Copy)] pub enum X87FpuInternalRegId { /// Floating-point control register Fctrl, /// Floating-point status register Fstat, /// Tag word Ftag, /// FPU instruction pointer segment Fiseg, /// FPU instruction pointer offset Fioff, /// FPU operand segment Foseg, /// FPU operand offset Fooff, /// Floating-point opcode Fop, } impl X87FpuInternalRegId { fn from_u8(val: u8) -> Option { use self::X87FpuInternalRegId::*; let r = match val { 0 => Fctrl, 1 => Fstat, 2 => Ftag, 3 => Fiseg, 4 => Fioff, 5 => Foseg, 6 => Fooff, 7 => Fop, _ => return None, }; Some(r) } } /// Segment register identifier. #[derive(Debug, Clone, Copy)] #[allow(clippy::upper_case_acronyms)] pub enum X86SegmentRegId { /// Code Segment CS, /// Stack Segment SS, /// Data Segment DS, /// Extra Segment ES, /// General Purpose Segment FS, /// General Purpose Segment GS, } impl X86SegmentRegId { fn from_u8(val: u8) -> Option { use self::X86SegmentRegId::*; let r = match val { 0 => CS, 1 => SS, 2 => DS, 3 => ES, 4 => FS, 5 => GS, _ => return None, }; Some(r) } } /// 32-bit x86 core + SSE register identifier. /// /// Source: /// Additionally: #[derive(Debug, Clone, Copy)] #[non_exhaustive] pub enum X86CoreRegId { /// Accumulator Eax, /// Count register Ecx, /// Data register Edx, /// Base register Ebx, /// Stack pointer Esp, /// Base pointer Ebp, /// Source index Esi, /// Destination index Edi, /// Instruction pointer Eip, /// Status register Eflags, /// Segment registers Segment(X86SegmentRegId), /// FPU registers: ST0 through ST7 St(u8), /// FPU internal registers Fpu(X87FpuInternalRegId), /// SIMD Registers: XMM0 through XMM7 Xmm(u8), /// SSE Status/Control Register Mxcsr, } impl RegId for X86CoreRegId { fn from_raw_id(id: usize) -> Option<(Self, Option)> { use self::X86CoreRegId::*; let (r, sz): (X86CoreRegId, usize) = match id { 0 => (Eax, 4), 1 => (Ecx, 4), 2 => (Edx, 4), 3 => (Ebx, 4), 4 => (Esp, 4), 5 => (Ebp, 4), 6 => (Esi, 4), 7 => (Edi, 4), 8 => (Eip, 4), 9 => (Eflags, 4), 10..=15 => (Segment(X86SegmentRegId::from_u8(id as u8 - 10)?), 4), 16..=23 => (St(id as u8 - 16), 10), 24..=31 => (Fpu(X87FpuInternalRegId::from_u8(id as u8 - 24)?), 4), 32..=39 => (Xmm(id as u8 - 32), 16), 40 => (Mxcsr, 4), _ => return None, }; Some((r, Some(NonZeroUsize::new(sz)?))) } } /// 64-bit x86 core + SSE register identifier. /// /// Source: /// Additionally: #[derive(Debug, Clone, Copy)] #[non_exhaustive] pub enum X86_64CoreRegId { /// General purpose registers: /// RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, r8-r15 Gpr(u8), /// Instruction pointer Rip, /// Status register Eflags, /// Segment registers Segment(X86SegmentRegId), /// FPU registers: ST0 through ST7 St(u8), /// FPU internal registers Fpu(X87FpuInternalRegId), /// SIMD Registers: XMM0 through XMM15 Xmm(u8), /// SSE Status/Control Register Mxcsr, } impl RegId for X86_64CoreRegId { fn from_raw_id(id: usize) -> Option<(Self, Option)> { use self::X86_64CoreRegId::*; let (r, sz): (X86_64CoreRegId, usize) = match id { 0..=15 => (Gpr(id as u8), 8), 16 => (Rip, 8), 17 => (Eflags, 4), 18..=23 => (Segment(X86SegmentRegId::from_u8(id as u8 - 18)?), 4), 24..=31 => (St(id as u8 - 24), 10), 32..=39 => (Fpu(X87FpuInternalRegId::from_u8(id as u8 - 32)?), 4), 40..=55 => (Xmm(id as u8 - 40), 16), 56 => (Mxcsr, 4), _ => return None, }; Some((r, Some(NonZeroUsize::new(sz)?))) } } #[cfg(test)] mod tests { use gdbstub::arch::RegId; use gdbstub::arch::Registers; /// Compare the following two values which are expected to be the same: /// * length of data written by `Registers::gdb_serialize()` in byte /// * sum of sizes of all registers obtained by `RegId::from_raw_id()` fn test() { // Obtain the data length written by `gdb_serialize` by passing a custom // closure. let mut serialized_data_len = 0; let counter = |b: Option| { if b.is_some() { serialized_data_len += 1; } }; Rs::default().gdb_serialize(counter); // Accumulate register sizes returned by `from_raw_id`. let mut i = 0; let mut sum_reg_sizes = 0; while let Some((_, size)) = RId::from_raw_id(i) { sum_reg_sizes += size.unwrap().get(); i += 1; } assert_eq!(serialized_data_len, sum_reg_sizes); } #[test] fn test_x86() { test::() } #[test] fn test_x86_64() { test::() } } ================================================ FILE: gdbstub_arch/src/x86/reg/mod.rs ================================================ //! `Register` structs for x86 architectures. use core::convert::TryInto; use gdbstub::arch::Registers; /// `RegId` definitions for x86 architectures. pub mod id; mod core32; mod core64; pub use core32::X86CoreRegs; pub use core64::X86_64CoreRegs; /// 80-bit floating point value pub type F80 = [u8; 10]; /// FPU registers #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct X87FpuInternalRegs { /// Floating-point control register pub fctrl: u32, /// Floating-point status register pub fstat: u32, /// Tag word pub ftag: u32, /// FPU instruction pointer segment pub fiseg: u32, /// FPU instruction pointer offset pub fioff: u32, /// FPU operand segment pub foseg: u32, /// FPU operand offset pub fooff: u32, /// Floating-point opcode pub fop: u32, } impl Registers for X87FpuInternalRegs { type ProgramCounter = u32; // HACK: this struct is never used as an architecture's main register file, so // using a dummy value here is fine. fn pc(&self) -> Self::ProgramCounter { 0 } fn gdb_serialize(&self, mut write_byte: impl FnMut(Option)) { macro_rules! write_bytes { ($bytes:expr) => { for b in $bytes { write_byte(Some(*b)) } }; } // Note: GDB section names don't make sense unless you read x87 FPU section 8.1: // https://web.archive.org/web/20150123212110/http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-1-manual.pdf write_bytes!(&self.fctrl.to_le_bytes()); write_bytes!(&self.fstat.to_le_bytes()); write_bytes!(&self.ftag.to_le_bytes()); write_bytes!(&self.fiseg.to_le_bytes()); write_bytes!(&self.fioff.to_le_bytes()); write_bytes!(&self.foseg.to_le_bytes()); write_bytes!(&self.fooff.to_le_bytes()); write_bytes!(&self.fop.to_le_bytes()); } fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { if bytes.len() != 0x20 { return Err(()); } let mut regs = bytes .chunks_exact(4) .map(|x| u32::from_le_bytes(x.try_into().unwrap())); self.fctrl = regs.next().ok_or(())?; self.fstat = regs.next().ok_or(())?; self.ftag = regs.next().ok_or(())?; self.fiseg = regs.next().ok_or(())?; self.fioff = regs.next().ok_or(())?; self.foseg = regs.next().ok_or(())?; self.fooff = regs.next().ok_or(())?; self.fop = regs.next().ok_or(())?; Ok(()) } } /// x86 segment registers. /// /// Source: #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct X86SegmentRegs { /// Code Segment pub cs: u32, /// Stack Segment pub ss: u32, /// Data Segment pub ds: u32, /// Extra Segment pub es: u32, /// General Purpose Segment pub fs: u32, /// General Purpose Segment pub gs: u32, } impl Registers for X86SegmentRegs { type ProgramCounter = u32; // HACK: this struct is never used as an architecture's main register file, so // using a dummy value here is fine. fn pc(&self) -> Self::ProgramCounter { 0 } fn gdb_serialize(&self, mut write_byte: impl FnMut(Option)) { macro_rules! write_bytes { ($bytes:expr) => { for b in $bytes { write_byte(Some(*b)) } }; } write_bytes!(&self.cs.to_le_bytes()); write_bytes!(&self.ss.to_le_bytes()); write_bytes!(&self.ds.to_le_bytes()); write_bytes!(&self.es.to_le_bytes()); write_bytes!(&self.fs.to_le_bytes()); write_bytes!(&self.gs.to_le_bytes()); } fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { if bytes.len() != core::mem::size_of::() * 6 { return Err(()); } let mut regs = bytes .chunks_exact(4) .map(|x| u32::from_le_bytes(x.try_into().unwrap())); self.cs = regs.next().ok_or(())?; self.ss = regs.next().ok_or(())?; self.ds = regs.next().ok_or(())?; self.es = regs.next().ok_or(())?; self.fs = regs.next().ok_or(())?; self.gs = regs.next().ok_or(())?; Ok(()) } } ================================================ FILE: rustfmt.toml ================================================ imports_granularity = "Item" wrap_comments = true ================================================ FILE: scripts/check_target_delegation.sh ================================================ #!/bin/bash set -e # Script to enforce that each Target method has a corresponding entry in impl_dyn_target # Get the list of methods in the Target trait target_methods=$(awk '/pub trait Target {/,/^}/' src/target/mod.rs | grep '^ fn ' | sed 's/ fn \([a-zA-Z_][a-zA-Z0-9_]*\).*/\1/') # Get the list of delegated methods in impl_dyn_target delegated_methods=$(awk '/macro_rules! impl_dyn_target {/,/^}/' src/target/mod.rs | grep '__delegate!\|__delegate_support!' | sed -n 's/.*fn \([a-zA-Z_][a-zA-Z0-9_]*\).*/\1/p; s/__delegate_support!(\([a-zA-Z_][a-zA-Z0-9_]*\).*/support_\1/p' | sed 's/^ *//') # Check for missing delegations missing="" for method in $target_methods; do if ! echo "$delegated_methods" | grep -q "^$method$"; then missing="$missing $method" fi done if [ -n "$missing" ]; then echo "Error: Missing delegations for methods:$missing" exit 1 fi echo "All Target methods have corresponding delegations in impl_dyn_target." ================================================ FILE: scripts/test_dead_code_elim.sh ================================================ #!/bin/bash # Must be run from the project's root directory. # checks if a certain packet has been dead-code-eliminated from the resulting binary. # Arg 1: example to build # Arg 2: packet name if [ -z "$1" ]; then echo "Must pass example name as first argument (e.g: armv4t)" exit 1 fi if [ -z "$2" ]; then echo "Must pass packet name as second argument (e.g: qRcmd)" exit 1 fi cargo build --release --example $1 --features="std __dead_code_marker" strip ./target/release/examples/$1 output=$(strings ./target/release/examples/$1 | sort | grep --color=always "<$2,") if [[ $output ]]; then echo $output echo "Dead code NOT eliminated!" exit 1 else echo "Dead code eliminated." exit 0 fi ================================================ FILE: src/arch.rs ================================================ //! Traits to encode architecture-specific target information. //! //! # Community created `Arch` Implementations //! //! Before getting your hands dirty and implementing a new `Arch` from scratch, //! make sure to check out [`gdbstub_arch`](https://docs.rs/gdbstub_arch), a //! companion crate to `gdbstub` which aggregates community-created `Arch` //! implementations for most common architectures! //! //! > _Note:_ Prior to `gdbstub 0.5`, `Arch` implementations were distributed as //! > a part of the main `gdbstub` crate (under the `gdbstub::arch` module). //! > //! > This wasn't ideal, any `gdbstub::arch`-level breaking-changes forced the //! > _entire_ `gdbstub` crate to release a new (potentially breaking!) version. //! > //! > 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; use num_traits::PrimInt; use num_traits::Unsigned; /// Register identifier for target registers. /// /// These identifiers are used by GDB to signal which register to read/wite when /// performing [single register accesses]. /// /// [single register accesses]: /// crate::target::ext::base::single_register_access::SingleRegisterAccess pub trait RegId: Sized + Debug { /// Map raw GDB register number to a corresponding `RegId` and optional /// register size. /// /// If the register size is specified here, gdbstub will include a runtime /// check that ensures target implementations do not send back more /// bytes than the register allows. /// /// Returns `None` if the register is not available. fn from_raw_id(id: usize) -> Option<(Self, Option)>; /// Map a `RegId` back to a raw GDB register number. /// /// Returns `None` if this mapping direction is not implemented. /// /// This method currently only needs to return `Some` for a register if that /// register is sent with /// [`GdbStubStateMachineInner::report_stop_with_regs`]. /// /// [`GdbStubStateMachineInner::report_stop_with_regs`]: /// crate::stub::state_machine::GdbStubStateMachineInner::report_stop_with_regs fn to_raw_id(&self) -> Option { None } } /// Stub implementation -- Returns `None` for all raw IDs. impl RegId for () { fn from_raw_id(_id: usize) -> Option<(Self, Option)> { None } } /// Methods to read/write architecture-specific registers. /// /// Registers must be de/serialized in the order specified by the architecture's /// `.xml` in the GDB source tree. /// /// e.g: for ARM: /// github.com/bminor/binutils-gdb/blob/master/gdb/features/arm/arm-core.xml // TODO: add way to de/serialize arbitrary "missing"/"uncollected" registers. pub trait Registers: Default + Debug + Clone + PartialEq { /// The type of the architecture's program counter / instruction pointer. /// Must match with the corresponding `Arch::Usize`. type ProgramCounter: Copy; /// Return the value of the program counter / instruction pointer. fn pc(&self) -> Self::ProgramCounter; /// Serialize `self` into a GDB register bytestream. /// /// Missing registers are serialized by passing `None` to write_byte. fn gdb_serialize(&self, write_byte: impl FnMut(Option)); /// Deserialize a GDB register bytestream into `self`. #[allow(clippy::result_unit_err)] fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()>; } /// Breakpoint kind for specific architectures. /// /// This trait corresponds to the _kind_ field of the "z" and "Z" breakpoint /// packets, as documented [here](https://sourceware.org/gdb/onlinedocs/gdb/Packets.html#insert-breakpoint-or-watchpoint-packet). /// /// A breakpoint "kind" is architecture-specific and typically indicates the /// size of the breakpoint in bytes that should be inserted. As such, most /// architectures will set `BreakpointKind = usize`. /// /// Some architectures, such as ARM and MIPS, have additional meanings for /// _kind_. See the [Architecture-Specific Protocol Details](https://sourceware.org/gdb/current/onlinedocs/gdb/Architecture_002dSpecific-Protocol-Details.html#Architecture_002dSpecific-Protocol-Details) /// section of the GBD documentation for more details. /// /// If no architecture-specific value is being used, _kind_ should be set to /// '0', and the `BreakpointKind` associated type should be `()`. pub trait BreakpointKind: Sized + Debug { /// Parse `Self` from a raw usize. fn from_usize(kind: usize) -> Option; } impl BreakpointKind for () { fn from_usize(kind: usize) -> Option { if kind != 0 { None } else { Some(()) } } } impl BreakpointKind for usize { #[allow(clippy::wrong_self_convention)] fn from_usize(kind: usize) -> Option { Some(kind) } } /// Encodes architecture-specific information, such as pointer size, register /// layout, etc... /// /// Types implementing `Arch` should be /// [Zero-variant Enums](https://doc.rust-lang.org/reference/items/enumerations.html#zero-variant-enums), /// as `Arch` impls are only ever used at the type level, and should never be /// explicitly instantiated. pub trait Arch { /// The architecture's pointer size (e.g: `u32` on a 32-bit system). type Usize: Debug + FromPrimitive + PrimInt + Unsigned + BeBytes + LeBytes; /// The architecture's register file. See [`Registers`] for more details. type Registers: Registers; /// The architecture's breakpoint "kind", used to determine the "size" /// of breakpoint to set. See [`BreakpointKind`] for more details. type BreakpointKind: BreakpointKind; /// Register identifier enum/struct. /// /// Used to access individual registers via `Target::read/write_register`. /// /// > NOTE: An arch's `RegId` type is not strictly required to have a 1:1 /// > correspondence with the `Registers` type, and may include register /// > identifiers which are separate from the main `Registers` structure. /// > (e.g: the RISC-V Control and Status registers) type RegId: RegId; /// (optional) Return the arch's description XML file (`target.xml`). /// /// Implementing this method enables GDB to automatically detect the /// target's architecture, saving the hassle of having to run `set /// architecture ` when starting a debugging session. /// /// These descriptions can be quite succinct. For example, the target /// description for an `armv4t` target can be as simple as: /// /// ``` /// r#"armv4t"#; /// ``` /// /// See the [GDB docs](https://sourceware.org/gdb/current/onlinedocs/gdb/Target-Description-Format.html) /// for details on the target description XML format. #[inline(always)] fn target_description_xml() -> Option<&'static str> { None } /// (optional) (LLDB extension) Return register info for the specified /// register. /// /// Implementing this method enables LLDB to dynamically query the target's /// register information one by one. /// /// Some targets don't have register context in the compiled version of the /// debugger. Help the debugger by dynamically supplying the register info /// from the target. The debugger will request the register info in a /// sequential manner till an error packet is received. In LLDB, the /// register info search has the following /// [order](https://github.com/llvm/llvm-project/blob/369ce54bb302f209239b8ebc77ad824add9df089/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp#L397-L402): /// /// 1. Use the target definition python file if one is specified. /// 2. If the target definition doesn't have any of the info from the /// target.xml (registers) then proceed to read the `target.xml`. /// 3. Fall back on the `qRegisterInfo` packets. /// 4. Use hardcoded defaults if available. /// /// See the LLDB [gdb-remote docs](https://github.com/llvm-mirror/lldb/blob/d01083a850f577b85501a0902b52fd0930de72c7/docs/lldb-gdb-remote.txt#L396) /// for more details on the available information that a single register can /// be described by and [#99](https://github.com/daniel5151/gdbstub/issues/99) /// for more information on LLDB compatibility. #[inline(always)] fn lldb_register_info(reg_id: usize) -> Option> { let _ = reg_id; None } } /// LLDB-specific types supporting [`Arch::lldb_register_info`] and /// [`LldbRegisterInfoOverride`] APIs. /// /// [`LldbRegisterInfoOverride`]: crate::target::ext::lldb_register_info_override::LldbRegisterInfoOverride pub mod lldb { /// The architecture's register information of a single register. pub enum RegisterInfo<'a> { /// The register info of a single register that should be written. Register(Register<'a>), /// The `qRegisterInfo` query shall be concluded. Done, } /// Describes the register info for a single register of /// the target. pub struct Register<'a> { /// The primary register name. pub name: &'a str, /// An alternate name for the register. pub alt_name: Option<&'a str>, /// Size in bits of a register. pub bitsize: usize, /// The offset within the 'g' and 'G' packet of the register data for /// this register. pub offset: usize, /// The encoding type of the register. pub encoding: Encoding, /// The preferred format for display of this register. pub format: Format, /// The register set name this register belongs to. pub set: &'a str, /// The GCC compiler registers number for this register. /// /// _Note:_ This denotes the same `KEY:VALUE;` pair as `ehframe:VALUE;`. /// See the LLDB [source](https://github.com/llvm/llvm-project/blob/b92436efcb7813fc481b30f2593a4907568d917a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp#L493). pub gcc: Option, /// The DWARF register number for this register that is used for this /// register in the debug information. pub dwarf: Option, /// Specify as a generic register. pub generic: Option, /// Other concrete register values this register is contained in. pub container_regs: Option<&'a [usize]>, /// Specifies which register values should be invalidated when this /// register is modified. pub invalidate_regs: Option<&'a [usize]>, } /// Describes the encoding type of the register. #[non_exhaustive] pub enum Encoding { /// Unsigned integer Uint, /// Signed integer Sint, /// IEEE 754 float IEEE754, /// Vector register Vector, } /// Describes the preferred format for display of this register. #[non_exhaustive] pub enum Format { /// Binary format Binary, /// Decimal format Decimal, /// Hexadecimal format Hex, /// Floating point format Float, /// 8 bit signed int vector VectorSInt8, /// 8 bit unsigned int vector VectorUInt8, /// 16 bit signed int vector VectorSInt16, /// 16 bit unsigned int vector VectorUInt16, /// 32 bit signed int vector VectorSInt32, /// 32 bit unsigned int vector VectorUInt32, /// 32 bit floating point vector VectorFloat32, /// 128 bit unsigned int vector VectorUInt128, } /// Describes the generic types that most CPUs have. #[non_exhaustive] pub enum Generic { /// Program counter register Pc, /// Stack pointer register Sp, /// Frame pointer register Fp, /// Return address register Ra, /// CPU flags register Flags, /// Function argument 1 Arg1, /// Function argument 2 Arg2, /// Function argument 3 Arg3, /// Function argument 4 Arg4, /// Function argument 5 Arg5, /// Function argument 6 Arg6, /// Function argument 7 Arg7, /// Function argument 8 Arg8, } } ================================================ FILE: src/common/mod.rs ================================================ //! Common types and definitions used across `gdbstub`. mod signal; pub use self::signal::Signal; use core::num::NonZeroUsize; /// Thread ID (as viewed by GDB) /// /// The choice to use a [`NonZeroUsize`] stems from the [GDB RSP Packet /// documentation], which states that thread IDs are "positive numbers with a /// target-specific interpretation". /// /// Target implementations may wish to map `Tid`s to/from their own /// target-specific thread ID type. (e.g: an emulator might treat `Tid` as a CPU /// index). /// /// [GDB RSP Packet documentation]: /// https://sourceware.org/gdb/current/onlinedocs/gdb.html/Packets.html#Packets pub type Tid = NonZeroUsize; /// Process ID (as viewed by GDB) /// /// The choice to use a [`NonZeroUsize`] stems from the [GDB RSP Packet /// documentation], which states that process IDs are "positive numbers with a /// target-specific interpretation". /// /// Target implementations may wish to map `Pid`s to/from their own /// target-specific process ID type. /// /// [GDB RSP Packet documentation]: /// https://sourceware.org/gdb/current/onlinedocs/gdb.html/Packets.html#Packets pub type Pid = NonZeroUsize; /// Endianness. /// /// This is used to report target endianness to the debugger as a /// response to certain commands. #[derive(Clone, Copy, Debug)] pub enum Endianness { /// Big-endian. Big, /// Little-endian. Little, } ================================================ FILE: src/common/signal.rs ================================================ /// Cross-platform signal numbers defined by the GDB Remote Serial Protocol. /// /// Transcribed from #[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] 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"] 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"] 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"] pub const SIG32: Self = Self(77); /* Yet another pain, IRIX 6 has SIG64. */ #[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"] 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"] pub const SIGINFO: Self = Self(142); /* Some signal we don't know about. */ #[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"] 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"] 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"] 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", 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", _ => "custom signal (not defined in GDB's signals.def file)" }; write!(f, "{}", s) } } ================================================ FILE: src/conn/impls/boxed.rs ================================================ use crate::conn::Connection; use crate::conn::ConnectionExt; use alloc::boxed::Box; impl Connection for Box> { type Error = E; fn write(&mut self, byte: u8) -> Result<(), Self::Error> { (**self).write(byte) } fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { (**self).write_all(buf) } fn flush(&mut self) -> Result<(), Self::Error> { (**self).flush() } fn on_session_start(&mut self) -> Result<(), Self::Error> { (**self).on_session_start() } } impl Connection for Box> { type Error = E; fn write(&mut self, byte: u8) -> Result<(), Self::Error> { (**self).write(byte) } fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { (**self).write_all(buf) } fn flush(&mut self) -> Result<(), Self::Error> { (**self).flush() } fn on_session_start(&mut self) -> Result<(), Self::Error> { (**self).on_session_start() } } impl ConnectionExt for Box> { fn read(&mut self) -> Result { (**self).read() } fn peek(&mut self) -> Result, Self::Error> { (**self).peek() } } ================================================ FILE: src/conn/impls/mod.rs ================================================ //! Implementations of the [`Connection`] trait for various built-in types // TODO: impl Connection for all `Read + Write` (blocked on specialization) #[cfg(feature = "alloc")] mod boxed; #[cfg(feature = "std")] mod tcpstream; #[cfg(all(feature = "std", unix))] mod unixstream; use crate::conn::Connection; use crate::conn::ConnectionExt; impl Connection for &mut dyn Connection { type Error = E; fn write(&mut self, byte: u8) -> Result<(), Self::Error> { (**self).write(byte) } fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { (**self).write_all(buf) } fn flush(&mut self) -> Result<(), Self::Error> { (**self).flush() } fn on_session_start(&mut self) -> Result<(), Self::Error> { (**self).on_session_start() } } impl Connection for &mut dyn ConnectionExt { type Error = E; fn write(&mut self, byte: u8) -> Result<(), Self::Error> { (**self).write(byte) } fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { (**self).write_all(buf) } fn flush(&mut self) -> Result<(), Self::Error> { (**self).flush() } fn on_session_start(&mut self) -> Result<(), Self::Error> { (**self).on_session_start() } } impl ConnectionExt for &mut dyn ConnectionExt { fn read(&mut self) -> Result { (**self).read() } fn peek(&mut self) -> Result, Self::Error> { (**self).peek() } } ================================================ FILE: src/conn/impls/tcpstream.rs ================================================ use crate::conn::Connection; use crate::conn::ConnectionExt; use std::net::TcpStream; impl Connection for TcpStream { type Error = std::io::Error; fn write(&mut self, byte: u8) -> Result<(), Self::Error> { use std::io::Write; Write::write_all(self, &[byte]) } fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { use std::io::Write; Write::write_all(self, buf) } fn flush(&mut self) -> Result<(), Self::Error> { use std::io::Write; Write::flush(self) } fn on_session_start(&mut self) -> Result<(), Self::Error> { // see issue #28 self.set_nodelay(true) } } impl ConnectionExt for TcpStream { fn read(&mut self) -> Result { use std::io::Read; self.set_nonblocking(false)?; let mut buf = [0u8]; match Read::read_exact(self, &mut buf) { Ok(_) => Ok(buf[0]), Err(e) => Err(e), } } fn peek(&mut self) -> Result, Self::Error> { self.set_nonblocking(true)?; let mut buf = [0u8]; match Self::peek(self, &mut buf) { Ok(_) => Ok(Some(buf[0])), Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => Ok(None), Err(e) => Err(e), } } } ================================================ FILE: src/conn/impls/unixstream.rs ================================================ 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 { fn peek(&self, buf: &mut [u8]) -> io::Result; } impl PeekExt for UnixStream { #[cfg(feature = "paranoid_unsafe")] #[allow(clippy::panic)] fn peek(&self, _buf: &mut [u8]) -> io::Result { panic!("cannot use `UnixStream::peek` with `paranoid_unsafe` until rust-lang/rust#73761 is stabilized"); } #[cfg(not(feature = "paranoid_unsafe"))] #[allow(non_camel_case_types)] fn peek(&self, buf: &mut [u8]) -> io::Result { use core::ffi::c_void; use std::os::unix::io::AsRawFd; // Define some libc types inline (to avoid bringing in entire libc dep) // every platform supported by the libc crate uses c_int = i32 type c_int = i32; type size_t = usize; type ssize_t = isize; const MSG_PEEK: c_int = 2; extern "C" { fn recv(socket: c_int, buf: *mut c_void, len: size_t, flags: c_int) -> ssize_t; } // from std/sys/unix/mod.rs pub fn cvt(t: isize) -> io::Result { if t == -1 { Err(io::Error::last_os_error()) } else { Ok(t) } } // from std/sys/unix/net.rs let ret = cvt(unsafe { recv( self.as_raw_fd(), buf.as_mut_ptr() as *mut c_void, buf.len(), MSG_PEEK, ) })?; Ok(ret as usize) } } impl Connection for UnixStream { type Error = std::io::Error; fn write(&mut self, byte: u8) -> Result<(), Self::Error> { use std::io::Write; Write::write_all(self, &[byte]) } fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { use std::io::Write; Write::write_all(self, buf) } fn flush(&mut self) -> Result<(), Self::Error> { use std::io::Write; Write::flush(self) } } impl ConnectionExt for UnixStream { fn read(&mut self) -> Result { use std::io::Read; self.set_nonblocking(false)?; let mut buf = [0u8]; match Read::read_exact(self, &mut buf) { Ok(_) => Ok(buf[0]), Err(e) => Err(e), } } fn peek(&mut self) -> Result, Self::Error> { self.set_nonblocking(true)?; let mut buf = [0u8]; match PeekExt::peek(self, &mut buf) { Ok(_) => Ok(Some(buf[0])), Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => Ok(None), Err(e) => Err(e), } } } ================================================ FILE: src/conn/mod.rs ================================================ //! Traits to perform in-order, serial, byte-wise I/O. mod impls; /// A trait to perform in-order, serial, byte-wise I/O. /// /// When the `std` feature is enabled, this trait is automatically implemented /// for [`TcpStream`](std::net::TcpStream) and /// [`UnixStream`](std::os::unix::net::UnixStream) (on unix systems). pub trait Connection { /// Transport-specific error type. type Error; /// Write a single byte. fn write(&mut self, byte: u8) -> Result<(), Self::Error>; /// Write the entire buffer, blocking until complete. /// /// This method's default implementation calls `self.write()` on each byte /// in the buffer. This can be quite inefficient, so if a more efficient /// implementation exists (such as calling `write_all()` on an underlying /// `std::io::Write` object), this method should be overwritten. fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { for b in buf { self.write(*b)?; } Ok(()) } /// Flush this Connection, ensuring that all intermediately buffered /// contents reach their destination. /// /// _Note:_ Not all `Connection`s have internal buffering (e.g: writing data /// to a UART TX register with FIFOs disabled). In these cases, it's fine to /// simply return `Ok(())`. fn flush(&mut self) -> Result<(), Self::Error>; /// Called at the start of a debugging session _before_ any GDB packets have /// been sent/received. /// /// This method's default implementation is a no-op. /// /// # Example /// /// The `on_session_start` implementation for `TcpStream` ensures that /// [`set_nodelay(true)`](std::net::TcpStream::set_nodelay) /// is called. The GDB remote serial protocol requires sending/receiving /// many small packets, so forgetting to enable `TCP_NODELAY` can result in /// a massively degraded debugging experience. fn on_session_start(&mut self) -> Result<(), Self::Error> { Ok(()) } } /// Extends [`Connection`] with `read` and `peek` methods. /// /// This trait is used as part of `gdbstub`'s quickstart /// [`GdbStub::run_blocking`](crate::stub::GdbStub::run_blocking) API. /// /// When the `std` feature is enabled, this trait is automatically implemented /// for [`TcpStream`](std::net::TcpStream) and /// [`UnixStream`](std::os::unix::net::UnixStream) (on unix systems). pub trait ConnectionExt: Connection { /// Read a single byte. fn read(&mut self) -> Result; /// Peek a single byte. This MUST be a **non-blocking** operation, returning /// `None` if no byte is available. /// /// Returns a byte (if one is available) without removing that byte from the /// queue. Subsequent calls to `peek` MUST return the same byte. fn peek(&mut self) -> Result, Self::Error>; } ================================================ FILE: src/internal/be_bytes.rs ================================================ /// A trait for working with structs as big-endian byte arrays. Automatically /// implemented for all built-in signed/unsigned integers. pub trait BeBytes: Sized { /// Write the memory representation of `self` as a byte array in /// big-endian (network) byte order into the provided buffer. #[allow(clippy::wrong_self_convention)] fn to_be_bytes(self, buf: &mut [u8]) -> Option; /// Parse `self` from a byte array in big-endian (network) byte order. /// Returns None upon overflow. fn from_be_bytes(buf: &[u8]) -> Option; } macro_rules! impl_to_be_bytes { ($($num:ty)*) => { $( impl BeBytes for $num { fn to_be_bytes(self, buf: &mut [u8]) -> Option { let len = core::mem::size_of::<$num>(); if buf.len() < len { return None } buf[..len].copy_from_slice(&<$num>::to_be_bytes(self)); Some(len) } fn from_be_bytes(buf: &[u8]) -> Option { let len = core::mem::size_of::<$num>(); let buf = if buf.len() > len { let (extra, buf) = buf.split_at(buf.len() - len); if extra.iter().any(|&b| b != 0) { return None } buf } else { buf }; let mut res: Self = 0; for b in buf.iter().copied() { let b: Self = b as Self; // `res <<= 8` causes the compiler to complain in the `u8` case res <<= 4; res <<= 4; res |= b; } Some(res) } } )* }; } impl_to_be_bytes!(u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize); #[cfg(test)] mod tests { use super::*; #[test] fn basic() { assert_eq!( 0x12345678, BeBytes::from_be_bytes(&[0x12, 0x34, 0x56, 0x78]).unwrap() ) } #[test] fn small() { assert_eq!( 0x123456, BeBytes::from_be_bytes(&[0x12, 0x34, 0x56]).unwrap() ) } #[test] fn too_big() { assert_eq!( 0x1234_u16, BeBytes::from_be_bytes(&[0xde, 0xad, 0xbe, 0xef]).unwrap_or(0x1234) ) } } ================================================ FILE: src/internal/le_bytes.rs ================================================ /// A trait for working with structs as little-endian byte arrays. Automatically /// implemented for all built-in signed/unsigned integers. pub trait LeBytes: Sized { /// Write the memory representation of `self` as a byte array in /// little-endian byte order into the provided buffer. #[allow(clippy::wrong_self_convention)] fn to_le_bytes(self, buf: &mut [u8]) -> Option; /// Parse `self` from a byte array in little-endian byte order. /// Returns None upon overflow. fn from_le_bytes(buf: &[u8]) -> Option; } macro_rules! impl_to_le_bytes { ($($num:ty)*) => { $( impl LeBytes for $num { fn to_le_bytes(self, buf: &mut [u8]) -> Option { let len = core::mem::size_of::<$num>(); if buf.len() < len { return None } buf[..len].copy_from_slice(&<$num>::to_le_bytes(self)); Some(len) } fn from_le_bytes(buf: &[u8]) -> Option { let len = core::mem::size_of::<$num>(); let buf = if buf.len() > len { let (extra, buf) = buf.split_at(buf.len() - len); if extra.iter().any(|&b| b != 0) { return None } buf } else { buf }; let mut res: Self = 0; for b in buf.iter().copied().rev() { let b: Self = b as Self; // `res <<= 8` causes the compiler to complain in the `u8` case res <<= 4; res <<= 4; res |= b; } Some(res) } } )* }; } impl_to_le_bytes!(u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize); #[cfg(test)] mod tests { use super::*; #[test] fn basic() { assert_eq!( 0x12345678, LeBytes::from_le_bytes(&[0x78, 0x56, 0x34, 0x12]).unwrap() ) } #[test] fn small() { assert_eq!( 0x123456, LeBytes::from_le_bytes(&[0x56, 0x34, 0x12]).unwrap() ) } #[test] fn too_big() { assert_eq!( 0x1234_u16, LeBytes::from_le_bytes(&[0xde, 0xad, 0xbe, 0xef]).unwrap_or(0x1234) ) } } ================================================ FILE: src/internal/mod.rs ================================================ //! Types / traits which are part of `gdbstub`'s public API, but don't need to //! be implemented by consumers of the library. mod be_bytes; mod le_bytes; pub use be_bytes::*; pub use le_bytes::*; ================================================ FILE: src/lib.rs ================================================ //! 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 //! //! By default, both the `std` and `alloc` features are enabled. //! //! When using `gdbstub` in `#![no_std]` contexts, make sure to set //! `default-features = false`. //! //! - `alloc` //! - Implement `Connection` for `Box`. //! - Log outgoing packets via `log::trace!` (uses a heap-allocated output //! buffer). //! - Provide built-in implementations for certain protocol features: //! - Use a heap-allocated packet buffer in `GdbStub` (if none is //! provided via `GdbStubBuilder::with_packet_buffer`). //! - (Monitor Command) Use a heap-allocated output buffer in //! `ConsoleOutput`. //! - `std` (implies `alloc`) //! - Implement `Connection` for [`TcpStream`](std::net::TcpStream) and //! [`UnixStream`](std::os::unix::net::UnixStream). //! - Implement [`std::error::Error`] for `gdbstub::Error`. //! - Add a `TargetError::Io` variant to simplify `std::io::Error` handling //! from Target methods. //! - `paranoid_unsafe` //! - Please refer to the [`unsafe` in `gdbstub`](https://github.com/daniel5151/gdbstub#unsafe-in-gdbstub) //! section of the README.md for more details. //! - `core_error` //! - Make `GdbStubError` implement [`core::error::Error`](https://doc.rust-lang.org/core/error/trait.Error.html) //! instead of `std::error::Error`. //! //! ## Getting Started //! //! This section provides a brief overview of the key traits and types used in //! `gdbstub`, and walks though the basic steps required to integrate `gdbstub` //! into a project. //! //! At a high level, there are only three things that are required to get up and //! running with `gdbstub`: a [`Connection`](#the-connection-trait), a //! [`Target`](#the-target-trait), and a [event loop](#the-event-loop). //! //! > _Note:_ I _highly recommended_ referencing some of the //! [examples](https://github.com/daniel5151/gdbstub#examples) listed in the //! project README when integrating `gdbstub` into a project for the first time. //! //! > In particular, the in-tree //! [`armv4t`](https://github.com/daniel5151/gdbstub/tree/master/examples/armv4t) //! example contains basic implementations off almost all protocol extensions, //! making it an incredibly valuable reference when implementing protocol //! extensions. //! //! ### The `Connection` Trait //! //! First things first: `gdbstub` needs some way to communicate with a GDB //! client. To facilitate this communication, `gdbstub` uses a custom //! [`Connection`](conn::Connection) trait. //! //! `Connection` is automatically implemented for common `std` types such as //! [`TcpStream`](std::net::TcpStream) and //! [`UnixStream`](std::os::unix::net::UnixStream). //! //! If you're using `gdbstub` in a `#![no_std]` environment, `Connection` will //! most likely need to be manually implemented on top of whatever in-order, //! serial, byte-wise I/O your particular platform has available (e.g: //! putchar/getchar over UART, using an embedded TCP stack, etc.). //! //! One common way to start a remote debugging session is to simply wait for a //! GDB client to connect via TCP: //! //! ```rust //! use std::io; //! use std::net::{TcpListener, TcpStream}; //! //! fn wait_for_gdb_connection(port: u16) -> io::Result { //! let sockaddr = format!("localhost:{}", port); //! eprintln!("Waiting for a GDB connection on {:?}...", sockaddr); //! let sock = TcpListener::bind(sockaddr)?; //! let (stream, addr) = sock.accept()?; //! //! // Blocks until a GDB client connects via TCP. //! // i.e: Running `target remote localhost:` from the GDB prompt. //! //! eprintln!("Debugger connected from {}", addr); //! Ok(stream) // `TcpStream` implements `gdbstub::Connection` //! } //! ``` //! //! ### 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 //! primary bridge between `gdbstub`'s generic GDB protocol implementation and a //! specific target's project/platform-specific code. //! //! At a high level, the `Target` trait is a collection of user-defined handler //! methods that the GDB client can invoke via the GDB remote serial protocol. //! For example, the `Target` trait includes methods to read/write //! registers/memory, start/stop execution, etc... //! //! **`Target` is the most important trait in `gdbstub`, and must be implemented //! by anyone integrating `gdbstub` into their project!** //! //! Please refer to the [`target` module documentation](target) for in-depth //! instructions on how to implement [`Target`](target::Target) for a particular //! platform. //! //! ## The Event Loop //! //! Once a [`Connection`](#the-connection-trait) has been established and the //! [`Target`](#the-target-trait) has been initialized, all that's left is to //! wire things up and decide what kind of event loop will be used to drive the //! debugging session! //! //! First things first, let's get an instance of `GdbStub` ready to run: //! //! ```rust,ignore //! // Set-up a valid `Target` //! let mut target = MyTarget::new()?; // implements `Target` //! //! // Establish a `Connection` //! let connection: TcpStream = wait_for_gdb_connection(9001); //! //! // Create a new `gdbstub::GdbStub` using the established `Connection`. //! let mut debugger = gdbstub::GdbStub::new(connection); //! ``` //! //! Cool, but how do you actually start the debugging session? // use an explicit doc attribute to avoid automatic rustfmt wrapping #![doc = "### `GdbStub::run_blocking`: The quick and easy way to get up and running with `gdbstub`"] //! //! If you've got an extra thread to spare, the quickest way to get up and //! running with `gdbstub` is by using the //! [`GdbStub::run_blocking`](stub::run_blocking) API alongside the //! [`BlockingEventLoop`] trait. //! //! If you are on a more resource constrained platform, and/or don't wish to //! dedicate an entire thread to `gdbstub`, feel free to skip ahead to the //! [following //! section](#gdbstubstatemachine-driving-gdbstub-in-an-async-event-loop--via-interrupt-handlers). //! //! A basic integration of `gdbstub` into a project using the //! `GdbStub::run_blocking` API might look something like this: //! //! ```rust //! # use gdbstub::target::ext::base::BaseOps; //! # //! # struct MyTarget; //! # //! # impl Target for MyTarget { //! # type Error = &'static str; //! # type Arch = gdbstub_arch::arm::Armv4t; // as an example //! # fn base_ops(&mut self) -> BaseOps { todo!() } //! # } //! # //! # impl MyTarget { //! # fn run_and_check_for_incoming_data( //! # &mut self, //! # conn: &mut impl Connection //! # ) -> MyTargetEvent { todo!() } //! # //! # fn stop_in_response_to_ctrl_c_interrupt( //! # &mut self //! # ) -> Result<(), &'static str> { todo!() } //! # } //! # //! # enum MyTargetEvent { //! # IncomingData, //! # StopReason(SingleThreadStopReason), //! # } //! # //! use gdbstub::common::Signal; //! use gdbstub::conn::{Connection, ConnectionExt}; // note the use of `ConnectionExt` //! use gdbstub::stub::{run_blocking, DisconnectReason, GdbStub}; //! use gdbstub::stub::SingleThreadStopReason; //! use gdbstub::target::Target; //! //! enum MyGdbBlockingEventLoop {} //! //! // The `run_blocking::BlockingEventLoop` groups together various callbacks //! // the `GdbStub::run_blocking` event loop requires you to implement. //! impl run_blocking::BlockingEventLoop for MyGdbBlockingEventLoop { //! type Target = MyTarget; //! type Connection = Box>; //! //! // or MultiThreadStopReason on multi threaded targets //! type StopReason = SingleThreadStopReason; //! //! // Invoked immediately after the target's `resume` method has been //! // called. The implementation should block until either the target //! // reports a stop reason, or if new data was sent over the connection. //! fn wait_for_stop_reason( //! target: &mut MyTarget, //! conn: &mut Self::Connection, //! ) -> Result< //! run_blocking::Event>, //! run_blocking::WaitForStopReasonError< //! ::Error, //! ::Error, //! >, //! > { //! // the specific mechanism to "select" between incoming data and target //! // events will depend on your project's architecture. //! // //! // some examples of how you might implement this method include: `epoll`, //! // `select!` across multiple event channels, periodic polling, etc... //! // //! // in this example, lets assume the target has a magic method that handles //! // this for us. //! let event = match target.run_and_check_for_incoming_data(conn) { //! MyTargetEvent::IncomingData => { //! let byte = conn //! .read() // method provided by the `ConnectionExt` trait //! .map_err(run_blocking::WaitForStopReasonError::Connection)?; //! //! run_blocking::Event::IncomingData(byte) //! } //! MyTargetEvent::StopReason(reason) => { //! run_blocking::Event::TargetStopped(reason) //! } //! }; //! //! Ok(event) //! } //! //! // Invoked when the GDB client sends a Ctrl-C interrupt. //! fn on_interrupt( //! target: &mut MyTarget, //! ) -> Result>, ::Error> { //! // notify the target that a ctrl-c interrupt has occurred. //! target.stop_in_response_to_ctrl_c_interrupt()?; //! //! // a pretty typical stop reason in response to a Ctrl-C interrupt is to //! // report a "Signal::SIGINT". //! Ok(Some(SingleThreadStopReason::Signal(Signal::SIGINT).into())) //! } //! } //! //! fn gdb_event_loop_thread( //! debugger: GdbStub>>, //! mut target: MyTarget //! ) { //! match debugger.run_blocking::(&mut target) { //! Ok(disconnect_reason) => match disconnect_reason { //! DisconnectReason::Disconnect => { //! println!("Client disconnected") //! } //! DisconnectReason::TargetExited(code) => { //! println!("Target exited with code {}", code) //! } //! DisconnectReason::TargetTerminated(sig) => { //! println!("Target terminated with signal {}", sig) //! } //! DisconnectReason::Kill => println!("GDB sent a kill command"), //! }, //! 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) //! } //! } //! } //! } //! ``` // use an explicit doc attribute to avoid automatic rustfmt wrapping #![doc = "### `GdbStubStateMachine`: Driving `gdbstub` in an async event loop / via interrupt handlers"] //! //! `GdbStub::run_blocking` requires that the target implement the //! [`BlockingEventLoop`] trait, which as the name implies, uses _blocking_ IO //! when handling certain events. Blocking the thread is a totally reasonable //! approach in most implementations, as one can simply spin up a separate //! thread to run the GDB stub (or in certain emulator implementations, run the //! emulator as part of the `wait_for_stop_reason` method). //! //! Unfortunately, this blocking behavior can be a non-starter when integrating //! `gdbstub` in projects that don't support / wish to avoid the traditional //! thread-based execution model, such as projects using `async/await`, or //! bare-metal `no_std` projects running on embedded hardware. //! //! In these cases, `gdbstub` provides access to the underlying //! [`GdbStubStateMachine`] API, which gives implementations full control over //! the GDB stub's "event loop". This API requires implementations to "push" //! data to the `gdbstub` implementation whenever new data becomes available //! (e.g: when a UART interrupt handler receives a byte, when the target hits a //! breakpoint, etc...), as opposed to the `GdbStub::run_blocking` API, which //! "pulls" these events in a blocking manner. //! //! See the [`GdbStubStateMachine`] docs for more details on how to use this //! API. //! //!
//! //! * * * //! //!
//! //! And with that lengthy introduction, I wish you the best of luck in your //! debugging adventures! //! //! If you have any suggestions, feature requests, or run into any problems, //! please start a discussion / open an issue over on the //! [`gdbstub` GitHub repo](https://github.com/daniel5151/gdbstub/). //! //! [`GdbStubStateMachine`]: stub::state_machine::GdbStubStateMachine //! [`BlockingEventLoop`]: stub::run_blocking::BlockingEventLoop #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "paranoid_unsafe", forbid(unsafe_code))] #![warn(missing_docs)] #[cfg(feature = "alloc")] extern crate alloc; #[macro_use] extern crate log; mod protocol; mod util; #[doc(hidden)] pub mod internal; pub mod arch; pub mod common; pub mod conn; pub mod stub; pub mod target; // https://users.rust-lang.org/t/compile-time-const-unwrapping/51619/7 // // This works from Rust 1.46.0 onwards, which stabilized branching and looping // in const contexts. macro_rules! unwrap { ($e:expr $(,)*) => { match $e { ::core::option::Option::Some(x) => x, #[allow(clippy::out_of_bounds_indexing)] ::core::option::Option::None => { ["tried to unwrap a None"][99]; loop {} } } }; } /// (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 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 { pub trait IsValidTid {} impl IsValidTid for () {} impl IsValidTid for crate::common::Tid {} } ================================================ FILE: src/protocol/commands/_QAgent.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct QAgent { pub value: bool, } impl<'a> ParseCommand<'a> for QAgent { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); let value = match body as &[u8] { b":0" => false, b":1" => true, _ => return None, }; Some(QAgent { value }) } } ================================================ FILE: src/protocol/commands/_QCatchSyscalls.rs ================================================ use super::prelude::*; use crate::protocol::common::lists::ArgListHex; #[derive(Debug)] pub enum QCatchSyscalls<'a> { Disable, Enable(ArgListHex<'a>), EnableAll, } impl<'a> ParseCommand<'a> for QCatchSyscalls<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); match body { [b':', b'0'] => Some(QCatchSyscalls::Disable), [b':', b'1', b';', sysno @ ..] => { Some(QCatchSyscalls::Enable(ArgListHex::from_packet(sysno)?)) } [b':', b'1'] => Some(QCatchSyscalls::EnableAll), _ => None, } } } ================================================ FILE: src/protocol/commands/_QDisableRandomization.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct QDisableRandomization { pub value: bool, } impl<'a> ParseCommand<'a> for QDisableRandomization { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); let value = match body as &[u8] { b":0" => false, b":1" => true, _ => return None, }; Some(QDisableRandomization { value }) } } ================================================ FILE: src/protocol/commands/_QEnvironmentHexEncoded.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct QEnvironmentHexEncoded<'a> { pub key: &'a [u8], pub value: Option<&'a [u8]>, } impl<'a> ParseCommand<'a> for QEnvironmentHexEncoded<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); let (key, value) = match body { [b':', keyval @ ..] => { let keyval = decode_hex_buf(keyval).ok()?; let mut keyval = keyval.splitn(2, |b| *b == b'='); let key = keyval.next()?; let value = match keyval.next()? { [] => None, s => Some(s), }; (key, value) } _ => return None, }; Some(QEnvironmentHexEncoded { key, value }) } } ================================================ FILE: src/protocol/commands/_QEnvironmentReset.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct QEnvironmentReset; impl<'a> ParseCommand<'a> for QEnvironmentReset { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { if !buf.into_body().is_empty() { return None; } Some(QEnvironmentReset) } } ================================================ FILE: src/protocol/commands/_QEnvironmentUnset.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct QEnvironmentUnset<'a> { pub key: &'a [u8], } impl<'a> ParseCommand<'a> for QEnvironmentUnset<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); let key = match body { [b':', key @ ..] => decode_hex_buf(key).ok()?, _ => return None, }; Some(QEnvironmentUnset { key }) } } ================================================ FILE: src/protocol/commands/_QSetWorkingDir.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct QSetWorkingDir<'a> { pub dir: Option<&'a [u8]>, } impl<'a> ParseCommand<'a> for QSetWorkingDir<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); let dir = match body { [b':', dir @ ..] => match decode_hex_buf(dir).ok()? { [] => None, s => Some(s as &[u8]), }, _ => return None, }; Some(QSetWorkingDir { dir }) } } ================================================ FILE: src/protocol/commands/_QStartNoAckMode.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct QStartNoAckMode; impl<'a> ParseCommand<'a> for QStartNoAckMode { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { if !buf.into_body().is_empty() { return None; } Some(QStartNoAckMode) } } ================================================ FILE: src/protocol/commands/_QStartupWithShell.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct QStartupWithShell { pub value: bool, } impl<'a> ParseCommand<'a> for QStartupWithShell { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); let value = match body as &[u8] { b":0" => false, b":1" => true, _ => return None, }; Some(QStartupWithShell { value }) } } ================================================ FILE: src/protocol/commands/_QTBuffer_upcase.rs ================================================ use super::prelude::*; use crate::target::ext::tracepoints::BufferShape; use crate::target::ext::tracepoints::TraceBufferConfig; #[derive(Debug)] pub struct QTBuffer(pub TraceBufferConfig); impl ParseCommand<'_> for QTBuffer { #[inline(always)] fn from_packet(buf: PacketBuf<'_>) -> Option { match buf.into_body() { [b':', body @ ..] => { let mut s = body.splitn_mut(2, |b| *b == b':'); let opt = s.next()?; // Clippy incorrect thinks this as_ref isn't needed, but it is. #[allow(clippy::useless_asref)] match opt.as_ref() { b"circular" => { let shape = s.next()?; Some(QTBuffer(TraceBufferConfig::Shape(match shape { [b'1'] => Some(BufferShape::Circular), [b'0'] => Some(BufferShape::Linear), _ => None, }?))) } b"size" => { let size = s.next()?; Some(QTBuffer(TraceBufferConfig::Size(match size { [b'-', b'1'] => None, i => Some(decode_hex(i).ok()?), }))) } _ => None, } } _ => None, } } } ================================================ FILE: src/protocol/commands/_QTDP.rs ================================================ use super::prelude::*; use crate::target::ext::tracepoints::Tracepoint; #[derive(Debug)] #[allow(clippy::upper_case_acronyms)] pub enum QTDP<'a> { Create(CreateTDP<'a>), Extend(ExtendTDP<'a>), } #[derive(Debug)] pub struct CreateTDP<'a> { pub number: Tracepoint, pub addr: &'a [u8], pub enable: bool, pub step: u64, pub pass: u64, pub unsupported_option: Option, pub more: bool, } #[derive(Debug)] pub struct ExtendTDP<'a> { pub number: Tracepoint, pub addr: &'a [u8], pub actions: &'a mut [u8], } impl<'a> ParseCommand<'a> for QTDP<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); match body { [b':', b'-', actions @ ..] => { let mut params = actions.splitn_mut(4, |b| *b == b':'); let number = Tracepoint(decode_hex(params.next()?).ok()?); let addr = decode_hex_buf(params.next()?).ok()?; let actions = params.next()?; Some(QTDP::Extend(ExtendTDP { number, addr, actions, })) } [b':', tracepoint @ ..] => { // Strip off the trailing '-' that indicates if there will be // more packets after this let (tracepoint, more) = match tracepoint { [rest @ .., b'-'] => (rest, true), x => (x, false), }; let mut params = tracepoint.splitn_mut(6, |b| *b == b':'); let n = Tracepoint(decode_hex(params.next()?).ok()?); let addr = decode_hex_buf(params.next()?).ok()?; let ena = params.next()?; let step = decode_hex(params.next()?).ok()?; let pass_and_end = params.next()?; let pass = decode_hex(pass_and_end).ok()?; // TODO: parse `F` fast tracepoint options // TODO: parse `X` tracepoint conditions let options = params.next(); let unsupported_option = match options { Some([b'F', ..]) => { /* TODO: fast tracepoints support */ Some(b'F') } Some([b'S']) => { /* TODO: static tracepoint support */ Some(b'S') } Some([b'X', ..]) => { /* TODO: trace conditions support */ Some(b'X') } Some(_) => { /* invalid option */ return None; } None => None, }; Some(QTDP::Create(CreateTDP { number: n, addr, enable: match ena { [b'E'] => Some(true), [b'D'] => Some(false), _ => None, }?, step, pass, more, unsupported_option, })) } _ => None, } } } ================================================ FILE: src/protocol/commands/_QTDPsrc.rs ================================================ use super::prelude::*; use crate::target::ext::tracepoints::Tracepoint; use crate::target::ext::tracepoints::TracepointSourceType; pub struct QTDPsrc<'a> { pub number: Tracepoint, pub addr: &'a [u8], pub kind: TracepointSourceType, pub start: u32, pub slen: u32, pub bytes: &'a mut [u8], } impl<'a> ParseCommand<'a> for QTDPsrc<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); match body { [b':', info @ ..] => { let mut params = info.splitn_mut(7, |b| *b == b':'); let number = Tracepoint(decode_hex(params.next()?).ok()?); let addr = decode_hex_buf(params.next()?).ok()?; // Clippy incorrect thinks this as_ref isn't needed, but it is. #[allow(clippy::useless_asref)] let kind = match params.next()?.as_ref() { b"at" => Some(TracepointSourceType::At), b"cond" => Some(TracepointSourceType::Cond), b"cmd" => Some(TracepointSourceType::Cmd), _ => None, }?; let start = decode_hex(params.next()?).ok()?; let slen = decode_hex(params.next()?).ok()?; let bytes = decode_hex_buf(params.next()?).ok()?; Some(QTDPsrc { number, addr, kind, start, slen, bytes, }) } _ => None, } } } ================================================ FILE: src/protocol/commands/_QTFrame.rs ================================================ use super::prelude::*; use crate::target::ext::tracepoints::FrameRequest; use crate::target::ext::tracepoints::Tracepoint; #[derive(Debug)] pub struct QTFrame<'a>(pub FrameRequest<&'a mut [u8]>); impl<'a> ParseCommand<'a> for QTFrame<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); match body { [b':', body @ ..] => { let mut s = body.split_mut(|b| *b == b':'); let selector = s.next()?; // Clippy incorrect thinks this as_ref isn't needed, but it is. #[allow(clippy::useless_asref)] Some(match selector.as_ref() { b"pc" => { let addr = decode_hex_buf(s.next()?).ok()?; QTFrame(FrameRequest::AtPC(addr)) } b"tdp" => { let tp = Tracepoint(decode_hex(s.next()?).ok()?); QTFrame(FrameRequest::Hit(tp)) } b"range" => { let start = decode_hex_buf(s.next()?).ok()?; let end = decode_hex_buf(s.next()?).ok()?; QTFrame(FrameRequest::Between(start, end)) } b"outside" => { let start = decode_hex_buf(s.next()?).ok()?; let end = decode_hex_buf(s.next()?).ok()?; QTFrame(FrameRequest::Outside(start, end)) } n => QTFrame(FrameRequest::Select(decode_hex(n).ok()?)), }) } _ => None, } } } ================================================ FILE: src/protocol/commands/_QTStart.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct QTStart; impl<'a> ParseCommand<'a> for QTStart { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { if !buf.into_body().is_empty() { return None; } Some(QTStart) } } ================================================ FILE: src/protocol/commands/_QTStop.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct QTStop; impl<'a> ParseCommand<'a> for QTStop { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { if !buf.into_body().is_empty() { return None; } Some(QTStop) } } ================================================ FILE: src/protocol/commands/_QTinit.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct QTinit; impl<'a> ParseCommand<'a> for QTinit { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { if !buf.into_body().is_empty() { return None; } Some(QTinit) } } ================================================ FILE: src/protocol/commands/_bc.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct bc; impl<'a> ParseCommand<'a> for bc { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { if !buf.into_body().is_empty() { return None; } Some(bc) } } ================================================ FILE: src/protocol/commands/_bs.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct bs; impl<'a> ParseCommand<'a> for bs { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { if !buf.into_body().is_empty() { return None; } Some(bs) } } ================================================ FILE: src/protocol/commands/_c.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct c<'a> { pub addr: Option<&'a [u8]>, } impl<'a> ParseCommand<'a> for c<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); if body.is_empty() { return Some(c { addr: None }); } let addr = match body { [] => None, _ => Some(decode_hex_buf(body).ok()? as &[u8]), }; Some(c { addr }) } } ================================================ FILE: src/protocol/commands/_d_upcase.rs ================================================ use super::prelude::*; use crate::common::Pid; #[derive(Debug)] pub struct D { pub pid: Option, } impl<'a> ParseCommand<'a> for D { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); let pid = match body { [b';', pid @ ..] => Some(Pid::new(decode_hex(pid).ok()?)?), _ => None, }; Some(D { pid }) } } ================================================ FILE: src/protocol/commands/_g.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct g; impl<'a> ParseCommand<'a> for g { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { if !buf.into_body().is_empty() { return None; } Some(g) } } ================================================ FILE: src/protocol/commands/_g_upcase.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct G<'a> { pub vals: &'a [u8], } impl<'a> ParseCommand<'a> for G<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { Some(G { vals: decode_hex_buf(buf.into_body()).ok()?, }) } } ================================================ FILE: src/protocol/commands/_h_upcase.rs ================================================ use super::prelude::*; use crate::protocol::common::thread_id::ThreadId; #[derive(Debug)] pub enum Op { StepContinue, Other, } #[derive(Debug)] pub struct H { pub kind: Op, pub thread: ThreadId, } impl<'a> ParseCommand<'a> for H { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); if body.is_empty() { return None; } let kind = match body[0] { b'g' => Op::Other, b'c' => Op::StepContinue, _ => return None, }; let thread: ThreadId = body[1..].try_into().ok()?; Some(H { kind, thread }) } } ================================================ FILE: src/protocol/commands/_k.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct k; impl<'a> ParseCommand<'a> for k { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { if !buf.into_body().is_empty() { return None; } Some(k) } } ================================================ FILE: src/protocol/commands/_m.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct m<'a> { pub addr: &'a [u8], pub len: usize, pub buf: &'a mut [u8], } impl<'a> ParseCommand<'a> for m<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { // the total packet buffer currently looks like: // // +------+--------------------+-------------------+-------+-----------------+ // | "$m" | addr (hex-encoded) | len (hex-encoded) | "#XX" | empty space ... | // +------+--------------------+-------------------+-------+-----------------+ // // Unfortunately, while `len` can be hex-decoded right here and now into a // `usize`, `addr` corresponds to a Target::Arch::Usize, which requires holding // on to a valid &[u8] reference into the buffer. // // While it's not _perfectly_ efficient, simply leaving the decoded addr in // place and wasting a couple bytes is probably the easiest way to tackle this // problem: // // +------+------------------+------------------------------------------------+ // | "$m" | addr (raw bytes) | usable buffer ... | // +------+------------------+------------------------------------------------+ let (buf, body_range) = buf.into_raw_buf(); let body = buf.get_mut(body_range.start..body_range.end)?; let mut body = body.split_mut(|b| *b == b','); let addr = decode_hex_buf(body.next()?).ok()?; let addr_len = addr.len(); let len = decode_hex(body.next()?).ok()?; // ensures that `split_at_mut` doesn't panic if buf.len() < body_range.start + addr_len { return None; } let (addr, buf) = buf.split_at_mut(body_range.start + addr_len); let addr = addr.get(b"$m".len()..)?; Some(m { addr, len, buf }) } } ================================================ FILE: src/protocol/commands/_m_upcase.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct M<'a> { pub addr: &'a [u8], pub val: &'a [u8], } impl<'a> ParseCommand<'a> for M<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); let mut body = body.split_mut(|&b| b == b',' || b == b':'); let addr = decode_hex_buf(body.next()?).ok()?; // The length part of the packet doesn't appear to actually be useful, // given that it can be trivially derived from the amount of data read // in via `val`. // // As such - we'll still parse it to ensure the packet is // spec-compliant, but we won't stash the parsed length anywhere. // // TODO?: dig into whether any GDB clients actually attempt to send over // a mismatched `len` and `val` (e.g: sending a longer `val`, with `len` // used to truncate the data). My gut feeling is that this would never // actually occur in practice (and the fact that `gdbstub` has gotten // away with not handling this for many years at this point reinforces // that gut feeling). let _len: usize = decode_hex(body.next()?).ok()?; let val = decode_hex_buf(body.next()?).ok()?; Some(M { addr, val }) } } ================================================ FILE: src/protocol/commands/_p.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct p<'a> { pub reg_id: usize, pub buf: &'a mut [u8], } impl<'a> ParseCommand<'a> for p<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let (buf, body_range) = buf.into_raw_buf(); let body = buf.get(body_range.start..body_range.end)?; if body.is_empty() { return None; } let reg_id = decode_hex(body).ok()?; Some(p { reg_id, buf }) } } ================================================ FILE: src/protocol/commands/_p_upcase.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct P<'a> { pub reg_id: usize, pub val: &'a [u8], } impl<'a> ParseCommand<'a> for P<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); let mut body = body.split_mut(|&b| b == b'='); let reg_id = decode_hex(body.next()?).ok()?; let val = decode_hex_buf(body.next()?).ok()?; Some(P { reg_id, val }) } } ================================================ FILE: src/protocol/commands/_qAttached.rs ================================================ use super::prelude::*; use crate::common::Pid; #[derive(Debug)] pub struct qAttached { pub pid: Option, } impl<'a> ParseCommand<'a> for qAttached { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); let pid = match body { [b':', pid @ ..] => Some(Pid::new(decode_hex(pid).ok()?)?), _ => None, }; Some(qAttached { pid }) } } ================================================ FILE: src/protocol/commands/_qC.rs ================================================ 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) } } ================================================ FILE: src/protocol/commands/_qHostInfo.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct qHostInfo; impl<'a> ParseCommand<'a> for qHostInfo { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { if !buf.into_body().is_empty() { return None; } Some(qHostInfo) } } ================================================ FILE: src/protocol/commands/_qOffsets.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct qOffsets; impl<'a> ParseCommand<'a> for qOffsets { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { crate::__dead_code_marker!("qOffsets", "from_packet"); if !buf.into_body().is_empty() { return None; } Some(qOffsets) } } ================================================ FILE: src/protocol/commands/_qProcessInfo.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct qProcessInfo; impl<'a> ParseCommand<'a> for qProcessInfo { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { if !buf.into_body().is_empty() { return None; } Some(qProcessInfo) } } ================================================ FILE: src/protocol/commands/_qRcmd.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct qRcmd<'a> { pub hex_cmd: &'a [u8], } impl<'a> ParseCommand<'a> for qRcmd<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { crate::__dead_code_marker!("qRcmd", "from_packet"); let body = buf.into_body(); match body { [] => Some(qRcmd { hex_cmd: &[] }), [b',', hex_cmd @ ..] => Some(qRcmd { hex_cmd: decode_hex_buf(hex_cmd).ok()?, }), _ => None, } } } ================================================ FILE: src/protocol/commands/_qRegisterInfo.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct qRegisterInfo { pub reg_id: usize, } impl<'a> ParseCommand<'a> for qRegisterInfo { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); let reg_id = decode_hex(body).ok()?; Some(qRegisterInfo { reg_id }) } } ================================================ FILE: src/protocol/commands/_qSupported.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct qSupported<'a> { pub packet_buffer_len: usize, pub features: Features<'a>, } impl<'a> ParseCommand<'a> for qSupported<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let packet_buffer_len = buf.full_len(); let body = buf.into_body(); match body { [b':', body @ ..] => Some(qSupported { packet_buffer_len, features: Features(body), }), _ => None, } } } #[derive(Debug)] pub struct Features<'a>(&'a [u8]); impl<'a> Features<'a> { pub fn into_iter(self) -> impl Iterator, ()>> + 'a { self.0.split(|b| *b == b';').map(|s| match s.last() { None => Err(()), Some(&c) => match c { b'+' | b'-' => { let feature = match &s[..s.len() - 1] { b"multiprocess" => Feature::Multiprocess, // TODO: implementing other features will require IDET plumbing _ => return Ok(None), }; Ok(Some((feature, c == b'+'))) } _ => { // TODO: add support for "xmlRegisters=" // that's the only feature packet that uses an '=', and AFAIK, it's not really // used anymore... Ok(None) } }, }) } } #[derive(Debug)] pub enum Feature { Multiprocess, } ================================================ FILE: src/protocol/commands/_qTBuffer.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct qTBuffer { pub offset: u64, pub length: usize, } impl<'a> ParseCommand<'a> for qTBuffer { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let (buf, body_range) = buf.into_raw_buf(); let body = &buf[body_range]; match body { [b':', body @ ..] => { let mut req_opts = body.split(|b| *b == b','); let (offset, length) = (req_opts.next()?, req_opts.next()?); let offset = decode_hex(offset).ok()?; let length = decode_hex(length).ok()?; Some(qTBuffer { offset, length }) } _ => None, } } } ================================================ FILE: src/protocol/commands/_qTP.rs ================================================ use super::prelude::*; use crate::target::ext::tracepoints::Tracepoint; #[derive(Debug)] pub struct qTP<'a> { pub tracepoint: Tracepoint, pub addr: &'a [u8], } impl<'a> ParseCommand<'a> for qTP<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); match body { [b':', body @ ..] => { let mut s = body.split_mut(|b| *b == b':'); let tracepoint = Tracepoint(decode_hex(s.next()?).ok()?); let addr = decode_hex_buf(s.next()?).ok()?; Some(qTP { tracepoint, addr }) } _ => None, } } } ================================================ FILE: src/protocol/commands/_qTStatus.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct qTStatus; impl<'a> ParseCommand<'a> for qTStatus { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { if !buf.into_body().is_empty() { return None; } Some(qTStatus) } } ================================================ FILE: src/protocol/commands/_qTfP.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct qTfP; impl<'a> ParseCommand<'a> for qTfP { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { if !buf.into_body().is_empty() { return None; } Some(qTfP) } } ================================================ FILE: src/protocol/commands/_qTfV.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct qTfV; impl<'a> ParseCommand<'a> for qTfV { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { if !buf.into_body().is_empty() { return None; } Some(qTfV) } } ================================================ FILE: src/protocol/commands/_qThreadExtraInfo.rs ================================================ use super::prelude::*; use crate::protocol::common::thread_id::ThreadId; use crate::protocol::ConcreteThreadId; #[derive(Debug)] pub struct qThreadExtraInfo<'a> { pub id: ConcreteThreadId, pub buf: &'a mut [u8], } impl<'a> ParseCommand<'a> for qThreadExtraInfo<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let (buf, body_range) = buf.into_raw_buf(); let body = buf.get(body_range.start..body_range.end)?; if body.is_empty() { return None; } match body { [b',', body @ ..] => { let id = ConcreteThreadId::try_from(ThreadId::try_from(body).ok()?).ok()?; Some(qThreadExtraInfo { id, buf }) } _ => None, } } } ================================================ FILE: src/protocol/commands/_qTsP.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct qTsP; impl<'a> ParseCommand<'a> for qTsP { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { if !buf.into_body().is_empty() { return None; } Some(qTsP) } } ================================================ FILE: src/protocol/commands/_qTsV.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct qTsV; impl<'a> ParseCommand<'a> for qTsV { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { if !buf.into_body().is_empty() { return None; } Some(qTsV) } } ================================================ FILE: src/protocol/commands/_qWasmCallStack.rs ================================================ use super::prelude::*; use crate::protocol::common::thread_id::ThreadId; use crate::protocol::ConcreteThreadId; #[derive(Debug)] pub struct qWasmCallStack { pub tid: ConcreteThreadId, } impl<'a> ParseCommand<'a> for qWasmCallStack { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); if body.is_empty() || body[0] != b':' { return None; } let tid = &body[1..]; let tid = ConcreteThreadId::try_from(ThreadId::try_from(tid).ok()?).ok()?; Some(qWasmCallStack { tid }) } } ================================================ FILE: src/protocol/commands/_qWasmGlobal.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct qWasmGlobal<'a> { pub frame: usize, pub global: usize, pub buf: &'a mut [u8], } impl<'a> ParseCommand<'a> for qWasmGlobal<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let (buf, body_range) = buf.into_raw_buf(); let body = buf.get(body_range.start..body_range.end)?; if body.is_empty() || body[0] != b':' { return None; } let mut parts = body[1..].split(|b| *b == b';'); let frame = parts.next()?; let frame = str::from_utf8(frame).ok()?.parse::().ok()?; let global = parts.next()?; let global = str::from_utf8(global).ok()?.parse::().ok()?; if parts.next().is_some() { // Too many parameters. return None; } Some(qWasmGlobal { frame, global, buf, }) } } ================================================ FILE: src/protocol/commands/_qWasmLocal.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct qWasmLocal<'a> { pub frame: usize, pub local: usize, pub buf: &'a mut [u8], } impl<'a> ParseCommand<'a> for qWasmLocal<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let (buf, body_range) = buf.into_raw_buf(); let body = buf.get(body_range.start..body_range.end)?; if body.is_empty() || body[0] != b':' { return None; } let mut parts = body[1..].split(|b| *b == b';'); let frame = parts.next()?; let frame = str::from_utf8(frame).ok()?.parse::().ok()?; let local = parts.next()?; let local = str::from_utf8(local).ok()?.parse::().ok()?; if parts.next().is_some() { // Too many parameters. return None; } Some(qWasmLocal { frame, local, buf, }) } } ================================================ FILE: src/protocol/commands/_qWasmStackValue.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct qWasmStackValue<'a> { pub frame: usize, pub index: usize, pub buf: &'a mut [u8], } impl<'a> ParseCommand<'a> for qWasmStackValue<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let (buf, body_range) = buf.into_raw_buf(); let body = buf.get(body_range.start..body_range.end)?; if body.is_empty() || body[0] != b':' { return None; } let mut parts = body[1..].split(|b| *b == b';'); let frame = parts.next()?; let frame = str::from_utf8(frame).ok()?.parse::().ok()?; let index = parts.next()?; let index = str::from_utf8(index).ok()?.parse::().ok()?; if parts.next().is_some() { // Too many parameters. return None; } Some(qWasmStackValue { frame, index, buf, }) } } ================================================ FILE: src/protocol/commands/_qXfer_auxv_read.rs ================================================ // use super::prelude::*; // unused use crate::protocol::common::qxfer::ParseAnnex; use crate::protocol::common::qxfer::QXferReadBase; pub type qXferAuxvRead<'a> = QXferReadBase<'a, AuxvAnnex>; #[derive(Debug)] pub struct AuxvAnnex; impl ParseAnnex<'_> for AuxvAnnex { #[inline(always)] fn from_buf(buf: &[u8]) -> Option { if buf != b"" { return None; } Some(AuxvAnnex) } } ================================================ FILE: src/protocol/commands/_qXfer_exec_file.rs ================================================ use super::prelude::*; use crate::common::Pid; use crate::protocol::common::qxfer::ParseAnnex; use crate::protocol::common::qxfer::QXferReadBase; pub type qXferExecFileRead<'a> = QXferReadBase<'a, ExecFileAnnex>; #[derive(Debug)] pub struct ExecFileAnnex { pub pid: Option, } impl ParseAnnex<'_> for ExecFileAnnex { #[inline(always)] fn from_buf(buf: &[u8]) -> Option { let pid = match buf { [] => None, buf => Some(Pid::new(decode_hex(buf).ok()?)?), }; Some(ExecFileAnnex { pid }) } } ================================================ FILE: src/protocol/commands/_qXfer_features_read.rs ================================================ // use super::prelude::*; // unused use crate::protocol::common::qxfer::ParseAnnex; use crate::protocol::common::qxfer::QXferReadBase; pub type qXferFeaturesRead<'a> = QXferReadBase<'a, FeaturesAnnex<'a>>; #[derive(Debug)] pub struct FeaturesAnnex<'a> { pub name: &'a [u8], } impl<'a> ParseAnnex<'a> for FeaturesAnnex<'a> { #[inline(always)] fn from_buf(buf: &'a [u8]) -> Option { Some(FeaturesAnnex { name: buf }) } } ================================================ FILE: src/protocol/commands/_qXfer_libraries_read.rs ================================================ use crate::protocol::common::qxfer::ParseAnnex; use crate::protocol::common::qxfer::QXferReadBase; pub type qXferLibrariesRead<'a> = QXferReadBase<'a, LibrariesAnnex>; #[derive(Debug)] pub struct LibrariesAnnex; impl ParseAnnex<'_> for LibrariesAnnex { #[inline(always)] fn from_buf(buf: &[u8]) -> Option { if buf != b"" { return None; } Some(LibrariesAnnex) } } ================================================ FILE: src/protocol/commands/_qXfer_libraries_svr4_read.rs ================================================ use crate::protocol::common::qxfer::ParseAnnex; use crate::protocol::common::qxfer::QXferReadBase; pub type qXferLibrariesSvr4Read<'a> = QXferReadBase<'a, LibrariesSvr4Annex>; #[derive(Debug)] pub struct LibrariesSvr4Annex; impl ParseAnnex<'_> for LibrariesSvr4Annex { #[inline(always)] fn from_buf(buf: &[u8]) -> Option { if buf != b"" { return None; } Some(LibrariesSvr4Annex) } } ================================================ FILE: src/protocol/commands/_qXfer_memory_map.rs ================================================ // use super::prelude::*; // unused use crate::protocol::common::qxfer::ParseAnnex; use crate::protocol::common::qxfer::QXferReadBase; pub type qXferMemoryMapRead<'a> = QXferReadBase<'a, MemoryMapAnnex>; #[derive(Debug)] pub struct MemoryMapAnnex; impl ParseAnnex<'_> for MemoryMapAnnex { #[inline(always)] fn from_buf(buf: &[u8]) -> Option { if buf != b"" { return None; } Some(MemoryMapAnnex) } } ================================================ FILE: src/protocol/commands/_qfThreadInfo.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct qfThreadInfo; impl<'a> ParseCommand<'a> for qfThreadInfo { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { if !buf.into_body().is_empty() { return None; } Some(qfThreadInfo) } } ================================================ FILE: src/protocol/commands/_qsThreadInfo.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct qsThreadInfo; impl<'a> ParseCommand<'a> for qsThreadInfo { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { if !buf.into_body().is_empty() { return None; } Some(qsThreadInfo) } } ================================================ FILE: src/protocol/commands/_r_upcase.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct R; impl<'a> ParseCommand<'a> for R { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { crate::__dead_code_marker!("R", "from_packet"); // Technically speaking, the `R` packet does include a hex-encoded byte as well, // but even the GDB docs mention that it's unused (heck, the source-level // comments in the GDB client suggest no-one really knows what it's used for). // // We'll pay some lip-service to this requirement by checking the body's length, // but we won't actually parse the number. let body = buf.into_body(); if body.len() != 2 { None } else { Some(R) } } } ================================================ FILE: src/protocol/commands/_s.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct s<'a> { pub addr: Option<&'a [u8]>, } impl<'a> ParseCommand<'a> for s<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); if body.is_empty() { return Some(s { addr: None }); } let addr = match body { [] => None, _ => Some(decode_hex_buf(body).ok()? as &[u8]), }; Some(s { addr }) } } ================================================ FILE: src/protocol/commands/_t_upcase.rs ================================================ use super::prelude::*; use crate::protocol::common::thread_id::ThreadId; #[derive(Debug)] pub struct T { pub thread: ThreadId, } impl<'a> ParseCommand<'a> for T { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); Some(T { thread: body.try_into().ok()?, }) } } ================================================ FILE: src/protocol/commands/_vAttach.rs ================================================ use super::prelude::*; use crate::common::Pid; #[derive(Debug)] pub struct vAttach { pub pid: Pid, } impl<'a> ParseCommand<'a> for vAttach { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { crate::__dead_code_marker!("vAttach", "from_packet"); let body = buf.into_body(); let pid = match body { [b';', pid @ ..] => Pid::new(decode_hex(pid).ok()?)?, _ => return None, }; Some(vAttach { pid }) } } ================================================ FILE: src/protocol/commands/_vCont.rs ================================================ use super::prelude::*; use crate::common::Signal; use crate::protocol::common::hex::HexString; use crate::protocol::common::thread_id::SpecificThreadId; use crate::protocol::common::thread_id::ThreadId; use crate::protocol::IdKind; // TODO?: instead of lazily parsing data, parse the strings into a compressed // binary representations that can be stuffed back into the packet buffer and // return an iterator over the binary data that's _guaranteed_ to be valid. This // would clean up some of the code in the vCont handler. // // The interesting part would be to see whether or not the simplified error // handing code will compensate for all the new code required to pre-validate // the data... #[derive(Debug)] pub enum vCont<'a> { Query, Actions(Actions<'a>), } impl<'a> ParseCommand<'a> for vCont<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); match body as &[u8] { b"?" => Some(vCont::Query), _ => Some(vCont::Actions(Actions::new_from_buf(body))), } } } #[derive(Debug)] pub enum Actions<'a> { Buf(ActionsBuf<'a>), FixedStep(SpecificThreadId), FixedCont(SpecificThreadId), } impl<'a> Actions<'a> { fn new_from_buf(buf: &'a [u8]) -> Actions<'a> { Actions::Buf(ActionsBuf(buf)) } #[inline(always)] pub fn new_step(tid: SpecificThreadId) -> Actions<'a> { Actions::FixedStep(tid) } #[inline(always)] pub fn new_continue(tid: SpecificThreadId) -> Actions<'a> { Actions::FixedCont(tid) } pub fn iter(&self) -> impl Iterator>> + '_ { match self { Actions::Buf(x) => EitherIter::A(x.iter()), Actions::FixedStep(x) => EitherIter::B(core::iter::once(Some(VContAction { kind: VContKind::Step, thread: Some(*x), }))), Actions::FixedCont(x) => EitherIter::B(core::iter::once(Some(VContAction { kind: VContKind::Continue, thread: Some(*x), }))), } } } #[derive(Debug)] pub struct ActionsBuf<'a>(&'a [u8]); impl<'a> ActionsBuf<'a> { fn iter(&self) -> impl Iterator>> + '_ { self.0.split(|b| *b == b';').skip(1).map(|act| { let mut s = act.split(|b| *b == b':'); let kind = s.next()?; let thread = match s.next() { Some(s) => { let mut tid = ThreadId::try_from(s).ok()?; // Based on my (somewhat superficial) readings of the // `gdbserver` and `gdb` codebases, it doesn't seem like // there's any consensus on how vCont packets with a TID of // `Any` should be be handled. // // `gdbserver` delegates the handling of this packet to // individual targets, and in-turn, it seems most targets // don't actually do any special handling of the 'Any' TID. // They'll explicitly check for the 'All' TID, but then // proceed to erroneously treat the 'Any' TID as though it // was simply a request for a TID with ID of '0' to be // resumed (which is prohibited by the GDB RSP spec). // // This behavior makes sense, given the context that AFAIK, // upstream GDB never actually sends vCont packets with a // 'Any' TID! Instead, upstream GDB will first query the // remote to obtain a list of valid TIDs, and then send over // a vCont packet with a specific TID selected. // So, with all that said - how should `gdbstub` handle // these sorts of packets? // // Unfortunately, it seems like there are some weird // third-party GDB clients out there that do in-fact send an // 'Any' TID as part of a vCont packet. // // See issue #150 for more info. // // As a workaround for these weird GDB clients, `gdbstub` // takes the pragmatic approach of treating this request as // though it the client requested _all_ threads to be // resumed. // // If this turns out to be wrong... `gdbstub` can explore a // more involved fix, whereby we bubble-up this `Any` // request to the stub code itself, whereupon the stub can // attempt to pick a "reasonable" TID to individually // resume. if tid.tid == IdKind::Any { tid.tid = IdKind::All; } Some(SpecificThreadId::try_from(tid).ok()?) } None => None, }; Some(VContAction { kind: VContKind::from_bytes(kind)?, thread, }) }) } } #[derive(Debug, Copy, Clone)] pub struct VContAction<'a> { pub kind: VContKind<'a>, pub thread: Option, } #[derive(Debug, Copy, Clone)] pub enum VContKind<'a> { Continue, ContinueWithSig(Signal), RangeStep(HexString<'a>, HexString<'a>), Step, StepWithSig(Signal), Stop, } impl VContKind<'_> { #[inline(always)] fn from_bytes(s: &[u8]) -> Option> { use self::VContKind::*; let res = match s { [b'c'] => Continue, [b's'] => Step, [b't'] => Stop, [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()?)) } _ => return None, }; Some(res) } } /// Helper type to unify iterators that output the same type. Returned as an /// opaque type from `Actions::iter()`. enum EitherIter { A(A), B(B), } impl Iterator for EitherIter where A: Iterator, B: Iterator, { type Item = T; #[inline(always)] fn next(&mut self) -> Option { match self { EitherIter::A(a) => a.next(), EitherIter::B(b) => b.next(), } } } ================================================ FILE: src/protocol/commands/_vFile_close.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct vFileClose { pub fd: u32, } impl<'a> ParseCommand<'a> for vFileClose { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); if body.is_empty() { return None; } match body { [b':', body @ ..] => { let fd = decode_hex(body).ok()?; Some(vFileClose { fd }) } _ => None, } } } ================================================ FILE: src/protocol/commands/_vFile_fstat.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct vFileFstat { pub fd: u32, } impl<'a> ParseCommand<'a> for vFileFstat { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); if body.is_empty() { return None; } match body { [b':', body @ ..] => { let fd = decode_hex(body).ok()?; Some(vFileFstat { fd }) } _ => None, } } } ================================================ FILE: src/protocol/commands/_vFile_open.rs ================================================ use super::prelude::*; use crate::target::ext::host_io::HostIoOpenFlags; use crate::target::ext::host_io::HostIoOpenMode; #[derive(Debug)] pub struct vFileOpen<'a> { pub filename: &'a [u8], pub flags: HostIoOpenFlags, pub mode: HostIoOpenMode, } impl<'a> ParseCommand<'a> for vFileOpen<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); if body.is_empty() { return None; } match body { [b':', body @ ..] => { let mut body = body.splitn_mut(3, |b| *b == b','); let filename = decode_hex_buf(body.next()?).ok()?; let flags = HostIoOpenFlags::from_bits(decode_hex(body.next()?).ok()?)?; let mode = HostIoOpenMode::from_bits(decode_hex(body.next()?).ok()?)?; Some(vFileOpen { filename, flags, mode, }) } _ => None, } } } ================================================ FILE: src/protocol/commands/_vFile_pread.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct vFilePread<'a> { pub fd: u32, pub count: usize, pub offset: u64, pub buf: &'a mut [u8], } impl<'a> ParseCommand<'a> for vFilePread<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let (buf, body_range) = buf.into_raw_buf(); let body = buf.get_mut(body_range.start..body_range.end)?; if body.is_empty() { return None; } match body { [b':', body @ ..] => { let mut body = body.splitn_mut(3, |b| *b == b','); let fd = decode_hex(body.next()?).ok()?; let count = decode_hex(body.next()?).ok()?; let offset = decode_hex(body.next()?).ok()?; Some(vFilePread { fd, count, offset, buf, }) } _ => None, } } } ================================================ FILE: src/protocol/commands/_vFile_pwrite.rs ================================================ use super::prelude::*; use crate::protocol::common::hex::decode_bin_buf; #[derive(Debug)] pub struct vFilePwrite<'a> { pub fd: u32, pub offset: &'a [u8], pub data: &'a [u8], } impl<'a> ParseCommand<'a> for vFilePwrite<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); if body.is_empty() { return None; } match body { [b':', body @ ..] => { let mut body = body.splitn_mut(3, |b| *b == b','); let fd = decode_hex(body.next()?).ok()?; let offset = decode_hex_buf(body.next()?).ok()?; let data = decode_bin_buf(body.next()?)?; Some(vFilePwrite { fd, offset, data }) } _ => None, } } } ================================================ FILE: src/protocol/commands/_vFile_readlink.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct vFileReadlink<'a> { pub filename: &'a [u8], pub buf: &'a mut [u8], } impl<'a> ParseCommand<'a> for vFileReadlink<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let (buf, body_range) = buf.into_raw_buf(); // TODO: rewrite to avoid panic let (body, buf) = buf[body_range.start..].split_at_mut(body_range.end - body_range.start); if body.is_empty() { return None; } match body { [b':', body @ ..] => { let filename = decode_hex_buf(body).ok()?; Some(vFileReadlink { filename, buf }) } _ => None, } } } ================================================ FILE: src/protocol/commands/_vFile_setfs.rs ================================================ use super::prelude::*; use crate::target::ext::host_io::FsKind; #[derive(Debug)] pub struct vFileSetfs { pub fs: FsKind, } impl<'a> ParseCommand<'a> for vFileSetfs { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); if body.is_empty() { return None; } match body { [b':', body @ ..] => { let fs = match crate::common::Pid::new(decode_hex(body).ok()?) { None => FsKind::Stub, Some(pid) => FsKind::Pid(pid), }; Some(vFileSetfs { fs }) } _ => None, } } } ================================================ FILE: src/protocol/commands/_vFile_unlink.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct vFileUnlink<'a> { pub filename: &'a [u8], } impl<'a> ParseCommand<'a> for vFileUnlink<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); if body.is_empty() { return None; } match body { [b':', body @ ..] => { let filename = decode_hex_buf(body).ok()?; Some(vFileUnlink { filename }) } _ => None, } } } ================================================ FILE: src/protocol/commands/_vFlashDone.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct vFlashDone; impl<'a> ParseCommand<'a> for vFlashDone { #[inline(always)] fn from_packet(_buf: PacketBuf<'a>) -> Option { Some(vFlashDone) } } ================================================ FILE: src/protocol/commands/_vFlashErase.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct vFlashErase<'a> { pub addr: &'a [u8], pub length: &'a [u8], } impl<'a> ParseCommand<'a> for vFlashErase<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); let mut body = body.splitn_mut(3, |&b| b == b',' || b == b':'); let _first_colon = body.next()?; let addr = decode_hex_buf(body.next()?).ok()?; let length = decode_hex_buf(body.next()?) .ok() .filter(|l| !l.is_empty())?; Some(Self { addr, length }) } } #[cfg(test)] mod tests { use super::*; macro_rules! test_buf { ($bufname:ident, $body:literal) => { let mut test = $body.to_vec(); let mut buf = PacketBuf::new_with_raw_body(&mut test).unwrap(); if !buf.strip_prefix(b"vFlashErase") { panic!("invalid test"); } let $bufname = buf; }; } #[test] fn valid_vFlashErase() { test_buf!(buf, b"vFlashErase:08000000,00004000"); let pkt = vFlashErase::from_packet(buf).unwrap(); assert_eq!(pkt.addr, [0x08, 0, 0, 0]); assert_eq!(pkt.length, [0, 0, 0x40, 0]); } #[test] fn invalid_vFlashErase_wrong_address() { test_buf!(buf, b"vFlashErase:abcdefg:00004000"); assert!(vFlashErase::from_packet(buf).is_none()); } #[test] fn invalid_vFlashErase_wrong_length() { test_buf!(buf, b"vFlashErase:08000000:abcdefg"); assert!(vFlashErase::from_packet(buf).is_none()); } #[test] fn invalid_vFlashErase_missing_address() { test_buf!(buf, b"vFlashErase:"); assert!(vFlashErase::from_packet(buf).is_none()); } #[test] fn invalid_vFlashErase_missing_length() { test_buf!(buf, b"vFlashErase:08000000:"); assert!(vFlashErase::from_packet(buf).is_none()); } } ================================================ FILE: src/protocol/commands/_vFlashWrite.rs ================================================ use super::prelude::*; use crate::protocol::common::hex::decode_bin_buf; #[derive(Debug)] pub struct vFlashWrite<'a> { pub addr: &'a [u8], pub val: &'a [u8], } impl<'a> ParseCommand<'a> for vFlashWrite<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); let mut body = body.splitn_mut(3, |&b| b == b':'); let _first_colon = body.next()?; let addr = decode_hex_buf(body.next()?) .ok() .filter(|a| !a.is_empty())?; let val = decode_bin_buf(body.next()?)?; Some(vFlashWrite { addr, val }) } } #[cfg(test)] mod tests { use super::*; macro_rules! test_buf { ($bufname:ident, $body:literal) => { let mut test = $body.to_vec(); let mut buf = PacketBuf::new_with_raw_body(&mut test).unwrap(); if !buf.strip_prefix(b"vFlashWrite") { panic!("invalid test"); } let $bufname = buf; }; } #[test] fn valid_vFlashWrite() { test_buf!( buf, b"vFlashWrite:08000000:\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A" ); let pkt = vFlashWrite::from_packet(buf).unwrap(); assert_eq!(pkt.addr, [0x08, 0, 0, 0]); assert_eq!(pkt.val, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); } #[test] fn invalid_vFlashWrite_wrong_address() { test_buf!( buf, b"vFlashWrite:abcdefg:\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A" ); assert!(vFlashWrite::from_packet(buf).is_none()) } #[test] fn invalid_vFlashWrite_missing_data() { test_buf!(buf, b"vFlashWrite:abcdefg:"); assert!(vFlashWrite::from_packet(buf).is_none()) } #[test] fn invalid_vFlashWrite_missing_address() { test_buf!(buf, b"vFlashWrite:"); assert!(vFlashWrite::from_packet(buf).is_none()) } } ================================================ FILE: src/protocol/commands/_vKill.rs ================================================ use super::prelude::*; use crate::common::Pid; #[derive(Debug)] pub struct vKill { pub pid: Pid, } impl<'a> ParseCommand<'a> for vKill { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); let pid = match body { [b';', pid @ ..] => Pid::new(decode_hex(pid).ok()?)?, _ => return None, }; Some(vKill { pid }) } } ================================================ FILE: src/protocol/commands/_vRun.rs ================================================ use super::prelude::*; use crate::protocol::common::lists::ArgListHex; #[derive(Debug)] pub struct vRun<'a> { pub filename: Option<&'a [u8]>, pub args: ArgListHex<'a>, } impl<'a> ParseCommand<'a> for vRun<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); let mut body = body.splitn_mut(3, |b| *b == b';'); let _first_semi = body.next()?; let filename = match decode_hex_buf(body.next()?).ok()? { [] => None, s => Some(s as &[u8]), }; let args = body.next().unwrap_or(&mut []); // args are optional Some(vRun { filename, args: ArgListHex::from_packet(args)?, }) } } #[cfg(test)] mod tests { use super::*; macro_rules! test_buf { ($bufname:ident, $body:literal) => { let mut test = $body.to_vec(); let mut buf = PacketBuf::new_with_raw_body(&mut test).unwrap(); if !buf.strip_prefix(b"vRun") { panic!("invalid test"); } let $bufname = buf; }; } #[test] fn valid_vRun_foobarbaz() { test_buf!(buf, b"vRun;;666f6f;626172;62617a"); let pkt = vRun::from_packet(buf).unwrap(); let args = pkt.args.into_iter().collect::>(); assert_eq!(pkt.filename, None); assert_eq!(args, &[b"foo", b"bar", b"baz"]); } #[test] fn valid_vRun_noname() { test_buf!(buf, b"vRun;"); let pkt = vRun::from_packet(buf).unwrap(); let args = pkt.args.into_iter().collect::>(); assert_eq!(pkt.filename, None); assert_eq!(args, &[] as &[&[u8]]); } #[test] fn valid_vRun_noargs() { test_buf!(buf, b"vRun;74657374"); let pkt = vRun::from_packet(buf).unwrap(); let args = pkt.args.into_iter().collect::>(); assert_eq!(pkt.filename, Some(&b"test"[..])); assert_eq!(args, &[] as &[&[u8]]); } #[test] fn valid_vRun_args() { test_buf!(buf, b"vRun;74657374;74657374"); let pkt = vRun::from_packet(buf).unwrap(); let args = pkt.args.into_iter().collect::>(); assert_eq!(pkt.filename, Some(&b"test"[..])); assert_eq!(args, &[b"test"]); } #[test] fn invalid_vRun_args() { test_buf!(buf, b"vRun;74657374;nothex"); assert!(vRun::from_packet(buf).is_none()); } #[test] fn invalid_vRun() { test_buf!(buf, b"vRun;nothex;nothex"); assert!(vRun::from_packet(buf).is_none()); } } ================================================ FILE: src/protocol/commands/_x_lowcase.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct x<'a> { pub addr: &'a [u8], pub len: usize, /// Reuse PacketBuf underlying buffer to read the binary data into it pub buf: &'a mut [u8], } impl<'a> ParseCommand<'a> for x<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { // the total packet buffer currently looks like: // // +------+--------------------+-------------------+-------+-----------------+ // | "$x" | addr (hex-encoded) | len (hex-encoded) | "#XX" | empty space ... | // +------+--------------------+-------------------+-------+-----------------+ // // Unfortunately, while `len` can be hex-decoded right here and now into a // `usize`, `addr` corresponds to a Target::Arch::Usize, which requires holding // on to a valid &[u8] reference into the buffer. // // While it's not _perfectly_ efficient, simply leaving the decoded addr in // place and wasting a couple bytes is probably the easiest way to tackle this // problem: // // +------+------------------+------------------------------------------------+ // | "$x" | addr (raw bytes) | usable buffer ... | // +------+------------------+------------------------------------------------+ let (buf, body_range) = buf.into_raw_buf(); let body = buf.get_mut(body_range.start..body_range.end)?; let mut body = body.split_mut(|b| *b == b','); let addr = decode_hex_buf(body.next()?).ok()?; let addr_len = addr.len(); let len = decode_hex(body.next()?).ok()?; // ensures that `split_at_mut` doesn't panic if buf.len() < body_range.start + addr_len { return None; } let (addr, buf) = buf.split_at_mut(body_range.start + addr_len); let addr = addr.get(b"$x".len()..)?; Some(x { addr, len, buf }) } } ================================================ FILE: src/protocol/commands/_x_upcase.rs ================================================ use super::prelude::*; use crate::protocol::common::hex::decode_bin_buf; #[derive(Debug)] pub struct X<'a> { pub addr: &'a [u8], pub val: &'a [u8], } impl<'a> ParseCommand<'a> for X<'a> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); let mut body = body.splitn_mut(3, |&b| b == b',' || b == b':'); let addr = decode_hex_buf(body.next()?).ok()?; // See the comment in `_m_upcase.rs` for why the `len` field is handled // this way. All the same rationale applies here (given that the X // packet is just a new-and-improved version of the M packet). let _len: usize = decode_hex(body.next()?).ok()?; let val = decode_bin_buf(body.next()?)?; Some(X { addr, val }) } } ================================================ FILE: src/protocol/commands/breakpoint.rs ================================================ use crate::protocol::common::hex::decode_hex; use crate::protocol::common::hex::decode_hex_buf; // Breakpoint packets are split up like this: // // Z0,addr,kind[;cond_list…][;cmds:persist,cmd_list…] // \_________/ // | // BasicBreakpoint // \_______________________________________________/ // | // BytecodeBreakpoint // // If the target does not implement the `Agent` extension, only the // `BasicBreakpoint` part is parsed, which helps cut down on binary bloat. #[derive(Debug)] pub struct BasicBreakpoint<'a> { pub type_: u8, pub addr: &'a [u8], /// architecture dependent pub kind: &'a [u8], } impl<'a> BasicBreakpoint<'a> { pub fn from_slice(body: &'a mut [u8]) -> Option> { let mut body = body.splitn_mut(4, |b| matches!(*b, b',' | b';')); let type_ = decode_hex(body.next()?).ok()?; let addr = decode_hex_buf(body.next()?).ok()?; let kind = decode_hex_buf(body.next()?).ok()?; Some(BasicBreakpoint { type_, addr, kind }) } } #[allow(dead_code)] // Bytecode hasn't yet been plumbed all the way through #[derive(Debug)] pub struct BytecodeBreakpoint<'a> { pub base: BasicBreakpoint<'a>, pub conds: Option>, pub cmds_persist: Option<(BytecodeList<'a>, bool)>, } impl<'a> BytecodeBreakpoint<'a> { pub fn from_slice(body: &'a mut [u8]) -> Option> { let mut body = body.splitn_mut(2, |b| *b == b';'); let base = BasicBreakpoint::from_slice(body.next()?)?; let mut conds = None; let mut cmds_persist = None; if let Some(rest) = body.next() { let mut s = rest.split_mut(|b| *b == b':'); let (raw_conds, raw_cmds) = match (s.next(), s.next()) { (Some(a), Some(b)) => (Some(strip_suffix_mut(a, b";cmds")?), Some(b)), (Some(a), None) => { if a.starts_with(b"cmds") { (None, Some(a)) } else { (Some(a), None) } } _ => return None, }; if let Some(raw_conds) = raw_conds { conds = Some(BytecodeList(raw_conds)); } if let Some(raw_cmds) = raw_cmds { let mut raw_cmds = raw_cmds.split_mut(|b| *b == b','); let raw_persist = decode_hex::(raw_cmds.next()?).ok()? != 0; let raw_cmds = raw_cmds.next()?; cmds_persist = Some((BytecodeList(raw_cmds), raw_persist)); } } Some(BytecodeBreakpoint { base, conds, cmds_persist, }) } } fn strip_suffix_mut<'a, T>(slice: &'a mut [T], suffix: &[T]) -> Option<&'a mut [T]> where T: PartialEq, { let (len, n) = (slice.len(), suffix.len()); if n <= len { let (head, tail) = slice.split_at_mut(len - n); if tail == suffix { return Some(head); } } None } /// A lazily evaluated iterator over a series of bytecode expressions. #[derive(Debug)] pub struct BytecodeList<'a>(&'a mut [u8]); impl<'a> BytecodeList<'a> { #[allow(dead_code)] pub fn into_iter(self) -> impl Iterator> + 'a { self.0.split_mut(|b| *b == b'X').skip(1).map(|s| { let mut s = s.split_mut(|b| *b == b','); let _len = s.next()?; let code = decode_hex_buf(s.next()?).ok()?; Some(code as &[u8]) }) } } ================================================ FILE: src/protocol/commands/exclamation_mark.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct ExclamationMark; impl<'a> ParseCommand<'a> for ExclamationMark { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { if !buf.into_body().is_empty() { return None; } Some(ExclamationMark) } } ================================================ FILE: src/protocol/commands/question_mark.rs ================================================ use super::prelude::*; #[derive(Debug)] pub struct QuestionMark; impl<'a> ParseCommand<'a> for QuestionMark { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { if !buf.into_body().is_empty() { return None; } Some(QuestionMark) } } ================================================ FILE: src/protocol/commands.rs ================================================ use crate::protocol::packet::PacketBuf; use crate::target::Target; use pastey::paste; /// Common imports used by >50% of all packet parsers. /// /// Do not clutter this prelude with types only used by a few packets. pub mod prelude { pub use crate::protocol::commands::ParseCommand; 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 { /// Try to parse a packet from the packet buffer. fn from_packet(buf: PacketBuf<'a>) -> Option; } macro_rules! commands { ( $( $ext:ident $(use $lt:lifetime)? { $($name:literal => $mod:ident::$command:ident$(<$lifetime:lifetime>)?,)* } )* ) => {paste! { // Most packets follow a consistent model of "only enabled when a // particular IDET is implemented", but there are some exceptions to // this rule that need to be special-cased: // // # Breakpoint packets (z, Z) // // Breakpoint packets are special-cased, as the "Z" packet is parsed // differently depending on whether or not the target implements the // `Agent` extension. // // While it's entirely possible to eagerly parse the "Z" packet for // bytecode, doing so would unnecessary bloat implementations that do // not support evaluating agent expressions. $($( #[allow(non_snake_case, non_camel_case_types)] pub mod $mod; )*)* pub mod breakpoint; pub mod ext { $( #[allow(non_camel_case_types, clippy::enum_variant_names, clippy::upper_case_acronyms)] pub enum [<$ext:camel>] $(<$lt>)? { $($command(super::$mod::$command<$($lifetime)?>),)* } )* use super::breakpoint::{BasicBreakpoint, BytecodeBreakpoint}; #[allow(non_camel_case_types)] pub enum Breakpoints<'a> { z(BasicBreakpoint<'a>), Z(BasicBreakpoint<'a>), // Bytecode hasn't yet been plumbed all the way through #[allow(dead_code)] ZWithBytecode(BytecodeBreakpoint<'a>), } } /// GDB commands pub enum Command<'a> { $( [<$ext:camel>](ext::[<$ext:camel>]$(<$lt>)?), )* Breakpoints(ext::Breakpoints<'a>), Unknown(&'a [u8]), } impl<'a> Command<'a> { pub fn from_packet( target: &mut impl Target, mut buf: PacketBuf<'a> ) -> Option> { // HACK: this locally-scoped trait enables using identifiers // that aren't top-level `Target` IDETs to split-up the packet // parsing code. trait Hack { fn support_base(&mut self) -> Option<()>; fn support_target_xml(&mut self) -> Option<()>; fn support_lldb_register_info(&mut self) -> Option<()>; fn support_resume(&mut self) -> Option<()>; 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_lowcase_packet(&mut self) -> Option<()>; fn support_x_upcase_packet(&mut self) -> Option<()>; fn support_thread_extra_info(&mut self) -> Option<()>; } impl Hack for T { fn support_base(&mut self) -> Option<()> { Some(()) } fn support_target_xml(&mut self) -> Option<()> { use crate::arch::Arch; if self.use_target_description_xml() && (T::Arch::target_description_xml().is_some() || self.support_target_description_xml_override().is_some()) { Some(()) } else { None } } fn support_lldb_register_info(&mut self) -> Option<()> { use crate::arch::Arch; if self.use_lldb_register_info() && (T::Arch::lldb_register_info(usize::MAX).is_some() || self.support_lldb_register_info_override().is_some()) { Some(()) } else { None } } fn support_resume(&mut self) -> Option<()> { self.base_ops().resume_ops().map(drop) } fn support_single_register_access(&mut self) -> Option<()> { use crate::target::ext::base::BaseOps; match self.base_ops() { BaseOps::SingleThread(ops) => ops.support_single_register_access().map(drop), BaseOps::MultiThread(ops) => ops.support_single_register_access().map(drop), } } fn support_reverse_step(&mut self) -> Option<()> { use crate::target::ext::base::ResumeOps; match self.base_ops().resume_ops()? { ResumeOps::SingleThread(ops) => ops.support_reverse_step().map(drop), ResumeOps::MultiThread(ops) => ops.support_reverse_step().map(drop), } } fn support_reverse_cont(&mut self) -> Option<()> { use crate::target::ext::base::ResumeOps; match self.base_ops().resume_ops()? { ResumeOps::SingleThread(ops) => ops.support_reverse_cont().map(drop), ResumeOps::MultiThread(ops) => ops.support_reverse_cont().map(drop), } } fn support_x_lowcase_packet(&mut self) -> Option<()> { if self.use_x_lowcase_packet() { Some(()) } else { None } } fn support_x_upcase_packet(&mut self) -> Option<()> { if self.use_x_upcase_packet() { Some(()) } else { None } } 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() { BaseOps::SingleThread(_) => None, BaseOps::MultiThread(ops) => ops.support_thread_extra_info().map(drop), } } } // TODO?: use tries for more efficient longest prefix matching $( #[allow(clippy::string_lit_as_bytes)] if target.[< support_ $ext >]().is_some() { $( if buf.strip_prefix($name.as_bytes()) { crate::__dead_code_marker!($name, "prefix_match"); let cmd = $mod::$command::from_packet(buf)?; return Some( Command::[<$ext:camel>]( ext::[<$ext:camel>]::$command(cmd) ) ) } )* } )* if let Some(_breakpoint_ops) = target.support_breakpoints() { use breakpoint::{BasicBreakpoint, BytecodeBreakpoint}; if buf.strip_prefix(b"z") { let cmd = BasicBreakpoint::from_slice(buf.into_body())?; return Some(Command::Breakpoints(ext::Breakpoints::z(cmd))) } if buf.strip_prefix(b"Z") { // TODO: agent bytecode currently unimplemented if true { let cmd = BasicBreakpoint::from_slice(buf.into_body())?; return Some(Command::Breakpoints(ext::Breakpoints::Z(cmd))) } else { let cmd = BytecodeBreakpoint::from_slice(buf.into_body())?; return Some(Command::Breakpoints(ext::Breakpoints::ZWithBytecode(cmd))) } } } Some(Command::Unknown(buf.into_body())) } } }}; } commands! { base use 'a { "?" => question_mark::QuestionMark, "D" => _d_upcase::D, "g" => _g::g, "G" => _g_upcase::G<'a>, "H" => _h_upcase::H, "k" => _k::k, "m" => _m::m<'a>, "M" => _m_upcase::M<'a>, "qAttached" => _qAttached::qAttached, "qfThreadInfo" => _qfThreadInfo::qfThreadInfo, "qsThreadInfo" => _qsThreadInfo::qsThreadInfo, "qSupported" => _qSupported::qSupported<'a>, "T" => _t_upcase::T, "vKill" => _vKill::vKill, } target_xml use 'a { "qXfer:features:read" => _qXfer_features_read::qXferFeaturesRead<'a>, } resume use 'a { "c" => _c::c<'a>, "s" => _s::s<'a>, "vCont" => _vCont::vCont<'a>, } x_lowcase_packet use 'a { "x" => _x_lowcase::x<'a>, } x_upcase_packet use 'a { "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>, } extended_mode use 'a { "!" => exclamation_mark::ExclamationMark, "qC" => _qC::qC, "QDisableRandomization" => _QDisableRandomization::QDisableRandomization, "QEnvironmentHexEncoded" => _QEnvironmentHexEncoded::QEnvironmentHexEncoded<'a>, "QEnvironmentReset" => _QEnvironmentReset::QEnvironmentReset, "QEnvironmentUnset" => _QEnvironmentUnset::QEnvironmentUnset<'a>, "QSetWorkingDir" => _QSetWorkingDir::QSetWorkingDir<'a>, "QStartupWithShell" => _QStartupWithShell::QStartupWithShell, "R" => _r_upcase::R, "vAttach" => _vAttach::vAttach, "vRun" => _vRun::vRun<'a>, } monitor_cmd use 'a { "qRcmd" => _qRcmd::qRcmd<'a>, } section_offsets { "qOffsets" => _qOffsets::qOffsets, } reverse_cont { "bc" => _bc::bc, } reverse_step { "bs" => _bs::bs, } memory_map use 'a { "qXfer:memory-map:read" => _qXfer_memory_map::qXferMemoryMapRead<'a>, } flash_operations use 'a { "vFlashErase" => _vFlashErase::vFlashErase<'a>, "vFlashWrite" => _vFlashWrite::vFlashWrite<'a>, "vFlashDone" => _vFlashDone::vFlashDone, } auxv use 'a { "qXfer:auxv:read" => _qXfer_auxv_read::qXferAuxvRead<'a>, } exec_file use 'a { "qXfer:exec-file:read" => _qXfer_exec_file::qXferExecFileRead<'a>, } host_io use 'a { "vFile:open" => _vFile_open::vFileOpen<'a>, "vFile:close" => _vFile_close::vFileClose, "vFile:pread" => _vFile_pread::vFilePread<'a>, "vFile:pwrite" => _vFile_pwrite::vFilePwrite<'a>, "vFile:fstat" => _vFile_fstat::vFileFstat, "vFile:unlink" => _vFile_unlink::vFileUnlink<'a>, "vFile:readlink" => _vFile_readlink::vFileReadlink<'a>, "vFile:setfs" => _vFile_setfs::vFileSetfs, } catch_syscalls use 'a { "QCatchSyscalls" => _QCatchSyscalls::QCatchSyscalls<'a>, } thread_extra_info use 'a { "qThreadExtraInfo" => _qThreadExtraInfo::qThreadExtraInfo<'a>, } lldb_register_info { "qRegisterInfo" => _qRegisterInfo::qRegisterInfo, } libraries_svr4 use 'a { "qXfer:libraries-svr4:read" => _qXfer_libraries_svr4_read::qXferLibrariesSvr4Read<'a>, } libraries use 'a { "qXfer:libraries:read" => _qXfer_libraries_read::qXferLibrariesRead<'a>, } tracepoints use 'a { "QTDPsrc" => _QTDPsrc::QTDPsrc<'a>, "QTDP" => _QTDP::QTDP<'a>, "QTinit" => _QTinit::QTinit, "QTBuffer" => _QTBuffer_upcase::QTBuffer, "QTStart" => _QTStart::QTStart, "QTStop" => _QTStop::QTStop, "QTFrame" => _QTFrame::QTFrame<'a>, "qTBuffer" => _qTBuffer::qTBuffer, "qTStatus" => _qTStatus::qTStatus, "qTP" => _qTP::qTP<'a>, "qTfP" => _qTfP::qTfP, "qTsP" => _qTsP::qTsP, // QTDV is unimplemented. "qTfV" => _qTfV::qTfV, "qTsV" => _qTsV::qTsV, } host_info { "qHostInfo" => _qHostInfo::qHostInfo, } process_info { "qProcessInfo" => _qProcessInfo::qProcessInfo, } wasm use 'a { "qWasmCallStack" => _qWasmCallStack::qWasmCallStack, "qWasmLocal" => _qWasmLocal::qWasmLocal<'a>, "qWasmGlobal" => _qWasmGlobal::qWasmGlobal<'a>, "qWasmStackValue" => _qWasmStackValue::qWasmStackValue<'a>, } } ================================================ FILE: src/protocol/common/hex.rs ================================================ use num_traits::CheckedAdd; use num_traits::CheckedMul; use num_traits::FromPrimitive; use num_traits::Zero; #[derive(Debug)] pub enum DecodeHexError { NotAscii, Empty, Overflow, InvalidOutput, } /// Decode a GDB hex string into the specified integer. /// /// GDB hex strings may include "xx", which represent "missing" data. This /// method simply treats "xx" as 0x00. pub fn decode_hex(buf: &[u8]) -> Result where I: FromPrimitive + Zero + CheckedAdd + CheckedMul, { use DecodeHexError::*; let radix = I::from_u8(16).ok_or(InvalidOutput)?; if buf.is_empty() { return Err(Empty); } let mut result = I::zero(); for &digit in buf { let x = I::from_u8(ascii2byte(digit).ok_or(NotAscii)?).ok_or(InvalidOutput)?; result = result.checked_mul(&radix).ok_or(Overflow)?; result = result.checked_add(&x).ok_or(Overflow)? } Ok(result) } /// Wrapper around a raw hex string. Enables "late" calls to `decode` from /// outside the `crate::protocol` module. #[derive(Debug, Clone, Copy)] pub struct HexString<'a>(pub &'a [u8]); impl HexString<'_> { pub fn decode(&self) -> Result where I: FromPrimitive + Zero + CheckedAdd + CheckedMul, { decode_hex(self.0) } } #[derive(Debug)] pub enum DecodeHexBufError { NotAscii, } #[inline] fn ascii2byte(c: u8) -> Option { match c { b'0'..=b'9' => Some(c - b'0'), b'a'..=b'f' => Some(c - b'a' + 10), b'A'..=b'F' => Some(c - b'A' + 10), b'x' | b'X' => Some(0), _ => None, } } /// Check if the byte `c` is a valid GDB hex digit `[0-9a-fA-FxX]` #[inline] pub fn is_hex(c: u8) -> bool { #[allow(clippy::match_like_matches_macro)] // mirror ascii2byte match c { b'0'..=b'9' => true, b'a'..=b'f' => true, b'A'..=b'F' => true, b'x' | b'X' => true, _ => false, } } /// Decode a GDB hex string into a byte slice _in place_. /// /// GDB hex strings may include "xx", which represent "missing" data. This /// method simply treats "xx" as 0x00. // TODO: maybe don't blindly translate "xx" as 0x00? #[cfg(not(feature = "paranoid_unsafe"))] pub fn decode_hex_buf(base_buf: &mut [u8]) -> Result<&mut [u8], DecodeHexBufError> { use DecodeHexBufError::*; if base_buf.is_empty() { return Ok(&mut []); } let odd_adust = base_buf.len() % 2; if odd_adust != 0 { base_buf[0] = ascii2byte(base_buf[0]).ok_or(NotAscii)?; } let buf = &mut base_buf[odd_adust..]; let decoded_len = buf.len() / 2; for i in 0..decoded_len { // SAFETY: rustc isn't smart enough to automatically elide these bound checks. // // If buf.len() == 0 or 1: trivially safe, since the for block is never taken // If buf.len() >= 2: the range of values for `i` is 0..(buf.len() / 2 - 1) let (hi, lo, b) = unsafe { ( // (buf.len() / 2 - 1) * 2 // == (buf.len() - 2) // since buf.len() is >2, this is in-bounds *buf.get_unchecked(i * 2), // (buf.len() / 2 - 1) * 2 + 1 // == (buf.len() - 1) // since buf.len() is >2, this is in-bounds *buf.get_unchecked(i * 2 + 1), // since buf.len() is >2, (buf.len() / 2 - 1) is always in-bounds buf.get_unchecked_mut(i), ) }; let hi = ascii2byte(hi).ok_or(NotAscii)?; let lo = ascii2byte(lo).ok_or(NotAscii)?; *b = (hi << 4) | lo; } // SAFETY: rustc isn't smart enough to automatically elide this bound check. // // Consider the different values (decoded_len + odd_adust) can take: // // buf.len() | (decoded_len + odd_adust) // -----------|--------------------------- // 0 | (0 + 0) == 0 // 1 | (0 + 1) == 1 // 2 | (1 + 0) == 1 // 3 | (1 + 1) == 2 // 4 | (2 + 0) == 2 // 5 | (2 + 1) == 3 // // Note that the computed index is always in-bounds. // // If I were still in undergrad, I could probably have whipped up a proper // mathematical proof by induction or whatnot, but hopefully this "proof by // example" ought to suffice. unsafe { Ok(base_buf.get_unchecked_mut(..decoded_len + odd_adust)) } } /// Decode a GDB hex string into a byte slice _in place_. /// /// GDB hex strings may include "xx", which represent "missing" data. This /// method simply treats "xx" as 0x00. // TODO: maybe don't blindly translate "xx" as 0x00? #[cfg(feature = "paranoid_unsafe")] pub fn decode_hex_buf(base_buf: &mut [u8]) -> Result<&mut [u8], DecodeHexBufError> { use DecodeHexBufError::*; let odd_adust = base_buf.len() % 2; if odd_adust != 0 { base_buf[0] = ascii2byte(base_buf[0]).ok_or(NotAscii)?; } let buf = &mut base_buf[odd_adust..]; let decoded_len = buf.len() / 2; for i in 0..decoded_len { let b = ascii2byte(buf[i * 2]).ok_or(NotAscii)? << 4 | ascii2byte(buf[i * 2 + 1]).ok_or(NotAscii)?; buf[i] = b; } Ok(&mut base_buf[..decoded_len + odd_adust]) } /// Decode GDB escaped binary bytes into origin bytes _in place_. // // Thanks reddit! // https://www.reddit.com/r/rust/comments/110qzq9/any_idea_why_rust_isnt_able_to_elide_this_bounds/ pub fn decode_bin_buf(buf: &mut [u8]) -> Option<&mut [u8]> { let mut i = 0; let len = buf.len(); for j in 0..len { if i >= len { return Some(&mut buf[..j]); } if buf[i] == b'}' { buf[j] = buf.get(i + 1)? ^ 0x20; i += 1; } else { buf[j] = buf[i]; } i += 1; } Some(buf) } #[cfg(test)] mod tests { use super::*; #[test] fn decode_hex_buf_odd() { let mut payload = b"ffffff4".to_vec(); let res = decode_hex_buf(&mut payload).unwrap(); assert_eq!(res, [0xf, 0xff, 0xff, 0xf4]); } #[test] fn decode_hex_buf_even() { let mut payload = b"0123456789abcdef".to_vec(); let res = decode_hex_buf(&mut payload).unwrap(); assert_eq!(res, [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); } #[test] fn decode_hex_buf_odd_alt() { let mut payload = b"12345".to_vec(); let res = decode_hex_buf(&mut payload).unwrap(); assert_eq!(res, [0x1, 0x23, 0x45]); } #[test] fn decode_hex_buf_short() { let mut payload = b"1".to_vec(); let res = decode_hex_buf(&mut payload).unwrap(); assert_eq!(res, [0x1]); } #[test] fn decode_hex_buf_empty() { let mut payload = b"".to_vec(); let res = decode_hex_buf(&mut payload).unwrap(); assert_eq!(res, []); } #[test] fn decode_bin_buf_escaped() { let mut payload = b"}\x03}\x04}]}\n".to_vec(); let res = decode_bin_buf(&mut payload).unwrap(); assert_eq!(res, [0x23, 0x24, 0x7d, 0x2a]); } } ================================================ FILE: src/protocol/common/lists.rs ================================================ 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)] pub struct ArgListHex<'a>(&'a mut [u8]); impl<'a> ArgListHex<'a> { pub fn from_packet(args: &'a mut [u8]) -> Option { // validate that args have valid hex encoding (with ';' delimiters). // this removes all the error handling from the lazy `Args` iterator. if args.iter().any(|b| !(is_hex(*b) || *b == b';')) { return None; } Some(Self(args)) } pub fn into_iter(self) -> impl Iterator + 'a { self.0 .split_mut(|b| *b == b';') // the `from_packet` method guarantees that the args are valid hex ascii, so this should // method should never fail. .map(|raw| decode_hex_buf(raw).unwrap_or(&mut [])) .map(|s| s as &[u8]) .filter(|s| !s.is_empty()) } } ================================================ FILE: src/protocol/common/mod.rs ================================================ pub mod hex; pub mod lists; pub mod qxfer; pub mod thread_id; ================================================ FILE: src/protocol/common/qxfer.rs ================================================ use crate::protocol::commands::ParseCommand; use crate::protocol::common::hex::decode_hex; use crate::protocol::packet::PacketBuf; /// Parse the `annex` field of a qXfer packet. Used in conjunction with /// `QXferBase` to cut keep qXfer packet parsing DRY. pub trait ParseAnnex<'a>: Sized { fn from_buf(buf: &'a [u8]) -> Option; } #[derive(Debug)] pub struct QXferReadBase<'a, T: ParseAnnex<'a>> { pub annex: T, pub offset: u64, pub length: usize, pub buf: &'a mut [u8], } impl<'a, T: ParseAnnex<'a>> ParseCommand<'a> for QXferReadBase<'a, T> { #[inline(always)] fn from_packet(buf: PacketBuf<'a>) -> Option { let (buf, body_range) = buf.into_raw_buf(); // this looks a bit wacky, but the compiler is dumb and won't elide bounds // checks without it. let (body, buf) = { let buf = buf.get_mut(body_range.start..)?; if body_range.end - body_range.start > buf.len() { return None; } buf.split_at_mut(body_range.end - body_range.start) }; if body.is_empty() { return None; } let mut body = body.split(|b| *b == b':').skip(1); let annex = T::from_buf(body.next()?)?; let mut body = body.next()?.split(|b| *b == b','); let offset = decode_hex(body.next()?).ok()?; let length = decode_hex(body.next()?).ok()?; Some(QXferReadBase { annex, offset, length, buf, }) } } ================================================ FILE: src/protocol/common/thread_id.rs ================================================ 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)] pub enum IdKind { /// All threads (-1) All, /// Any thread (0) Any, /// Thread with specific ID (id > 0) WithId(NonZeroUsize), } /// Unique Thread ID. #[derive(PartialEq, Eq, Debug, Clone, Copy)] pub struct ThreadId { /// Process ID (may or may not be present). pub pid: Option, /// Thread ID. pub tid: IdKind, } impl TryFrom<&[u8]> for ThreadId { type Error = (); fn try_from(s: &[u8]) -> Result { match s { [b'p', s @ ..] => { // p. let mut s = s.split(|b| *b == b'.'); let pid: IdKind = s.next().ok_or(())?.try_into()?; let tid: IdKind = match s.next() { Some(s) => s.try_into()?, None => IdKind::All, // sending only p is valid }; Ok(ThreadId { pid: Some(pid), tid, }) } _ => { // let tid: IdKind = s.try_into()?; Ok(ThreadId { pid: None, tid }) } } } } impl TryFrom<&[u8]> for IdKind { type Error = (); fn try_from(s: &[u8]) -> Result { Ok(match s { b"-1" => IdKind::All, b"0" => IdKind::Any, id => IdKind::WithId(NonZeroUsize::new(decode_hex(id).map_err(drop)?).ok_or(())?), }) } } impl TryFrom<&mut [u8]> for ThreadId { type Error = (); fn try_from(s: &mut [u8]) -> Result { Self::try_from(s as &[u8]) } } impl TryFrom<&mut [u8]> for IdKind { type Error = (); fn try_from(s: &mut [u8]) -> Result { Self::try_from(s as &[u8]) } } /// Like [`IdKind`], without the `Any` variant. Typically used when working /// with vCont (i.e: where the `Any` variant wouldn't be valid). #[derive(PartialEq, Eq, Debug, Clone, Copy)] pub enum SpecificIdKind { /// Thread with specific ID (id > 0) WithId(core::num::NonZeroUsize), /// All threads (-1) All, } /// Like [`ThreadId`], without the `Any` variants. Typically used when working /// with vCont (i.e: where the `Any` variant wouldn't be valid). #[derive(Debug, Copy, Clone)] pub struct SpecificThreadId { /// Process ID (may or may not be present). pub pid: Option, /// Thread ID. pub tid: SpecificIdKind, } impl TryFrom for SpecificIdKind { type Error = (); fn try_from(id: IdKind) -> Result { Ok(match id { IdKind::All => SpecificIdKind::All, IdKind::WithId(id) => SpecificIdKind::WithId(id), IdKind::Any => return Err(()), }) } } impl TryFrom for SpecificThreadId { type Error = (); fn try_from(thread: ThreadId) -> Result { Ok(SpecificThreadId { pid: match thread.pid { None => None, Some(id_kind) => Some(id_kind.try_into()?), }, tid: thread.tid.try_into()?, }) } } /// Like [`ThreadId`], without the `Any`, or `All` variants. #[derive(Debug, Copy, Clone)] pub struct ConcreteThreadId { /// Process ID (may or may not be present). pub pid: Option, /// Thread ID. pub tid: NonZeroUsize, } impl TryFrom for ConcreteThreadId { type Error = (); fn try_from(thread: ThreadId) -> Result { Ok(ConcreteThreadId { pid: match thread.pid { None => None, Some(id_kind) => Some(id_kind.try_into()?), }, tid: thread.tid.try_into()?, }) } } impl TryFrom for NonZeroUsize { type Error = (); fn try_from(value: IdKind) -> Result { match value { IdKind::WithId(v) => Ok(v), _ => Err(()), } } } ================================================ FILE: src/protocol/console_output.rs ================================================ #[cfg(feature = "alloc")] use alloc::vec::Vec; use core::fmt; /// Helper struct to send console output to GDB. /// /// The recommended way to interact with `ConsoleOutput` is through the provided /// [`output!`] and [`outputln!`] macros. /// /// On resource constrained systems which might want to avoid using Rust's /// [fairly "heavy" formatting machinery](https://jamesmunns.com/blog/fmt-unreasonably-expensive/), /// the `write_raw()` method can be used to write raw data directly to the GDB /// console. /// /// When the `alloc` feature is disabled, all output buffering is disabled, and /// each call to `output!` will automatically flush data over the Connection. /// /// [`output!`]: crate::output /// [`outputln!`]: crate::outputln // TODO: support user-provided output buffers for no-`alloc` environments. pub struct ConsoleOutput<'a> { #[cfg(feature = "alloc")] buf: Vec, callback: &'a mut dyn FnMut(&[u8]), } impl fmt::Write for ConsoleOutput<'_> { fn write_str(&mut self, s: &str) -> fmt::Result { self.write_raw(s.as_bytes()); Ok(()) } } impl<'a> ConsoleOutput<'a> { pub(crate) fn new(callback: &'a mut dyn FnMut(&[u8])) -> ConsoleOutput<'a> { ConsoleOutput { #[cfg(feature = "alloc")] buf: Vec::new(), callback, } } /// Write raw (non UTF-8) data to the GDB console. pub fn write_raw(&mut self, bytes: &[u8]) { cfg_if::cfg_if! { if #[cfg(feature = "alloc")] { self.buf.extend_from_slice(bytes); } else { (self.callback)(bytes); } } } /// Flush the internal output buffer. /// /// Only available when `alloc` is enabled. #[cfg(feature = "alloc")] pub fn flush(&mut self) { if !self.buf.is_empty() { (self.callback)(&self.buf); self.buf.clear() } } } impl Drop for ConsoleOutput<'_> { fn drop(&mut self) { #[cfg(feature = "alloc")] self.flush() } } /// Send formatted data to the GDB client console. /// /// The first argument must be a [`ConsoleOutput`]. #[macro_export] macro_rules! output { ($console_output:expr, $($args:tt)*) => {{ use core::fmt::Write; let _ = write!($console_output, $($args)*); }}; } /// Send formatted data to the GDB client console, with a newline appended. /// /// The first argument must be a [`ConsoleOutput`]. #[macro_export] macro_rules! outputln { ($console_output:expr) => {{ use core::fmt::Write; let _ = writeln!($console_output); }}; ($console_output:expr,) => { outputln!($console_output) }; ($console_output:expr, $($args:tt)*) => {{ use core::fmt::Write; let _ = writeln!($console_output, $($args)*); }}; } ================================================ FILE: src/protocol/mod.rs ================================================ //! GDB protocol internals. //! //! 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; mod response_writer; pub(crate) mod commands; pub(crate) mod recv_packet; 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; pub(crate) use response_writer::ResponseWriter; ================================================ FILE: src/protocol/packet.rs ================================================ use crate::protocol::commands::Command; use crate::protocol::common::hex::decode_hex; use crate::target::Target; /// Packet parse error. #[derive(Debug)] pub enum PacketParseError { #[allow(dead_code)] // used as part of Debug impl ChecksumMismatched { checksum: u8, calculated: u8, }, EmptyBuf, MissingChecksum, MalformedChecksum, MalformedCommand, #[allow(dead_code)] // used as part of Debug impl UnexpectedHeader(u8), } /// Top-Level GDB packet pub enum Packet<'a> { Ack, Nack, Interrupt, Command(Command<'a>), } /// Wrapper around a byte buffer containing a GDB packet, while also tracking /// the range of the buffer containing the packet's "body". /// /// A newly constructed `PacketBuf` will have a body that spans the entire data /// portion of the packet (i.e: `b"$data#checksum"`), but this range can be /// further restricted as part of packet parsing. /// /// Notably, `PacketBuf` will _always_ maintain a mutable reference back to the /// _entire_ underlying packet buffer. This makes it possible to re-use any /// unused buffer space as "scratch" space. One notable example of this use-case /// is the 'm' packet, which recycles unused packet buffer space as a buffer for /// the target's `read_memory` method. pub struct PacketBuf<'a> { buf: &'a mut [u8], body_range: core::ops::Range, } impl<'a> PacketBuf<'a> { /// Validate the contents of the raw packet buffer, checking for checksum /// consistency and structural correctness. pub fn new(pkt_buf: &'a mut [u8]) -> Result, PacketParseError> { if pkt_buf.is_empty() { return Err(PacketParseError::EmptyBuf); } // split buffer into body and checksum components let mut parts = pkt_buf[1..].split(|b| *b == b'#'); let body = parts.next().unwrap(); // spit iter always returns at least one element let checksum = parts .next() .ok_or(PacketParseError::MissingChecksum)? .get(..2) .ok_or(PacketParseError::MalformedChecksum)?; // validate the checksum let checksum = decode_hex(checksum).map_err(|_| PacketParseError::MalformedChecksum)?; let calculated = body.iter().fold(0u8, |a, x| a.wrapping_add(*x)); if calculated != checksum { return Err(PacketParseError::ChecksumMismatched { checksum, calculated, }); } let body_range = 1..(body.len() + 1); // compensate for the leading '$' Ok(PacketBuf { buf: pkt_buf, body_range, }) } /// (used for tests) Create a packet buffer from a raw body buffer, skipping /// the header/checksum trimming stage. #[cfg(test)] pub fn new_with_raw_body(body: &'a mut [u8]) -> Result, PacketParseError> { let len = body.len(); Ok(PacketBuf { buf: body, body_range: 0..len, }) } /// Strip the specified prefix from the packet buffer, returning `true` if /// there was a prefix match. pub fn strip_prefix(&mut self, prefix: &[u8]) -> bool { let body = { // SAFETY: The public interface of `PacketBuf` ensures that `self.body_range` // always stays within the bounds of the provided buffer. #[cfg(not(feature = "paranoid_unsafe"))] unsafe { self.buf.get_unchecked_mut(self.body_range.clone()) } #[cfg(feature = "paranoid_unsafe")] &mut self.buf[self.body_range.clone()] }; if body.starts_with(prefix) { // SAFETY: if the current buffer range `starts_with` the specified prefix, then // it is safe to bump `body_range.start` by the prefix length. self.body_range = (self.body_range.start + prefix.len())..self.body_range.end; true } else { false } } /// Return a mutable reference to slice of the packet buffer corresponding /// to the current body. pub fn into_body(self) -> &'a mut [u8] { // SAFETY: The public interface of `PacketBuf` ensures that `self.body_range` // always stays within the bounds of the provided buffer. #[cfg(not(feature = "paranoid_unsafe"))] unsafe { self.buf.get_unchecked_mut(self.body_range) } #[cfg(feature = "paranoid_unsafe")] &mut self.buf[self.body_range] } /// Return a mutable reference to the _entire_ underlying packet buffer, and /// the current body's range. pub fn into_raw_buf(self) -> (&'a mut [u8], core::ops::Range) { (self.buf, self.body_range) } /// Returns the length of the _entire_ underlying packet buffer - not just /// the length of the current range. /// /// This method is used when handing the `qSupported` packet in order to /// obtain the maximum packet size the stub supports. pub fn full_len(&self) -> usize { self.buf.len() } } impl<'a> Packet<'a> { pub fn from_buf( target: &mut impl Target, buf: &'a mut [u8], ) -> Result, PacketParseError> { // cannot have empty packet if buf.is_empty() { return Err(PacketParseError::EmptyBuf); } match buf[0] { b'$' => Ok(Packet::Command( Command::from_packet(target, PacketBuf::new(buf)?) .ok_or(PacketParseError::MalformedCommand)?, )), b'+' => Ok(Packet::Ack), b'-' => Ok(Packet::Nack), 0x03 => Ok(Packet::Interrupt), _ => Err(PacketParseError::UnexpectedHeader(buf[0])), } } } ================================================ FILE: src/protocol/recv_packet.rs ================================================ use crate::util::managed_vec::CapacityError; use crate::util::managed_vec::ManagedVec; #[cfg(feature = "trace-pkt")] use alloc::string::String; use managed::ManagedSlice; enum State { Ready, Body, Checksum1, Checksum2, } /// Receives a packet incrementally using a asynchronous state machine. pub struct RecvPacketStateMachine { state: State, idx: usize, } impl RecvPacketStateMachine { pub fn new() -> Self { RecvPacketStateMachine { state: State::Ready, idx: 0, } } pub fn pump<'b>( &mut self, packet_buffer: &'b mut ManagedSlice<'_, u8>, byte: u8, ) -> Result, CapacityError> { let mut buf = ManagedVec::new_with_idx(packet_buffer, self.idx); buf.push(byte)?; self.idx += 1; match self.state { State::Ready => { if byte == b'$' { self.state = State::Body; } else { self.idx = 0; } } State::Body => { if byte == b'#' { self.state = State::Checksum1; } } State::Checksum1 => self.state = State::Checksum2, State::Checksum2 => { self.state = State::Ready; self.idx = 0; } } if matches!(self.state, State::Ready) { #[cfg(feature = "trace-pkt")] trace!("<-- {}", String::from_utf8_lossy(buf.as_slice())); Ok(Some(packet_buffer)) } else { Ok(None) } } } ================================================ FILE: src/protocol/response_writer.rs ================================================ 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; use num_traits::PrimInt; /// Newtype around a Connection error. Having a newtype allows implementing a /// `From> for crate::Error`, which greatly /// simplifies some of the error handling in the main gdbstub. #[derive(Debug, Clone)] pub struct Error(pub C); /// A wrapper around [`Connection`] that computes the single-byte checksum of /// incoming / outgoing data. pub struct ResponseWriter<'a, C: Connection> { inner: &'a mut C, started: bool, checksum: u8, rle_enabled: bool, rle_char: u8, rle_repeat: u8, // buffer to log outgoing packets. only allocates if logging is enabled. #[cfg(feature = "trace-pkt")] msg: Vec, } impl<'a, C: Connection + 'a> ResponseWriter<'a, C> { /// Creates a new ResponseWriter pub fn new(inner: &'a mut C, rle_enabled: bool) -> Self { Self { inner, started: false, checksum: 0, rle_enabled, rle_char: 0, rle_repeat: 0, #[cfg(feature = "trace-pkt")] msg: Vec::new(), } } /// Consumes self, writing out the final '#' and checksum pub fn flush(mut self) -> Result<(), Error> { // don't include the '#' in checksum calculation let checksum = if self.rle_enabled { self.write(b'#')?; // (note: even though `self.write` was called, the the '#' char hasn't been // added to the checksum, and is just sitting in the RLE buffer) self.checksum } else { let checksum = self.checksum; self.write(b'#')?; checksum }; self.write_hex(checksum)?; // HACK: "write" a dummy char to force an RLE flush if self.rle_enabled { self.write(0)?; } #[cfg(feature = "trace-pkt")] trace!("--> ${}", String::from_utf8_lossy(&self.msg)); self.inner.flush().map_err(Error)?; Ok(()) } /// Get a mutable reference to the underlying connection. pub fn as_conn(&mut self) -> &mut C { self.inner } fn inner_write(&mut self, byte: u8) -> Result<(), Error> { #[cfg(feature = "trace-pkt")] if log_enabled!(log::Level::Trace) { if self.rle_enabled { match self.msg.as_slice() { [.., c, b'*'] => { let c = *c; self.msg.pop(); for _ in 0..(byte - 29) { self.msg.push(c); } } _ => self.msg.push(byte), } } else { self.msg.push(byte) } } if !self.started { self.started = true; self.inner.write(b'$').map_err(Error)?; } self.checksum = self.checksum.wrapping_add(byte); self.inner.write(byte).map_err(Error) } fn write(&mut self, byte: u8) -> Result<(), Error> { if !self.rle_enabled { return self.inner_write(byte); } const ASCII_FIRST_PRINT: u8 = b' '; const ASCII_LAST_PRINT: u8 = b'~'; // handle RLE let rle_printable = (ASCII_FIRST_PRINT - 4 + (self.rle_repeat + 1)) <= ASCII_LAST_PRINT; if byte == self.rle_char && rle_printable { self.rle_repeat += 1; Ok(()) } else { loop { match self.rle_repeat { 0 => {} // happens once, after the first char is written // RLE doesn't win, just output the byte 1 | 2 | 3 => { for _ in 0..self.rle_repeat { self.inner_write(self.rle_char)? } } // RLE would output an invalid char ('#' or '$') 7 | 8 => { self.inner_write(self.rle_char)?; self.rle_repeat -= 1; continue; } // RLE wins for repetitions >4 _ => { self.inner_write(self.rle_char)?; self.inner_write(b'*')?; self.inner_write(ASCII_FIRST_PRINT - 4 + self.rle_repeat)?; } } self.rle_char = byte; self.rle_repeat = 1; break Ok(()); } } } /// Write an entire string over the connection. pub fn write_str(&mut self, s: &str) -> Result<(), Error> { for b in s.as_bytes().iter() { self.write(*b)?; } Ok(()) } /// Write a single byte as a hex string (two ascii chars) fn write_hex(&mut self, byte: u8) -> Result<(), Error> { for &digit in [(byte & 0xf0) >> 4, byte & 0x0f].iter() { let c = match digit { 0..=9 => b'0' + digit, 10..=15 => b'a' + digit - 10, // This match arm is unreachable, but the compiler isn't smart enough to optimize // out the branch. As such, using `unreachable!` here would introduce panicking // code to `gdbstub`. // // In this case, it'd be totally reasonable to use // `unsafe { core::hint::unreachable_unchecked() }`, but i'll be honest, using some // spooky unsafe compiler hints just to eek out a smidge more performance here just // isn't worth the cognitive overhead. // // Moreover, I've played around with this code in godbolt.org, and it turns out that // leaving this match arm as `=> digit` ends up generating the _exact same code_ as // using `unreachable_unchecked` (at least on x86_64 targets compiled using the // latest Rust compiler). YMMV on other platforms. _ => digit, }; self.write(c)?; } Ok(()) } /// Write a byte-buffer as a hex string (i.e: two ascii chars / byte). pub fn write_hex_buf(&mut self, data: &[u8]) -> Result<(), Error> { for b in data.iter() { self.write_hex(*b)?; } Ok(()) } /// Write data using the binary protocol. pub fn write_binary(&mut self, data: &[u8]) -> Result<(), Error> { for &b in data.iter() { match b { b'#' | b'$' | b'}' | b'*' => { self.write(b'}')?; self.write(b ^ 0x20)? } _ => self.write(b)?, } } Ok(()) } /// Write a number as a big-endian hex string using the most compact /// representation possible (i.e: trimming leading zeros). pub fn write_num(&mut self, digit: D) -> Result<(), Error> { if digit.is_zero() { return self.write_hex(0); } let mut buf = [0; 16]; // infallible (unless digit is a >128 bit number) let len = digit.to_be_bytes(&mut buf).unwrap(); let buf = &buf[..len]; for b in buf.iter().copied().skip_while(|&b| b == 0) { self.write_hex(b)? } Ok(()) } /// Write a number as a decimal string, converting every digit to an ascii /// char. pub fn write_dec( &mut self, mut digit: D, ) -> Result<(), Error> { if digit.is_zero() { return self.write(b'0'); } let one: D = one(); let ten = (one << 3) + (one << 1); let mut d = digit; let mut pow_10 = one; // Get the number of digits in digit while d >= ten { d = d / ten; pow_10 = pow_10 * ten; } // Write every digit from left to right as an ascii char while !pow_10.is_zero() { let mut byte = 0; // We have a single digit here which uses up to 4 bit for i in 0..4 { if !((digit / pow_10) & (one << i)).is_zero() { byte += 1 << i; } } self.write(b'0' + byte)?; digit = digit % pow_10; pow_10 = pow_10 / ten; } Ok(()) } #[inline] fn write_specific_id_kind(&mut self, tid: SpecificIdKind) -> Result<(), Error> { match tid { SpecificIdKind::All => self.write_str("-1")?, SpecificIdKind::WithId(id) => self.write_num(id.get())?, }; Ok(()) } pub fn write_specific_thread_id( &mut self, tid: SpecificThreadId, ) -> Result<(), Error> { if let Some(pid) = tid.pid { self.write_str("p")?; self.write_specific_id_kind(pid)?; self.write_str(".")?; } self.write_specific_id_kind(tid.tid)?; Ok(()) } } #[cfg(test)] mod tests { use super::*; use alloc::vec::Vec; /// A mock connection that captures all written bytes struct MockConnection { data: Vec, } impl MockConnection { fn new() -> Self { Self { data: Vec::new() } } } impl Connection for MockConnection { type Error = (); fn write(&mut self, byte: u8) -> Result<(), Self::Error> { self.data.push(byte); Ok(()) } fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { self.data.extend_from_slice(buf); Ok(()) } fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } } /// Check that packet body (between '$' and '#') contains no '$' or '#'. fn assert_no_special_chars_in_body(data: &[u8]) { let hash_pos = data.iter().rposition(|&b| b == b'#').unwrap(); for &byte in &data[1..hash_pos] { assert!( byte != b'$' && byte != b'#', "found {:?} in packet body", byte as char ); } } /// RLE must not produce '#' in packet body. #[test] fn rle_avoids_hash() { let mut conn = MockConnection::new(); let mut writer = ResponseWriter::new(&mut conn, true); writer.write_str("0000000").unwrap(); writer.flush().unwrap(); assert_no_special_chars_in_body(&conn.data); } /// RLE must not produce '$' in packet body. #[test] fn rle_avoids_dollar() { let mut conn = MockConnection::new(); let mut writer = ResponseWriter::new(&mut conn, true); writer.write_str("00000000").unwrap(); writer.flush().unwrap(); assert_no_special_chars_in_body(&conn.data); } } ================================================ FILE: src/stub/builder.rs ================================================ 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)] pub enum GdbStubBuilderError { /// Must provide buffer using `with_packet_buffer` in `#![no_std]` mode. MissingPacketBuffer, /// Custom packet buffer size is larger than the provided buffer's length. PacketBufSizeMismatch, } impl Display for GdbStubBuilderError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use self::GdbStubBuilderError::*; match self { MissingPacketBuffer => write!( f, "Must provide buffer using `with_packet_buffer` in `#![no_std]` mode." ), PacketBufSizeMismatch => write!( f, "`packet_buffer_size` is larger than `with_packet_buffer`'s size." ), } } } #[cfg(feature = "std")] impl std::error::Error for GdbStubBuilderError {} /// Helper to construct and customize [`GdbStub`]. pub struct GdbStubBuilder<'a, T: Target, C: Connection> { conn: C, packet_buffer: Option<&'a mut [u8]>, packet_buffer_size: Option, _target: PhantomData, } impl<'a, T: Target, C: Connection> GdbStubBuilder<'a, T, C> { /// Create a new `GdbStubBuilder` using the provided Connection. pub fn new(conn: C) -> GdbStubBuilder<'static, T, C> { GdbStubBuilder { conn, packet_buffer: None, packet_buffer_size: None, _target: PhantomData, } } /// Use a pre-allocated packet buffer (instead of heap-allocating). /// /// _Note:_ This method is _required_ when the `alloc` feature is disabled! pub fn with_packet_buffer(mut self, packet_buffer: &'a mut [u8]) -> Self { self.packet_buffer = Some(packet_buffer); self } /// Specify a custom size for the packet buffer. Defaults to 4096 bytes. /// /// When used alongside `with_packet_buffer`, the provided `size` must be /// less than or equal to the length of the packet buffer. pub fn packet_buffer_size(mut self, size: usize) -> Self { self.packet_buffer_size = Some(size); self } /// Build the GdbStub, returning an error if something went wrong. pub fn build(self) -> Result, GdbStubBuilderError> { let packet_buffer = match self.packet_buffer { Some(buf) => { let buf = match self.packet_buffer_size { Some(custom_len) => { if custom_len > buf.len() { return Err(GdbStubBuilderError::PacketBufSizeMismatch); } else { &mut buf[..custom_len] } } None => buf, }; ManagedSlice::Borrowed(buf) } None => { cfg_if::cfg_if! { if #[cfg(feature = "alloc")] { use alloc::vec; // need to pick some arbitrary value to report to GDB // 4096 seems reasonable? let len = self.packet_buffer_size.unwrap_or(4096); ManagedSlice::Owned(vec![0; len]) } else { return Err(GdbStubBuilderError::MissingPacketBuffer); } } } }; Ok(GdbStub { conn: self.conn, packet_buffer, inner: GdbStubImpl::new(), }) } } ================================================ FILE: src/stub/core_impl/auxv.rs ================================================ use super::prelude::*; use crate::protocol::commands::ext::Auxv; impl GdbStubImpl { pub(crate) fn handle_auxv( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: Auxv<'_>, ) -> Result> { let ops = match target.support_auxv() { Some(ops) => ops, None => return Ok(HandlerStatus::Handled), }; crate::__dead_code_marker!("auxv", "impl"); let handler_status = match command { Auxv::qXferAuxvRead(cmd) => { let ret = ops .get_auxv(cmd.offset, cmd.length, cmd.buf) .handle_error()?; if ret == 0 { res.write_str("l")?; } else { res.write_str("m")?; // TODO: add more specific error variant? res.write_binary(cmd.buf.get(..ret).ok_or(Error::PacketBufferOverflow)?)?; } HandlerStatus::Handled } }; Ok(handler_status) } } ================================================ FILE: src/stub/core_impl/base.rs ================================================ use super::prelude::*; 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, Error> { let tid = match target.base_ops() { BaseOps::SingleThread(_) => Some(SINGLE_THREAD_TID), BaseOps::MultiThread(ops) => { let mut first_tid = None; ops.list_active_threads(&mut |tid| { if first_tid.is_none() { first_tid = Some(tid); } }) .map_err(Error::TargetError)?; // 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>, target: &mut T, command: Base<'_>, ) -> Result> { let handler_status = match command { // ------------------ Handshaking and Queries ------------------- // Base::qSupported(cmd) => { use crate::protocol::commands::_qSupported::Feature; // perform incoming feature negotiation for feature in cmd.features.into_iter() { let (feature, supported) = match feature { Ok(Some(v)) => v, Ok(None) => continue, Err(()) => { return Err(Error::PacketParse( crate::protocol::PacketParseError::MalformedCommand, )) } }; match feature { Feature::Multiprocess => self.features.set_multiprocess(supported), } } res.write_str("PacketSize=")?; res.write_num(cmd.packet_buffer_len)?; // these are the few features that gdbstub unconditionally supports res.write_str(concat!(";vContSupported+", ";multiprocess+",))?; if target.use_no_ack_mode() { res.write_str(";QStartNoAckMode+")?; } if target.use_fork_stop_reason() { res.write_str(";fork-events+")?; } if target.use_vfork_stop_reason() { res.write_str(";vfork-events+")?; } if target.use_vforkdone_stop_reason() { res.write_str(";vforkdone-events+")?; } if target.use_x_lowcase_packet() { res.write_str(";binary-upload+")?; } if let Some(resume_ops) = target.base_ops().resume_ops() { let (reverse_cont, reverse_step) = match resume_ops { ResumeOps::MultiThread(ops) => ( ops.support_reverse_cont().is_some(), ops.support_reverse_step().is_some(), ), ResumeOps::SingleThread(ops) => ( ops.support_reverse_cont().is_some(), ops.support_reverse_step().is_some(), ), }; if reverse_cont { res.write_str(";ReverseContinue+")?; } if reverse_step { res.write_str(";ReverseStep+")?; } } if let Some(ops) = target.support_extended_mode() { if ops.support_configure_aslr().is_some() { res.write_str(";QDisableRandomization+")?; } if ops.support_configure_env().is_some() { res.write_str(";QEnvironmentHexEncoded+")?; res.write_str(";QEnvironmentUnset+")?; res.write_str(";QEnvironmentReset+")?; } if ops.support_configure_startup_shell().is_some() { res.write_str(";QStartupWithShell+")?; } if ops.support_configure_working_dir().is_some() { res.write_str(";QSetWorkingDir+")?; } } if let Some(ops) = target.support_breakpoints() { if ops.support_sw_breakpoint().is_some() { res.write_str(";swbreak+")?; } if ops.support_hw_breakpoint().is_some() || ops.support_hw_watchpoint().is_some() { res.write_str(";hwbreak+")?; } } if let Some(ops) = target.support_tracepoints() { // There are a number of optional tracepoint extensions that // gdbstub should eventually implement. // * `StaticTracepoint` for static tracepoint support. // * `EnableDisableTracepoints` for enabling/disabling tracepoints during a // trace experiment. // * `tracenz` for the tracenz agent bytecode operation. // * The `Qbtrace:*` family for branch tracing. // * `InstallInTrace` allows for gdbstub to deliver tracepoint configuration // commands while the trace experiment is running instead of them only taking // affect on the next `tstart` command. // // For now, gdbstub doesn't provide trait extensions for these // options and so we don't report support. We do report support // for one extension however: // * `QTBuffer:size` for configuring the trace buffer size, since the target is // allowed to implement it as a no-op. res.write_str(";QTBuffer:size+")?; if ops.support_tracepoint_source().is_some() { res.write_str(";TracepointSource+")?; } } if target.support_catch_syscalls().is_some() { res.write_str(";QCatchSyscalls+")?; } if target.use_target_description_xml() && (T::Arch::target_description_xml().is_some() || target.support_target_description_xml_override().is_some()) { res.write_str(";qXfer:features:read+")?; } if target.support_memory_map().is_some() { res.write_str(";qXfer:memory-map:read+")?; } if target.support_exec_file().is_some() { res.write_str(";qXfer:exec-file:read+")?; } if target.support_auxv().is_some() { res.write_str(";qXfer:auxv:read+")?; } if target.support_libraries_svr4().is_some() { res.write_str(";qXfer:libraries-svr4:read+")?; } if target.support_libraries().is_some() { res.write_str(";qXfer:libraries:read+")?; } HandlerStatus::Handled } // -------------------- "Core" Functionality -------------------- // Base::QuestionMark(_) => { // 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() { // when _not_ running in extended mode, just report that we're attaching to an // existing process. None => true, // assume attached to an existing process // When running in extended mode, we must defer to the target Some(ops) => { match cmd.pid { Some(pid) => ops.query_if_attached(pid).handle_error()?.was_attached(), None => true, // assume attached to an existing process } } }; res.write_str(if is_attached { "1" } else { "0" })?; HandlerStatus::Handled } Base::g(_) => { let mut regs: ::Registers = Default::default(); match target.base_ops() { BaseOps::SingleThread(ops) => ops.read_registers(&mut regs), BaseOps::MultiThread(ops) => { ops.read_registers(&mut regs, self.current_mem_tid) } } .handle_error()?; let mut err = Ok(()); regs.gdb_serialize(|val| { let res = match val { Some(b) => res.write_hex_buf(&[b]), None => res.write_str("xx"), }; if let Err(e) = res { err = Err(e); } }); err?; HandlerStatus::Handled } Base::G(cmd) => { let mut regs: ::Registers = Default::default(); regs.gdb_deserialize(cmd.vals) .map_err(|_| Error::TargetMismatch)?; match target.base_ops() { BaseOps::SingleThread(ops) => ops.write_registers(®s), BaseOps::MultiThread(ops) => ops.write_registers(®s, self.current_mem_tid), } .handle_error()?; HandlerStatus::NeedsOk } Base::m(cmd) => { read_addr_handler::( |_, data| res.write_hex_buf(data), self.current_mem_tid, target, cmd.buf, cmd.len, cmd.addr, )?; HandlerStatus::Handled } Base::M(cmd) => { let addr = ::Usize::from_be_bytes(cmd.addr) .ok_or(Error::TargetMismatch)?; match target.base_ops() { BaseOps::SingleThread(ops) => ops.write_addrs(addr, cmd.val), BaseOps::MultiThread(ops) => { ops.write_addrs(addr, cmd.val, self.current_mem_tid) } } .handle_error()?; HandlerStatus::NeedsOk } Base::k(_) | Base::vKill(_) => { match target.support_extended_mode() { // When not running in extended mode, stop the `GdbStub` and disconnect. None => HandlerStatus::Disconnect(DisconnectReason::Kill), // When running in extended mode, a kill command does not necessarily result in // a disconnect... Some(ops) => { let pid = match command { Base::vKill(cmd) => Some(cmd.pid), _ => None, }; let should_terminate = ops.kill(pid).handle_error()?; if should_terminate.into_bool() { // manually write OK, since we need to return a DisconnectReason res.write_str("OK")?; HandlerStatus::Disconnect(DisconnectReason::Kill) } else { HandlerStatus::NeedsOk } } } } Base::D(cmd) => { // TODO: plumb-through Pid when exposing full multiprocess + extended mode let _pid = cmd.pid; res.write_str("OK")?; // manually write OK, since we need to return a DisconnectReason HandlerStatus::Disconnect(DisconnectReason::Disconnect) } // ------------------- Multi-threading Support ------------------ // Base::H(cmd) => { use crate::protocol::commands::_h_upcase::Op; match cmd.kind { Op::Other => match cmd.thread.tid { 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 => 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) } }, } HandlerStatus::NeedsOk } 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(pid)), tid: SpecificIdKind::WithId(SINGLE_THREAD_TID), })?, BaseOps::MultiThread(ops) => { let mut err: Result<_, Error> = Ok(()); let mut first = true; ops.list_active_threads(&mut |tid| { // TODO: replace this with a try block (once stabilized) let e = (|| { if !first { res.write_str(",")? } first = false; res.write_specific_thread_id(SpecificThreadId { pid: self .features .multiprocess() .then_some(SpecificIdKind::WithId(pid)), tid: SpecificIdKind::WithId(tid), })?; Ok(()) })(); if let Err(e) = e { err = Err(e) } }) .map_err(Error::TargetError)?; err?; } } HandlerStatus::Handled } Base::qsThreadInfo(_) => { res.write_str("l")?; HandlerStatus::Handled } Base::T(cmd) => { let alive = match cmd.thread.tid { IdKind::WithId(tid) => match target.base_ops() { BaseOps::SingleThread(_) => tid == SINGLE_THREAD_TID, BaseOps::MultiThread(ops) => { ops.is_thread_alive(tid).map_err(Error::TargetError)? } }, _ => return Err(Error::PacketUnexpected), }; if alive { HandlerStatus::NeedsOk } else { // any error code will do return Err(Error::NonFatalError(1)); } } }; Ok(handler_status) } } // shared by both the 'm' and 'x' packet handlers pub(crate) fn read_addr_handler( mut write_res: impl FnMut( usize, &[u8], ) -> Result<(), crate::protocol::ResponseWriterError>, current_mem_tid: Tid, target: &mut T, buf: &mut [u8], len: usize, addr: &[u8], ) -> Result<(), Error> { let addr = ::Usize::from_be_bytes(addr).ok_or(Error::TargetMismatch)?; let mut i = 0; let mut n = len; while n != 0 { let chunk_size = n.min(buf.len()); let addr = addr + num_traits::NumCast::from(i).ok_or(Error::TargetMismatch)?; let data = &mut buf[..chunk_size]; let data_len = match target.base_ops() { BaseOps::SingleThread(ops) => ops.read_addrs(addr, data), BaseOps::MultiThread(ops) => ops.read_addrs(addr, data, current_mem_tid), } .handle_error()?; // TODO: add more specific error variant? let data = data.get(..data_len).ok_or(Error::PacketBufferOverflow)?; write_res(i, data)?; n -= chunk_size; i += chunk_size; } Ok(()) } ================================================ FILE: src/stub/core_impl/breakpoints.rs ================================================ use super::prelude::*; use crate::arch::Arch; use crate::arch::BreakpointKind; use crate::protocol::commands::ext::Breakpoints; enum CmdKind { Add, Remove, } impl GdbStubImpl { #[inline(always)] fn handle_breakpoint_common( &mut self, ops: crate::target::ext::breakpoints::BreakpointsOps<'_, T>, cmd: crate::protocol::commands::breakpoint::BasicBreakpoint<'_>, cmd_kind: CmdKind, ) -> Result> { let addr = ::Usize::from_be_bytes(cmd.addr).ok_or(Error::TargetMismatch)?; macro_rules! bp_kind { () => { BeBytes::from_be_bytes(cmd.kind) .and_then(::BreakpointKind::from_usize) .ok_or(Error::TargetMismatch)? }; } let supported = match cmd.type_ { 0 if ops.support_sw_breakpoint().is_some() => { let ops = ops.support_sw_breakpoint().unwrap(); let bp_kind = bp_kind!(); match cmd_kind { CmdKind::Add => ops.add_sw_breakpoint(addr, bp_kind), CmdKind::Remove => ops.remove_sw_breakpoint(addr, bp_kind), } } 1 if ops.support_hw_breakpoint().is_some() => { let ops = ops.support_hw_breakpoint().unwrap(); let bp_kind = bp_kind!(); match cmd_kind { CmdKind::Add => ops.add_hw_breakpoint(addr, bp_kind), CmdKind::Remove => ops.remove_hw_breakpoint(addr, bp_kind), } } 2 | 3 | 4 if ops.support_hw_watchpoint().is_some() => { use crate::target::ext::breakpoints::WatchKind; let kind = match cmd.type_ { 2 => WatchKind::Write, 3 => WatchKind::Read, 4 => WatchKind::ReadWrite, #[allow(clippy::unreachable)] // will be optimized out _ => unreachable!(), }; let len = ::Usize::from_be_bytes(cmd.kind) .ok_or(Error::TargetMismatch)?; let ops = ops.support_hw_watchpoint().unwrap(); match cmd_kind { CmdKind::Add => ops.add_hw_watchpoint(addr, len, kind), CmdKind::Remove => ops.remove_hw_watchpoint(addr, len, kind), } } // explicitly handle unguarded variants of known breakpoint types 0 | 1 | 2 | 3 | 4 => return Ok(HandlerStatus::Handled), // warn if the GDB client ever sends a type outside the known types other => { warn!("unknown breakpoint type: {}", other); return Ok(HandlerStatus::Handled); } }; match supported.handle_error()? { true => Ok(HandlerStatus::NeedsOk), false => Err(Error::NonFatalError(22)), } } pub(crate) fn handle_breakpoints( &mut self, _res: &mut ResponseWriter<'_, C>, target: &mut T, command: Breakpoints<'_>, ) -> Result> { let ops = match target.support_breakpoints() { Some(ops) => ops, None => return Ok(HandlerStatus::Handled), }; crate::__dead_code_marker!("breakpoints", "impl"); let handler_status = match command { Breakpoints::z(cmd) => self.handle_breakpoint_common(ops, cmd, CmdKind::Remove)?, Breakpoints::Z(cmd) => self.handle_breakpoint_common(ops, cmd, CmdKind::Add)?, // TODO: handle ZWithBytecode once agent expressions are implemented _ => HandlerStatus::Handled, }; Ok(handler_status) } } ================================================ FILE: src/stub/core_impl/catch_syscalls.rs ================================================ use super::prelude::*; 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 { pub(crate) fn handle_catch_syscalls( &mut self, _res: &mut ResponseWriter<'_, C>, target: &mut T, command: CatchSyscalls<'_>, ) -> Result> { let ops = match target.support_catch_syscalls() { Some(ops) => ops, None => return Ok(HandlerStatus::Handled), }; crate::__dead_code_marker!("catch_syscalls", "impl"); let handler_status = match command { CatchSyscalls::QCatchSyscalls(cmd) => { match cmd { QCatchSyscalls::Disable => ops.disable_catch_syscalls().handle_error()?, QCatchSyscalls::Enable(sysno) => { let mut error = false; let mut filter = sysno .into_iter() .map(::Usize::from_be_bytes) .take_while(|x| { error = x.is_none(); !error }) .flatten(); ops.enable_catch_syscalls(Some(SyscallNumbers { inner: &mut filter })) .handle_error()?; if error { return Err(Error::TargetMismatch); } } QCatchSyscalls::EnableAll => ops.enable_catch_syscalls(None).handle_error()?, } HandlerStatus::NeedsOk } }; Ok(handler_status) } } ================================================ FILE: src/stub/core_impl/exec_file.rs ================================================ use super::prelude::*; use crate::protocol::commands::ext::ExecFile; impl GdbStubImpl { pub(crate) fn handle_exec_file( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: ExecFile<'_>, ) -> Result> { let ops = match target.support_exec_file() { Some(ops) => ops, None => return Ok(HandlerStatus::Handled), }; crate::__dead_code_marker!("exec_file", "impl"); let handler_status = match command { ExecFile::qXferExecFileRead(cmd) => { let ret = ops .get_exec_file(cmd.annex.pid, cmd.offset, cmd.length, cmd.buf) .handle_error()?; if ret == 0 { res.write_str("l")?; } else { res.write_str("m")?; // TODO: add more specific error variant? res.write_binary(cmd.buf.get(..ret).ok_or(Error::PacketBufferOverflow)?)?; } HandlerStatus::Handled } }; Ok(handler_status) } } ================================================ FILE: src/stub/core_impl/extended_mode.rs ================================================ 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( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: ExtendedMode<'_>, ) -> Result> { let ops = match target.support_extended_mode() { Some(ops) => ops, None => return Ok(HandlerStatus::Handled), }; crate::__dead_code_marker!("extended_mode", "impl"); let handler_status = match command { ExtendedMode::ExclamationMark(_cmd) => { ops.on_start().map_err(Error::TargetError)?; HandlerStatus::NeedsOk } ExtendedMode::R(_cmd) => { ops.restart().map_err(Error::TargetError)?; 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), })?; HandlerStatus::Handled } ExtendedMode::vRun(cmd) => { use crate::target::ext::extended_mode::Args; let _pid = ops .run(cmd.filename, Args::new(&mut cmd.args.into_iter())) .handle_error()?; self.report_reasonable_stop_reason(res, target)? } // --------- ASLR --------- // ExtendedMode::QDisableRandomization(cmd) if ops.support_configure_aslr().is_some() => { let ops = ops.support_configure_aslr().unwrap(); ops.cfg_aslr(cmd.value).handle_error()?; HandlerStatus::NeedsOk } // --------- Environment --------- // ExtendedMode::QEnvironmentHexEncoded(cmd) if ops.support_configure_env().is_some() => { let ops = ops.support_configure_env().unwrap(); ops.set_env(cmd.key, cmd.value).handle_error()?; HandlerStatus::NeedsOk } ExtendedMode::QEnvironmentUnset(cmd) if ops.support_configure_env().is_some() => { let ops = ops.support_configure_env().unwrap(); ops.remove_env(cmd.key).handle_error()?; HandlerStatus::NeedsOk } ExtendedMode::QEnvironmentReset(_cmd) if ops.support_configure_env().is_some() => { let ops = ops.support_configure_env().unwrap(); ops.reset_env().handle_error()?; HandlerStatus::NeedsOk } // --------- Working Dir --------- // ExtendedMode::QSetWorkingDir(cmd) if ops.support_configure_working_dir().is_some() => { let ops = ops.support_configure_working_dir().unwrap(); ops.cfg_working_dir(cmd.dir).handle_error()?; HandlerStatus::NeedsOk } // --------- Startup Shell --------- // ExtendedMode::QStartupWithShell(cmd) if ops.support_configure_startup_shell().is_some() => { let ops = ops.support_configure_startup_shell().unwrap(); ops.cfg_startup_with_shell(cmd.value).handle_error()?; HandlerStatus::NeedsOk } _ => HandlerStatus::Handled, }; Ok(handler_status) } } ================================================ FILE: src/stub/core_impl/flash.rs ================================================ use super::prelude::*; use crate::arch::Arch; use crate::protocol::commands::ext::FlashOperations; impl GdbStubImpl { pub(crate) fn handle_flash_operations( &mut self, _res: &mut ResponseWriter<'_, C>, target: &mut T, command: FlashOperations<'_>, ) -> Result> { let ops = match target.support_flash_operations() { Some(ops) => ops, None => return Ok(HandlerStatus::Handled), }; let handler_status = match command { FlashOperations::vFlashErase(cmd) => { let addr = ::Usize::from_be_bytes(cmd.addr) .ok_or(Error::TargetMismatch)?; let length = ::Usize::from_be_bytes(cmd.length) .ok_or(Error::TargetMismatch)?; ops.flash_erase(addr, length).handle_error()?; HandlerStatus::NeedsOk } FlashOperations::vFlashWrite(cmd) => { let addr = ::Usize::from_be_bytes(cmd.addr) .ok_or(Error::TargetMismatch)?; ops.flash_write(addr, cmd.val).handle_error()?; HandlerStatus::NeedsOk } FlashOperations::vFlashDone(_) => { ops.flash_done().handle_error()?; HandlerStatus::NeedsOk } }; Ok(handler_status) } } ================================================ FILE: src/stub/core_impl/host_io.rs ================================================ use super::prelude::*; use crate::arch::Arch; 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( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: HostIo<'_>, ) -> Result> { let ops = match target.support_host_io() { Some(ops) => ops, None => return Ok(HandlerStatus::Handled), }; crate::__dead_code_marker!("host_io", "impl"); macro_rules! handle_hostio_result { ( if let Ok($val:pat) = $ret:expr => $callback:block ) => {{ match $ret { Ok($val) => $callback, Err(HostIoError::Errno(errno)) => { res.write_str("F-1,")?; res.write_num(errno as u32)?; } Err(HostIoError::Fatal(e)) => return Err(Error::TargetError(e)), } }}; } let handler_status = match command { HostIo::vFileOpen(cmd) if ops.support_open().is_some() => { let ops = ops.support_open().unwrap(); handle_hostio_result! { if let Ok(fd) = ops.open(cmd.filename, cmd.flags, cmd.mode) => { res.write_str("F")?; res.write_num(fd)?; } } HandlerStatus::Handled } HostIo::vFileClose(cmd) if ops.support_close().is_some() => { let ops = ops.support_close().unwrap(); handle_hostio_result! { if let Ok(()) = ops.close(cmd.fd) => { res.write_str("F0")?; } } HandlerStatus::Handled } HostIo::vFilePread(cmd) if ops.support_pread().is_some() => { let ops = ops.support_pread().unwrap(); handle_hostio_result! { if let Ok(ret) = ops.pread(cmd.fd, cmd.count, cmd.offset, cmd.buf) => { res.write_str("F")?; res.write_num(ret)?; res.write_str(";")?; res.write_binary(cmd.buf.get(..ret).ok_or(Error::PacketBufferOverflow)?)?; } }; HandlerStatus::Handled } HostIo::vFilePwrite(cmd) if ops.support_pwrite().is_some() => { let offset = ::Usize::from_be_bytes(cmd.offset) .ok_or(Error::TargetMismatch)?; let ops = ops.support_pwrite().unwrap(); handle_hostio_result! { if let Ok(ret) = ops.pwrite(cmd.fd, offset, cmd.data) => { res.write_str("F")?; res.write_num(ret)?; } }; HandlerStatus::Handled } HostIo::vFileFstat(cmd) if ops.support_fstat().is_some() => { let ops = ops.support_fstat().unwrap(); handle_hostio_result! { if let Ok(stat) = ops.fstat(cmd.fd) => { let size = core::mem::size_of::(); res.write_str("F")?; res.write_num(size)?; res.write_str(";")?; res.write_binary(&stat.st_dev.to_be_bytes())?; res.write_binary(&stat.st_ino.to_be_bytes())?; res.write_binary(&(stat.st_mode.bits()).to_be_bytes())?; res.write_binary(&stat.st_nlink.to_be_bytes())?; res.write_binary(&stat.st_uid.to_be_bytes())?; res.write_binary(&stat.st_gid.to_be_bytes())?; res.write_binary(&stat.st_rdev.to_be_bytes())?; res.write_binary(&stat.st_size.to_be_bytes())?; res.write_binary(&stat.st_blksize.to_be_bytes())?; res.write_binary(&stat.st_blocks.to_be_bytes())?; res.write_binary(&stat.st_atime.to_be_bytes())?; res.write_binary(&stat.st_mtime.to_be_bytes())?; res.write_binary(&stat.st_ctime.to_be_bytes())?; } }; HandlerStatus::Handled } HostIo::vFileUnlink(cmd) if ops.support_unlink().is_some() => { let ops = ops.support_unlink().unwrap(); handle_hostio_result! { if let Ok(()) = ops.unlink(cmd.filename) => { res.write_str("F0")?; } }; HandlerStatus::Handled } HostIo::vFileReadlink(cmd) if ops.support_readlink().is_some() => { let ops = ops.support_readlink().unwrap(); handle_hostio_result! { if let Ok(ret) = ops.readlink(cmd.filename, cmd.buf) => { res.write_str("F")?; res.write_num(ret)?; res.write_str(";")?; res.write_binary(cmd.buf.get(..ret).ok_or(Error::PacketBufferOverflow)?)?; } }; HandlerStatus::Handled } HostIo::vFileSetfs(cmd) if ops.support_setfs().is_some() => { let ops = ops.support_setfs().unwrap(); handle_hostio_result! { if let Ok(()) = ops.setfs(cmd.fs) => { res.write_str("F0")?; } }; HandlerStatus::Handled } _ => HandlerStatus::Handled, }; Ok(handler_status) } } ================================================ FILE: src/stub/core_impl/host_process_info.rs ================================================ use super::prelude::*; use crate::common::Endianness; use crate::common::Pid; use crate::protocol::commands::ext::HostInfo; use crate::protocol::commands::ext::ProcessInfo; use crate::protocol::ResponseWriterError; use crate::target::ext::host_info::HostInfoResponse; use crate::target::ext::process_info::ProcessInfoResponse; // Convert the two distinct public enums to a single unified enum internally, so // that we can reuse serialization code when convenient. pub(crate) enum InfoResponse<'a> { Pid(Pid), Triple(&'a str), Endianness(Endianness), PointerSize(usize), } impl<'a> InfoResponse<'a> { pub(crate) fn write_response( &self, res: &mut ResponseWriter<'_, C>, ) -> Result<(), ResponseWriterError> { match self { InfoResponse::Pid(pid) => { res.write_str("pid:")?; res.write_dec(usize::from(*pid))?; } InfoResponse::Triple(triple) => { res.write_str("triple:")?; res.write_hex_buf(triple.as_bytes())?; } InfoResponse::Endianness(endian) => { res.write_str("endian:")?; res.write_str(match endian { Endianness::Big => "big;", Endianness::Little => "little;", })?; } InfoResponse::PointerSize(p) => { res.write_str("ptrsize:")?; res.write_dec(*p)?; } } res.write_str(";")?; Ok(()) } } impl<'a> From<&HostInfoResponse<'a>> for InfoResponse<'a> { fn from(resp: &HostInfoResponse<'a>) -> Self { match *resp { HostInfoResponse::Triple(s) => InfoResponse::Triple(s), HostInfoResponse::Endianness(e) => InfoResponse::Endianness(e), HostInfoResponse::PointerSize(p) => InfoResponse::PointerSize(p), } } } impl<'a> From<&ProcessInfoResponse<'a>> for InfoResponse<'a> { fn from(resp: &ProcessInfoResponse<'a>) -> Self { match *resp { ProcessInfoResponse::Pid(pid) => InfoResponse::Pid(pid), ProcessInfoResponse::Triple(s) => InfoResponse::Triple(s), ProcessInfoResponse::Endianness(e) => InfoResponse::Endianness(e), ProcessInfoResponse::PointerSize(p) => InfoResponse::PointerSize(p), } } } impl GdbStubImpl { pub(crate) fn handle_process_info( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: ProcessInfo, ) -> Result> { let ops = match target.support_process_info() { Some(ops) => ops, None => return Ok(HandlerStatus::Handled), }; crate::__dead_code_marker!("process_info", "impl"); let mut result = Ok(()); let mut write_info = |info: &ProcessInfoResponse<'_>| { if result.is_ok() { if let Err(e) = InfoResponse::from(info).write_response(res) { result = Err(e); } } }; match command { ProcessInfo::qProcessInfo(_cmd) => { ops.process_info(&mut write_info) .map_err(Error::TargetError)?; result?; } }; Ok(HandlerStatus::Handled) } pub(crate) fn handle_host_info( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: HostInfo, ) -> Result> { let ops = match target.support_host_info() { Some(ops) => ops, None => return Ok(HandlerStatus::Handled), }; crate::__dead_code_marker!("host_info", "impl"); let mut result = Ok(()); let mut write_info = |info: &HostInfoResponse<'_>| { if result.is_ok() { if let Err(e) = InfoResponse::from(info).write_response(res) { result = Err(e); } } }; match command { HostInfo::qHostInfo(_cmd) => { ops.host_info(&mut write_info).map_err(Error::TargetError)?; result?; } }; Ok(HandlerStatus::Handled) } } ================================================ FILE: src/stub/core_impl/libraries.rs ================================================ use super::prelude::*; use crate::protocol::commands::ext::Libraries; use crate::protocol::commands::ext::LibrariesSvr4; impl GdbStubImpl { pub(crate) fn handle_libraries_svr4( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: LibrariesSvr4<'_>, ) -> Result> { let ops = match target.support_libraries_svr4() { Some(ops) => ops, None => return Ok(HandlerStatus::Handled), }; crate::__dead_code_marker!("libraries-svr4", "impl"); let handler_status = match command { LibrariesSvr4::qXferLibrariesSvr4Read(cmd) => { let ret = ops .get_libraries_svr4(cmd.offset, cmd.length, cmd.buf) .handle_error()?; if ret == 0 { res.write_str("l")?; } else { res.write_str("m")?; // TODO: add more specific error variant? res.write_binary(cmd.buf.get(..ret).ok_or(Error::PacketBufferOverflow)?)?; } HandlerStatus::Handled } }; Ok(handler_status) } pub(crate) fn handle_libraries( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: Libraries<'_>, ) -> Result> { let ops = match target.support_libraries() { Some(ops) => ops, None => return Ok(HandlerStatus::Handled), }; crate::__dead_code_marker!("libraries", "impl"); let handler_status = match command { Libraries::qXferLibrariesRead(cmd) => { let ret = ops .get_libraries(cmd.offset, cmd.length, cmd.buf) .handle_error()?; if ret == 0 { res.write_str("l")?; } else { res.write_str("m")?; res.write_binary(cmd.buf.get(..ret).ok_or(Error::PacketBufferOverflow)?)?; } HandlerStatus::Handled } }; Ok(handler_status) } } ================================================ FILE: src/stub/core_impl/lldb_register_info.rs ================================================ use super::prelude::*; 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( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: LldbRegisterInfo, ) -> Result> { if !target.use_lldb_register_info() { return Ok(HandlerStatus::Handled); } let handler_status = match command { LldbRegisterInfo::qRegisterInfo(cmd) => { let mut err = Ok(()); let cb = &mut |reg: Option>| { let res = match reg { // TODO: replace this with a try block (once stabilized) Some(reg) => (|| { res.write_str("name:")?; res.write_str(reg.name)?; if let Some(alt_name) = reg.alt_name { res.write_str(";alt-name:")?; res.write_str(alt_name)?; } res.write_str(";bitsize:")?; res.write_dec(reg.bitsize)?; res.write_str(";offset:")?; res.write_dec(reg.offset)?; res.write_str(";encoding:")?; res.write_str(match reg.encoding { Encoding::Uint => "uint", Encoding::Sint => "sint", Encoding::IEEE754 => "ieee754", Encoding::Vector => "vector", })?; res.write_str(";format:")?; res.write_str(match reg.format { Format::Binary => "binary", Format::Decimal => "decimal", Format::Hex => "hex", Format::Float => "float", Format::VectorSInt8 => "vector-sint8", Format::VectorUInt8 => "vector-uint8", Format::VectorSInt16 => "vector-sint16", Format::VectorUInt16 => "vector-uint16", Format::VectorSInt32 => "vector-sint32", Format::VectorUInt32 => "vector-uint32", Format::VectorFloat32 => "vector-float32", Format::VectorUInt128 => "vector-uint128", })?; res.write_str(";set:")?; res.write_str(reg.set)?; if let Some(gcc) = reg.gcc { res.write_str(";gcc:")?; res.write_dec(gcc)?; } if let Some(dwarf) = reg.dwarf { res.write_str(";dwarf:")?; res.write_dec(dwarf)?; } if let Some(generic) = reg.generic { res.write_str(";generic:")?; res.write_str(match generic { Generic::Pc => "pc", Generic::Sp => "sp", Generic::Fp => "fp", Generic::Ra => "ra", Generic::Flags => "flags", Generic::Arg1 => "arg1", Generic::Arg2 => "arg2", Generic::Arg3 => "arg3", Generic::Arg4 => "arg4", Generic::Arg5 => "arg5", Generic::Arg6 => "arg6", Generic::Arg7 => "arg7", Generic::Arg8 => "arg8", })?; } if let Some(c_regs) = reg.container_regs { res.write_str(";container-regs:")?; res.write_num(c_regs[0])?; for reg in c_regs.iter().skip(1) { res.write_str(",")?; res.write_num(*reg)?; } } if let Some(i_regs) = reg.invalidate_regs { res.write_str(";invalidate-regs:")?; res.write_num(i_regs[0])?; for reg in i_regs.iter().skip(1) { res.write_str(",")?; res.write_num(*reg)?; } } res.write_str(";") })(), // In fact, this doesn't has to be E45! It could equally well be any // other error code or even an eOk, eAck or eNack! It turns out that // 0x45 == 69, so presumably the LLDB people were just having some fun // here. For a little discussion on this and LLDB source code pointers, // see https://github.com/daniel5151/gdbstub/pull/103#discussion_r888590197 _ => res.write_str("E45"), }; if let Err(e) = res { err = Err(e); } }; if let Some(ops) = target.support_lldb_register_info_override() { 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, Callback { cb, token: CallbackToken(core::marker::PhantomData), }, ) .map_err(Error::TargetError)?; err?; } else if let Some(reg) = T::Arch::lldb_register_info(cmd.reg_id) { match reg { LLDBRegisterInfo::Register(reg) => cb(Some(reg)), LLDBRegisterInfo::Done => cb(None), }; } HandlerStatus::Handled } }; Ok(handler_status) } } ================================================ FILE: src/stub/core_impl/memory_map.rs ================================================ use super::prelude::*; use crate::protocol::commands::ext::MemoryMap; impl GdbStubImpl { pub(crate) fn handle_memory_map( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: MemoryMap<'_>, ) -> Result> { let ops = match target.support_memory_map() { Some(ops) => ops, None => return Ok(HandlerStatus::Handled), }; crate::__dead_code_marker!("memory_map", "impl"); let handler_status = match command { MemoryMap::qXferMemoryMapRead(cmd) => { let ret = ops .memory_map_xml(cmd.offset, cmd.length, cmd.buf) .handle_error()?; if ret == 0 { res.write_str("l")?; } else { res.write_str("m")?; // TODO: add more specific error variant? res.write_binary(cmd.buf.get(..ret).ok_or(Error::PacketBufferOverflow)?)?; } HandlerStatus::Handled } }; Ok(handler_status) } } ================================================ FILE: src/stub/core_impl/monitor_cmd.rs ================================================ use super::prelude::*; use crate::protocol::commands::ext::MonitorCmd; use crate::protocol::ConsoleOutput; impl GdbStubImpl { pub(crate) fn handle_monitor_cmd( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: MonitorCmd<'_>, ) -> Result> { let ops = match target.support_monitor_cmd() { Some(ops) => ops, None => return Ok(HandlerStatus::Handled), }; crate::__dead_code_marker!("monitor_cmd", "impl"); let handler_status = match command { MonitorCmd::qRcmd(cmd) => { let use_rle = ops.use_rle(); let mut err: Result<_, Error> = Ok(()); let mut callback = |msg: &[u8]| { // TODO: replace this with a try block (once stabilized) let e = (|| { let mut res = ResponseWriter::new(res.as_conn(), use_rle); res.write_str("O")?; res.write_hex_buf(msg)?; res.flush()?; Ok(()) })(); if let Err(e) = e { err = Err(e) } }; ops.handle_monitor_cmd(cmd.hex_cmd, ConsoleOutput::new(&mut callback)) .map_err(Error::TargetError)?; err?; HandlerStatus::NeedsOk } }; Ok(handler_status) } } ================================================ FILE: src/stub/core_impl/no_ack_mode.rs ================================================ 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) } } ================================================ FILE: src/stub/core_impl/resume.rs ================================================ use super::prelude::*; use super::DisconnectReason; use crate::arch::Arch; use crate::common::Signal; use crate::common::Tid; use crate::protocol::commands::_vCont::Actions; 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; impl GdbStubImpl { pub(crate) fn handle_stop_resume( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: Resume<'_>, ) -> Result> { let mut ops = match target.base_ops().resume_ops() { Some(ops) => ops, None => return Ok(HandlerStatus::Handled), }; let actions = match command { Resume::vCont(cmd) => { use crate::protocol::commands::_vCont::vCont; match cmd { vCont::Query => { // Continue is part of the base protocol res.write_str("vCont;c;C")?; // Single stepping is optional if match &mut ops { ResumeOps::SingleThread(ops) => ops.support_single_step().is_some(), ResumeOps::MultiThread(ops) => ops.support_single_step().is_some(), } { res.write_str(";s;S")?; } // Range stepping is optional if match &mut ops { ResumeOps::SingleThread(ops) => ops.support_range_step().is_some(), ResumeOps::MultiThread(ops) => ops.support_range_step().is_some(), } { res.write_str(";r")?; } // doesn't actually invoke vCont return Ok(HandlerStatus::Handled); } vCont::Actions(actions) => actions, } } // TODO?: support custom resume addr in 'c' and 's' // // vCont doesn't have a notion of "resume addr", and since the implementation of these // packets reuse vCont infrastructure, supporting this obscure feature will be a bit // annoying... // // TODO: add `support_legacy_s_c_packets` flag (similar to `use_X_packet`) Resume::c(cmd) => { let _addr = cmd.addr; Actions::new_continue(SpecificThreadId { pid: None, tid: self.current_resume_tid, }) } Resume::s(cmd) => { let _addr = cmd.addr; Actions::new_step(SpecificThreadId { pid: None, tid: self.current_resume_tid, }) } }; self.do_vcont(ops, actions) } fn do_vcont_single_thread( ops: &mut dyn crate::target::ext::base::singlethread::SingleThreadResume< Arch = T::Arch, Error = T::Error, >, actions: &Actions<'_>, ) -> Result<(), Error> { use crate::protocol::commands::_vCont::VContKind; let mut actions = actions.iter(); let first_action = actions .next() .ok_or(Error::PacketParse( crate::protocol::PacketParseError::MalformedCommand, ))? .ok_or(Error::PacketParse( crate::protocol::PacketParseError::MalformedCommand, ))?; let invalid_second_action = match actions.next() { None => false, Some(act) => match act { None => { return Err(Error::PacketParse( crate::protocol::PacketParseError::MalformedCommand, )) } Some(act) => !matches!(act.kind, VContKind::Continue), }, }; if invalid_second_action || actions.next().is_some() { return Err(Error::PacketUnexpected); } match first_action.kind { VContKind::Continue | VContKind::ContinueWithSig(_) => { let signal = match first_action.kind { VContKind::ContinueWithSig(sig) => Some(sig), _ => None, }; ops.resume(signal).map_err(Error::TargetError)?; Ok(()) } VContKind::Step | VContKind::StepWithSig(_) if ops.support_single_step().is_some() => { let ops = ops.support_single_step().unwrap(); let signal = match first_action.kind { VContKind::StepWithSig(sig) => Some(sig), _ => None, }; ops.step(signal).map_err(Error::TargetError)?; Ok(()) } VContKind::RangeStep(start, end) if ops.support_range_step().is_some() => { let ops = ops.support_range_step().unwrap(); let start = start.decode().map_err(|_| Error::TargetMismatch)?; let end = end.decode().map_err(|_| Error::TargetMismatch)?; ops.resume_range_step(start, end) .map_err(Error::TargetError)?; Ok(()) } // TODO: update this case when non-stop mode is implemented VContKind::Stop => Err(Error::PacketUnexpected), // Instead of using `_ =>`, explicitly list out any remaining unguarded cases. VContKind::RangeStep(..) | VContKind::Step | VContKind::StepWithSig(..) => { error!("GDB client sent resume action not reported by `vCont?`"); Err(Error::PacketUnexpected) } } } fn do_vcont_multi_thread( ops: &mut dyn crate::target::ext::base::multithread::MultiThreadResume< Arch = T::Arch, Error = T::Error, >, actions: &Actions<'_>, ) -> Result<(), Error> { ops.clear_resume_actions().map_err(Error::TargetError)?; // Track whether the packet contains a wildcard/default continue action // (e.g., `c` or `c:-1`). // // Presence of this action implies "Scheduler Locking" is OFF. // Absence implies "Scheduler Locking" is ON. let mut has_wildcard_continue = false; for action in actions.iter() { use crate::protocol::commands::_vCont::VContKind; let action = action.ok_or(Error::PacketParse( crate::protocol::PacketParseError::MalformedCommand, ))?; match action.kind { VContKind::Continue | VContKind::ContinueWithSig(_) => { let signal = match action.kind { VContKind::ContinueWithSig(sig) => Some(sig), _ => None, }; match action.thread.map(|thread| thread.tid) { // An action with no thread-id matches all threads None | Some(SpecificIdKind::All) => { // Target API contract specifies that the default // resume action for all threads is continue. has_wildcard_continue = true; } Some(SpecificIdKind::WithId(tid)) => ops .set_resume_action_continue(tid, signal) .map_err(Error::TargetError)?, } } VContKind::Step | VContKind::StepWithSig(_) if ops.support_single_step().is_some() => { let ops = ops.support_single_step().unwrap(); let signal = match action.kind { VContKind::StepWithSig(sig) => Some(sig), _ => None, }; match action.thread.map(|thread| thread.tid) { // An action with no thread-id matches all threads None | Some(SpecificIdKind::All) => { error!("GDB client sent 'step' as default resume action"); return Err(Error::PacketUnexpected); } Some(SpecificIdKind::WithId(tid)) => { ops.set_resume_action_step(tid, signal) .map_err(Error::TargetError)?; } }; } VContKind::RangeStep(start, end) if ops.support_range_step().is_some() => { let ops = ops.support_range_step().unwrap(); match action.thread.map(|thread| thread.tid) { // An action with no thread-id matches all threads None | Some(SpecificIdKind::All) => { error!("GDB client sent 'range step' as default resume action"); return Err(Error::PacketUnexpected); } Some(SpecificIdKind::WithId(tid)) => { let start = start.decode().map_err(|_| Error::TargetMismatch)?; let end = end.decode().map_err(|_| Error::TargetMismatch)?; ops.set_resume_action_range_step(tid, start, end) .map_err(Error::TargetError)?; } }; } // 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(..) => { error!("GDB client sent resume action not reported by `vCont?`"); return Err(Error::PacketUnexpected); } } } if !has_wildcard_continue { let Some(locking_ops) = ops.support_scheduler_locking() else { return Err(Error::MissingMultiThreadSchedulerLocking); }; locking_ops .set_resume_action_scheduler_lock() .map_err(Error::TargetError)?; } ops.resume().map_err(Error::TargetError) } fn do_vcont( &mut self, ops: ResumeOps<'_, T::Arch, T::Error>, actions: Actions<'_>, ) -> Result> { match ops { ResumeOps::SingleThread(ops) => Self::do_vcont_single_thread(ops, &actions)?, ResumeOps::MultiThread(ops) => Self::do_vcont_multi_thread(ops, &actions)?, }; Ok(HandlerStatus::DeferredStopReason) } 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.0)?; if let Some(tid) = tid { self.current_mem_tid = tid; self.current_resume_tid = SpecificIdKind::WithId(tid); res.write_str("thread:")?; res.write_specific_thread_id(SpecificThreadId { pid: self .features .multiprocess() .then_some(SpecificIdKind::WithId(self.get_current_pid(target)?)), tid: SpecificIdKind::WithId(tid), })?; res.write_str(";")?; } Ok(()) } pub(crate) fn finish_exec( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, stop_reason: MultiThreadStopReason<::Usize>, ) -> Result> { /// Helper macro to gate certain stop reasons on whether the target /// supports it. macro_rules! guard { (reverse_exec) => {{ if let Some(resume_ops) = target.base_ops().resume_ops() { let (reverse_cont, reverse_step) = match resume_ops { ResumeOps::MultiThread(ops) => ( ops.support_reverse_cont().is_some(), ops.support_reverse_step().is_some(), ), ResumeOps::SingleThread(ops) => ( ops.support_reverse_cont().is_some(), ops.support_reverse_step().is_some(), ), }; reverse_cont || reverse_step } else { false } }}; (break $op:ident) => { target .support_breakpoints() .and_then(|ops| ops.$op()) .is_some() }; (catch_syscall) => { target.support_catch_syscalls().is_some() }; (fork) => { target.use_fork_stop_reason() }; (vfork) => { target.use_vfork_stop_reason() }; (vforkdone) => { target.use_vforkdone_stop_reason() }; } let status = match stop_reason { MultiThreadStopReason::DoneStep => { res.write_str("S")?; res.write_num(Signal::SIGTRAP.0)?; FinishExecStatus::Handled } MultiThreadStopReason::Signal(sig) => { res.write_str("S")?; res.write_num(sig.0)?; FinishExecStatus::Handled } MultiThreadStopReason::Exited(code) => { res.write_str("W")?; res.write_num(code)?; FinishExecStatus::Disconnect(DisconnectReason::TargetExited(code)) } MultiThreadStopReason::Terminated(sig) => { res.write_str("X")?; res.write_num(sig.0)?; FinishExecStatus::Disconnect(DisconnectReason::TargetTerminated(sig)) } MultiThreadStopReason::SignalWithThread { 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, 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, target, Some(tid), Signal::SIGTRAP)?; res.write_str("hwbreak:;")?; FinishExecStatus::Handled } MultiThreadStopReason::Watch { tid, kind, addr } if guard!(break support_hw_watchpoint) => { crate::__dead_code_marker!("hw_watchpoint", "stop_reason"); self.write_stop_common(res, target, Some(tid), Signal::SIGTRAP)?; use crate::target::ext::breakpoints::WatchKind; match kind { WatchKind::Write => res.write_str("watch:")?, WatchKind::Read => res.write_str("rwatch:")?, WatchKind::ReadWrite => res.write_str("awatch:")?, } res.write_num(addr)?; res.write_str(";")?; FinishExecStatus::Handled } MultiThreadStopReason::ReplayLog { tid, pos } if guard!(reverse_exec) => { crate::__dead_code_marker!("reverse_exec", "stop_reason"); self.write_stop_common(res, target, tid, Signal::SIGTRAP)?; res.write_str("replaylog:")?; res.write_str(match pos { ReplayLogPosition::Begin => "begin", ReplayLogPosition::End => "end", })?; res.write_str(";")?; FinishExecStatus::Handled } MultiThreadStopReason::CatchSyscall { tid, number, position, } if guard!(catch_syscall) => { crate::__dead_code_marker!("catch_syscall", "stop_reason"); self.write_stop_common(res, target, tid, Signal::SIGTRAP)?; res.write_str(match position { CatchSyscallPosition::Entry => "syscall_entry:", CatchSyscallPosition::Return => "syscall_return:", })?; res.write_num(number)?; res.write_str(";")?; FinishExecStatus::Handled } MultiThreadStopReason::Library(tid) => { self.write_stop_common(res, target, Some(tid), Signal::SIGTRAP)?; res.write_str("library:;")?; FinishExecStatus::Handled } MultiThreadStopReason::Fork { cur_tid, new_tid } if guard!(fork) => { crate::__dead_code_marker!("fork_events", "stop_reason"); self.write_stop_common(res, target, Some(cur_tid), Signal::SIGTRAP)?; res.write_str("fork:")?; res.write_specific_thread_id(SpecificThreadId { pid: self .features .multiprocess() .then_some(SpecificIdKind::WithId(self.get_current_pid(target)?)), tid: SpecificIdKind::WithId(new_tid), })?; res.write_str(";")?; FinishExecStatus::Handled } MultiThreadStopReason::VFork { cur_tid, new_tid } if guard!(vfork) => { crate::__dead_code_marker!("vfork_events", "stop_reason"); self.write_stop_common(res, target, Some(cur_tid), Signal::SIGTRAP)?; res.write_str("vfork:")?; res.write_specific_thread_id(SpecificThreadId { pid: self .features .multiprocess() .then_some(SpecificIdKind::WithId(self.get_current_pid(target)?)), tid: SpecificIdKind::WithId(new_tid), })?; res.write_str(";")?; FinishExecStatus::Handled } MultiThreadStopReason::VForkDone(tid) if guard!(vforkdone) => { crate::__dead_code_marker!("vforkdone_events", "stop_reason"); self.write_stop_common(res, target, Some(tid), Signal::SIGTRAP)?; res.write_str("vforkdone:;")?; FinishExecStatus::Handled } // Explicitly avoid using `_ =>` to handle the "unguarded" variants, as doing so would // squelch the useful compiler error that crops up whenever stop reasons are added. MultiThreadStopReason::SwBreak(_) | MultiThreadStopReason::HwBreak(_) | MultiThreadStopReason::Watch { .. } | MultiThreadStopReason::ReplayLog { .. } | MultiThreadStopReason::CatchSyscall { .. } | MultiThreadStopReason::Fork { .. } | MultiThreadStopReason::VFork { .. } | MultiThreadStopReason::VForkDone { .. } => { return Err(Error::UnsupportedStopReason); } }; Ok(status) } } pub(crate) enum FinishExecStatus { Handled, Disconnect(DisconnectReason), } ================================================ FILE: src/stub/core_impl/reverse_exec.rs ================================================ use super::prelude::*; 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; use crate::target::ext::base::reverse_exec::ReverseStep as ReverseStepTrait; use crate::target::ext::base::ResumeOps; macro_rules! defn_ops { ($name:ident, $reverse_trait:ident, $f:ident) => { enum $name<'a, A: Arch, E> { SingleThread(&'a mut dyn $reverse_trait<(), Arch = A, Error = E>), MultiThread(&'a mut dyn $reverse_trait), } impl<'a, A, E> $name<'a, A, E> where A: Arch, { #[inline(always)] fn from_target(target: &mut T) -> Option<$name<'_, T::Arch, T::Error>> where T: Target, { let ops = match target.base_ops().resume_ops()? { ResumeOps::SingleThread(ops) => $name::SingleThread(ops.$f()?), ResumeOps::MultiThread(ops) => $name::MultiThread(ops.$f()?), }; Some(ops) } } }; } defn_ops!(ReverseContOps, ReverseContTrait, support_reverse_cont); defn_ops!(ReverseStepOps, ReverseStepTrait, support_reverse_step); impl GdbStubImpl { pub(crate) fn handle_reverse_cont( &mut self, _res: &mut ResponseWriter<'_, C>, target: &mut T, command: ReverseCont, ) -> Result> { let ops = match ReverseContOps::<'_, T::Arch, T::Error>::from_target(target) { Some(ops) => ops, None => return Ok(HandlerStatus::Handled), }; crate::__dead_code_marker!("reverse_cont", "impl"); let handler_status = match command { ReverseCont::bc(_) => { match ops { ReverseContOps::MultiThread(ops) => { ops.reverse_cont().map_err(Error::TargetError)? } ReverseContOps::SingleThread(ops) => { ops.reverse_cont().map_err(Error::TargetError)? } } HandlerStatus::DeferredStopReason } }; Ok(handler_status) } // FIXME: De-duplicate with above code? pub(crate) fn handle_reverse_step( &mut self, _res: &mut ResponseWriter<'_, C>, target: &mut T, command: ReverseStep, ) -> Result> { let ops = match ReverseStepOps::<'_, T::Arch, T::Error>::from_target(target) { Some(ops) => ops, None => return Ok(HandlerStatus::Handled), }; crate::__dead_code_marker!("reverse_step", "impl"); let handler_status = match command { ReverseStep::bs(_) => { let tid = match self.current_resume_tid { // NOTE: Can't single-step all cores. SpecificIdKind::All => return Err(Error::PacketUnexpected), SpecificIdKind::WithId(tid) => tid, }; match ops { ReverseStepOps::MultiThread(ops) => { ops.reverse_step(tid).map_err(Error::TargetError)? } ReverseStepOps::SingleThread(ops) => { ops.reverse_step(()).map_err(Error::TargetError)? } } HandlerStatus::DeferredStopReason } }; Ok(handler_status) } } ================================================ FILE: src/stub/core_impl/section_offsets.rs ================================================ use super::prelude::*; use crate::protocol::commands::ext::SectionOffsets; impl GdbStubImpl { pub(crate) fn handle_section_offsets( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: SectionOffsets, ) -> Result> { let ops = match target.support_section_offsets() { Some(ops) => ops, None => return Ok(HandlerStatus::Handled), }; crate::__dead_code_marker!("section_offsets", "impl"); let handler_status = match command { SectionOffsets::qOffsets(_cmd) => { use crate::target::ext::section_offsets::Offsets; match ops.get_section_offsets().map_err(Error::TargetError)? { Offsets::Sections { text, data, bss } => { res.write_str("Text=")?; res.write_num(text)?; res.write_str(";Data=")?; res.write_num(data)?; // "Note: while a Bss offset may be included in the response, // GDB ignores this and instead applies the Data offset to the Bss section." // // While this would suggest that it's OK to omit `Bss=` entirely, recent // versions of GDB seem to require that `Bss=` is present. // // See https://github.com/bminor/binutils-gdb/blob/master/gdb/remote.c#L4149-L4159 let bss = bss.unwrap_or(data); res.write_str(";Bss=")?; res.write_num(bss)?; } Offsets::Segments { text_seg, data_seg } => { res.write_str("TextSeg=")?; res.write_num(text_seg)?; if let Some(data) = data_seg { res.write_str(";DataSeg=")?; res.write_num(data)?; } } } HandlerStatus::Handled } }; Ok(handler_status) } } ================================================ FILE: src/stub/core_impl/single_register_access.rs ================================================ use super::prelude::*; use crate::arch::Arch; use crate::arch::RegId; use crate::protocol::commands::ext::SingleRegisterAccess; use crate::target::ext::base::BaseOps; impl GdbStubImpl { fn inner( res: &mut ResponseWriter<'_, C>, ops: crate::target::ext::base::single_register_access::SingleRegisterAccessOps<'_, Tid, T>, command: SingleRegisterAccess<'_>, id: Tid, ) -> Result> where Tid: crate::is_valid_tid::IsValidTid, { let handler_status = match command { SingleRegisterAccess::p(p) => { let reg = ::RegId::from_raw_id(p.reg_id); let (reg_id, reg_size) = match reg { None => { warn!("reg id {} does not map onto any known register", p.reg_id); return Ok(HandlerStatus::Handled); } Some(v) => v, }; let mut buf = p.buf; if let Some(size) = reg_size { buf = buf .get_mut(..size.get()) .ok_or(Error::PacketBufferOverflow)?; } let len = ops.read_register(id, reg_id, buf).handle_error()?; if len == 0 { if let Some(size) = reg_size { for _ in 0..size.get() { res.write_str("xx")?; } } else { return Err(Error::TargetMismatch); } } else { if let Some(size) = reg_size { if size.get() != len { return Err(Error::TargetMismatch); } } else { buf = buf.get_mut(..len).ok_or(Error::PacketBufferOverflow)?; } res.write_hex_buf(buf)?; } HandlerStatus::Handled } SingleRegisterAccess::P(p) => { let reg = ::RegId::from_raw_id(p.reg_id); match reg { // empty packet indicates unrecognized query None => return Ok(HandlerStatus::Handled), Some((reg_id, _)) => ops.write_register(id, reg_id, p.val).handle_error()?, } HandlerStatus::NeedsOk } }; Ok(handler_status) } pub(crate) fn handle_single_register_access( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: SingleRegisterAccess<'_>, ) -> Result> { match target.base_ops() { BaseOps::SingleThread(ops) => match ops.support_single_register_access() { None => Ok(HandlerStatus::Handled), Some(ops) => Self::inner(res, ops, command, ()), }, BaseOps::MultiThread(ops) => match ops.support_single_register_access() { None => Ok(HandlerStatus::Handled), Some(ops) => Self::inner(res, ops, command, self.current_mem_tid), }, } } } ================================================ FILE: src/stub/core_impl/target_xml.rs ================================================ use super::prelude::*; use crate::arch::Arch; use crate::protocol::commands::ext::TargetXml; impl GdbStubImpl { pub(crate) fn handle_target_xml( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: TargetXml<'_>, ) -> Result> { if !target.use_target_description_xml() { return Ok(HandlerStatus::Handled); } let handler_status = match command { TargetXml::qXferFeaturesRead(cmd) => { let ret = if let Some(ops) = target.support_target_description_xml_override() { ops.target_description_xml(cmd.annex.name, cmd.offset, cmd.length, cmd.buf) .handle_error()? } else if let Some(xml) = T::Arch::target_description_xml() { if cmd.annex.name != b"target.xml" { // TODO: not the best error... should probably report to the user the // isn't supported at the Arch level (yet) return Err(Error::PacketUnexpected); } let xml = xml.trim().as_bytes(); let xml_len = xml.len(); let start = xml_len.min(cmd.offset as usize); let end = xml_len.min((cmd.offset as usize).saturating_add(cmd.length)); // LLVM isn't smart enough to realize that `start <= end`, and fails to elide a // `slice_end_index_len_fail` check unless we include this seemingly useless // call to `min`. let data = &xml[start.min(end)..end]; let n = data.len().min(cmd.buf.len()); cmd.buf[..n].copy_from_slice(&data[..n]); n } else { // If the target hasn't provided their own XML, then the initial response to // "qSupported" wouldn't have included "qXfer:features:read", and gdb wouldn't // send this packet unless it was explicitly marked as supported. return Err(Error::PacketUnexpected); }; if ret == 0 { res.write_str("l")?; } else { res.write_str("m")?; // TODO: add more specific error variant? res.write_binary(cmd.buf.get(..ret).ok_or(Error::PacketBufferOverflow)?)?; } HandlerStatus::Handled } }; Ok(handler_status) } } ================================================ FILE: src/stub/core_impl/thread_extra_info.rs ================================================ use super::prelude::*; use crate::protocol::commands::ext::ThreadExtraInfo; use crate::target::ext::base::BaseOps; impl GdbStubImpl { pub(crate) fn handle_thread_extra_info( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: ThreadExtraInfo<'_>, ) -> Result> { let ops = match target.base_ops() { BaseOps::SingleThread(_) => return Ok(HandlerStatus::Handled), BaseOps::MultiThread(ops) => match ops.support_thread_extra_info() { Some(ops) => ops, None => return Ok(HandlerStatus::Handled), }, }; crate::__dead_code_marker!("thread_extra_info", "impl"); let handler_status = match command { ThreadExtraInfo::qThreadExtraInfo(info) => { // TODO: plumb through PID when true multi-process support is added let _pid = info.id.pid; let size = ops .thread_extra_info(info.id.tid, info.buf) .map_err(Error::TargetError)?; let data = info.buf.get(..size).ok_or(Error::PacketBufferOverflow)?; res.write_hex_buf(data)?; HandlerStatus::Handled } }; Ok(handler_status) } } ================================================ FILE: src/stub/core_impl/tracepoints.rs ================================================ use super::prelude::*; use crate::arch::Arch; use crate::internal::BeBytes; use crate::protocol::commands::_QTDPsrc::QTDPsrc; use crate::protocol::commands::_qTBuffer::qTBuffer; use crate::protocol::commands::ext::Tracepoints; use crate::protocol::commands::prelude::decode_hex; use crate::protocol::commands::prelude::decode_hex_buf; use crate::protocol::commands::_QTDP::CreateTDP; use crate::protocol::commands::_QTDP::ExtendTDP; use crate::protocol::commands::_QTDP::QTDP; use crate::protocol::ResponseWriterError; use crate::target::ext::tracepoints::ExperimentExplanation; use crate::target::ext::tracepoints::ExperimentStatus; use crate::target::ext::tracepoints::FrameDescription; use crate::target::ext::tracepoints::FrameRequest; use crate::target::ext::tracepoints::NewTracepoint; use crate::target::ext::tracepoints::SourceTracepoint; use crate::target::ext::tracepoints::Tracepoint; use crate::target::ext::tracepoints::TracepointAction; use crate::target::ext::tracepoints::TracepointEnumerateCursor; use crate::target::ext::tracepoints::TracepointEnumerateStep; use crate::target::ext::tracepoints::TracepointSourceType; use crate::target::ext::tracepoints::TracepointStatus; use managed::ManagedSlice; use num_traits::PrimInt; impl NewTracepoint { /// Parse from a raw CreateTDP packet. fn from_tdp(ctdp: CreateTDP<'_>) -> Option<(Self, bool)> { Some(( Self { number: ctdp.number, addr: U::from_be_bytes(ctdp.addr)?, enabled: ctdp.enable, pass_count: ctdp.pass, step_count: ctdp.step, }, ctdp.more, )) } } impl NewTracepoint { /// Write this as a qTfP/qTsP response pub(crate) fn write( &self, res: &mut ResponseWriter<'_, C>, ) -> Result<(), Error> { res.write_str("T")?; res.write_num(self.number.0)?; res.write_str(":")?; res.write_num(self.addr)?; res.write_str(":")?; res.write_str(if self.enabled { "E" } else { "D" })?; res.write_str(":")?; res.write_num(self.step_count)?; res.write_str(":")?; res.write_num(self.pass_count)?; Ok(()) } } /// A list of actions that a tracepoint should be extended with. #[derive(Debug)] pub(crate) struct ExtendTracepoint<'a, U> { /// The tracepoint that is having actions appended to its definition. pub number: Tracepoint, /// The PC address of the tracepoint that is being extended. /// This is currently unused information sent as part of the packet by GDB, /// but may be required for implementing while-stepping actions later. #[allow(dead_code)] pub addr: U, /// The unparsed action data. pub data: ManagedSlice<'a, u8>, } impl<'a, U: BeBytes> ExtendTracepoint<'a, U> { /// Parse from a raw ExtendTDP packet. fn from_tdp(dtdp: ExtendTDP<'a>) -> Option { Some(Self { number: dtdp.number, addr: U::from_be_bytes(dtdp.addr)?, data: ManagedSlice::Borrowed(dtdp.actions), }) } /// Parse the actions that should be added to the definition of this /// tracepoint, calling `f` on each action. /// /// Returns `Err` if parsing of actions failed, or hit unsupported actions. /// Return `Ok(more)` on success, where more indicates if more actions are /// expect in later packets. If the actions weren't from a GDB packet, more /// is None. pub(crate) fn actions( mut self, f: impl FnMut(&TracepointAction<'_, U>), ) -> Result, Error> { Self::parse_raw_actions(&mut self.data, f) } fn parse_raw_actions( actions: &mut [u8], mut f: impl FnMut(&TracepointAction<'_, U>), ) -> Result, Error> { let (actions, more) = match actions { [rest @ .., b'-'] => (rest, true), x => (x, false), }; // TODO: There's no "packet unsupported", so for now we stub out unimplemented // functionality by reporting the commands malformed instead. use crate::protocol::PacketParseError::MalformedCommand; let mut unparsed: Option<&mut [u8]> = Some(actions); loop { match unparsed { Some([b'S', ..]) => { // TODO: how can gdbstub even implement this? it changes how // future packets should be interpreted, but as a trait we // can't keep a flag around for that (unless we specifically // have a `mark_while_stepping` callback for the target to // keep track future tracepoint_extends should be treated different). // If we go that route we also would need to return two vectors // here, "normal" actions and "while stepping" actions...but // "normals" actions may still be "while stepping" actions, // just continued from the previous packet, which we forgot // about! // // We use 'W' to indicate "while-stepping", since we're already // using 'S' elsewhere for static tracepoints. return Err(Error::TracepointFeatureUnimplemented(b'W')); } Some([b'R', mask @ ..]) => { let mask_end = mask .iter() .enumerate() .find(|(_i, b)| matches!(b, b'S' | b'R' | b'M' | b'X')); // We may or may not have another action after our mask let mask = if let Some(mask_end) = mask_end { let (mask_bytes, next) = mask.split_at_mut(mask_end.0); unparsed = Some(next); decode_hex_buf(mask_bytes).or(Err(Error::PacketParse(MalformedCommand)))? } else { unparsed = None; decode_hex_buf(mask).or(Err(Error::PacketParse(MalformedCommand)))? }; (f)(&TracepointAction::Registers { mask: ManagedSlice::Borrowed(mask), }); } Some([b'M', _mem_args @ ..]) => { // Unimplemented: even simple actions like `collect *(int*)0x0` // are actually assembled as `X` bytecode actions return Err(Error::TracepointFeatureUnimplemented(b'M')); } Some([b'X', eval_args @ ..]) => { let mut len_end = eval_args.splitn_mut(2, |b| *b == b','); let (len_bytes, rem) = ( len_end.next().ok_or(Error::PacketParse(MalformedCommand))?, len_end.next().ok_or(Error::PacketParse(MalformedCommand))?, ); let len: usize = decode_hex(len_bytes).or(Err(Error::PacketParse(MalformedCommand)))?; if rem.len() < len * 2 { return Err(Error::PacketParse(MalformedCommand)); } let (expr_bytes, next_bytes) = rem.split_at_mut(len * 2); let expr = decode_hex_buf(expr_bytes).or(Err(Error::PacketParse(MalformedCommand)))?; (f)(&TracepointAction::Expression { expr: ManagedSlice::Borrowed(expr), }); unparsed = Some(next_bytes); } Some([]) | None => { break; } _ => return Err(Error::PacketParse(MalformedCommand)), } } Ok(Some(more)) } } impl<'a, U: BeBytes> SourceTracepoint<'a, U> { /// Parse from a raw CreateTDP packet. fn from_src(src: QTDPsrc<'a>) -> Option { Some(Self { number: src.number, addr: U::from_be_bytes(src.addr)?, kind: src.kind, start: src.start, slen: src.slen, bytes: ManagedSlice::Borrowed(src.bytes), }) } } impl SourceTracepoint<'_, U> { /// Write this as a qTfP/qTsP response pub(crate) fn write( &self, res: &mut ResponseWriter<'_, C>, ) -> Result<(), Error> { res.write_str("Z")?; res.write_num(self.number.0)?; res.write_str(":")?; res.write_num(self.addr)?; res.write_str(":")?; res.write_str(match self.kind { TracepointSourceType::At => "at", TracepointSourceType::Cond => "cond", TracepointSourceType::Cmd => "cmd", })?; res.write_str(":")?; // We use the start and slen from the SourceTracepoint instead of // start=0 slen=bytes.len() because, although we can assume GDB to be able // to handle arbitrary sized packets, the target implementation might still // be giving us chunked source (such as if it's parroting the chunked source // that GDB initially gave us). res.write_num(self.start)?; res.write_str(":")?; res.write_num(self.slen)?; res.write_str(":")?; res.write_hex_buf(self.bytes.as_ref())?; Ok(()) } } impl TracepointAction<'_, U> { /// Write this as a qTfP/qTsP response pub(crate) fn write( &self, tp: Tracepoint, addr: U, res: &mut ResponseWriter<'_, C>, ) -> Result<(), Error> { res.write_str("A")?; res.write_num(tp.0)?; res.write_str(":")?; res.write_num(addr)?; res.write_str(":")?; match self { TracepointAction::Registers { mask } => { res.write_str("R")?; res.write_hex_buf(mask)?; } TracepointAction::Memory { basereg, offset, length, } => { res.write_str("M")?; match basereg { Some(r) => res.write_num(*r), None => res.write_str("-1"), }?; res.write_str(",")?; res.write_num(*offset)?; res.write_str(",")?; res.write_num(*length)?; } TracepointAction::Expression { expr } => { res.write_str("X")?; res.write_num(expr.len())?; res.write_str(",")?; res.write_hex_buf(expr)?; } } Ok(()) } } impl ExperimentStatus<'_> { pub(crate) fn write( &self, res: &mut ResponseWriter<'_, C>, ) -> Result<(), ResponseWriterError> { use crate::target::ext::tracepoints::ExperimentStatus::*; if let Running = self { return res.write_str("T1"); } // We're stopped for some reason, and may have an explanation for why res.write_str("T0")?; match self { Running => { /* unreachable */ } NotRunning => { /* no information */ } NotRun => res.write_str(";tnotrun:0")?, Stop(ref t) => match t { Some(text) => { res.write_str(";tstop:")?; res.write_hex_buf(text)?; res.write_str(":0")?; } None => res.write_str(";tstop:0")?, }, Full => res.write_str(";tfull:0")?, Disconnected => res.write_str(";tdisconnected:0")?, PassCount(tpnum) => { res.write_str(";tpasscount:")?; res.write_num(tpnum.0)?; } Error(text, tpnum) => { res.write_str(";terror:")?; res.write_hex_buf(text)?; res.write_str(":")?; res.write_num(tpnum.0)?; } Unknown => res.write_str(";tunknown:0")?, } Ok(()) } } impl ExperimentExplanation<'_> { pub(crate) fn write( &self, res: &mut ResponseWriter<'_, C>, ) -> Result<(), Error> { use ExperimentExplanation::*; match self { Frames(u) => { res.write_str("tframes:")?; res.write_num(*u)?; } Created(u) => { res.write_str("tcreated:")?; res.write_num(*u)?; } Size(u) => { res.write_str("tsize:")?; res.write_num(*u)?; } Free(u) => { res.write_str("tfree:")?; res.write_num(*u)?; } Circular(u) => { res.write_str("circular:")?; res.write_num(if *u { 1 } else { 0 })?; } DisconnectedTracing(dis) => match dis { true => res.write_str("disconn:1")?, false => res.write_str("disconn:0")?, }, Other(body) => res.write_str(body.as_ref())?, }; Ok(()) } } impl<'a, U: crate::internal::BeBytes> From> for Option> { fn from(s: FrameRequest<&'a mut [u8]>) -> Self { Some(match s { FrameRequest::Select(u) => FrameRequest::Select(u), FrameRequest::AtPC(u) => FrameRequest::AtPC(U::from_be_bytes(u)?), FrameRequest::Hit(tp) => FrameRequest::Hit(tp), FrameRequest::Between(s, e) => { FrameRequest::Between(U::from_be_bytes(s)?, U::from_be_bytes(e)?) } FrameRequest::Outside(s, e) => { FrameRequest::Outside(U::from_be_bytes(s)?, U::from_be_bytes(e)?) } }) } } impl GdbStubImpl { pub(crate) fn handle_tracepoints( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: Tracepoints<'_>, ) -> Result> { let ops = match target.support_tracepoints() { Some(ops) => ops, None => return Ok(HandlerStatus::Handled), }; crate::__dead_code_marker!("tracepoints", "impl"); match command { Tracepoints::QTinit(_) => { ops.tracepoints_init().handle_error()?; // GDB documentation doesn't say it, but this requires "OK" in order // to signify we support tracepoints. return Ok(HandlerStatus::NeedsOk); } Tracepoints::qTStatus(_) => { let mut err: Option> = None; let mut has_status = false; ops.trace_experiment_status(&mut |status: ExperimentStatus<'_>| { // If the target implementation calls us multiple times, then // we would erroneously serialize an invalid response. Guard // against it in the simplest way. if has_status { return; } if let Err(e) = status.write(res) { err = Some(e.into()) } else { has_status = true; } }) .handle_error()?; if has_status { // Only bother trying to get info if we also have a status ops.trace_experiment_info(&mut |explanation: ExperimentExplanation<'_>| { if let Err(e) = res .write_str(";") .map_err(|e| e.into()) .and_then(|()| explanation.write::(res)) { err = Some(e) } }) .handle_error()?; } if let Some(e) = err { return Err(e); } } Tracepoints::qTP(qtp) => { let addr = ::Usize::from_be_bytes(qtp.addr) .ok_or(Error::TargetMismatch)?; let TracepointStatus { hit_count, bytes_used, } = ops.tracepoint_status(qtp.tracepoint, addr).handle_error()?; res.write_str("V")?; res.write_num(hit_count)?; res.write_str(":")?; res.write_num(bytes_used)?; } Tracepoints::QTDP(q) => { match q { QTDP::Create(ctdp) => { if let Some(feat) = ctdp.unsupported_option { // We have some options we don't know how to process, so bail out. return Err(Error::TracepointFeatureUnimplemented(feat)); } let (new_tracepoint, more) = NewTracepoint::<::Usize>::from_tdp(ctdp) .ok_or(Error::TargetMismatch)?; let tp = new_tracepoint.number; ops.tracepoint_create_begin(new_tracepoint).handle_error()?; if !more { ops.tracepoint_create_complete(tp).handle_error()?; } } QTDP::Extend(dtdp) => { let extend_tracepoint = ExtendTracepoint::<::Usize>::from_tdp(dtdp) .ok_or(Error::TargetMismatch)?; let tp = extend_tracepoint.number; let mut err: Option> = None; let more = extend_tracepoint.actions(|action| { if let Err(e) = ops.tracepoint_create_continue(tp, action).handle_error() { err = Some(e) } }); if let Some(e) = err { return Err(e); } match more { Ok(Some(true)) => { // We expect additional QTDP packets, so don't // complete it yet. } Ok(None) | Ok(Some(false)) => { ops.tracepoint_create_complete(tp).handle_error()?; } Err(e) => { return Err(e); } } } }; // TODO: support qRelocInsn? return Ok(HandlerStatus::NeedsOk); } Tracepoints::QTDPsrc(src) => { if let Some(supports_sources) = ops.support_tracepoint_source() { let source = SourceTracepoint::<::Usize>::from_src(src) .ok_or(Error::TargetMismatch)?; supports_sources .tracepoint_attach_source(source) .handle_error()?; // Documentation doesn't mention this, but it needs OK return Ok(HandlerStatus::NeedsOk); } } Tracepoints::qTBuffer(buf) => { let qTBuffer { offset, length } = buf; let mut wrote: Result> = Ok(false); ops.trace_buffer_request(offset, length, &mut |data| { if let Err(e) = res.write_hex_buf(data) { wrote = Err(e.into()) } else { wrote = Ok(true) } }) .handle_error()?; if !wrote? { res.write_str("l")?; } } Tracepoints::QTBuffer(conf) => { ops.trace_buffer_configure(conf.0).handle_error()?; // Documentation doesn't mention this, but it needs OK return Ok(HandlerStatus::NeedsOk); } Tracepoints::QTStart(_) => { ops.trace_experiment_start().handle_error()?; // Documentation doesn't mention this, but it needs OK // TODO: qRelocInsn reply support? return Ok(HandlerStatus::NeedsOk); } Tracepoints::QTStop(_) => { ops.trace_experiment_stop().handle_error()?; // Documentation doesn't mention this, but it needs OK return Ok(HandlerStatus::NeedsOk); } Tracepoints::QTFrame(req) => { let parsed_qtframe: Option::Usize>> = req.0.into(); let parsed_req = parsed_qtframe.ok_or(Error::TargetMismatch)?; let mut err: Result<_, Error> = Ok(()); let mut any_results = false; ops.select_frame(parsed_req, &mut |desc| { let e = (|| -> Result<_, _> { match desc { FrameDescription::FrameNumber(n) => { res.write_str("F")?; res.write_num(n)?; any_results = true; } FrameDescription::Hit(tdp) => { res.write_str("T")?; res.write_num(tdp.0)?; } } Ok(()) })(); if let Err(e) = e { err = Err(e) } }) .handle_error()?; if !any_results { res.write_str("F-1")?; } } // The GDB protocol for this is very weird: it sends this first packet // to initialize a state machine on our stub, and then sends the subsequent // packets N times in order to drive the state machine to the end in // order to ask for all our tracepoints and their actions. gdbstub // uses a target allocated state machine that it drives in response // to these packets, so that it can provide a nice typed API. Tracepoints::qTfP(_) => { // Reset our state machine let state = ops.tracepoint_enumerate_state(); state.cursor = None; let mut err: Option> = None; let mut started = None; let step = ops .tracepoint_enumerate_start(None, &mut |ctp| { // We need to know what tracepoint to begin stepping, since the // target will just tell us there's TracepointEnumerateStep::More // otherwise. started = Some((ctp.number, ctp.addr)); let e = ctp.write::(res); if let Err(e) = e { err = Some(e) } }) .handle_error()?; if let Some(e) = err { return Err(e); } if let Some((tp, addr)) = started { ops.tracepoint_enumerate_state().cursor = Some(TracepointEnumerateCursor::New { tp, addr }); } self.handle_tracepoint_state_machine_step(target, step)?; } Tracepoints::qTsP(_) => { let state = ops.tracepoint_enumerate_state(); let mut err: Option> = None; let step = match state.cursor { None => { // If we don't have a cursor, than the last // packet responded // with a TracepointEnumerateStep::Done. We don't have // anything else to report. None } Some(TracepointEnumerateCursor::New { tp, .. }) => { // If we don't have any progress, the last packet was // a Next(tp) and we need to start reporting a new tracepoint Some( ops.tracepoint_enumerate_start(Some(tp), &mut |ctp| { let e = ctp.write::(res); if let Err(e) = e { err = Some(e) } }) .handle_error()?, ) } Some(TracepointEnumerateCursor::Action { tp, addr, step }) => { // Otherwise we should be continuing the advance the current tracepoint. Some( ops.tracepoint_enumerate_action(tp, step, &mut |action| { let e = action.write::(tp, addr, res); if let Err(e) = e { err = Some(e) } }) .handle_error()?, ) } Some(TracepointEnumerateCursor::Source { tp, step, .. }) => { if let Some(supports_sources) = ops.support_tracepoint_source() { Some( supports_sources .tracepoint_enumerate_source(tp, step, &mut |src| { let e = src.write::(res); if let Err(e) = e { err = Some(e) } }) .handle_error()?, ) } else { // If the target doesn't support tracepoint sources but told // us to enumerate one anyways, then the target has an error. return Err(Error::TracepointUnsupportedSourceEnumeration); } } }; if let Some(e) = err { return Err(e); } if let Some(step) = step { self.handle_tracepoint_state_machine_step(target, step)?; } } // Likewise, the same type of driven state machine is used for trace // state variables Tracepoints::qTfV(_) => { // TODO: State variables } Tracepoints::qTsV(_) => { // TODO: State variables } }; Ok(HandlerStatus::Handled) } fn handle_tracepoint_state_machine_step( &mut self, target: &mut T, step: TracepointEnumerateStep<::Usize>, ) -> Result<(), Error> { let ops = match target.support_tracepoints() { Some(ops) => ops, None => return Ok(()), }; let state = ops.tracepoint_enumerate_state(); let next = match (state.cursor.as_ref(), step) { (None, _) => None, (Some(_), TracepointEnumerateStep::Done) => None, // Transition to enumerating actions ( Some(&TracepointEnumerateCursor::New { tp, addr }), TracepointEnumerateStep::Action, ) => Some(TracepointEnumerateCursor::Action { tp, addr, step: 0 }), ( Some(&TracepointEnumerateCursor::Source { tp, addr, .. }), TracepointEnumerateStep::Action, ) => Some(TracepointEnumerateCursor::Action { tp, addr, step: 0 }), ( Some(&TracepointEnumerateCursor::Action { tp, addr, step }), TracepointEnumerateStep::Action, ) => Some(TracepointEnumerateCursor::Action { tp, addr, step: step + 1, }), // Transition to enumerating sources ( Some( &TracepointEnumerateCursor::New { tp, addr } | &TracepointEnumerateCursor::Action { tp, addr, .. }, ), TracepointEnumerateStep::Source, ) => Some(TracepointEnumerateCursor::Source { tp, addr, step: 0 }), ( Some(&TracepointEnumerateCursor::Source { tp, addr, step }), TracepointEnumerateStep::Source, ) => Some(TracepointEnumerateCursor::Source { tp, addr, step: step + 1, }), // Transition to the next tracepoint (Some(_), TracepointEnumerateStep::Next { tp, addr }) => { Some(TracepointEnumerateCursor::New { tp, addr }) } }; state.cursor = next; Ok(()) } } ================================================ FILE: src/stub/core_impl/wasm.rs ================================================ use super::prelude::*; use crate::protocol::commands::ext::Wasm; impl GdbStubImpl { pub(crate) fn handle_wasm( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: Wasm<'_>, ) -> Result> { let ops = match target.support_wasm() { Some(ops) => ops, None => return Ok(HandlerStatus::Handled), }; crate::__dead_code_marker!("wasm", "impl"); match command { Wasm::qWasmCallStack(cmd) => { let mut error: Result<(), Error> = Ok(()); ops.wasm_call_stack(cmd.tid.tid, &mut |pc| { if let Err(e) = res.write_hex_buf(&pc.to_le_bytes()) { error = Err(e.into()); } }) .map_err(Error::TargetError)?; error?; } Wasm::qWasmLocal(cmd) => { let len = ops .read_wasm_local(self.current_mem_tid, cmd.frame, cmd.local, cmd.buf) .map_err(Error::TargetError)?; res.write_hex_buf(&cmd.buf[0..len])?; } Wasm::qWasmGlobal(cmd) => { let len = ops .read_wasm_global(self.current_mem_tid, cmd.frame, cmd.global, cmd.buf) .map_err(Error::TargetError)?; res.write_hex_buf(&cmd.buf[0..len])?; } Wasm::qWasmStackValue(cmd) => { let len = ops .read_wasm_stack(self.current_mem_tid, cmd.frame, cmd.index, cmd.buf) .map_err(Error::TargetError)?; res.write_hex_buf(&cmd.buf[0..len])?; } }; Ok(HandlerStatus::Handled) } } ================================================ FILE: src/stub/core_impl/x_lowcase_packet.rs ================================================ use super::prelude::*; use crate::protocol::commands::ext::XLowcasePacket; use crate::stub::core_impl::base::read_addr_handler; impl GdbStubImpl { pub(crate) fn handle_x_lowcase_packet( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: XLowcasePacket<'_>, ) -> Result> { if !target.use_x_lowcase_packet() { return Ok(HandlerStatus::Handled); } crate::__dead_code_marker!("x_lowcase_packet", "impl"); let handler_status = match command { XLowcasePacket::x(cmd) => { read_addr_handler::( |i, data| { // Start data with 'b' to indicate binary data if i == 0 { res.write_str("b")?; } res.write_binary(data) }, self.current_mem_tid, target, cmd.buf, cmd.len, cmd.addr, )?; HandlerStatus::Handled } }; Ok(handler_status) } } ================================================ FILE: src/stub/core_impl/x_upcase_packet.rs ================================================ use super::prelude::*; use crate::arch::Arch; use crate::protocol::commands::ext::XUpcasePacket; use crate::target::ext::base::BaseOps; impl GdbStubImpl { pub(crate) fn handle_x_upcase_packet( &mut self, _res: &mut ResponseWriter<'_, C>, target: &mut T, command: XUpcasePacket<'_>, ) -> Result> { if !target.use_x_upcase_packet() { return Ok(HandlerStatus::Handled); } crate::__dead_code_marker!("x_upcase_packet", "impl"); let handler_status = match command { XUpcasePacket::X(cmd) => { let addr = ::Usize::from_be_bytes(cmd.addr) .ok_or(Error::TargetMismatch)?; match target.base_ops() { BaseOps::SingleThread(ops) => ops.write_addrs(addr, cmd.val), BaseOps::MultiThread(ops) => { ops.write_addrs(addr, cmd.val, self.current_mem_tid) } } .handle_error()?; HandlerStatus::NeedsOk } }; Ok(handler_status) } } ================================================ FILE: src/stub/core_impl.rs ================================================ use crate::common::Signal; use crate::common::Tid; use crate::conn::Connection; use crate::protocol::commands::Command; 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. /// /// Do not clutter this prelude with types only used by a few extensions. mod prelude { pub(super) use crate::conn::Connection; 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; pub(super) use crate::stub::core_impl::HandlerStatus; pub(super) use crate::stub::error::InternalError as Error; pub(super) use crate::target::Target; } mod auxv; mod base; mod breakpoints; mod catch_syscalls; mod exec_file; mod extended_mode; mod flash; mod host_io; mod host_process_info; mod libraries; mod lldb_register_info; mod memory_map; mod monitor_cmd; mod no_ack_mode; mod resume; mod reverse_exec; mod section_offsets; mod single_register_access; mod target_xml; mod thread_extra_info; mod tracepoints; mod wasm; mod x_lowcase_packet; mod x_upcase_packet; pub(crate) use resume::FinishExecStatus; pub(crate) mod target_result_ext { use crate::stub::error::InternalError; use crate::target::TargetError; /// Extension trait to ease working with `TargetResult` in the GdbStub /// implementation. pub(super) trait TargetResultExt { /// Encapsulates the boilerplate associated with handling /// `TargetError`s, such as bailing-out on Fatal errors, or /// returning response codes. fn handle_error(self) -> Result>; } impl TargetResultExt for Result> { fn handle_error(self) -> Result> { let code = match self { Ok(v) => return Ok(v), Err(TargetError::Fatal(e)) => return Err(InternalError::TargetError(e)), // Recoverable errors: // Error code 121 corresponds to `EREMOTEIO` lol Err(TargetError::NonFatal) => 121, Err(TargetError::Errno(code)) => code, #[cfg(feature = "std")] Err(TargetError::Io(e)) => e.raw_os_error().unwrap_or(121) as u8, }; Err(InternalError::NonFatalError(code)) } } } /// Describes why the GDB session ended. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum DisconnectReason { /// Target exited with given status code TargetExited(u8), /// Target terminated with given signal TargetTerminated(Signal), /// GDB issued a disconnect command Disconnect, /// GDB issued a kill command Kill, } pub enum State { Pump, DeferredStopReason, CtrlCInterrupt, Disconnect(DisconnectReason), } pub(crate) struct GdbStubImpl { _target: PhantomData, _connection: PhantomData, current_mem_tid: Tid, current_resume_tid: SpecificIdKind, features: ProtocolFeatures, } pub enum HandlerStatus { Handled, NeedsOk, DeferredStopReason, Disconnect(DisconnectReason), } impl GdbStubImpl { pub fn new() -> GdbStubImpl { GdbStubImpl { _target: PhantomData, _connection: PhantomData, // NOTE: `current_mem_tid` and `current_resume_tid` are never queried prior to being set // by the GDB client (via the 'H' packet), so it's fine to use dummy values here. // // The alternative would be to use `Option`, and while this would be more "correct", it // would introduce a _lot_ of noisy and heavy error handling logic all over the place. // // Plus, even if the GDB client is acting strangely and doesn't overwrite these values, // the target will simply return a non-fatal error, which is totally fine. current_mem_tid: SINGLE_THREAD_TID, current_resume_tid: SpecificIdKind::WithId(SINGLE_THREAD_TID), features: ProtocolFeatures::empty(), } } pub fn handle_packet( &mut self, target: &mut T, conn: &mut C, packet: Packet<'_>, ) -> Result> { match packet { Packet::Ack => Ok(State::Pump), Packet::Nack => Err(InternalError::ClientSentNack), Packet::Interrupt => { debug!("<-- interrupt packet"); Ok(State::CtrlCInterrupt) } Packet::Command(command) => { // Acknowledge the command if !self.features.no_ack_mode() { conn.write(b'+').map_err(InternalError::conn_write)?; } let mut res = ResponseWriter::new(conn, target.use_rle()); let disconnect_reason = match self.handle_command(&mut res, target, command) { Ok(HandlerStatus::Handled) => None, Ok(HandlerStatus::NeedsOk) => { res.write_str("OK")?; None } Ok(HandlerStatus::DeferredStopReason) => return Ok(State::DeferredStopReason), Ok(HandlerStatus::Disconnect(reason)) => Some(reason), // HACK: handling this "dummy" error is required as part of the // `TargetResultExt::handle_error()` machinery. Err(InternalError::NonFatalError(code)) => { res.write_str("E")?; res.write_num(code)?; None } Err(e) => return Err(e), }; // every response needs to be flushed, _except_ for the response to a kill // packet, but ONLY when extended mode is NOT implemented. let is_kill = matches!(disconnect_reason, Some(DisconnectReason::Kill)); if !(target.support_extended_mode().is_none() && is_kill) { res.flush()?; } let state = match disconnect_reason { Some(reason) => State::Disconnect(reason), None => State::Pump, }; Ok(state) } } } fn handle_command( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, cmd: Command<'_>, ) -> 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::XLowcasePacket(cmd) => self.handle_x_lowcase_packet(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) } Command::Breakpoints(cmd) => self.handle_breakpoints(res, target, cmd), Command::CatchSyscalls(cmd) => self.handle_catch_syscalls(res, target, cmd), Command::ExtendedMode(cmd) => self.handle_extended_mode(res, target, cmd), Command::MonitorCmd(cmd) => self.handle_monitor_cmd(res, target, cmd), Command::SectionOffsets(cmd) => self.handle_section_offsets(res, target, cmd), Command::ReverseCont(cmd) => self.handle_reverse_cont(res, target, cmd), Command::ReverseStep(cmd) => self.handle_reverse_step(res, target, cmd), Command::MemoryMap(cmd) => self.handle_memory_map(res, target, cmd), Command::FlashOperations(cmd) => self.handle_flash_operations(res, target, cmd), Command::HostIo(cmd) => self.handle_host_io(res, target, cmd), Command::ExecFile(cmd) => self.handle_exec_file(res, target, cmd), Command::Auxv(cmd) => self.handle_auxv(res, target, cmd), Command::ThreadExtraInfo(cmd) => self.handle_thread_extra_info(res, target, cmd), Command::LldbRegisterInfo(cmd) => self.handle_lldb_register_info(res, target, cmd), Command::LibrariesSvr4(cmd) => self.handle_libraries_svr4(res, target, cmd), Command::Libraries(cmd) => self.handle_libraries(res, target, cmd), Command::Tracepoints(cmd) => self.handle_tracepoints(res, target, cmd), Command::HostInfo(cmd) => self.handle_host_info(res, target, cmd), Command::ProcessInfo(cmd) => self.handle_process_info(res, target, cmd), Command::Wasm(cmd) => self.handle_wasm(res, target, cmd), // in the worst case, the command could not be parsed... Command::Unknown(cmd) => { // HACK: if the user accidentally sends a resume command to a // target without resume support, inform them of their mistake + // return a dummy stop reason. if target.base_ops().resume_ops().is_none() && target.use_resume_stub() { let is_resume_pkt = cmd .first() .map(|c| matches!(c, b'c' | b'C' | b's' | b'S')) .unwrap_or(false); if is_resume_pkt { warn!("attempted to resume target without resume support!"); // TODO: omit this message if non-stop mode is active { let mut res = ResponseWriter::new(res.as_conn(), target.use_rle()); res.write_str("O")?; res.write_hex_buf(b"target has not implemented `support_resume()`\n")?; res.flush()?; } res.write_str("S05")?; } } info!("Unknown command: {:?}", core::str::from_utf8(cmd)); Ok(HandlerStatus::Handled) } } } } #[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! { impl ProtocolFeatures: u8 { const NO_ACK_MODE = 1 << 0; const MULTIPROCESS = 1 << 1; } } impl ProtocolFeatures { #[inline(always)] fn no_ack_mode(&self) -> bool { self.contains(ProtocolFeatures::NO_ACK_MODE) } #[inline(always)] fn set_no_ack_mode(&mut self, val: bool) { self.set(ProtocolFeatures::NO_ACK_MODE, val) } #[inline(always)] fn multiprocess(&self) -> bool { self.contains(ProtocolFeatures::MULTIPROCESS) } #[inline(always)] fn set_multiprocess(&mut self, val: bool) { self.set(ProtocolFeatures::MULTIPROCESS, val) } } ================================================ FILE: src/stub/error.rs ================================================ use crate::protocol::PacketParseError; use crate::protocol::ResponseWriterError; use crate::util::managed_vec::CapacityError; #[cfg(feature = "core_error")] use core::error::Error as CoreError; use core::fmt::Debug; use core::fmt::Display; use core::fmt::{self}; #[cfg(all(feature = "std", not(feature = "core_error")))] use std::error::Error as CoreError; /// 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, } #[derive(Debug)] pub(crate) enum InternalError { /// Connection Error Connection(C, ConnectionErrorKind), /// Target encountered a fatal error. TargetError(T), ClientSentNack, PacketBufferOverflow, PacketParse(PacketParseError), PacketUnexpected, TargetMismatch, UnsupportedStopReason, UnexpectedStepPacket, ImplicitSwBreakpoints, // 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, TracepointFeatureUnimplemented(u8), TracepointUnsupportedSourceEnumeration, MissingMultiThreadSchedulerLocking, MissingToRawId, // Internal - A non-fatal error occurred (with errno-style error code) // // This "dummy" error is required as part of the internal // `TargetResultExt::handle_error()` machinery, and will never be // propagated up to the end user. #[doc(hidden)] NonFatalError(u8), } 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 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: Display, T: Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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, "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`"), TracepointFeatureUnimplemented(feat) => write!(f, "GDB client sent us a tracepoint packet using feature {}, but `gdbstub` doesn't implement it. If this is something you require, please file an issue at https://github.com/daniel5151/gdbstub/issues", *feat as char), TracepointUnsupportedSourceEnumeration => write!(f, "The target doesn't support the gdbstub TracepointSource extension, but attempted to transition to enumerating tracepoint sources"), MissingMultiThreadSchedulerLocking => write!(f, "GDB requested Scheduler Locking, but the Target does not implement the `MultiThreadSchedulerLocking` IDET"), MissingToRawId => write!(f, "A RegId was used with an API that requires raw register IDs to be available (e.g. `report_stop_with_regs`) but returned `None` from `to_raw_id()`"), NonFatalError(_) => write!(f, "Internal non-fatal error. You should never see this! Please file an issue if you do!"), } } } #[cfg(any(feature = "std", feature = "core_error"))] impl CoreError for GdbStubError where 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() } } ================================================ FILE: src/stub/mod.rs ================================================ //! The core [`GdbStub`] type, used to drive a GDB debugging session for a //! particular [`Target`] over a given [`Connection`]. 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; mod error; mod stop_reason; pub mod state_machine; 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 /// using the [`GdbStub::run_blocking`] method. /// /// Reminder: to use `gdbstub` in a non-blocking manner (e.g: via /// async/await, unix polling, from an interrupt handler, etc...) you will /// need to interface with the /// [`GdbStubStateMachine`](state_machine::GdbStubStateMachine) API /// directly. pub trait BlockingEventLoop { /// The Target being driven. type Target: Target; /// Connection being used to drive the target. type Connection: ConnectionExt; /// Which variant of the `StopReason` type should be used. Single /// threaded targets should use [`SingleThreadStopReason`], whereas /// multi threaded targets should use [`MultiThreadStopReason`]. /// /// [`SingleThreadStopReason`]: crate::stub::SingleThreadStopReason /// [`MultiThreadStopReason`]: crate::stub::MultiThreadStopReason type StopReason: IntoStopReason; /// Invoked immediately after the target's `resume` method has been /// called. The implementation should block until either the target /// reports a stop reason, or if new data was sent over the connection. /// /// The specific mechanism to "select" between these two events is /// implementation specific. Some examples might include: `epoll`, /// `select!` across multiple event channels, periodic polling, etc... fn wait_for_stop_reason( target: &mut Self::Target, conn: &mut Self::Connection, ) -> Result< Event, WaitForStopReasonError< ::Error, ::Error, >, >; /// Invoked when the GDB client sends a Ctrl-C interrupt. /// /// Depending on how the target is implemented, it may or may not make /// sense to immediately return a stop reason as part of handling the /// Ctrl-C interrupt. e.g: in some cases, it may be better to send the /// target a signal upon receiving a Ctrl-C interrupt _without_ /// immediately sending a stop reason, and instead deferring the stop /// reason to some later point in the target's execution. /// /// _Suggestion_: If you're unsure which stop reason to report, /// [`BaseStopReason::Signal(Signal::SIGINT)`] is a sensible default. /// /// [`BaseStopReason::Signal(Signal::SIGINT)`]: /// crate::stub::BaseStopReason::Signal fn on_interrupt( target: &mut Self::Target, ) -> Result, ::Error>; } /// Returned by the `wait_for_stop_reason` closure in /// [`GdbStub::run_blocking`] pub enum Event { /// GDB Client sent data while the target was running. IncomingData(u8), /// The target has stopped. TargetStopped(StopReason), } /// Error value returned by the `wait_for_stop_reason` closure in /// [`GdbStub::run_blocking`] pub enum WaitForStopReasonError { /// A fatal target error has occurred. Target(T), /// A fatal connection error has occurred. Connection(C), } } /// Debug a [`Target`] using the GDB Remote Serial Protocol over a given /// [`Connection`]. pub struct GdbStub<'a, T: Target, C: Connection> { conn: C, packet_buffer: ManagedSlice<'a, u8>, inner: core_impl::GdbStubImpl, } impl<'a, T: Target, C: Connection> GdbStub<'a, T, C> { /// Create a [`GdbStubBuilder`] using the provided Connection. pub fn builder(conn: C) -> GdbStubBuilder<'a, T, C> { GdbStubBuilder::new(conn) } /// Create a new `GdbStub` using the provided connection. /// /// _Note:_ `new` is only available when the `alloc` feature is enabled, as /// it will use a dynamically allocated `Vec` as a packet buffer. /// /// For fine-grained control over various `GdbStub` options, including the /// ability to specify a fixed-size buffer, use the [`GdbStub::builder`] /// method instead. #[cfg(feature = "alloc")] pub fn new(conn: C) -> GdbStub<'a, T, C> { GdbStubBuilder::new(conn).build().unwrap() } /// (Quickstart) Start a GDB remote debugging session using a blocking event /// loop. /// /// This method provides a quick and easy way to get up and running with /// `gdbstub` without directly having to immediately interface with the /// lower-level [state-machine](state_machine::GdbStubStateMachine) /// based interface. /// /// Instead, an implementation simply needs to provide a implementation of /// [`run_blocking::BlockingEventLoop`], which is a simplified set /// of methods describing how to drive the target. /// /// `GdbStub::run_blocking` returns once the GDB client closes the debugging /// session, or if the target triggers a disconnect. /// /// Note that this implementation is **blocking**, which many not be /// preferred (or suitable) in all cases. To use `gdbstub` in a non-blocking /// manner (e.g: via async/await, unix polling, from an interrupt handler, /// etc...) you will need to interface with the underlying /// [`GdbStubStateMachine`](state_machine::GdbStubStateMachine) API /// directly. pub fn run_blocking( self, target: &mut T, ) -> Result> where C: ConnectionExt, E: run_blocking::BlockingEventLoop, { let mut gdb = self.run_state_machine(target)?; loop { 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(InternalError::conn_read)?; gdb.incoming_data(target, byte)? } state_machine::GdbStubStateMachine::Disconnected(gdb) => { // run_blocking keeps things simple, and doesn't expose a way to re-use the // state machine break Ok(gdb.get_reason()); } 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(InternalError::TargetError)?; gdb.interrupt_handled(target, stop_reason)? } state_machine::GdbStubStateMachine::Running(mut gdb) => { 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()); match event { Ok(BlockingEventLoopEvent::TargetStopped(stop_reason)) => { gdb.report_stop(target, stop_reason)? } Ok(BlockingEventLoopEvent::IncomingData(byte)) => { gdb.incoming_data(target, byte)? } Err(WaitForStopReasonError::Target(e)) => { break Err(InternalError::TargetError(e).into()); } Err(WaitForStopReasonError::Connection(e)) => { break Err(InternalError::conn_read(e).into()); } } } } } } /// Starts a GDB remote debugging session, converting this instance of /// `GdbStub` into a /// [`GdbStubStateMachine`](state_machine::GdbStubStateMachine) that is /// ready to receive data. pub fn run_state_machine( mut self, target: &mut T, ) -> Result, GdbStubError> { // Check if the target hasn't explicitly opted into implicit sw breakpoints { let support_software_breakpoints = target .support_breakpoints() .map(|ops| ops.support_sw_breakpoint().is_some()) .unwrap_or(false); if !support_software_breakpoints && !target.guard_rail_implicit_sw_breakpoints() { return Err(InternalError::ImplicitSwBreakpoints.into()); } } // Perform any connection initialization { self.conn .on_session_start() .map_err(InternalError::conn_init)?; } Ok(state_machine::GdbStubStateMachineInner::from_plain_gdbstub(self).into()) } } ================================================ FILE: src/stub/state_machine.rs ================================================ //! Low-level state-machine interface that underpins [`GdbStub`]. // // TODO: write some proper documentation + examples of how to interface with // this API. //! //! # Hey, what gives? Where are all the docs!? //! //! Yep, sorry about that! //! //! `gdbstub` 0.6 turned out ot be a pretty massive release, and documenting //! everything has proven to be a somewhat gargantuan task that's kept delaying //! the release data further and further back... //! //! To avoid blocking the release any further, I've decided to leave this bit of //! the API sparsely documented. //! //! If you're interested in using this API directly (e.g: to integrate `gdbstub` //! into a `no_std` project, or to use `gdbstub` in a non-blocking manner //! alongside `async/await` / a project specific event loop), your best bet //! would be to review the following bits of code to get a feel for the API: //! //! - The implementation of [`GdbStub::run_blocking`] //! - Implementations of [`BlockingEventLoop`] used alongside //! `GdbStub::run_blocking` (e.g: the in-tree `armv4t` / `armv4t_multicore` //! examples) //! - Real-world projects using the API (see the repo's README.md) //! //! If you have any questions, feel free to open a discussion thread over at the //! [`gdbstub` GitHub repo](https://github.com/daniel5151/gdbstub/). //! //! [`BlockingEventLoop`]: super::run_blocking::BlockingEventLoop //! [`GdbStub::run_blocking`]: super::GdbStub::run_blocking 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::arch::RegId; use crate::conn::Connection; use crate::protocol::recv_packet::RecvPacketStateMachine; 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::stub::BaseStopReason; use crate::target::Target; use managed::ManagedSlice; /// State-machine interface to `GdbStub`. /// /// See the [module level documentation](self) for more details. pub enum GdbStubStateMachine<'a, T, C> where T: Target, C: Connection, { /// The target is completely stopped, and the GDB stub is waiting for /// additional input. Idle(GdbStubStateMachineInner<'a, state::Idle, T, C>), /// The target is currently running, and the GDB client is waiting for /// the target to report a stop reason. /// /// Note that the client may still send packets to the target /// (e.g: to trigger a Ctrl-C interrupt). Running(GdbStubStateMachineInner<'a, state::Running, T, C>), /// The GDB client has sent a Ctrl-C interrupt to the target. CtrlCInterrupt(GdbStubStateMachineInner<'a, state::CtrlCInterrupt, T, C>), /// The GDB client has disconnected. Disconnected(GdbStubStateMachineInner<'a, state::Disconnected, T, C>), } /// State machine typestates. /// /// The types in this module are used to parameterize instances of /// [`GdbStubStateMachineInner`], thereby enforcing that certain API methods /// can only be called while the stub is in a certain state. // As an internal implementation detail, they _also_ carry state-specific // 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 pub(crate) const MODULE_PATH: &str = concat!(module_path!(), "::"); /// Typestate corresponding to the "Idle" state. #[non_exhaustive] pub struct Idle { pub(crate) deferred_ctrlc_stop_reason: Option::Arch as Arch>::Usize>>, } /// Typestate corresponding to the "Running" state. #[non_exhaustive] pub struct Running {} /// Typestate corresponding to the "CtrlCInterrupt" state. #[non_exhaustive] pub struct CtrlCInterrupt { pub(crate) from_idle: bool, } /// Typestate corresponding to the "Disconnected" state. #[non_exhaustive] pub struct Disconnected { pub(crate) reason: DisconnectReason, } } /// Internal helper macro to convert between a particular inner state into /// its corresponding `GdbStubStateMachine` variant. macro_rules! impl_from_inner { ($state:ident $($tt:tt)*) => { impl<'a, T, C> From> for GdbStubStateMachine<'a, T, C> where T: Target, C: Connection, { fn from(inner: GdbStubStateMachineInner<'a, state::$state $($tt)*, T, C>) -> Self { GdbStubStateMachine::$state(inner) } } }; } impl_from_inner!(Idle); impl_from_inner!(Running); impl_from_inner!(CtrlCInterrupt); impl_from_inner!(Disconnected); /// Internal helper trait to cut down on boilerplate required to transition /// between states. trait Transition<'a, T, C> where T: Target, C: Connection, { /// Transition between different state machine states fn transition(self, state: S2) -> GdbStubStateMachineInner<'a, S2, T, C>; } impl<'a, S1, T, C> Transition<'a, T, C> for GdbStubStateMachineInner<'a, S1, T, C> where T: Target, C: Connection, { #[inline(always)] fn transition(self, state: S2) -> GdbStubStateMachineInner<'a, S2, T, C> { if log::log_enabled!(log::Level::Trace) { let s1 = core::any::type_name::(); let s2 = core::any::type_name::(); log::trace!( "transition: {:?} --> {:?}", s1.strip_prefix(state::MODULE_PATH).unwrap_or(s1), s2.strip_prefix(state::MODULE_PATH).unwrap_or(s2) ); } GdbStubStateMachineInner { i: self.i, state } } } // split off `GdbStubStateMachineInner`'s non state-dependant data into separate // struct for code bloat optimization (i.e: `transition` will generate better // code when the struct is cleaved this way). struct GdbStubStateMachineReallyInner<'a, T: Target, C: Connection> { conn: C, packet_buffer: ManagedSlice<'a, u8>, recv_packet: RecvPacketStateMachine, inner: GdbStubImpl, } /// Core state machine implementation that is parameterized by various /// [states](state). Can be converted back into the appropriate /// [`GdbStubStateMachine`] variant via [`Into::into`]. pub struct GdbStubStateMachineInner<'a, S, T: Target, C: Connection> { i: GdbStubStateMachineReallyInner<'a, T, C>, state: S, } /// Methods which can be called regardless of the current state. impl GdbStubStateMachineInner<'_, S, T, C> { /// Return a mutable reference to the underlying connection. pub fn borrow_conn(&mut self) -> &mut C { &mut self.i.conn } } /// Methods which can only be called from the [`GdbStubStateMachine::Idle`] /// state. impl<'a, T: Target, C: Connection> GdbStubStateMachineInner<'a, state::Idle, T, C> { /// Internal entrypoint into the state machine. pub(crate) fn from_plain_gdbstub( stub: GdbStub<'a, T, C>, ) -> GdbStubStateMachineInner<'a, state::Idle, T, C> { GdbStubStateMachineInner { i: GdbStubStateMachineReallyInner { conn: stub.conn, packet_buffer: stub.packet_buffer, recv_packet: RecvPacketStateMachine::new(), inner: stub.inner, }, state: state::Idle { deferred_ctrlc_stop_reason: None, }, } } /// Pass a byte to the GDB stub. pub fn incoming_data( mut self, target: &mut T, byte: u8, ) -> 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(InternalError::PacketParse)?; let state = self .i .inner .handle_packet(target, &mut self.i.conn, packet)?; Ok(match state { State::Pump => self.into(), State::Disconnect(reason) => self.transition(state::Disconnected { reason }).into(), State::DeferredStopReason => { match self.state.deferred_ctrlc_stop_reason { // if we were interrupted while idle, immediately report the deferred stop // reason after transitioning into the running state Some(reason) => { return self .transition(state::Running {}) .report_stop(target, reason) } // otherwise, just transition into the running state as usual None => self.transition(state::Running {}).into(), } } State::CtrlCInterrupt => self .transition(state::CtrlCInterrupt { from_idle: true }) .into(), }) } } /// Methods which can only be called from the /// [`GdbStubStateMachine::Running`] state. impl<'a, T: Target, C: Connection> GdbStubStateMachineInner<'a, state::Running, T, C> { /// Report a target stop reason back to GDB. pub fn report_stop( self, target: &mut T, reason: impl IntoStopReason, ) -> Result, GdbStubError> { self.report_stop_impl(target, reason, None) } /// Report a target stop reason back to GDB, including expedited register /// values in the stop reply T-packet. /// /// **Note:** In order to use this method, the Target's [`Arch`] /// implementation MUST implement a valid [`RegId::to_raw_id`] /// implementation. If the method is unimplemented, `gdbstub` will report an /// error. /// /// The iterator yields `(register_number, value_bytes)` pairs that are /// written as expedited registers in the T-packet. Values should be in /// target byte order (typically little-endian). /// /// This may be useful to use, rather than [`Self::report_stop`], when we /// want to provide register values immediately to, for example, avoid a /// round-trip, or work around a quirk/bug in a debugger that does not /// otherwise request new register values. /// /// [`RegId::to_raw_id`]: crate::arch::RegId::to_raw_id pub fn report_stop_with_regs( self, target: &mut T, reason: impl IntoStopReason, // FUTURE: (breaking) explore adding a `RegIdWithVal` construct, in // order to tighten up this typing even further? regs: &mut dyn Iterator::Arch as Arch>::RegId, &[u8])>, ) -> Result, GdbStubError> { self.report_stop_impl(target, reason, Some(regs)) } /// Shared implementation for the `report_stop`/`report_stop_with_regs` API. /// Takes an `Option` around the `&mut dyn Iterator` to avoid making a /// dynamic vtable dispatch in the common `report_stop` case. fn report_stop_impl( mut self, target: &mut T, reason: impl IntoStopReason, regs: Option<&mut dyn Iterator::Arch as Arch>::RegId, &[u8])>>, ) -> Result, GdbStubError> { let reason: BaseStopReason<_, _> = reason.into(); let mut res = ResponseWriter::new(&mut self.i.conn, target.use_rle()); let event = self.i.inner.finish_exec(&mut res, target, reason)?; if let Some(regs) = regs { if reason.is_t_packet() { for (reg_id, value) in regs { let reg = reg_id.to_raw_id().ok_or(InternalError::MissingToRawId)?; res.write_num(reg).map_err(InternalError::from)?; res.write_str(":").map_err(InternalError::from)?; res.write_hex_buf(value).map_err(InternalError::from)?; res.write_str(";").map_err(InternalError::from)?; } } } res.flush().map_err(InternalError::from)?; Ok(match event { FinishExecStatus::Handled => self .transition(state::Idle { deferred_ctrlc_stop_reason: None, }) .into(), FinishExecStatus::Disconnect(reason) => { self.transition(state::Disconnected { reason }).into() } }) } /// Pass a byte to the GDB stub. pub fn incoming_data( mut self, target: &mut T, byte: u8, ) -> 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(InternalError::PacketParse)?; let state = self .i .inner .handle_packet(target, &mut self.i.conn, packet)?; Ok(match state { State::Pump => self.transition(state::Running {}).into(), State::Disconnect(reason) => self.transition(state::Disconnected { reason }).into(), State::DeferredStopReason => self.transition(state::Running {}).into(), State::CtrlCInterrupt => self .transition(state::CtrlCInterrupt { from_idle: false }) .into(), }) } } /// Methods which can only be called from the /// [`GdbStubStateMachine::CtrlCInterrupt`] state. impl<'a, T: Target, C: Connection> GdbStubStateMachineInner<'a, state::CtrlCInterrupt, T, C> { /// Acknowledge the Ctrl-C interrupt. /// /// Passing `None` as a stop reason will return the state machine to /// whatever state it was in pre-interruption, without immediately returning /// a stop reason. /// /// Depending on how the target is implemented, it may or may not make sense /// to immediately return a stop reason as part of handling the Ctrl-C /// interrupt. e.g: in some cases, it may be better to send the target a /// signal upon receiving a Ctrl-C interrupt _without_ immediately sending a /// stop reason, and instead deferring the stop reason to some later point /// in the target's execution. /// /// Some notes on handling Ctrl-C interrupts: /// /// - Stubs are not required to recognize these interrupt mechanisms, and /// the precise meaning associated with receipt of the interrupt is /// implementation defined. /// - If the target supports debugging of multiple threads and/or processes, /// it should attempt to interrupt all currently-executing threads and /// processes. /// - If the stub is successful at interrupting the running program, it /// should send one of the stop reply packets (see Stop Reply Packets) to /// GDB as a result of successfully stopping the program pub fn interrupt_handled( self, target: &mut T, stop_reason: Option>, ) -> Result, GdbStubError> { if self.state.from_idle { // target is stopped - we cannot report the stop reason yet Ok(self .transition(state::Idle { deferred_ctrlc_stop_reason: stop_reason.map(Into::into), }) .into()) } else { // target is running - we can immediately report the stop reason let gdb = self.transition(state::Running {}); match stop_reason { Some(reason) => gdb.report_stop(target, reason), None => Ok(gdb.into()), } } } } /// Methods which can only be called from the /// [`GdbStubStateMachine::Disconnected`] state. impl<'a, T: Target, C: Connection> GdbStubStateMachineInner<'a, state::Disconnected, T, C> { /// Inspect why the GDB client disconnected. pub fn get_reason(&self) -> DisconnectReason { self.state.reason } /// Reuse the existing state machine instance, reentering the idle loop. pub fn return_to_idle(self) -> GdbStubStateMachine<'a, T, C> { self.transition(state::Idle { deferred_ctrlc_stop_reason: None, }) .into() } } ================================================ FILE: src/stub/stop_reason.rs ================================================ //! Stop reasons reported back to the GDB client. use crate::arch::Arch; use crate::common::Signal; use crate::common::Tid; use crate::target::ext::base::reverse_exec::ReplayLogPosition; use crate::target::ext::breakpoints::WatchKind; use crate::target::ext::catch_syscalls::CatchSyscallPosition; use crate::target::Target; /// Describes why a thread stopped. /// /// Single threaded targets should set `Tid` to `()`, whereas multi threaded /// targets should set `Tid` to [`Tid`]. To make things easier, it is /// recommended to use the [`SingleThreadStopReason`] and /// [`MultiThreadStopReason`] when possible. /// /// /// /// Targets MUST only respond with stop reasons that correspond to IDETs that /// target has implemented. Not doing so will result in a runtime error. /// /// e.g: A target which has not implemented the [`HwBreakpoint`] IDET must not /// return a `HwBreak` stop reason. While this is not enforced at compile time, /// doing so will result in a runtime `UnsupportedStopReason` error. /// /// [`HwBreakpoint`]: crate::target::ext::breakpoints::HwBreakpoint #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[non_exhaustive] pub enum BaseStopReason { /// Completed the single-step request. DoneStep, /// The process exited with the specified exit status. Exited(u8), /// The process terminated with the specified signal number. Terminated(Signal), /// The program received a signal. Signal(Signal), /// A specific thread received a signal. SignalWithThread { /// Tid of the associated thread tid: Tid, /// The signal signal: Signal, }, /// A thread hit a software breakpoint (e.g. due to a trap instruction). /// /// Requires: [`SwBreakpoint`]. /// /// NOTE: This does not necessarily have to be a breakpoint configured by /// the client/user of the current GDB session. /// /// [`SwBreakpoint`]: crate::target::ext::breakpoints::SwBreakpoint SwBreak(Tid), /// A thread hit a hardware breakpoint. /// /// Requires: [`HwBreakpoint`]. /// /// [`HwBreakpoint`]: crate::target::ext::breakpoints::HwBreakpoint HwBreak(Tid), /// A thread hit a watchpoint. /// /// Requires: [`HwWatchpoint`]. /// /// [`HwWatchpoint`]: crate::target::ext::breakpoints::HwWatchpoint Watch { /// Tid of the associated thread tid: Tid, /// Kind of watchpoint that was hit kind: WatchKind, /// Address of watched memory addr: U, }, /// The program has reached the end of the logged replay events. /// /// Requires: [`ReverseCont`] or [`ReverseStep`]. /// /// This is used for GDB's reverse execution. When playing back a recording, /// you may hit the end of the buffer of recorded events, and as such no /// further execution can be done. This stop reason tells GDB that this has /// occurred. /// /// [`ReverseCont`]: crate::target::ext::base::reverse_exec::ReverseCont /// [`ReverseStep`]: crate::target::ext::base::reverse_exec::ReverseStep ReplayLog { /// (optional) Tid of the associated thread. tid: Option, /// The point reached in a replay log (i.e: beginning vs. end). pos: ReplayLogPosition, }, /// The program has reached a syscall entry or return location. /// /// Requires: [`CatchSyscalls`]. /// /// [`CatchSyscalls`]: crate::target::ext::catch_syscalls::CatchSyscalls CatchSyscall { /// (optional) Tid of the associated thread. tid: Option, /// The syscall number. number: U, /// The location the event occurred at. position: CatchSyscallPosition, }, /// A thread hit a specific library event. /// /// This stop reason indicates that loaded libraries have changed. The /// debugger should fetch a new list of loaded libraries. Library(Tid), /// A thread created a new process via fork. /// /// This indicates that a fork system call was executed, creating a new /// child process. Fork { /// Tid of the thread that called fork cur_tid: Tid, /// Tid of the new child process new_tid: core::num::NonZeroUsize, }, /// A thread created a new process via vfork. /// /// Similar to Fork, but the parent process is suspended until the child /// calls exec or exits, as the parent and child temporarily share the /// same address space. VFork { /// Tid of the thread that called vfork cur_tid: Tid, /// Tid of the new child process new_tid: core::num::NonZeroUsize, }, /// A vfork child process has completed its operation. /// /// This indicates that a child process created by vfork has either called /// exec or terminated, so the address spaces of parent and child are no /// longer shared. VForkDone(Tid), } impl BaseStopReason { /// Does this stop reason respond with a `T` packet? pub(crate) fn is_t_packet(&self) -> bool { match self { Self::SignalWithThread { .. } | Self::SwBreak(_) | Self::HwBreak(_) | Self::Watch { .. } | Self::ReplayLog { .. } | Self::CatchSyscall { .. } | Self::Library(_) | Self::Fork { .. } | Self::VFork { .. } | Self::VForkDone(_) => true, Self::DoneStep | Self::Signal(_) | Self::Exited(_) | Self::Terminated(_) => false, } } } /// A stop reason for a single threaded target. /// /// Threads are identified using the unit type `()` (as there is only a single /// possible thread-id). pub type SingleThreadStopReason = BaseStopReason<(), U>; /// A stop reason for a multi threaded target. /// /// Threads are identified using a [`Tid`]. pub type MultiThreadStopReason = BaseStopReason; impl From> for BaseStopReason { fn from(st_stop_reason: BaseStopReason<(), U>) -> BaseStopReason { match st_stop_reason { BaseStopReason::DoneStep => BaseStopReason::DoneStep, BaseStopReason::Exited(code) => BaseStopReason::Exited(code), BaseStopReason::Terminated(sig) => BaseStopReason::Terminated(sig), BaseStopReason::SignalWithThread { signal, .. } => BaseStopReason::SignalWithThread { tid: crate::SINGLE_THREAD_TID, signal, }, BaseStopReason::SwBreak(_) => BaseStopReason::SwBreak(crate::SINGLE_THREAD_TID), BaseStopReason::HwBreak(_) => BaseStopReason::HwBreak(crate::SINGLE_THREAD_TID), BaseStopReason::Watch { kind, addr, .. } => BaseStopReason::Watch { tid: crate::SINGLE_THREAD_TID, kind, addr, }, BaseStopReason::Signal(sig) => BaseStopReason::Signal(sig), BaseStopReason::ReplayLog { pos, .. } => BaseStopReason::ReplayLog { tid: None, pos }, BaseStopReason::CatchSyscall { number, position, .. } => BaseStopReason::CatchSyscall { tid: None, number, position, }, BaseStopReason::Library(_) => BaseStopReason::Library(crate::SINGLE_THREAD_TID), BaseStopReason::Fork { new_tid, .. } => BaseStopReason::Fork { cur_tid: crate::SINGLE_THREAD_TID, new_tid, }, BaseStopReason::VFork { new_tid, .. } => BaseStopReason::VFork { cur_tid: crate::SINGLE_THREAD_TID, new_tid, }, BaseStopReason::VForkDone(_) => BaseStopReason::VForkDone(crate::SINGLE_THREAD_TID), } } } mod private { pub trait Sealed {} impl Sealed for super::SingleThreadStopReason {} impl Sealed for super::MultiThreadStopReason {} } /// A marker trait implemented by [`SingleThreadStopReason`] and /// [`MultiThreadStopReason`]. pub trait IntoStopReason: private::Sealed + Into::Arch as Arch>::Usize>> { } impl IntoStopReason for SingleThreadStopReason<<::Arch as Arch>::Usize> {} impl IntoStopReason for MultiThreadStopReason<<::Arch as Arch>::Usize> {} ================================================ FILE: src/target/ext/auxv.rs ================================================ //! Access the target’s auxiliary vector. use crate::target::Target; use crate::target::TargetResult; /// Target Extension - Access the target’s auxiliary vector. pub trait Auxv: Target { /// Get auxiliary vector from the target. /// /// Return the number of bytes written into `buf` (which may be less than /// `length`). /// /// If `offset` is greater than the length of the underlying data, return /// `Ok(0)`. fn get_auxv(&self, offset: u64, length: usize, buf: &mut [u8]) -> TargetResult; } define_ext!(AuxvOps, Auxv); ================================================ FILE: src/target/ext/base/mod.rs ================================================ //! Base operations required to debug most targets (e.g: read/write //! memory/registers, step/resume, etc...) //! //! It is **highly recommended** that single threaded targets implement the //! simplified `singlethread` API, as `gdbstub` includes optimized //! implementations of certain internal routines when operating in single //! threaded mode. use crate::arch::Arch; pub mod multithread; pub mod reverse_exec; pub mod single_register_access; pub mod singlethread; /// Base required operations for single/multi threaded targets. pub enum BaseOps<'a, A, E> { /// Single-threaded target SingleThread(&'a mut dyn singlethread::SingleThreadBase), /// Multi-threaded target MultiThread(&'a mut dyn multithread::MultiThreadBase), } pub(crate) enum ResumeOps<'a, A, E> { /// Single-threaded target SingleThread(&'a mut dyn singlethread::SingleThreadResume), /// Multi-threaded target MultiThread(&'a mut dyn multithread::MultiThreadResume), } impl<'a, A: Arch, E> BaseOps<'a, A, E> { #[inline(always)] pub(crate) fn resume_ops(self) -> Option> { let ret = match self { BaseOps::SingleThread(ops) => ResumeOps::SingleThread(ops.support_resume()?), BaseOps::MultiThread(ops) => ResumeOps::MultiThread(ops.support_resume()?), }; Some(ret) } } ================================================ FILE: src/target/ext/base/multithread.rs ================================================ //! Base debugging operations for multi threaded targets. use crate::arch::Arch; use crate::common::Signal; use crate::common::Tid; use crate::target::Target; use crate::target::TargetResult; /// Base required debugging operations for multi threaded targets. pub trait MultiThreadBase: Target { /// Read the target's registers. /// /// If the registers could not be accessed, an appropriate non-fatal error /// should be returned. fn read_registers( &mut self, regs: &mut ::Registers, tid: Tid, ) -> TargetResult<(), Self>; /// Write the target's registers. /// /// If the registers could not be accessed, an appropriate non-fatal error /// should be returned. fn write_registers( &mut self, regs: &::Registers, tid: Tid, ) -> TargetResult<(), Self>; /// Support for single-register access. /// See [`SingleRegisterAccess`] for more details. /// /// While this is an optional feature, it is **highly recommended** to /// implement it when possible, as it can significantly improve performance /// on certain architectures. /// /// [`SingleRegisterAccess`]: /// super::single_register_access::SingleRegisterAccess #[inline(always)] fn support_single_register_access( &mut self, ) -> Option> { None } /// Read bytes from the specified address range and return the number of /// bytes that were read. /// /// 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; /// Write bytes to the specified address range. /// /// 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. fn write_addrs( &mut self, start_addr: ::Usize, data: &[u8], tid: Tid, ) -> TargetResult<(), Self>; /// List all currently active threads. /// /// See [the section above](#bare-metal-targets) on implementing /// thread-related methods on bare-metal (threadless) targets. /// /// _Note_: Implementors should mark this method as `#[inline(always)]`, as /// this will result in better codegen (namely, by sidestepping any of the /// `dyn FnMut` closure machinery). fn list_active_threads( &mut self, thread_is_active: &mut dyn FnMut(Tid), ) -> Result<(), Self::Error>; /// Check if the specified thread is alive. /// /// As a convenience, this method provides a default implementation which /// uses `list_active_threads` to do a linear-search through all active /// threads. On thread-heavy systems, it may be more efficient /// to override this method with a more direct query. #[allow(clippy::wrong_self_convention)] // requires breaking change to fix fn is_thread_alive(&mut self, tid: Tid) -> Result { let mut found = false; self.list_active_threads(&mut |active_tid| { if tid == active_tid { found = true; } })?; Ok(found) } /// Support for resuming the target (e.g: via `continue` or `step`) #[inline(always)] fn support_resume(&mut self) -> Option> { None } /// Support for providing thread extra information. #[inline(always)] fn support_thread_extra_info( &mut self, ) -> Option> { None } } /// Target extension - support for resuming multi threaded targets. pub trait MultiThreadResume: Target { /// Resume execution on the target. /// /// Prior to calling `resume`, `gdbstub` will call `clear_resume_actions`, /// followed by zero or more calls to the `set_resume_action_XXX` methods, /// specifying any thread-specific resume actions. /// /// Upon returning from the `resume` method, the target being debugged /// should be configured to run according to whatever resume actions the /// GDB client had specified using any of the `set_resume_action_XXX` /// methods. /// /// # Default Resume Behavior /// /// By default, any thread that wasn't explicitly resumed by a /// `set_resume_action_XXX` method should be resumed as though it was /// resumed with `set_resume_action_continue`. /// /// **However**, if [`support_scheduler_locking`] is implemented and /// [`set_resume_action_scheduler_lock`] has been called for the current /// resume cycle, this default changes: **unmentioned threads must remain /// stopped.** /// /// [`support_scheduler_locking`]: Self::support_scheduler_locking /// [`set_resume_action_scheduler_lock`]: MultiThreadSchedulerLocking::set_resume_action_scheduler_lock /// /// # Protocol Extensions /// /// A basic target implementation only needs to implement support for /// `set_resume_action_continue`, with all other resume actions requiring /// their corresponding protocol extension to be implemented: /// /// Action | Protocol Extension /// ----------------------------|------------------------------ /// Optimized [Single Stepping] | See [`support_single_step()`] /// Optimized [Range Stepping] | See [`support_range_step()`] /// [Scheduler Locking] | See [`support_scheduler_locking()`] /// "Stop" | Used in "Non-Stop" mode \* /// /// \* "Non-Stop" mode is currently unimplemented in `gdbstub` /// /// [Single stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#index-stepi /// [Range Stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#range-stepping /// [Scheduler Locking]: https://sourceware.org/gdb/current/onlinedocs/gdb#index-scheduler-locking-mode /// [`support_single_step()`]: Self::support_single_step /// [`support_range_step()`]: Self::support_range_step /// [`support_scheduler_locking()`]: Self::support_scheduler_locking /// /// # Additional Considerations /// /// ### Adjusting PC after a breakpoint is hit /// /// The [GDB remote serial protocol documentation](https://sourceware.org/gdb/current/onlinedocs/gdb/Stop-Reply-Packets.html#swbreak-stop-reason) /// notes the following: /// /// > On some architectures, such as x86, at the architecture level, when a /// > breakpoint instruction executes the program counter points at the /// > breakpoint address plus an offset. On such targets, the stub is /// > responsible for adjusting the PC to point back at the breakpoint /// > address. /// /// Omitting PC adjustment may result in unexpected execution flow and/or /// breakpoints not appearing to work correctly. /// /// ### Bare-Metal Targets /// /// On bare-metal targets (such as microcontrollers or emulators), it's /// common to treat individual _CPU cores_ as a separate "threads". e.g: /// in a dual-core system, [CPU0, CPU1] might be mapped to [TID1, TID2] /// (note that TIDs cannot be zero). /// /// In this case, the `Tid` argument of `read/write_addrs` becomes quite /// relevant, as different cores may have different memory maps. fn resume(&mut self) -> Result<(), Self::Error>; /// Clear all previously set resume actions. fn clear_resume_actions(&mut self) -> Result<(), Self::Error>; /// Continue the specified thread. /// /// See the [`resume`](Self::resume) docs for information on when this is /// called. /// /// The GDB client may also include a `signal` which should be passed to the /// target. fn set_resume_action_continue( &mut self, tid: Tid, signal: Option, ) -> Result<(), Self::Error>; /// Support for optimized [single stepping]. /// /// [single stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#index-stepi #[inline(always)] fn support_single_step(&mut self) -> Option> { None } /// Support for optimized [range stepping]. /// /// [range stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#range-stepping #[inline(always)] fn support_range_step(&mut self) -> Option> { None } /// Support for [reverse stepping] a target. /// /// [reverse stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Reverse-Execution.html #[inline(always)] fn support_reverse_step( &mut self, ) -> Option> { None } /// Support for [reverse continuing] a target. /// /// [reverse continuing]: https://sourceware.org/gdb/current/onlinedocs/gdb/Reverse-Execution.html #[inline(always)] fn support_reverse_cont( &mut self, ) -> Option> { None } /// Support for [scheduler locking]. /// /// [scheduler locking]: https://sourceware.org/gdb/current/onlinedocs/gdb#index-scheduler-locking-mode #[inline(always)] fn support_scheduler_locking(&mut self) -> Option> { None } } define_ext!(MultiThreadResumeOps, MultiThreadResume); /// Target Extension - Optimized single stepping for multi threaded targets. /// See [`MultiThreadResume::support_single_step`]. pub trait MultiThreadSingleStep: Target + MultiThreadResume { /// [Single step] the specified target thread. /// /// Single stepping will step the target a single "step" - typically a /// single instruction. /// /// The GDB client may also include a `signal` which should be passed to the /// target. /// /// If your target does not support signals (e.g: the target is a bare-metal /// microcontroller / emulator), the recommended behavior is to return a /// target-specific fatal error /// /// [Single step]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#index-stepi fn set_resume_action_step( &mut self, tid: Tid, signal: Option, ) -> Result<(), Self::Error>; } define_ext!(MultiThreadSingleStepOps, MultiThreadSingleStep); /// Target Extension - Optimized range stepping for multi threaded targets. /// See [`MultiThreadResume::support_range_step`]. pub trait MultiThreadRangeStepping: Target + MultiThreadResume { /// [Range step] the specified target thread. /// /// Range Stepping will step the target once, and keep stepping the target /// as long as execution remains between the specified start (inclusive) /// and end (exclusive) addresses, or another stop condition is met /// (e.g: a breakpoint it hit). /// /// If the range is empty (`start` == `end`), then the action becomes /// equivalent to the ‘s’ action. In other words, single-step once, and /// report the stop (even if the stepped instruction jumps to start). /// /// _Note:_ A stop reply may be sent at any point even if the PC is still /// within the stepping range; for example, it is valid to implement range /// stepping in a degenerate way as a single instruction step operation. /// /// [Range step]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#range-stepping fn set_resume_action_range_step( &mut self, tid: Tid, start: ::Usize, end: ::Usize, ) -> Result<(), Self::Error>; } define_ext!(MultiThreadRangeSteppingOps, MultiThreadRangeStepping); /// Target Extension - support for GDB's "Scheduler Locking" mode. /// See [`MultiThreadResume::support_scheduler_locking`]. pub trait MultiThreadSchedulerLocking: Target + MultiThreadResume { /// Configure the target to enable "Scheduler Locking" for the upcoming /// resume. /// /// This method is invoked when the GDB client expects only a specific set /// of threads to run, while all other threads remain frozen. This behavior /// is typically toggled in GDB using the `set scheduler-locking on` /// command. /// /// When this method is called, the implementation must ensure that any /// threads not explicitly resumed via previous `set_resume_action_...` /// calls **remain stopped**. /// /// This prevents any "implicit continue" behavior for unmentioned threads, /// satisfying GDB's expectation that only the designated threads will /// advance during the next resume. fn set_resume_action_scheduler_lock(&mut self) -> Result<(), Self::Error>; } define_ext!(MultiThreadSchedulerLockingOps, MultiThreadSchedulerLocking); ================================================ FILE: src/target/ext/base/reverse_exec.rs ================================================ //! Support for reverse debugging targets. use crate::target::Target; /// Target Extension - Reverse continue for targets. pub trait ReverseCont: Target where Tid: crate::is_valid_tid::IsValidTid, { /// [Reverse continue] the target. /// /// Reverse continue allows the target to run backwards until it reaches the /// end of the replay log. /// /// [Reverse continue]: https://sourceware.org/gdb/current/onlinedocs/gdb/Reverse-Execution.html fn reverse_cont(&mut self) -> Result<(), Self::Error>; } /// See [`ReverseCont`] pub type ReverseContOps<'a, Tid, T> = &'a mut dyn ReverseCont::Arch, Error = ::Error>; /// Target Extension - Reverse stepping for targets. pub trait ReverseStep: Target where Tid: crate::is_valid_tid::IsValidTid, { /// [Reverse step] the specified `Tid`. /// /// On single threaded targets, `tid` is set to `()` and can be ignored. /// /// Reverse stepping allows the target to run backwards by one "step" - /// typically a single instruction. /// /// [Reverse step]: https://sourceware.org/gdb/current/onlinedocs/gdb/Reverse-Execution.html fn reverse_step(&mut self, tid: Tid) -> Result<(), Self::Error>; } /// See [`ReverseStep`] pub type ReverseStepOps<'a, Tid, T> = &'a mut dyn ReverseStep::Arch, Error = ::Error>; /// Describes the point reached in a replay log (used alongside /// [`BaseStopReason::ReplayLog`](crate::stub::BaseStopReason::ReplayLog)) #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ReplayLogPosition { /// Reached the beginning of the replay log. Begin, /// Reached the end of the replay log. End, } ================================================ FILE: src/target/ext/base/single_register_access.rs ================================================ //! Support for single-register read/write access. use crate::arch::Arch; use crate::target::Target; use crate::target::TargetResult; /// Target Extension - Support for single-register access. /// /// While this is an optional feature, it is **highly recommended** to /// implement it when possible, as it can significantly improve performance /// on certain architectures. /// /// If this extension is not implemented, the GDB client will fall-back to /// accessing _all_ registers, even in cases where it only requires knowing a /// single register's value. /// /// Moreover, certain architectures have registers that are not accessible as /// part of the default default register file used by the `read/write_registers` /// methods, and can only be accessed via this extension (e.g: the RISC-V /// Control and Status registers). pub trait SingleRegisterAccess: Target where Tid: crate::is_valid_tid::IsValidTid, { /// Read to a single register on the target. /// /// The `tid` field identifies which thread the value should be read from. /// On single threaded targets, `tid` is set to `()` and can be ignored. /// /// Implementations should write the value of the register using target's /// native byte order in the buffer `buf`. /// /// Return the number of bytes written into `buf` or `0` if the register is /// valid but unavailable. /// /// If the requested register could not be accessed, an appropriate /// non-fatal error should be returned. fn read_register( &mut self, tid: Tid, reg_id: ::RegId, buf: &mut [u8], ) -> TargetResult; /// Write from a single register on the target. /// /// The `tid` field identifies which thread the value should be written to. /// On single threaded targets, `tid` is set to `()` and can be ignored. /// /// The `val` buffer contains the new value of the register in the target's /// native byte order. It is guaranteed to be the exact length as the target /// register. /// /// If the requested register could not be accessed, an appropriate /// non-fatal error should be returned. fn write_register( &mut self, tid: Tid, reg_id: ::RegId, val: &[u8], ) -> TargetResult<(), Self>; } /// See [`SingleRegisterAccess`] pub type SingleRegisterAccessOps<'a, Tid, T> = &'a mut dyn SingleRegisterAccess::Arch, Error = ::Error>; ================================================ FILE: src/target/ext/base/singlethread.rs ================================================ //! Base debugging operations for single threaded targets. use crate::arch::Arch; use crate::common::Signal; use crate::target::Target; use crate::target::TargetResult; /// Base required debugging operations for single threaded targets. pub trait SingleThreadBase: Target { /// Read the target's registers. fn read_registers( &mut self, regs: &mut ::Registers, ) -> TargetResult<(), Self>; /// Write the target's registers. fn write_registers(&mut self, regs: &::Registers) -> TargetResult<(), Self>; /// Support for single-register access. /// See [`SingleRegisterAccess`] for more details. /// /// While this is an optional feature, it is **highly recommended** to /// implement it when possible, as it can significantly improve performance /// on certain architectures. /// /// [`SingleRegisterAccess`]: /// super::single_register_access::SingleRegisterAccess #[inline(always)] fn support_single_register_access( &mut self, ) -> Option> { None } /// Read bytes from the specified address range and return the number of /// bytes that were read. /// /// 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; /// Write bytes to the specified address range. /// /// 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. fn write_addrs( &mut self, start_addr: ::Usize, data: &[u8], ) -> TargetResult<(), Self>; /// Support for resuming the target (e.g: via `continue` or `step`) #[inline(always)] fn support_resume(&mut self) -> Option> { None } } /// Target extension - support for resuming single threaded targets. pub trait SingleThreadResume: Target { /// Resume execution on the target. /// /// The GDB client may also include a `signal` which should be passed to the /// target. /// /// # Additional Considerations /// /// ### Adjusting PC after a breakpoint is hit /// /// The [GDB remote serial protocol documentation](https://sourceware.org/gdb/current/onlinedocs/gdb/Stop-Reply-Packets.html#swbreak-stop-reason) /// notes the following: /// /// > On some architectures, such as x86, at the architecture level, when a /// > breakpoint instruction executes the program counter points at the /// > breakpoint address plus an offset. On such targets, the stub is /// > responsible for adjusting the PC to point back at the breakpoint /// > address. /// /// Omitting PC adjustment may result in unexpected execution flow and/or /// breakpoints not appearing to work correctly. fn resume(&mut self, signal: Option) -> Result<(), Self::Error>; /// Support for optimized [single stepping]. /// /// [single stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#index-stepi #[inline(always)] fn support_single_step(&mut self) -> Option> { None } /// Support for optimized [range stepping]. /// /// [range stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#range-stepping #[inline(always)] fn support_range_step(&mut self) -> Option> { None } /// Support for [reverse stepping] a target. /// /// [reverse stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Reverse-Execution.html #[inline(always)] fn support_reverse_step( &mut self, ) -> Option> { None } /// Support for [reverse continuing] a target. /// /// [reverse continuing]: https://sourceware.org/gdb/current/onlinedocs/gdb/Reverse-Execution.html #[inline(always)] fn support_reverse_cont( &mut self, ) -> Option> { None } } define_ext!(SingleThreadResumeOps, SingleThreadResume); /// Target Extension - Optimized single stepping for single threaded targets. /// See [`SingleThreadResume::support_single_step`]. pub trait SingleThreadSingleStep: Target + SingleThreadResume { /// [Single step] the target. /// /// Single stepping will step the target a single "step" - typically a /// single instruction. /// The GDB client may also include a `signal` which should be passed to the /// target. /// /// [Single step]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#index-stepi fn step(&mut self, signal: Option) -> Result<(), Self::Error>; } define_ext!(SingleThreadSingleStepOps, SingleThreadSingleStep); /// Target Extension - Optimized range stepping for single threaded targets. /// See [`SingleThreadResume::support_range_step`]. pub trait SingleThreadRangeStepping: Target + SingleThreadResume { /// [Range step] the target. /// /// Range Stepping will step the target once, and keep stepping the target /// as long as execution remains between the specified start (inclusive) /// and end (exclusive) addresses, or another stop condition is met /// (e.g: a breakpoint it hit). /// /// If the range is empty (`start` == `end`), then the action becomes /// equivalent to the ‘s’ action. In other words, single-step once, and /// report the stop (even if the stepped instruction jumps to start). /// /// _Note:_ A stop reply may be sent at any point even if the PC is still /// within the stepping range; for example, it is valid to implement range /// stepping in a degenerate way as a single instruction step operation. /// /// [Range step]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#range-stepping fn resume_range_step( &mut self, start: ::Usize, end: ::Usize, ) -> Result<(), Self::Error>; } define_ext!(SingleThreadRangeSteppingOps, SingleThreadRangeStepping); ================================================ FILE: src/target/ext/breakpoints.rs ================================================ //! Add/Remove various kinds of breakpoints. use crate::arch::Arch; use crate::target::Target; use crate::target::TargetResult; /// Target Extension - Set/Remove Breakpoints. pub trait Breakpoints: Target { /// Support for setting / removing software breakpoints. #[inline(always)] fn support_sw_breakpoint(&mut self) -> Option> { None } /// Support for setting / removing hardware breakpoints. #[inline(always)] fn support_hw_breakpoint(&mut self) -> Option> { None } /// Support for setting / removing hardware watchpoints. #[inline(always)] fn support_hw_watchpoint(&mut self) -> Option> { None } } define_ext!(BreakpointsOps, Breakpoints); /// Nested Target Extension - Set/Remove Software Breakpoints. /// /// See [this stackoverflow discussion](https://stackoverflow.com/questions/8878716/what-is-the-difference-between-hardware-and-software-breakpoints) /// about the differences between hardware and software breakpoints. /// /// _Recommendation:_ If you're implementing `Target` for an emulator that's /// using an _interpreted_ CPU (as opposed to a JIT), the simplest way to /// implement "software" breakpoints would be to check the `PC` value after each /// CPU cycle, ignoring the specified breakpoint `kind` entirely. pub trait SwBreakpoint: Target + Breakpoints { /// Add a new software breakpoint. /// /// Return `Ok(false)` if the operation could not be completed. fn add_sw_breakpoint( &mut self, addr: ::Usize, kind: ::BreakpointKind, ) -> TargetResult; /// Remove an existing software breakpoint. /// /// Return `Ok(false)` if the operation could not be completed. fn remove_sw_breakpoint( &mut self, addr: ::Usize, kind: ::BreakpointKind, ) -> TargetResult; } define_ext!(SwBreakpointOps, SwBreakpoint); /// Nested Target Extension - Set/Remove Hardware Breakpoints. /// /// See [this stackoverflow discussion](https://stackoverflow.com/questions/8878716/what-is-the-difference-between-hardware-and-software-breakpoints) /// about the differences between hardware and software breakpoints. /// /// _Recommendation:_ If you're implementing `Target` for an emulator that's /// using an _interpreted_ CPU (as opposed to a JIT), there shouldn't be any /// reason to implement this extension (as software breakpoints are likely to be /// just-as-fast). pub trait HwBreakpoint: Target + Breakpoints { /// Add a new hardware breakpoint. /// /// Return `Ok(false)` if the operation could not be completed. fn add_hw_breakpoint( &mut self, addr: ::Usize, kind: ::BreakpointKind, ) -> TargetResult; /// Remove an existing hardware breakpoint. /// /// Return `Ok(false)` if the operation could not be completed. fn remove_hw_breakpoint( &mut self, addr: ::Usize, kind: ::BreakpointKind, ) -> TargetResult; } define_ext!(HwBreakpointOps, HwBreakpoint); /// The kind of watchpoint that should be set/removed. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum WatchKind { /// Fire when the memory location is written to. Write, /// Fire when the memory location is read from. Read, /// Fire when the memory location is written to and/or read from. ReadWrite, } /// Nested Target Extension - Set/Remove Hardware Watchpoints. /// /// See the [GDB documentation](https://sourceware.org/gdb/current/onlinedocs/gdb/Set-Watchpoints.html) /// regarding watchpoints for how they're supposed to work. /// /// _Note:_ If this extension isn't implemented, GDB will default to using /// _software watchpoints_, which tend to be excruciatingly slow (as hey are /// implemented by single-stepping the system, and reading the watched memory /// location after each step). pub trait HwWatchpoint: Target + Breakpoints { /// Add a new hardware watchpoint. /// The number of bytes to watch is specified by `len`. /// /// Return `Ok(false)` if the operation could not be completed. fn add_hw_watchpoint( &mut self, addr: ::Usize, len: ::Usize, kind: WatchKind, ) -> TargetResult; /// Remove an existing hardware watchpoint. /// The number of bytes to watch is specified by `len`. /// /// Return `Ok(false)` if the operation could not be completed. fn remove_hw_watchpoint( &mut self, addr: ::Usize, len: ::Usize, kind: WatchKind, ) -> TargetResult; } define_ext!(HwWatchpointOps, HwWatchpoint); ================================================ FILE: src/target/ext/catch_syscalls.rs ================================================ //! Enable or disable catching syscalls from the inferior process. use crate::arch::Arch; use crate::target::Target; use crate::target::TargetResult; /// Target Extension - Enable and disable catching syscalls from the inferior /// process. /// /// Implementing this extension allows the target to support the `catch syscall` /// GDB client command. See [GDB documentation](https://sourceware.org/gdb/onlinedocs/gdb/Set-Catchpoints.html) /// for further details. pub trait CatchSyscalls: Target { /// Enables catching syscalls from the inferior process. /// /// If `filter` is `None`, then all syscalls should be reported to GDB. If a /// filter is provided, only the syscalls listed in the filter should be /// reported to GDB. /// /// Note: filters are not combined, subsequent calls this method should /// replace any existing syscall filtering. fn enable_catch_syscalls( &mut self, filter: Option::Usize>>, ) -> TargetResult<(), Self>; /// Disables catching syscalls from the inferior process. fn disable_catch_syscalls(&mut self) -> TargetResult<(), Self>; } define_ext!(CatchSyscallsOps, CatchSyscalls); /// Describes where the syscall catchpoint was triggered at. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum CatchSyscallPosition { /// Reached the entry location of the syscall. Entry, /// Reached the return location of the syscall. Return, } /// Iterator of syscall numbers that should be reported to GDB. pub struct SyscallNumbers<'a, U> { pub(crate) inner: &'a mut dyn Iterator, } impl Iterator for SyscallNumbers<'_, U> { type Item = U; fn next(&mut self) -> Option { self.inner.next() } } ================================================ FILE: src/target/ext/exec_file.rs ================================================ //! Provide exec-file path for the target. use crate::common::Pid; use crate::target::Target; use crate::target::TargetResult; /// Target Extension - Provide current exec-file. /// /// NOTE: this extension is primarily intended to be used alongside the [`Host /// I/O Extensions`](crate::target::ext::host_io), which enables the GDB client /// to read the executable file directly from the target pub trait ExecFile: Target { /// Get full absolute path of the file that was executed to create /// process `pid` running on the remote system. /// /// If `pid` is `None`, return the filename corresponding to the /// currently executing process. /// /// Return the number of bytes written into `buf` (which may be less than /// `length`). /// /// If `offset` is greater than the length of the underlying data, return /// `Ok(0)`. fn get_exec_file( &self, pid: Option, offset: u64, length: usize, buf: &mut [u8], ) -> TargetResult; } define_ext!(ExecFileOps, ExecFile); ================================================ FILE: src/target/ext/extended_mode.rs ================================================ //! 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. use crate::common::*; use crate::target::Target; use crate::target::TargetResult; /// Returned from `ExtendedMode::kill` /// /// Retuning `ShouldTerminate::Yes` will cause the `GdbStub` to immediately /// shut down and return a `DisconnectReason::Kill`. Returning /// `ShouldTerminate::No` will keep the `GdbStub` running and listening for /// further run/attach requests. pub enum ShouldTerminate { /// Terminate GdbStub Yes, /// Don't Terminate GdbStub No, } impl ShouldTerminate { /// Convert `ShouldTerminate::Yes` into `true`, and `ShouldTerminate::No` /// into `false` pub fn into_bool(self) -> bool { match self { ShouldTerminate::Yes => true, ShouldTerminate::No => false, } } } /// Describes how the target attached to a process. pub enum AttachKind { /// It attached to an existing process. Attach, /// It spawned a new process. Run, } impl AttachKind { pub(crate) fn was_attached(self) -> bool { match self { AttachKind::Attach => true, AttachKind::Run => false, } } } /// Target Extension - Support /// [Extended Mode](https://sourceware.org/gdb/current/onlinedocs/gdb/Connecting.html) functionality. /// /// # Extended Mode for Single/Multi Threaded Targets /// /// While extended-mode is primarily intended to be implemented by targets which /// support debugging multiple processes, there's no reason why a basic /// single/multi-threaded target can't implement these extensions as well. /// /// For example, instead of "spawning" a process, the `run` command could be /// used to reset the execution state instead (e.g: resetting an emulator). pub trait ExtendedMode: Target { /// Spawn and attach to the program `filename`, passing it the provided /// `args` on its command line. /// /// The program is created in the stopped state. /// /// If no filename is provided, the stub may use a default program (e.g. the /// last program run), or a non fatal error should be returned. /// /// `filename` and `args` are not guaranteed to be valid UTF-8, and are /// passed as raw byte arrays. If the filenames/arguments could not be /// converted into an appropriate representation, a non fatal error should /// be returned. /// /// _Note:_ This method's implementation should handle any additional /// configuration options set via the various `ConfigureXXX` extensions to /// `ExtendedMode`. e.g: if the [`ConfigureEnv`](trait.ConfigureEnv.html) /// extension is implemented and enabled, this method should set the spawned /// processes' environment variables accordingly. fn run(&mut self, filename: Option<&[u8]>, args: Args<'_, '_>) -> TargetResult; /// 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). fn attach(&mut self, pid: Pid) -> TargetResult<(), Self>; /// Query if specified PID was spawned by the target (via `run`), or if the /// target attached to an existing process (via `attach`). /// /// If the PID doesn't correspond to a process the target has run or /// attached to, a non fatal error should be returned. fn query_if_attached(&mut self, pid: Pid) -> TargetResult; /// Called when the GDB client sends a Kill request. /// /// If the PID doesn't correspond to a process the target has run or /// attached to, a non fatal error should be returned. /// /// GDB may or may not specify a specific PID to kill. When no PID is /// specified, the target is free to decide what to do (e.g: kill the /// last-used pid, terminate the connection, etc...). /// /// If `ShouldTerminate::Yes` is returned, `GdbStub` will immediately stop /// and return a `DisconnectReason::Kill`. Otherwise, the connection will /// remain open, and `GdbStub` will continue listening for run/attach /// requests. fn kill(&mut self, pid: Option) -> TargetResult; /// Restart the program being debugged. /// /// The GDB docs don't do a good job describing what a "restart" operation /// entails. For reference, the official `gdbserver` seems to kill all /// inferior processes, and then re-run whatever program was provided on the /// command line (if one was provided). /// /// _Author's Note:_ Based on my current (as of Sept 2020) understanding of /// the GDB client;s source code, it seems that the "R" packet is _never_ /// sent so-long as the target implements the "vRun" packet (which /// corresponds to this trait's `run` method). As such, while `gdbstub` /// exposes this functionality, and "requires" an implementation, unless /// you're running a fairly old version of GDB, it should be fine to /// simply stub it out -- e.g: using the `unimplemented!()` macro / /// returning a fatal error. fn restart(&mut self) -> Result<(), Self::Error>; /// (optional) Invoked when GDB client switches to extended mode. /// /// The default implementation is a no-op. /// /// Target implementations can override this implementation if they need to /// perform any operations once extended mode is activated. fn on_start(&mut self) -> Result<(), Self::Error> { Ok(()) } /// Support for enabling / disabling ASLR for spawned processes. #[inline(always)] fn support_configure_aslr(&mut self) -> Option> { None } /// Support for setting / removing / resetting environment variables for /// spawned processes. #[inline(always)] fn support_configure_env(&mut self) -> Option> { None } /// Support for configuring if spawned processes should be spawned using a /// shell. #[inline(always)] fn support_configure_startup_shell(&mut self) -> Option> { None } /// Support for configuring the working directory for spawned processes. #[inline(always)] 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); /// Iterator of `args` passed to a spawned process (used in /// `ExtendedMode::run`) pub struct Args<'a, 'args> { inner: &'a mut dyn Iterator, } impl core::fmt::Debug for Args<'_, '_> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "Args {{ .. }}") } } impl<'a, 'b> Args<'a, 'b> { pub(crate) fn new(inner: &'a mut dyn Iterator) -> Args<'a, 'b> { Args { inner } } } impl<'args> Iterator for Args<'_, 'args> { type Item = &'args [u8]; fn next(&mut self) -> Option { self.inner.next() } } /// Nested Target Extension - Enable/Disable ASLR for spawned processes (for a /// more consistent debugging experience). /// /// Corresponds to GDB's [`set disable-randomization`](https://sourceware.org/gdb/onlinedocs/gdb/Starting.html) command. pub trait ConfigureAslr: ExtendedMode { /// Enable/Disable ASLR for spawned processes. fn cfg_aslr(&mut self, enabled: bool) -> TargetResult<(), Self>; } define_ext!(ConfigureAslrOps, ConfigureAslr); /// Nested Target Extension - Set/Remove/Reset the Environment variables for /// spawned processes. /// /// Corresponds to GDB's [`set environment`](https://sourceware.org/gdb/onlinedocs/gdb/Environment.html#set-environment) cmd. /// /// _Note:_ Environment variables are not guaranteed to be UTF-8, and are passed /// as raw byte arrays. If the provided keys/values could not be converted into /// an appropriate representation, a non fatal error should be returned. pub trait ConfigureEnv: ExtendedMode { /// Set an environment variable. fn set_env(&mut self, key: &[u8], val: Option<&[u8]>) -> TargetResult<(), Self>; /// Remove an environment variable. fn remove_env(&mut self, key: &[u8]) -> TargetResult<(), Self>; /// Reset all environment variables to their initial state (i.e: undo all /// previous `set/remove_env` calls). fn reset_env(&mut self) -> TargetResult<(), Self>; } define_ext!(ConfigureEnvOps, ConfigureEnv); /// Nested Target Extension - Configure if spawned processes should be spawned /// using a shell. /// /// Corresponds to GDB's [`set startup-with-shell`](https://sourceware.org/gdb/onlinedocs/gdb/Starting.html) command. pub trait ConfigureStartupShell: ExtendedMode { /// Configure if spawned processes should be spawned using a shell. /// /// On UNIX-like targets, it is possible to start the inferior using a shell /// program. This is the default behavior on both `GDB` and `gdbserver`. fn cfg_startup_with_shell(&mut self, enabled: bool) -> TargetResult<(), Self>; } define_ext!(ConfigureStartupShellOps, ConfigureStartupShell); /// Nested Target Extension - Configure the working directory for spawned /// processes. /// /// Corresponds to GDB's [`set cwd` and `cd`](https://sourceware.org/gdb/onlinedocs/gdb/Working-Directory.html) commands. pub trait ConfigureWorkingDir: ExtendedMode { /// Set the working directory for spawned processes. /// /// If no directory is provided, the stub should reset the value to it's /// original value. /// /// The path is not guaranteed to be valid UTF-8, and is passed as a raw /// byte array. If the path could not be converted into an appropriate /// representation, a non fatal error should be returned. fn cfg_working_dir(&mut self, dir: Option<&[u8]>) -> TargetResult<(), Self>; } 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); ================================================ FILE: src/target/ext/flash.rs ================================================ //! Provide flash operations on the target. use crate::arch::Arch; use crate::target::Target; use crate::target::TargetResult; /// Flash memory operations. /// It's necessary to implement this extension to support GDB `load` command. /// /// Typically, a GDB `load` command sequence starts by issuing a `flash_erase` /// command, followed by multiple `flash_write` commands (typically one for each /// loadable ELF section), and ends with a `flash_done` command. /// /// The regions containing the addresses to be flashed must be specified as /// "flash" regions in the memory map xml, returned by /// [MemoryMap::memory_map_xml][crate::target::ext::memory_map::MemoryMap::memory_map_xml]. pub trait Flash: Target { /// Erase `length` bytes of the target's flash memory starting from /// `start_addr`. /// /// GDB ensures `start_addr` and `length` are aligned to flash memory /// block boundaries as defined by the memory map xml. fn flash_erase( &mut self, start_addr: ::Usize, length: ::Usize, ) -> TargetResult<(), Self>; /// Write bytes to the target's flash memory. /// /// GDB guarantees that the memory ranges specified by `flash_write` /// commands sent before a `flash_done` do not overlap and appear in /// order of increasing addresses. /// /// See [GDB Documentation] for more details. /// /// [GDB Documentation]: https://sourceware.org/gdb/current/onlinedocs/gdb.html/Packets.html fn flash_write( &mut self, start_addr: ::Usize, data: &[u8], ) -> TargetResult<(), Self>; /// Indicate to the target that flash programming is finished. /// /// By GDB documentation, you can batch flash erase and write operations /// until this is called. fn flash_done(&mut self) -> TargetResult<(), Self>; } define_ext!(FlashOps, Flash); ================================================ FILE: src/target/ext/host_info.rs ================================================ //! (LLDB extension) Provide host information to the debugger. //! //! This allows for reporting key-value metadata, for example the target triple, //! endianness, and pointer size. //! //! This corresponds to the `qHostInfo` command in the LLDB extensions. use crate::common::Endianness; use crate::target::Target; /// A response key-value pair to a [HostInfo::host_info] query. /// /// A response consists of a list of key-value pairs, each of which is /// represented by one instance of this enum. /// /// The allowed responses are documented in the [LLDB extension documentation]. /// Not all supported responses are currently represented in this enum. If you /// need another one, please feel free to send a PR! /// /// [LLDB extension documentation]: /// https://lldb.llvm.org/resources/lldbplatformpackets.html #[derive(Clone, Copy)] #[non_exhaustive] pub enum HostInfoResponse<'a> { /// The target triple for the debuggee, as a string. Triple(&'a str), /// The target endianness. Endianness(Endianness), /// The pointer size. PointerSize(usize), } /// (LLDB extension) Target Extension - Provide host information. pub trait HostInfo: Target { /// Write a response to a host-info query. /// /// Call `write_item` with each `HostInfoResponse` you wish to send. fn host_info( &self, write_item: &mut dyn FnMut(&HostInfoResponse<'_>), ) -> Result<(), Self::Error>; } define_ext!(HostInfoOps, HostInfo); ================================================ FILE: src/target/ext/host_io.rs ================================================ //! Provide Host I/O operations for the target. 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! { impl HostIoOpenFlags: u32 { /// A read-only file. const O_RDONLY = 0x0; /// A write-only file. const O_WRONLY = 0x1; /// A read-write file. const O_RDWR = 0x2; /// Append to an existing file. const O_APPEND = 0x8; /// Create a non-existent file. const O_CREAT = 0x200; /// Truncate an existing file. const O_TRUNC = 0x400; /// Exclusive access. const O_EXCL = 0x800; /// LLDB extension: Do not block. const O_NONBLOCK = 1 << 28; /// LLDB extension: Do not follow symlinks. const O_DONT_FOLLOW_SYMLINKS = 1 << 29; /// LLDB extension: Close the file when executing a new process. const O_CLOSE_ON_EXEC = 1 << 30; /// LLDB extension: Invalid value. const O_INVALID = 1 << 31; } } /// 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! { impl HostIoOpenMode: u32 { /// A regular file. const S_IFREG = 0o100000; /// A directory. const S_IFDIR = 0o40000; /// User read permissions. const S_IRUSR = 0o400; /// User write permissions. const S_IWUSR = 0o200; /// User execute permissions. const S_IXUSR = 0o100; /// Group read permissions. const S_IRGRP = 0o40; /// Group write permissions const S_IWGRP = 0o20; /// Group execute permissions. const S_IXGRP = 0o10; /// World read permissions. const S_IROTH = 0o4; /// World write permissions const S_IWOTH = 0o2; /// World execute permissions. const S_IXOTH = 0o1; } } /// Data returned by a host fstat request. /// /// Extracted from the GDB documentation at /// [struct stat](https://sourceware.org/gdb/current/onlinedocs/gdb/struct-stat.html#struct-stat) #[derive(Debug)] pub struct HostIoStat { /// The device. pub st_dev: u32, /// The inode. pub st_ino: u32, /// Protection bits. pub st_mode: HostIoOpenMode, /// The number of hard links. pub st_nlink: u32, /// The user id of the owner. pub st_uid: u32, /// The group id of the owner. pub st_gid: u32, /// The device type, if an inode device. pub st_rdev: u32, /// The size of the file in bytes. pub st_size: u64, /// The blocksize for the filesystem. pub st_blksize: u64, /// The number of blocks allocated. pub st_blocks: u64, /// The last time the file was accessed, in seconds since the epoch. pub st_atime: u32, /// The last time the file was modified, in seconds since the epoch. pub st_mtime: u32, /// The last time the file was changed, in seconds since the epoch. pub st_ctime: u32, } /// Select the filesystem vFile operations will operate on. Used by vFile setfs /// command. #[derive(Debug)] pub enum FsKind { /// Select the filesystem as seen by the remote stub. Stub, /// Select the filesystem as seen by process pid. Pid(crate::common::Pid), } /// Errno values for Host I/O operations. /// /// Extracted from the GDB documentation at /// #[derive(Debug)] pub enum HostIoErrno { /// Operation not permitted (POSIX.1-2001). EPERM = 1, /// No such file or directory (POSIX.1-2001). /// /// Typically, this error results when a specified pathname does not exist, /// or one of the components in the directory prefix of a pathname does not /// exist, or the specified pathname is a dangling symbolic link. ENOENT = 2, /// Interrupted function call (POSIX.1-2001); see signal(7). EINTR = 4, /// Input/output error (POSIX.1-2001). EIO = 5, /// Bad file descriptor (POSIX.1-2001). EBADF = 9, /// Permission denied (POSIX.1-2001). EACCES = 13, /// Bad address (POSIX.1-2001). EFAULT = 14, /// Device or resource busy (POSIX.1-2001). EBUSY = 16, /// File exists (POSIX.1-2001). EEXIST = 17, /// No such device (POSIX.1-2001). ENODEV = 19, /// Not a directory (POSIX.1-2001). ENOTDIR = 20, /// Is a directory (POSIX.1-2001). EISDIR = 21, /// Invalid argument (POSIX.1-2001). EINVAL = 22, /// Too many open files in system (POSIX.1-2001). On Linux, this is probably /// a result of encountering the /proc/sys/fs/file-max limit (see proc(5)). ENFILE = 23, /// Too many open files (POSIX.1-2001). Commonly caused by exceeding the /// RLIMIT_NOFILE resource limit described in getrlimit(2). EMFILE = 24, /// File too large (POSIX.1-2001). EFBIG = 27, /// No space left on device (POSIX.1-2001). ENOSPC = 28, /// Invalid seek (POSIX.1-2001). ESPIPE = 29, /// Read-only filesystem (POSIX.1-2001). EROFS = 30, /// Function not implemented (POSIX.1-2001). ENOSYS = 88, /// Filename too long (POSIX.1-2001). ENAMETOOLONG = 91, /// Unknown errno - there may not be a GDB mapping for this value EUNKNOWN = 9999, } /// The error type for Host I/O operations. pub enum HostIoError { /// An operation-specific non-fatal error code. /// /// See [`HostIoErrno`] for more details. Errno(HostIoErrno), /// A target-specific fatal error. /// /// **WARNING:** Returning this error will immediately halt the target's /// 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 /// execution after resolving the error and/or setting up a post-mortem /// debugging environment. Fatal(E), } /// When the `std` feature is enabled, `HostIoError` implements /// `From`, mapping [`std::io::ErrorKind`] to the appropriate /// [`HostIoErrno`] when possible, and falling back to [`HostIoErrno::EUNKNOWN`] /// when no mapping exists. #[cfg(feature = "std")] impl From for HostIoError { fn from(e: std::io::Error) -> HostIoError { use std::io::ErrorKind::*; let errno = match e.kind() { PermissionDenied => HostIoErrno::EPERM, NotFound => HostIoErrno::ENOENT, Interrupted => HostIoErrno::EINTR, AlreadyExists => HostIoErrno::EEXIST, InvalidInput => HostIoErrno::EINVAL, _ => HostIoErrno::EUNKNOWN, }; HostIoError::Errno(errno) } } /// A specialized `Result` type for Host I/O operations. Supports reporting /// non-fatal errors back to the GDB client. /// /// See [`HostIoError`] for more details. pub type HostIoResult = Result::Error>>; /// Target Extension - Perform I/O operations on host pub trait HostIo: Target { /// Support `open` operation. #[inline(always)] fn support_open(&mut self) -> Option> { None } /// Support `close` operation. #[inline(always)] fn support_close(&mut self) -> Option> { None } /// Support `pread` operation. #[inline(always)] fn support_pread(&mut self) -> Option> { None } /// Support `pwrite` operation. #[inline(always)] fn support_pwrite(&mut self) -> Option> { None } /// Support `fstat` operation. #[inline(always)] fn support_fstat(&mut self) -> Option> { None } /// Support `unlink` operation. #[inline(always)] fn support_unlink(&mut self) -> Option> { None } /// Support `readlink` operation. #[inline(always)] fn support_readlink(&mut self) -> Option> { None } /// Support `setfs` operation. #[inline(always)] fn support_setfs(&mut self) -> Option> { None } } define_ext!(HostIoOps, HostIo); /// Nested Target Extension - Host I/O open operation. pub trait HostIoOpen: HostIo { /// Open a file at `filename` and return a file descriptor for it, or return /// [`HostIoError::Errno`] if an error occurs. /// /// `flags` are the flags used when opening the file (see /// [`HostIoOpenFlags`]), and `mode` is the mode used if the file is /// created (see [`HostIoOpenMode`]). fn open( &mut self, filename: &[u8], flags: HostIoOpenFlags, mode: HostIoOpenMode, ) -> HostIoResult; } define_ext!(HostIoOpenOps, HostIoOpen); /// Nested Target Extension - Host I/O close operation. pub trait HostIoClose: HostIo { /// Close the open file corresponding to `fd`. fn close(&mut self, fd: u32) -> HostIoResult<(), Self>; } define_ext!(HostIoCloseOps, HostIoClose); /// Nested Target Extension - Host I/O pread operation. pub trait HostIoPread: HostIo { /// Read data from the open file corresponding to `fd`. /// /// Up to `count` bytes will be read from the file, starting at `offset` /// relative to the start of the file. /// /// Return the number of bytes written into `buf` (which may be less than /// `count`). /// /// If `offset` is greater than the length of the underlying data, return /// `Ok(0)`. fn pread( &mut self, fd: u32, count: usize, offset: u64, buf: &mut [u8], ) -> HostIoResult; } define_ext!(HostIoPreadOps, HostIoPread); /// Nested Target Extension - Host I/O pwrite operation. pub trait HostIoPwrite: HostIo { /// Write `data` to the open file corresponding to `fd`. /// /// Start the write at `offset` from the start of the file. /// /// Return the number of bytes written, which may be shorter /// than the length of data, or [`HostIoError::Errno`] if an error occurred. fn pwrite( &mut self, fd: u32, offset: ::Usize, data: &[u8], ) -> HostIoResult<::Usize, Self>; } define_ext!(HostIoPwriteOps, HostIoPwrite); /// Nested Target Extension - Host I/O fstat operation. pub trait HostIoFstat: HostIo { /// Get information about the open file corresponding to `fd`. /// /// On success return a [`HostIoStat`] struct. /// Return [`HostIoError::Errno`] if an error occurs. fn fstat(&mut self, fd: u32) -> HostIoResult; } define_ext!(HostIoFstatOps, HostIoFstat); /// Nested Target Extension - Host I/O unlink operation. pub trait HostIoUnlink: HostIo { /// Delete the file at `filename` on the target. fn unlink(&mut self, filename: &[u8]) -> HostIoResult<(), Self>; } define_ext!(HostIoUnlinkOps, HostIoUnlink); /// Nested Target Extension - Host I/O readlink operation. pub trait HostIoReadlink: HostIo { /// Read value of symbolic link `filename` on the target. /// /// Return the number of bytes written into `buf`. /// /// Unlike most other Host IO handlers, if the resolved file path exceeds /// the length of the provided `buf`, the target should NOT return a /// partial response, and MUST return a `Err(HostIoErrno::ENAMETOOLONG)`. fn readlink(&mut self, filename: &[u8], buf: &mut [u8]) -> HostIoResult; } define_ext!(HostIoReadlinkOps, HostIoReadlink); /// Nested Target Extension - Host I/O setfs operation. pub trait HostIoSetfs: HostIo { /// Select the filesystem on which vFile operations with filename arguments /// will operate. This is required for GDB to be able to access files on /// remote targets where the remote stub does not share a common filesystem /// with the inferior(s). /// /// See [`FsKind`] for the meaning of `fs`. /// /// If setfs indicates success, the selected filesystem remains selected /// until the next successful setfs operation. fn setfs(&mut self, fs: FsKind) -> HostIoResult<(), Self>; } define_ext!(HostIoSetfsOps, HostIoSetfs); ================================================ FILE: src/target/ext/libraries.rs ================================================ //! Report information about the loaded shared libraries for targets where there //! are possibly multiple files to be debugged mapped into the same address //! space. use crate::target::Target; use crate::target::TargetResult; /// Target Extension - List an SVR4 (System-V/Unix) target's libraries. pub trait LibrariesSvr4: Target { /// Get library list XML for this target. /// /// See the [GDB Documentation] for a description of the format. /// /// [GDB Documentation]: https://sourceware.org/gdb/current/onlinedocs/gdb.html/Library-List-Format-for-SVR4-Targets.html /// /// Return the number of bytes written into `buf` (which may be less than /// `length`). /// /// If `offset` is greater than the length of the underlying data, return /// `Ok(0)`. fn get_libraries_svr4( &self, offset: u64, length: usize, buf: &mut [u8], ) -> TargetResult; } define_ext!(LibrariesSvr4Ops, LibrariesSvr4); /// Target Extension - List a target's libraries (Windows/generic format). /// /// This is used for targets where library offsets are maintained externally /// (e.g., Windows PE targets). Unlike SVR4 format, this uses a simpler XML /// structure with segment addresses. pub trait Libraries: Target { /// Get library list XML for this target. /// /// The expected XML format is: /// ```xml /// /// /// /// /// /// /// ``` /// /// Note that on Windows, the `address` is not the image base, but the /// address of the first section. See the [GDB Documentation] for more /// details. /// /// [GDB Documentation]: https://sourceware.org/gdb/current/onlinedocs/gdb.html/Library-List-Format.html /// /// Return the number of bytes written into `buf` (which may be less than /// `length`). /// /// If `offset` is greater than the length of the underlying data, return /// `Ok(0)`. fn get_libraries( &self, offset: u64, length: usize, buf: &mut [u8], ) -> TargetResult; } define_ext!(LibrariesOps, Libraries); ================================================ FILE: src/target/ext/lldb_register_info_override.rs ================================================ //! (LLDB extension) Override the register info specified by `Target::Arch`. use crate::arch::lldb::Register; use crate::target::Target; /// This type serves as a "proof of callback", ensuring that either /// `reg_info.done()` or `reg_info.write()` have been called from within the /// `register_info` function. The only way to obtain a valid instance of this /// type is by invoking one of those two methods. pub struct CallbackToken<'a>(pub(crate) core::marker::PhantomData<&'a *mut ()>); /// `register_info` callbacks pub struct Callback<'a> { pub(crate) cb: &'a mut dyn FnMut(Option>), pub(crate) token: CallbackToken<'a>, } impl<'a> Callback<'a> { /// The `qRegisterInfo` query shall be concluded. #[inline(always)] pub fn done(self) -> CallbackToken<'a> { (self.cb)(None); self.token } /// Write the register info of a single register. #[inline(always)] pub fn write(self, reg: Register<'_>) -> CallbackToken<'a> { (self.cb)(Some(reg)); self.token } } /// Target Extension - Override the target register info specified by /// `Target::Arch`. /// /// _Note:_ Unless you're working with a particularly dynamic, /// 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`] 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, reg_info: Callback<'a>, ) -> Result, Self::Error>; } define_ext!(LldbRegisterInfoOverrideOps, LldbRegisterInfoOverride); ================================================ FILE: src/target/ext/memory_map.rs ================================================ //! Provide a memory map for the target. use crate::target::Target; use crate::target::TargetResult; /// Target Extension - Read the target's memory map. pub trait MemoryMap: Target { /// Get memory map XML file from the target. /// /// See the [GDB Documentation] for a description of the format. /// /// [GDB Documentation]: https://sourceware.org/gdb/onlinedocs/gdb/Memory-Map-Format.html /// /// Return the number of bytes written into `buf` (which may be less than /// `length`). /// /// If `offset` is greater than the length of the underlying data, return /// `Ok(0)`. fn memory_map_xml( &self, offset: u64, length: usize, buf: &mut [u8], ) -> TargetResult; } define_ext!(MemoryMapOps, MemoryMap); ================================================ FILE: src/target/ext/mod.rs ================================================ //! Extensions to [`Target`](super::Target) which add support for various //! subsets of the GDB Remote Serial Protocol. //! //! ### Note: Missing Protocol Extensions //! //! `gdbstub`'s development is guided by the needs of its contributors, with new //! features being added on an "as-needed" basis. //! //! If there's a GDB protocol extensions you're interested in that hasn't been //! implemented in `gdbstub` yet, (e.g: remote filesystem access, tracepoint //! support, etc...), consider opening an issue / filing a PR on the //! [`gdbstub` GitHub repo](https://github.com/daniel5151/gdbstub/). //! //! Check out the [GDB Remote Configuration Docs](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Configuration.html) //! for a table of GDB commands + their corresponding Remote Serial Protocol //! packets. //! //! ## How Protocol Extensions Work - Inlineable Dyn Extension Traits (IDETs) //! //! The GDB protocol is massive, and contains all sorts of optional //! functionality. In the early versions of `gdbstub`, the `Target` trait //! directly implemented a method for _every single protocol extension_. If this //! trend continued, there would've been literally _hundreds_ of associated //! methods - of which only a small subset were ever used at once! //! //! Aside from the cognitive complexity of having so many methods on a single //! trait, this approach had numerous other drawbacks as well: //! //! - Implementations that did not implement all available protocol extensions //! still had to "pay" for the unused packet parsing/handler code, resulting //! in substantial code bloat, even on `no_std` platforms. //! - `GdbStub`'s internal implementation needed to include a large number of //! _runtime_ checks to deal with incorrectly implemented `Target`s. //! - No way to enforce "mutually-dependent" trait methods at compile-time. //! - e.g: When implementing hardware breakpoint extensions, targets //! _must_ implement both the `add_breakpoint` and //! `remove_breakpoints` methods. //! - No way to enforce "mutually-exclusive" trait methods at compile-time. //! - e.g: The `resume` method for single-threaded targets has a much //! simpler API than for multi-threaded targets, but it would be //! incorrect for a target to implement both. //! //! At first blush, it seems the the solution to all these issues is obvious: //! simply tie each protocol extension to a `cargo` feature! And yes, while this //! would indeed work, there would be several serious ergonomic drawbacks: //! //! - There would be _hundreds_ of individual feature flags that would need to //! be toggled by end users. //! - It would be functionally impossible to _test_ all permutations of //! enabled/disabled cargo features. //! - A single binary would need to rely on some [non-trivial `cargo`-fu](https://github.com/rust-lang/cargo/issues/674) //! in order to have multiple `Target` implementations in a single binary. //! //! After much experimentation and iteration, `gdbstub` ended up taking a //! radically different approach to implementing and enumerating available //! features, using a technique called **Inlineable Dyn Extension Traits**. //! //! > _Author's note:_ As far as I can tell, this isn't a very well-known trick, //! > or at the very least, I've personally never encountered any library that //! > uses this sort of API. As such, I've decided to be a bit cheeky and give //! > it a name! At some point, I'm hoping to write a standalone blog post which //! > further explores this technique, comparing it to other/existing //! > approaches, and diving into details of the how the compiler optimizes this //! > sort of code. In fact, I've already got a [very rough github repo](https://github.com/daniel5151/optional-trait-methods) //! > with some of my findings. //! //! So, what are "Inlineable Dyn Extension Traits"? Well, let's break it down: //! //! - **Extension Traits** - A common [Rust convention](https://rust-lang.github.io/rfcs/0445-extension-trait-conventions.html#what-is-an-extension-trait) //! to extend the functionality of a Trait, _without_ modifying the original //! trait. //! - **Dyn** - Alludes to the use of Dynamic Dispatch via [Trait Objects](https://doc.rust-lang.org/book/ch17-02-trait-objects.html). //! - **Inlineable** - Alludes to the fact that this approach can be easily //! inlined, making it a truly zero-cost abstraction. //! //! In a nutshell, Inlineable Dyn Extension Traits (or IDETs) are an abuse of //! the Rust trait system + modern compiler optimizations to emulate zero-cost, //! runtime-enumerable optional trait methods! //! //! #### Technical overview //! //! The basic principles behind Inlineable Dyn Extension Traits are best //! explained though example: //! //! Lets say we want to add an optional protocol extension described by an //! `ProtocolExt` trait to a base `Protocol` trait. How would we do that using //! IDETs? //! //! - (library) Define a `trait ProtocolExt: Protocol { ... }` which includes //! all the methods required by the protocol extension: //! - _Note:_ Making `ProtocolExt` a subtrait of `Protocol` is not strictly //! required, but it does enable transparently using `Protocol`'s //! associated types as part of `ProtocolExt`'s method definitions. //! //! ```rust,ignore //! /// `foo` and `bar` are mutually-dependent methods. //! trait ProtocolExt: Protocol { //! fn foo(&self); //! // can use associated types in method signature! //! fn bar(&mut self) -> Result<(), Self::Error>; //! } //! ``` //! //! - (library) "Associate" the `ProtocolExt` extension trait to the original //! `Protocol` trait by adding a new `Protocol` method that "downcasts" `self` //! into a `&mut dyn ProtocolExt`. //! //! ```rust,ignore //! trait Protocol { //! // ... other methods ... //! //! // Optional extension //! #[inline(always)] //! fn support_protocol_ext(&mut self) -> Option> { //! // disabled by default //! None //! } //! //! // Mutually-exclusive extensions //! fn get_ext_a_or_b(&mut self) -> EitherOrExt; //! } //! //! // Using a typedef for readability //! type ProtocolExtOps = //! &'a mut dyn ProtocolExt::Arch, Error = ::Error>; //! //! enum EitherOrExt { //! ProtocolExtA(&'a mut dyn ProtocolExtA), //! ProtocolExtB(&'a mut dyn ProtocolExtB), //! } //! ``` //! //! - (user) Implements the `ProtocolExt` extension for their target (just like //! a normal trait). //! //! ```rust,ignore //! impl ProtocolExt for MyTarget { //! fn foo(&self) { ... } //! fn bar(&mut self) -> Result<(), Self::Error> { ... } //! } //! ``` //! //! - (user) Implements the base `Protocol` trait, overriding the //! `support_protocol_ext` method to return `Some(self)`, which will //! effectively "enable" the extension. //! //! ```rust,ignore //! impl Protocol for MyTarget { //! // Optional extension //! #[inline(always)] //! fn support_protocol_ext(&mut self) -> Option> { //! Some(self) // will not compile unless `MyTarget` also implements `ProtocolExt` //! } //! //! // Mutually-exclusive extensions //! #[inline(always)] //! fn get_ext_a_or_b(&mut self) -> EitherOrExt { //! EitherOrExt::ProtocolExtA(self) //! } //! } //! ``` //! //! > Please note the use of `#[inline(always)]` when enabling IDET methods. //! > While LLVM is usually smart enough to inline single-level IDETs (such as //! > in the example above), nested IDETs will often require a bit of "help" //! > from the `inline` directive to be correctly optimized. //! //! Now, here's where IDETs really shine: If the user didn't implement //! `ProtocolExt`, but _did_ try to enable the feature by overriding //! `support_protocol_ext` to return `Some(self)`, they'll get a compile-time //! error that looks something like this: //! //! ```text //! error[E0277]: the trait bound `MyTarget: ProtocolExt` is not satisfied //! --> path/to/implementation.rs:44:14 //! | //! 44 | Some(self) //! | ^^^^ the trait `ProtocolExt` is not implemented for `MyTarget` //! | //! = note: required for the cast to the object type `dyn ProtocolExt` //! ``` //! //! The Rust compiler is preventing you from enabling a feature you haven't //! implemented _at compile time!_ //! //! - (library) Is able to _query_ whether or not an extension is available, //! _without_ having to actually invoke any method on the target! //! //! ```rust,ignore //! fn execute_protocol(mut target: impl Target) { //! match target.support_protocol_ext() { //! Some(ops) => ops.foo(), //! None => { /* fallback when not enabled */ } //! } //! } //! ``` //! //! This is already pretty cool, but what's _even cooler_ is that if you take a //! look at the generated assembly of a monomorphized `execute_protocol` method //! (e.g: using godbolt.org), you'll find that the compiler is able to //! efficiently inline and devirtualize _all_ the calls to //! `support_protocol_ext` method, which in-turn allows the dead-code-eliminator //! to work its magic, and remove the unused branches from the generated code! //! i.e: If a target implemention didn't implement the `ProtocolExt` extension, //! then that `match` statement in `execute_protocol` would simply turn into a //! noop! //! //! If IDETs are something you're interested in, consider checking out //! [daniel5151/optional-trait-methods](https://github.com/daniel5151/optional-trait-methods) //! for some sample code that shows off the power of IDETs. It's not //! particularly polished, but it does includes code snippets which can be //! pasted into godbolt.org directly to confirm the optimizations described //! above, and a brief writeup which compares / contrasts alternatives to IDETs. //! //! Long story short: Optimizing compilers really are magic! //! //! #### Summary: The Benefits of IDETs //! //! IDETs solve the numerous issues and shortcomings that arise from the //! traditional single trait + "optional" methods approach: //! //! - **Compile-time enforcement of mutually-dependent methods** //! - By grouping mutually-dependent methods behind a single extension trait //! and marking them all as required methods, the Rust compiler is able to //! catch missing mutually-dependent methods at compile time, with no need //! for any runtime checks! //! - **Compile-time enforcement of mutually-exclusive methods** //! - By grouping mutually-exclusive methods behind two extension traits, and //! wrapping those in an `enum`, the API is able to document //! mutually-exclusive functions _at the type-level_, in-turn enabling the //! library to omit any runtime checks! //! - _Note:_ Strictly speaking, this isn't really compile time //! "enforcement", as there's nothing stopping an "adversarial" //! implementation from implementing both sets of methods, and then //! "flipping" between the two at runtime. Nonetheless, it serves as a good //! guardrail. //! - **Enforce dead-code-elimination _without_ `cargo` feature flags** //! - This is a really awesome trick: by wrapping code in an `if //! target.support_protocol_ext().is_some()` block, it's possible to //! specify _arbitrary_ blocks of code to be feature-dependent! //! - This is used to great effect in `gdbstub` to optimize-out any packet //! parsing / handler code for unimplemented protocol extensions. macro_rules! doc_comment { ($x:expr, $($tt:tt)*) => { #[doc = $x] $($tt)* }; } macro_rules! define_ext { ($extname:ident, $exttrait:ident) => { doc_comment! { concat!("See [`", stringify!($exttrait), "`](trait.", stringify!($exttrait), ".html)."), pub type $extname<'a, T> = &'a mut dyn $exttrait::Arch, Error = ::Error>; } }; } pub mod auxv; pub mod base; pub mod breakpoints; pub mod catch_syscalls; pub mod exec_file; pub mod extended_mode; pub mod flash; pub mod host_info; pub mod host_io; pub mod libraries; pub mod lldb_register_info_override; pub mod memory_map; pub mod monitor_cmd; pub mod process_info; pub mod section_offsets; pub mod target_description_xml_override; pub mod thread_extra_info; pub mod tracepoints; pub mod wasm; ================================================ FILE: src/target/ext/monitor_cmd.rs ================================================ //! Create custom target-specific debugging commands accessible via GDB's //! `monitor` command! pub use crate::output; pub use crate::outputln; pub use crate::protocol::ConsoleOutput; use crate::target::Target; /// Target Extension - Handle custom GDB `monitor` commands. pub trait MonitorCmd: Target { /// Handle custom commands sent using the `monitor` command. /// /// The GDB remote serial protocol includes a built-in mechanism to send /// arbitrary commands to the remote stub: the `monitor` command. For /// example, running `monitor dbg` from the GDB client will invoke /// `handle_monitor_cmd` with `cmd = b"dbg"`. /// /// Commands are _not_ guaranteed to be valid UTF-8, hence the use of /// `&[u8]` as opposed to `&str`. /// /// Intermediate console output can be written back to the GDB client using /// the provided `ConsoleOutput` object + the /// [`gdbstub::output!`](macro.output.html) macro. /// /// _Note:_ The maximum length of incoming commands is limited by the size /// of the packet buffer provided to the [`GdbStub`](struct.GdbStub.html). /// Specifically, commands can only be up to `(buf.len() - 10) / 2` bytes. fn handle_monitor_cmd(&mut self, cmd: &[u8], out: ConsoleOutput<'_>) -> Result<(), Self::Error>; } define_ext!(MonitorCmdOps, MonitorCmd); ================================================ FILE: src/target/ext/process_info.rs ================================================ //! (LLDB extension) Provide process information to the debugger. //! //! This allows for reporting key-value metadata, for example the current PID, //! target triple, endianness, and pointer size. //! //! This corresponds to the `qHostInfo` command in the LLDB extensions. use crate::common::Endianness; use crate::common::Pid; use crate::target::Target; /// A response key-value pair to a [ProcessInfo::process_info] query. /// /// A response consists of a list of key-value pairs, each of which is /// represented by one instance of this enum. /// /// The allowed responses are documented in the [LLDB extension documentation]. /// Not all supported responses are currently represented in this enum. If you /// need another one, please feel free to send a PR! /// /// [LLDB extension documentation]: /// https://lldb.llvm.org/resources/lldbplatformpackets.html #[derive(Clone, Copy)] #[non_exhaustive] pub enum ProcessInfoResponse<'a> { /// The current process PID. Pid(Pid), /// The target triple for the debuggee, as a string. Triple(&'a str), /// The target endianness. Endianness(Endianness), /// The pointer size. PointerSize(usize), } /// (LLDB extension) Target Extension - Provide process information. pub trait ProcessInfo: Target { /// Write the response to process-info query. /// /// Call `write_item` with each `InfoResponse` you wish to send. fn process_info( &self, write_item: &mut dyn FnMut(&ProcessInfoResponse<'_>), ) -> Result<(), Self::Error>; } define_ext!(ProcessInfoOps, ProcessInfo); ================================================ FILE: src/target/ext/section_offsets.rs ================================================ //! Get section/segment relocation offsets from the target. //! //! For some targets, sections may be relocated from their base address. As //! a result, the stub may need to tell GDB the final section addresses //! to ensure that debug symbols are resolved correctly after relocation. //! //! _Note:_ This extension corresponds to the `qOffsets` command, which is //! limited to reporting the offsets for code, data and bss, and is //! generally considered a legacy feature. //! //! For System-V architectures GDB may use the `qXfer:libraries-svr4:read` //! command to try to learn about loaded libraries and this can be implemented //! with the [`LibrariesSvr4` //! trait](crate::target::ext::libraries::LibrariesSvr4). Note that not all //! targets may query this and it may not be applicable in all situations //! either. //! //! For targets where library offsets are maintained externally (e.g. Windows) //! you should consider implementing the more flexible `qXfer:library:read`. //! See issue [#20](https://github.com/daniel5151/gdbstub/issues/20) for more //! info. //! //! For System-V architectures GDB is capable of extracting library offsets //! from memory if it knows the base address of the dynamic linker. The base //! address can be specified by either implementing this command or by including //! a `AT_BASE` entry in the response to the more modern `qXfer:auxv:read` //! command. See issue [#20](https://github.com/daniel5151/gdbstub/issues/20) //! for more info. use crate::arch::Arch; use crate::target::Target; /// Describes the offset the target loaded the image sections at, so the target /// can notify GDB that it needs to adjust the addresses of symbols. /// /// GDB supports either section offsets, or segment addresses. pub enum Offsets { /// Section offsets relative to their base addresses. Sections { /// The offset of the `.text` section. text: U, /// The offset of the `.data` section. data: U, /// The offset of the `.bss` section. /// /// _Note:_ GDB expects that `bss` is either `None` or equal to `data`. bss: Option, }, /// Absolute addresses of the first two segments. /// /// _Note:_ any extra segments will kept at fixed offsets relative to the /// last relocated segment. Segments { /// The absolute address of the first segment which conventionally /// contains program code. text_seg: U, /// The absolute address of the second segment which conventionally /// contains modifiable data. data_seg: Option, }, } /// Target Extension - Get section/segment relocation offsets from the target. /// /// Corresponds to the `qOffset` command. See the [section_offset module /// documentation](index.html). pub trait SectionOffsets: Target { /// Return the target's current section (or segment) offsets. fn get_section_offsets(&mut self) -> Result::Usize>, Self::Error>; } define_ext!(SectionOffsetsOps, SectionOffsets); ================================================ FILE: src/target/ext/target_description_xml_override.rs ================================================ //! Override the target description XML specified by `Target::Arch`. use crate::target::Target; use crate::target::TargetResult; /// Target Extension - Override the target description XML specified by /// `Target::Arch`. /// /// _Note:_ Unless you're working with a particularly dynamic, /// runtime-configurable target, it's unlikely that you'll need to implement /// this extension. pub trait TargetDescriptionXmlOverride: Target { /// Read a target's description XML file at the specified `annex`. /// /// The "root" `annex` will always be `b"target.xml"`, though advanced /// targets may choose to split `target.xml` into multiple files via the /// the `` XML tag. If the GDB client /// encounter any such tags, it will re-invoke this handler with `annex` /// specified to point to `b"other_file.xml"`. /// /// Refer to the /// [target_description_xml](crate::arch::Arch::target_description_xml) /// docs for more info. /// /// Return the number of bytes written into `buf` (which may be less than /// `length`). /// /// If `offset` is greater than the length of the underlying data, return /// `Ok(0)`. fn target_description_xml( &self, annex: &[u8], offset: u64, length: usize, buf: &mut [u8], ) -> TargetResult; } define_ext!( TargetDescriptionXmlOverrideOps, TargetDescriptionXmlOverride ); ================================================ FILE: src/target/ext/thread_extra_info.rs ================================================ //! Provide extra information for a thread use crate::common::Tid; use crate::target::Target; /// Target Extension - Provide extra information for a thread pub trait ThreadExtraInfo: Target { /// Provide extra information about a thread /// /// GDB queries for extra information for a thread as part of the /// `info threads` command. This function will be called once /// for each active thread. /// /// A string can be copied into `buf` that will then be displayed /// to the client. The string is displayed as `(value)`, such as: /// /// `Thread 1.1 (value)` /// /// Return the number of bytes written into `buf`. fn thread_extra_info(&self, tid: Tid, buf: &mut [u8]) -> Result; } define_ext!(ThreadExtraInfoOps, ThreadExtraInfo); ================================================ FILE: src/target/ext/tracepoints.rs ================================================ //! Support for //! [Tracepoint](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Tracepoints.html) //! extensions. //! //! ## Implementation Status //! //! Most fundamental tracepoint operations are supported, but there quite a few //! packets / APIs that are not yet implemented, such as: //! //! - Fast Tracepoints //! - Tracepoint Conditions //! - Trace State Variables //! //! If you are interested in extending this API to support these additional //! features, please consider opening an Issue / PR on the `gdbstub` GitHub //! repo. use crate::target::Arch; use crate::target::Target; use crate::target::TargetResult; #[cfg(feature = "alloc")] use alloc::borrow::ToOwned; use managed::ManagedSlice; /// A tracepoint, identified by a unique number. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Tracepoint(pub usize); /// A state variable, identified by a unique number. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct StateVariable(usize); /// Describes a new tracepoint. GDB may ask for the state of current /// tracepoints, which are described with this same structure. #[derive(Debug, Clone)] pub struct NewTracepoint { /// The tracepoint number pub number: Tracepoint, /// If the tracepoint is enabled or not pub enabled: bool, /// The address the tracepoint is set at. pub addr: U, /// The tracepoint's step count pub step_count: u64, /// The tracepoint's pass count. pub pass_count: u64, } /// Describes how to collect information for a trace frame when the tracepoint /// it is attached to is hit. A tracepoint may have more than one action /// attached. #[derive(Debug)] pub enum TracepointAction<'a, U> { /// Collect registers. Registers { /// A bitmask of which registers should be collected. The least /// significant bit is numberered zero. Note that the mask may /// be larger than the word length. mask: ManagedSlice<'a, u8>, }, /// Collect memory.`len` bytes of memory starting at the address in register /// number `basereg`, plus `offset`. If `basereg` is None, then treat it /// as a fixed address. Memory { /// If `Some`, then calculate the address of memory to collect relative /// to the value of this register number. If `None` then memory /// should be collected from a fixed address. basereg: Option, /// The offset used to calculate the address to collect memory from. offset: U, /// How many bytes of memory to collect. length: u64, }, /// Collect data according to an agent bytecode program. Expression { /// The GDB agent bytecode program to evaluate. expr: ManagedSlice<'a, u8>, }, } /// What type of information a tracepoint source item is about. #[derive(Debug, Clone, Copy)] pub enum TracepointSourceType { /// Describes the location the tracepoint is at. At, /// Describes the conditional expression for a tracepoint. Cond, /// Describes the action command that should be executed when a tracepoint /// is hit. Cmd, } /// Source string fragment for a tracepoint. A tracepoint may have more than one /// source string, such as being describes by one source string for the location /// and another for the actions, or by GDB splitting a larger source string /// into multiple fragments. GDB may ask for the source of current tracepoints, /// which are described by this same structure. #[derive(Debug)] pub struct SourceTracepoint<'a, U> { /// The tracepoint that the source string is specifying. pub number: Tracepoint, /// The PC address of the tracepoint that the source string is specifying. pub addr: U, /// What type of information for this tracepoint the string fragment is /// about. pub kind: TracepointSourceType, /// The offset in bytes within the overall source string this fragment is /// within. pub start: u32, /// The total length of the overall source string this fragment is within. pub slen: u32, /// The data for this source string fragment. pub bytes: ManagedSlice<'a, u8>, } #[cfg(feature = "alloc")] impl SourceTracepoint<'_, U> { /// Allocate an owned copy of this structure. pub fn get_owned<'b>(&self) -> SourceTracepoint<'b, U> { SourceTracepoint { number: self.number, addr: self.addr, kind: self.kind, start: self.start, slen: self.slen, bytes: ManagedSlice::Owned(self.bytes.to_owned()), } } } #[cfg(feature = "alloc")] impl TracepointAction<'_, U> { /// Allocate an owned copy of this structure. pub fn get_owned<'b>(&self) -> TracepointAction<'b, U> { use core::ops::Deref; match self { TracepointAction::Registers { mask } => TracepointAction::Registers { mask: ManagedSlice::Owned(mask.deref().into()), }, TracepointAction::Memory { basereg, offset, length, } => TracepointAction::Memory { basereg: *basereg, offset: *offset, length: *length, }, TracepointAction::Expression { expr } => TracepointAction::Expression { expr: ManagedSlice::Owned(expr.deref().into()), }, } } } /// The running state of a trace experiment. #[derive(Debug)] pub enum ExperimentStatus<'a> { /// The experiment is currently running Running, /// The experiment is not currently running, with no more information given /// as to why. NotRunning, /// No trace has been ran yet. NotRun, /// The trace was stopped by the user. May contain an optional user-supplied /// stop reason. Stop(Option<&'a [u8]>), /// The trace stopped because the buffer is full. Full, /// The trace stopped because GDB disconnect from the target. Disconnected, /// The trace stopped because the specified tracepoint exceeded its pass /// count. PassCount(Tracepoint), /// The trace stopped because the specified tracepoint had an error. Error(&'a [u8], Tracepoint), /// The trace stopped for some other reason. Unknown, } /// An explanation of some detail of the currently running trace experiment. #[derive(Debug)] pub enum ExperimentExplanation<'a> { /// The number of trace frames in the buffer. Frames(usize), /// The total number of trace frames created during the run. This may be /// larger than the trace frame count, if the buffer is circular. Created(usize), /// The total size of the trace buffer, in bytes. Size(usize), /// The number of bytes still unused in the buffer. Free(usize), /// The value of the circular trace buffer flag. True means the trace buffer /// is circular and old trace frames will be discarded if necessary to /// make room, false means that the trace buffer is linear and may fill /// up. Circular(bool), /// The value of the disconnected tracing flag. True means that tracing will /// continue after GDB disconnects, false means that the trace run will /// stop. DisconnectedTracing(bool), /// Report a raw string as a trace status explanation. Other(&'a str), } /// Shape of the trace buffer #[derive(Debug)] pub enum BufferShape { /// A circular trace buffer Circular, /// A linear trace buffer Linear, } /// Configuration for the trace buffer. #[derive(Debug)] pub enum TraceBufferConfig { /// Set the buffer's shape. Shape(BufferShape), /// Set the buffer's size in bytes. If None, the target should use whatever /// size it prefers. Size(Option), } /// Request to select a new frame from the trace buffer. #[derive(Debug)] pub enum FrameRequest { /// Select the specified tracepoint frame in the buffer. Select(u64), /// Select a tracepoint frame that has a specified PC after the currently /// selected frame. AtPC(U), /// Select a tracepoint frame that hit a specified tracepoint after the /// currently selected frame. Hit(Tracepoint), /// Select a tramepoint frame that has a PC between a start (inclusive) and /// end (inclusive). Between(U, U), /// Select a tracepoint frame that has a PC outside the range of addresses /// (exclusive). Outside(U, U), } /// Describes a detail of a frame from the trace buffer #[derive(Debug)] pub enum FrameDescription { /// The frame is at the specified index in the trace buffer FrameNumber(u64), /// The frame is a hit of the specified tracepoint Hit(Tracepoint), } /// The state of a tracepoint. #[derive(Debug)] pub struct TracepointStatus { /// The number of times a tracepoint has been hit in a trace run. pub hit_count: u64, /// The number of bytes the tracepoint accounts for in the trace buffer. pub bytes_used: u64, } #[derive(Debug)] pub(crate) enum TracepointEnumerateCursor { New { tp: Tracepoint, addr: U }, Action { tp: Tracepoint, addr: U, step: u64 }, Source { tp: Tracepoint, addr: U, step: u64 }, } /// The current state of enumerating tracepoints. gdbstub uses it as an opaque /// bookkeeping record for what information has already been reported when GDB /// downloads tracepoints on attachment. #[derive(Debug, Default)] pub struct TracepointEnumerateState { pub(crate) cursor: Option>, } /// How to transition the [`TracepointEnumerateState`] state machine after /// reporting an item for tracepoint enumeration. #[derive(Debug)] pub enum TracepointEnumerateStep { /// The current tracepoint that is being enumerated has more actions. /// /// Increments the step counter if the state machine was already /// enumerating actions, otherwise it is reset to 0 and GDB will start /// enumerating actions. Action, /// The current tracepoint that is being enumerated has more source strings. /// /// Increments the step counter if the state machine was already /// enumerating sources strings, otherwise it is reset to 0 and GDB will /// start enumerating source strings. /// /// Targets should only return this transition if they implement /// [`Tracepoints::support_tracepoint_source`], or else it indicates an /// error and the state machine iteration will stop. Source, /// The current tracepoint is done being enumerated, and GDB should next /// enumerate a different one. Next { /// The next tracepoint to move to. tp: Tracepoint, /// The PC of the next tracepoint. addr: U, }, /// All tracepoints have been enumerated, and the state machine is done. Done, } /// Target Extension - Provide tracepoints. pub trait Tracepoints: Target { /// Clear any saved tracepoints and empty the trace frame buffer. fn tracepoints_init(&mut self) -> TargetResult<(), Self>; /// Begin creating a new tracepoint according to the description `tdp`. fn tracepoint_create_begin( &mut self, tdp: NewTracepoint<::Usize>, ) -> TargetResult<(), Self>; /// Configure an existing tracepoint, appending an additional action to its /// definition. /// /// This method will only ever be called in-between /// [`Tracepoints::tracepoint_create_begin`] and /// [`Tracepoints::tracepoint_create_complete`] invocations for a new /// tracepoint. fn tracepoint_create_continue( &mut self, tp: Tracepoint, action: &TracepointAction<'_, ::Usize>, ) -> TargetResult<(), Self>; /// Complete the creation of a tracepoint. All of its actions are expected /// to have been received. /// /// This method will only ever be called after all of the /// [`Tracepoints::tracepoint_create_begin`] and /// [`Tracepoints::tracepoint_create_continue`] invocations for a new /// tracepoint. fn tracepoint_create_complete(&mut self, tp: Tracepoint) -> TargetResult<(), Self>; /// Request the status of tracepoint `tp` at address `addr`. /// /// Returns a [`TracepointStatus`] with the requested information. fn tracepoint_status( &self, tp: Tracepoint, addr: ::Usize, ) -> TargetResult; /// Return the stub's tracepoint enumeration state. gdbstub internally /// uses this state to support GDB downloading tracepoints on attachment, /// but requires the target implementation to provide storage for it. /// /// The state instance that this returns should be the same across multiple /// calls and unmodified, or else gdbstub will be unable to transition the /// state machine during enumeration correctly. /// /// For the average trait implementations, this will look like: /// /// ```rust,ignore /// struct MyTarget { /// tracepoint_enumerate_state: TracepointEnumerateState, /// ... /// } /// /// impl MyTarget { /// fn new() -> Self { /// MyTarget { /// tracepoint_enumerate_state: TracepointEnumerateState::default(), /// ... /// } /// } /// } /// /// impl Tracepoints for MyTarget { /// fn tracepoint_enumerate_state( /// &mut self, /// ) -> &mut TracepointEnumerateState<::Usize> { /// &mut self.tracepoint_enumerate_state /// } /// } /// ``` fn tracepoint_enumerate_state( &mut self, ) -> &mut TracepointEnumerateState<::Usize>; /// Begin enumerating a new tracepoint. If `tp` is None, then the first /// tracepoint recorded should be reported via `f`, otherwise the requested /// tracepoint should be. /// /// After reporting a tracepoint, [`TracepointEnumerateStep`] describes what /// information is still available. Unlike tracepoint *creation*, which is /// driven by GDB, for *enumeration* it's the responsibility of the trait /// implementation to correctly sequence the state transitions so that /// GDB is able to enumerate all of the information for the tracepoints the /// implementation has saved via the various `tracepoint_enumerate_*` /// methods. /// /// For the average implementation, it should report the requested /// tracepoint, and then return /// [`TracepointEnumerateStep::Action`] to transition to reporting /// actions for the tracepoint. If the trait implements /// [`TracepointSource`], it can instead return /// [`TracepointEnumerateStep::Source`] to begin reporting source items /// instead. fn tracepoint_enumerate_start( &mut self, tp: Option, f: &mut dyn FnMut(&NewTracepoint<::Usize>), ) -> TargetResult::Usize>, Self>; /// Enumerate an action attached to a tracepoint. `step` is which action /// item is being asked for, so that the implementation can respond with /// multiple items across multiple function calls. Each action should be /// reported via `f`. /// /// After reporting a tracepoint action, [`TracepointEnumerateStep`] /// describes what information will next be enumerated: this may be /// [`TracepointEnumerateStep::Action`] if there are more actions that /// still need to be reported, for example. fn tracepoint_enumerate_action( &mut self, tp: Tracepoint, step: u64, f: &mut dyn FnMut(&TracepointAction<'_, ::Usize>), ) -> TargetResult::Usize>, Self>; /// Reconfigure the trace buffer to include or modify an attribute. fn trace_buffer_configure(&mut self, config: TraceBufferConfig) -> TargetResult<(), Self>; /// Read up to `len` bytes from the trace buffer, starting at `offset`. /// The trace buffer is treated as a contiguous collection of traceframes, /// as per [GDB's trace file format](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Trace-File-Format.html). /// The function `f` should be called to report as many bytes from /// the trace buffer that were requested as possible. fn trace_buffer_request( &mut self, offset: u64, len: usize, f: &mut dyn FnMut(&mut [u8]), ) -> TargetResult<(), Self>; /// Return the status of the current trace experiment. fn trace_experiment_status( &self, report: &mut dyn FnMut(ExperimentStatus<'_>), ) -> TargetResult<(), Self>; /// List any statistical information for the current trace experiment, by /// calling `report` with each [`ExperimentExplanation`] item. fn trace_experiment_info( &self, report: &mut dyn FnMut(ExperimentExplanation<'_>), ) -> TargetResult<(), Self>; /// Start a new trace experiment. fn trace_experiment_start(&mut self) -> TargetResult<(), Self>; /// Stop the currently running trace experiment. fn trace_experiment_stop(&mut self) -> TargetResult<(), Self>; /// Select a new frame in the trace buffer. The target should attempt to /// fulfill the request according to the [`FrameRequest`]. If it's /// successful it should call `report` with a series of calls describing /// the found frame, and then record it as the currently selected frame. /// Future register and memory requests should be fulfilled from the /// currently selected frame. fn select_frame( &mut self, frame: FrameRequest<::Usize>, report: &mut dyn FnMut(FrameDescription), ) -> TargetResult<(), Self>; /// Support for setting and enumerating the source strings for tracepoint /// actions. /// /// The GDB client will attempt to download tracepoints when it attaches to /// the stub, which will trigger the /// `Tracepoints::tracepoint_enumerate_*` state machine enumeration /// codepaths. GDB depends on accurately enumerating source strings for /// tracepoints, or else for agent bytecode programs (which are most /// non-trivial `collect` actions) it won't be able to parse them and /// throw away the actions attached to the tracepoint. [`TracepointSource`] /// allows for recording and then reporting action source /// strings for targets that want to fully support GDB tracepoint /// downloading. #[inline(always)] fn support_tracepoint_source(&mut self) -> Option> { None } } /// Target Extension - Support setting and enumerating source strings for /// tracepoint actions. /// /// GDB requires source strings to be accurately reported back to it when it /// attaches to a target in order to download tracepoints, or else it will /// locally not be able to parse them and throw away the attached actions. /// /// Downloading tracepoints is only triggered for new GDB clients attaching to a /// stub that already has tracepoints loaded, however. An example use case would /// be one GDB client attaching, creating tracepoints, disconnecting, and then a /// new GDB client starting and connecting to the stub to download the loaded /// tracepoints. If supporting that behavior is unimportant and you only need to /// work with a single GDB session, it is safe to not implement this extension. pub trait TracepointSource: Tracepoints { /// Configure an existing tracepoint, appending a new source string /// fragment. fn tracepoint_attach_source( &mut self, src: SourceTracepoint<'_, ::Usize>, ) -> TargetResult<(), Self>; /// Enumerate the source strings that describe a tracepoint. `step` is which /// source string is being asked for, so that the implementation can /// respond with multiple items across multiple function calls. Each /// source string should be reported via `f`. /// /// After reporting a tracepoint source string, [`TracepointEnumerateStep`] /// describes what source string will next be enumerated. fn tracepoint_enumerate_source( &mut self, tp: Tracepoint, step: u64, f: &mut dyn FnMut(&SourceTracepoint<'_, ::Usize>), ) -> TargetResult::Usize>, Self>; } define_ext!(TracepointsOps, Tracepoints); define_ext!(TracepointSourceOps, TracepointSource); ================================================ FILE: src/target/ext/wasm.rs ================================================ //! (LLDB extension) Provide Wasm-specific actions for the target. //! //! ### Address Encoding //! //! LLDB's Wasm extensions to the GDB RSP use a specific encoding for addresses, //! both for commands in this extension trait, as well as for commands in the //! base protocol (e.g. for reading and writing memory and setting breakpoints). //! //! The need for this scheme arises from the fact that Wasm is natively //! "multimemory": there can be many code modules, and many linear memories, and //! each is a native entity (rather than mapped into a larger single address //! space) in the VM definition. The LLDB GDB RSP extensions map these native //! entities into an address space where the upper 32 bits encode the index of a //! particular Wasm code module or linear (data) memory and the lower 32 bits //! encode an offset. //! //! See the [LLDB source code] (particularly `WasmAddressType` and //! `wasm_addr_t`) for a description of the encoding of the PC values. //! //! [LLDB source code]: //! https://github.com/llvm/llvm-project/blob/main/lldb/source/Plugins/Process/wasm/ProcessWasm.h //! //! An implementation of this address encoding/decoding can be found in the //! `gdbstub_arch` crate in `gdbstub_arch::wasm::addr`. use crate::common::Tid; use crate::target::Target; /// (LLDB extension) Target Extension - perform Wasm-specific actions. pub trait Wasm: Target { /// Get the Wasm call stack for a given thread. /// /// The addresses provided for the PC at each frame should be encoded as per /// the [Wasm address encoding]. /// /// To avoid allocation, the call stack PCs should be returned to the caller /// by calling the given callback, in order from innermost (most recently /// called) frame to outermost. /// /// [Wasm address encoding]: `self#Address_Encoding` fn wasm_call_stack(&self, tid: Tid, next_pc: &mut dyn FnMut(u64)) -> Result<(), Self::Error>; /// Get the Wasm local for a given thread, frame index, and local index. /// /// The Wasm local's value should be placed into `buf`, and the length /// should be returned. If the Wasm local or frame does not exist, this /// method should return `0`. /// /// `buf` will be long enough to allow for the largest possible supported /// Wasm value (i.e., at least a `v128` SIMD value). Values should be /// encoded in little-endian format with their native length (e.g., 4 bytes /// for a Wasm `i32` or `f32` type, or 8 bytes for a Wasm `i64` or `f64` /// type). fn read_wasm_local( &self, tid: Tid, frame: usize, local: usize, buf: &mut [u8], ) -> Result; /// Get the Wasm operand-stack value for a given thread, frame index, and /// stack index. Top-of-stack is index 0, and values below that have /// incrementing indices. /// /// The Wasm operand's value should be placed into `buf`, and the length /// should be returned. If the Wasm local or frame does not exist, this /// method should return `0`. /// /// `buf` will be long enough to allow for the largest possible supported /// Wasm value (i.e., at least a `v128` SIMD value). Values should be /// encoded in little-endian format with their native length (e.g., 4 bytes /// for a Wasm `i32` or `f32` type, or 8 bytes for a Wasm `i64` or `f64` /// type). fn read_wasm_stack( &self, tid: Tid, frame: usize, index: usize, buf: &mut [u8], ) -> Result; /// Get the Wasm global value for a given thread, frame, and global index. /// The global index is relative to the module whose function corresponds to /// that frame. /// /// The Wasm global's value should be placed into `buf`, and the length /// should be returned. If the Wasm local or frame does not exist, this /// method should return `0`. /// /// `buf` will be long enough to allow for the largest possible supported /// Wasm value (i.e., at least a `v128` SIMD value). Values should be /// encoded in little-endian format with their native length (e.g., 4 bytes /// for a Wasm `i32` or `f32` type, or 8 bytes for a Wasm `i64` or `f64` /// type). fn read_wasm_global( &self, tid: Tid, frame: usize, global: usize, buf: &mut [u8], ) -> Result; } define_ext!(WasmOps, Wasm); ================================================ FILE: src/target/mod.rs ================================================ //! The core [`Target`] trait, and all its various protocol extension traits. //! //! The [`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 protocol implementation and a //! target's project/platform-specific code. //! //! **`Target` is the most important trait in `gdbstub`, and must be implemented //! by all consumers of the library!** //! //! # Implementing `Target` //! //! `gdbstub` uses a technique called ["Inlineable Dyn Extension Traits"](ext) //! (IDETs) to expose an ergonomic and extensible interface to the GDB protocol. //! It's not a very common pattern, and can seem a little "weird" at first //! glance, but IDETs are actually very straightforward to use! //! //! **TL;DR:** Whenever you see a method that returns something that looks like //! `Option`, you can enable that protocol extension by //! implementing the `ProtocolExt` type on your target, and overriding the //! `Option` method to return `Some(self)`. //! //! Please refer to the [documentation in the `ext` module](ext) for more //! information on IDETs, including a more in-depth explanation of how they //! work, and how `Target` leverages them to provide fine grained control over //! enabled protocol features. //! //! ## Associated Types //! //! - The [`Target::Arch`](trait.Target.html#associatedtype.Arch) associated //! type encodes information about the target's architecture, such as its //! pointer size, register layout, etc... `gdbstub` comes with several //! built-in architecture definitions, which can be found under the //! [`arch`](../arch/index.html) module. //! //! - The [`Target::Error`](trait.Target.html#associatedtype.Error) associated //! type allows implementors to plumb-through their own project-specific fatal //! error type into the `Target` trait. This is a big-boost to library //! ergonomics, as it enables consumers of `gdbstub` to preserve //! target-specific context while using `gdbstub`, without having to do any //! "error-stashing". //! //! For example: consider an emulated target where certain devices might return //! a `MyEmuError::ContractViolation` error whenever they're accessed //! "improperly" (e.g: setting registers in the wrong order). By setting `type //! Error = MyEmuError`, the method signature of the `Target`'s `resume` method //! becomes `fn resume(&mut self, ...) -> Result<_, MyEmuError>`, which makes it //! possible to preserve the target-specific error while using `gdbstub`! //! //! ## Required Methods (Base Protocol) //! //! A minimal `Target` implementation only needs to implement a single method: //! [`Target::base_ops`](trait.Target.html#tymethod.base_ops). This method is //! used to select which set of [`base`](crate::target::ext::base) //! debugging operations will be used to control the target. These are //! fundamental operations such as reading/writing memory, etc... //! //! All other methods are entirely optional! Check out the //! [`ext`](ext#modules) module for a full list of currently supported protocol //! extensions. //! //! ## Optional Protocol Extensions //! //! The GDB protocol is _massive_, and there are plenty of optional protocol //! extensions that targets can implement to enhance the base debugging //! experience. //! //! These protocol extensions range from relatively mundane things such as //! setting/removing breakpoints or reading/writing individual registers, but //! also include fancy things such as support for time travel debugging, running //! shell commands remotely, or even performing file IO on the target! //! //! `gdbstub` uses a somewhat unique approach to exposing these many features, //! called **Inlinable Dyn Extension Traits (IDETs)**. While this might sound a //! bit daunting, the API is actually quite straightforward, and described in //! great detail under the [`ext` module's documentation](ext). //! //! After getting the base protocol up and running, do take a moment to skim //! through and familiarize yourself with the [many different protocol //! extensions](ext# modules) that `gdbstub` implements. There are some really //! nifty ones that you might not even realize you need! //! //! As a suggestion on where to start, consider implementing some of the //! breakpoint related extensions under //! [`breakpoints`](crate::target::ext::breakpoints). While setting/removing //! breakpoints is technically an "optional" part of the GDB protocol, I'm sure //! you'd be hard pressed to find a debugger that doesn't support breakpoints. //! //! ### Note: Missing Protocol Extensions //! //! `gdbstub`'s development is guided by the needs of its contributors, with //! new features being added on an "as-needed" basis. //! //! If there's a GDB protocol extensions you're interested in that hasn't been //! implemented in `gdbstub` yet, (e.g: remote filesystem access, tracepoint //! support, etc...), consider opening an issue / filing a PR on the //! [`gdbstub` GitHub repo](https://github.com/daniel5151/gdbstub/). //! //! Check out the [GDB Remote Configuration Docs](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Configuration.html) //! for a table of GDB commands + their corresponding Remote Serial Protocol //! packets. //! //! ### Example: A fairly minimal Single Threaded `Target` //! //! This example includes a handful of required and optional target features, //! and shows off the basics of how to work with IDETs. //! //! ```rust //! use gdbstub::common::Signal; //! use gdbstub::target::{Target, TargetResult}; //! use gdbstub::target::ext::base::BaseOps; //! use gdbstub::target::ext::base::singlethread::{ //! SingleThreadResumeOps, SingleThreadSingleStepOps //! }; //! use gdbstub::target::ext::base::singlethread::{ //! SingleThreadBase, SingleThreadResume, SingleThreadSingleStep //! }; //! use gdbstub::target::ext::breakpoints::{Breakpoints, SwBreakpoint}; //! use gdbstub::target::ext::breakpoints::{BreakpointsOps, SwBreakpointOps}; //! //! struct MyTarget; //! //! impl Target for MyTarget { //! type Error = (); //! type Arch = gdbstub_arch::arm::Armv4t; // as an example //! //! #[inline(always)] //! fn base_ops(&mut self) -> BaseOps { //! BaseOps::SingleThread(self) //! } //! //! // opt-in to support for setting/removing breakpoints //! #[inline(always)] //! fn support_breakpoints(&mut self) -> Option> { //! Some(self) //! } //! } //! //! impl SingleThreadBase for MyTarget { //! fn read_registers( //! &mut self, //! regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs, //! ) -> TargetResult<(), Self> { todo!() } //! //! fn write_registers( //! &mut self, //! regs: &gdbstub_arch::arm::reg::ArmCoreRegs //! ) -> TargetResult<(), Self> { todo!() } //! //! fn read_addrs( //! &mut self, //! start_addr: u32, //! data: &mut [u8], //! ) -> TargetResult { todo!() } //! //! fn write_addrs( //! &mut self, //! start_addr: u32, //! data: &[u8], //! ) -> TargetResult<(), Self> { todo!() } //! //! // most targets will want to support at resumption as well... //! //! #[inline(always)] //! fn support_resume(&mut self) -> Option> { //! Some(self) //! } //! } //! //! impl SingleThreadResume for MyTarget { //! fn resume( //! &mut self, //! signal: Option, //! ) -> Result<(), Self::Error> { todo!() } //! //! // ...and if the target supports resumption, it'll likely want to support //! // single-step resume as well //! //! #[inline(always)] //! fn support_single_step( //! &mut self //! ) -> Option> { //! Some(self) //! } //! } //! //! impl SingleThreadSingleStep for MyTarget { //! fn step( //! &mut self, //! signal: Option, //! ) -> Result<(), Self::Error> { todo!() } //! } //! //! impl Breakpoints for MyTarget { //! // there are several kinds of breakpoints - this target uses software breakpoints //! #[inline(always)] //! fn support_sw_breakpoint(&mut self) -> Option> { //! Some(self) //! } //! } //! //! impl SwBreakpoint for MyTarget { //! fn add_sw_breakpoint( //! &mut self, //! addr: u32, //! kind: gdbstub_arch::arm::ArmBreakpointKind, //! ) -> TargetResult { todo!() } //! //! fn remove_sw_breakpoint( //! &mut self, //! addr: u32, //! kind: gdbstub_arch::arm::ArmBreakpointKind, //! ) -> TargetResult { todo!() } //! } //! ``` //! //! ## A note on error handling //! //! As you explore the various protocol extension traits, you'll often find that //! functions don't return a typical [`Result`], //! and will instead return a [`TargetResult`]. //! //! At first glance this might look a bit strange, since it looks like the `Err` //! variant of `TargetResult` is `Self` instead of `Self::Error`! //! //! Thankfully, there's a good reason for why that's the case. In a nutshell, //! `TargetResult` wraps a typical `Result` with a few //! additional error types which can be reported back to the GDB client via the //! GDB RSP. //! //! For example, if the GDB client tried to read memory from invalid memory, //! instead of immediately terminating the entire debugging session, it's //! possible to simply return a `Err(TargetError::Errno(14)) // EFAULT`, which //! will notify the GDB client that the operation has failed. //! //! See the [`TargetError`] docs for more details. //! //! ## A note on all the `::` syntax //! //! As you explore `Target` and its many extension traits, you'll enounter //! many method signatures that use this pretty gnarly bit of Rust type syntax. //! //! If [rust-lang/rust#38078](https://github.com/rust-lang/rust/issues/38078) //! gets fixed, then types like `::Foo` could be simplified //! to just `Self::Arch::Foo`, but until then, the much more explicit //! [fully qualified syntax](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name) //! must be used instead. //! //! To improve the readability and maintainability of your own implementation, //! it'd be best to swap out the fully qualified syntax with whatever concrete //! 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; pub mod ext; /// The error type for various methods on `Target` and its assorted associated /// extension traits. /// /// # Error Handling over the GDB Remote Serial Protocol /// /// The GDB Remote Serial Protocol has less-than-stellar support for error /// handling, typically taking the form of a single-byte /// [`errno`-style error codes](https://chromium.googlesource.com/chromiumos/docs/+/HEAD/constants/errnos.md). /// Moreover, often times the GDB client will simply _ignore_ the specific error /// code returned by the stub, and print a generic failure message instead. /// /// As such, while it's certainly better to use appropriate error codes when /// possible (e.g: returning a `EFAULT` (14) when reading from invalid memory), /// it's often fine to simply return the more general `TargetError::NonFatal` /// instead, and avoid the headache of picking a "descriptive" error code. Under /// the good, `TargetError::NonFatal` is sent to the GDB client as a generic /// `EREMOTEIO` (121) error. /// /// # `From` and `Into` implementations /// /// - `From<()>` -> `TargetError::NonFatal` /// - `From` -> `TargetError::Io(io::Error)` (requires `std` feature) /// /// When using a custom target-specific fatal error type, users are encouraged /// to write the following impl to simplify error handling in `Target` methods: /// /// ```rust /// use gdbstub::target::TargetError; /// /// /// Target-specific Fatal Error /// enum MyTargetFatalError { /// // ... /// } /// /// impl From for TargetError { /// fn from(e: MyTargetFatalError) -> Self { /// TargetError::Fatal(e) /// } /// } /// ``` /// /// Unfortunately, a blanket impl such as `impl From for /// TargetError` isn't possible, as it could result in impl conflicts. /// For example, if a Target decided to use `()` as its fatal error type, then /// there would be conflict with the existing `From<()>` impl. #[non_exhaustive] pub enum TargetError { /// A non-specific, non-fatal error has occurred. NonFatal, /// Non-fatal I/O Error. Only available when the `std` feature is enabled. /// /// At the moment, this is just shorthand for /// `TargetError::NonFatal(e.raw_os_err().unwrap_or(121))`. Error code `121` /// corresponds to `EREMOTEIO`. /// /// In the future, `gdbstub` may add support for the "QEnableErrorStrings" /// LLDB protocol extension, which would allow sending additional error /// context (in the form of an ASCII string) when an I/O error occurs. If /// this is something you're interested in, consider opening a PR! #[cfg(feature = "std")] Io(std::io::Error), /// An operation-specific non-fatal error code. Errno(u8), /// A target-specific fatal error. /// /// **WARNING:** Returning this error will immediately terminate the GDB /// debugging session, and return a /// [`GdbStubError`](crate::stub::GdbStubError)! Fatal(E), } /// Converts a `()` into a `TargetError::NonFatal`. impl From<()> for TargetError { fn from(_: ()) -> TargetError { TargetError::NonFatal } } /// Converts a `std::io::Error` into a `TargetError::Io`. #[cfg(feature = "std")] impl From for TargetError { fn from(e: std::io::Error) -> TargetError { TargetError::Io(e) } } /// A specialized `Result` type for `Target` operations. Supports reporting /// non-fatal errors back to the GDB client. /// /// See [`TargetError`] for more details. /// /// _Note:_ While it's typically parameterized as `TargetResult`, the /// error value is in-fact `TargetError` (not `Self`). pub type TargetResult = Result::Error>>; /// Describes the architecture and capabilities of a target which can be /// debugged by [`GdbStub`](../struct.GdbStub.html). /// /// The [`Target`](trait.Target.html) 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 protocol implementation and a /// target's project/platform-specific code. /// /// **`Target` is the most important trait in `gdbstub`, and must be implemented /// by anyone who uses the library!** /// /// Please refer to the the documentation in the [`target` module](self) /// for more information on how to implement and work with `Target` and its /// various extension traits. pub trait Target { /// The target's architecture. type Arch: Arch; /// A target-specific **fatal** error. type Error; /// Base operations such as reading/writing from memory/registers, /// stopping/resuming the target, etc.... /// /// For example, on a single-threaded target: /// /// ```rust /// use gdbstub::target::Target; /// use gdbstub::target::ext::base::BaseOps; /// use gdbstub::target::ext::base::singlethread::SingleThreadBase; /// # use gdbstub::target::TargetResult; /// # struct MyTarget; /// /// impl Target for MyTarget { /// // ... /// # type Arch = gdbstub_arch::arm::Armv4t; /// # type Error = (); /// /// fn base_ops(&mut self) -> BaseOps { /// BaseOps::SingleThread(self) /// } /// } /// /// // ...and then implement the associated base IDET /// impl SingleThreadBase for MyTarget { /// // ... /// # fn read_registers( /// # &mut self, /// # regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs, /// # ) -> TargetResult<(), Self> { todo!() } /// # /// # fn write_registers( /// # &mut self, /// # regs: &gdbstub_arch::arm::reg::ArmCoreRegs /// # ) -> TargetResult<(), Self> { todo!() } /// # /// # fn read_addrs( /// # &mut self, /// # start_addr: u32, /// # data: &mut [u8], /// # ) -> TargetResult { todo!() } /// # /// # fn write_addrs( /// # &mut self, /// # start_addr: u32, /// # data: &[u8], /// # ) -> TargetResult<(), Self> { todo!() } /// } /// ``` fn base_ops(&mut self) -> ext::base::BaseOps<'_, Self::Arch, Self::Error>; /// If the target supports resumption, but hasn't implemented explicit /// support for software breakpoints (via /// [`SwBreakpoints`](ext::breakpoints::SwBreakpoint)), notify the user /// that the GDB client may set "implicit" software breakpoints by /// rewriting the target's instruction stream. /// /// Targets that wish to use the GDB client's implicit software breakpoint /// handler must explicitly **opt-in** to this somewhat surprising GDB /// feature by overriding this method to return `true`. /// /// # Context /// /// An "implicit" software breakpoint is set by the GDB client by manually /// writing a software breakpoint instruction into target memory via the /// target's `write_addrs` implementation. i.e: the GDB client will /// overwrite the target's instruction stream with a software breakpoint /// instruction, with the expectation that the target has a implemented a /// breakpoint exception handler. /// /// # Implications /// /// While this is a reasonable (and useful!) bit of behavior when targeting /// many classes of remote stub (e.g: bare-metal, separate process), there /// are many `gdbstub` implementations that do _not_ implement "software /// breakpoints" by naively rewriting the target's instruction stream. /// /// - e.g: a `gdbstub` implemented in an emulator is unlikely to implement /// "software breakpoints" by hooking into the emulated hardware's /// breakpoint handler, and would likely implement "breakpoints" by /// maintaining a list of addresses to stop at as part of its core /// interpreter loop. /// - e.g: a `gdbstub` implemented in a hypervisor would require special /// coordination with the guest kernel to support software breakpoints, as /// there would need to be some way to distinguish between "in-guest" /// debugging, and "hypervisor" debugging. /// /// As such, `gdbstub` includes this `guard_rail_implicit_sw_breakpoints` /// method. /// /// As the name suggests, this method acts as a "guard rail" that /// warns users from accidentally opting into this "implicit" breakpoint /// functionality, and being exceptionally confused as to why their /// target is acting weird. /// /// If `gdbstub` detects that the target has not implemented a software /// breakpoint handler, it will check if /// `guard_rail_implicit_sw_breakpoints()` has been enabled, and if it /// has not, it will trigger a runtime error that points the user at this /// very documentation. /// /// # A note on breakpoints /// /// Aside from setting breakpoints at the explicit behest of the user (e.g: /// when setting breakpoints via the `b` command in GDB), the GDB client may /// also set/remove _temporary breakpoints_ as part of other commands. /// /// 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. #[inline(always)] fn guard_rail_implicit_sw_breakpoints(&self) -> bool { false } /// Enable/disable support for activating "no ack mode". /// /// By default, this method returns `true`. /// /// _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 use_no_ack_mode(&self) -> bool { true } /// Enable/disable using the more efficient `X` packet to write to target /// memory (as opposed to the basic `M` packet). /// /// By default, this method returns `true`. /// /// _Author's note:_ Unless you're _really_ trying to squeeze `gdbstub` onto /// a particularly resource-constrained platform, you may as well leave this /// optimization enabled. #[inline(always)] fn use_x_upcase_packet(&self) -> bool { true } /// Enable/disable using the more efficient `x` packet to read to target /// memory (as opposed to the basic `m` packet). /// /// **By default, this method returns `false`.** /// /// This packet is disabled by default in order to maximize out-of-the-box /// compatibility between `gdbstub` and all released GDB and LLDB versions. /// For more context, see the discussion at /// [daniel5151/gdbstub#163](https://github.com/daniel5151/gdbstub/issues/163#issuecomment-4049691552). /// /// `gdbstub` implements the `x` packet according to the GDB RSP spec, and /// as such, enabling this packet will break compatibility with older LLDB /// version. /// /// _Author's note:_ If you are _certain_ that your target will only even be /// debugged used alongside a sufficiently recent GDB / LLDB version (i.e: /// those released sometime after ~early 2026), you can set this to `true` /// for improved performance. That said, unless you're planning to fetch /// a _lot_ of memory data from your `Target`, you may as well leave /// this optimization disabled, and guarantee client compatibility. #[inline(always)] fn use_x_lowcase_packet(&self) -> bool { false } /// Whether `gdbstub` should provide a "stub" `resume` implementation on /// targets without support for resumption. /// /// At the time of writing, the mainline GDB client does not gracefully /// handle targets that do not support support resumption, and will hang /// indefinitely if a user inadvertently attempts to `continue` or `step` /// such a target. /// /// To make the `gdbstub` user experience a bit better, the library includes /// bit of "stub" code to gracefully handle these cases. /// /// If a user attempts to resume a target that hasn't implemented support /// for resumption, `gdbstub` will write a brief message back to the GDB /// client console, and will immediately return a "stopped with TRAP" stop /// reason. /// /// This method controls whether or not this bt of behavior is enabled. /// /// _Author's note:_ Unless you're _really_ trying to squeeze `gdbstub` onto /// a particularly resource-constrained platform, you may as well leave this /// enabled. The resulting stub code is entirely optimized out on targets /// that implement support for resumption. #[inline(always)] fn use_resume_stub(&self) -> bool { true } /// Enable/Disable the use of run-length encoding on outgoing packets. /// /// This is enabled by default, as RLE can save substantial amounts of /// bandwidth down the wire. /// /// _Author's note:_ There are essentially no reasons to disable RLE, unless /// you happen to be using a custom GDB client that doesn't support RLE. #[inline(always)] fn use_rle(&self) -> bool { true } /// Whether to send a target description XML to the client. /// /// Setting this to `false` will override both /// [`Target::support_target_description_xml_override`] and the associated /// [`Arch::target_description_xml`]. /// /// _Author's note:_ Having the GDB client autodetect your target's /// architecture and register set is really useful, so unless you're /// _really_ trying to squeeze `gdbstub` onto a particularly /// resource-constrained platform, you may as well leave this enabled. #[inline(always)] fn use_target_description_xml(&self) -> bool { true } /// (LLDB extension) Whether to send register information to the client. /// /// Setting this to `false` will override both /// [`Target::support_lldb_register_info_override`] and the associated /// [`Arch::lldb_register_info`]. /// /// _Author's note:_ Having the LLDB client autodetect your target's /// register set is really useful, so unless you're _really_ trying to /// squeeze `gdbstub` onto a particularly resource-constrained platform, you /// may as well leave this enabled. #[inline(always)] fn use_lldb_register_info(&self) -> bool { true } /// Enable support for [`BaseStopReason::Fork`]. /// /// By default, this method returns `true`. /// /// _Author's note:_ Unless you're _really_ trying to squeeze `gdbstub` onto /// a particularly resource-constrained platform (and looking to save ~100 /// bytes), you may as well leave this enabled. /// /// [`BaseStopReason::Fork`]: crate::stub::BaseStopReason::Fork #[inline(always)] fn use_fork_stop_reason(&self) -> bool { true } /// Enable support for [`BaseStopReason::VFork`]. /// /// By default, this method returns `true`. /// /// _Author's note:_ Unless you're _really_ trying to squeeze `gdbstub` onto /// a particularly resource-constrained platform (and looking to save ~100 /// bytes), you may as well leave this enabled. /// /// [`BaseStopReason::VFork`]: crate::stub::BaseStopReason::VFork #[inline(always)] fn use_vfork_stop_reason(&self) -> bool { true } /// Enable support for [`BaseStopReason::VForkDone`]. /// /// By default, this method returns `true`. /// /// _Author's note:_ Unless you're _really_ trying to squeeze `gdbstub` onto /// a particularly resource-constrained platform (and looking to save ~100 /// bytes), you may as well leave this enabled. /// /// [`BaseStopReason::VForkDone`]: crate::stub::BaseStopReason::VForkDone #[inline(always)] fn use_vforkdone_stop_reason(&self) -> bool { true } /// Support for setting / removing breakpoints. #[inline(always)] fn support_breakpoints(&mut self) -> Option> { None } /// Support for handling custom GDB `monitor` commands. #[inline(always)] fn support_monitor_cmd(&mut self) -> Option> { None } /// Support for Extended Mode operations. #[inline(always)] fn support_extended_mode(&mut self) -> Option> { None } /// Support for handling requests to get the target's current section (or /// segment) offsets. #[inline(always)] fn support_section_offsets( &mut self, ) -> Option> { None } /// Support for setting / removing tracepoints. #[inline(always)] fn support_tracepoints(&mut self) -> Option> { None } /// Support for overriding the target description XML specified by /// `Target::Arch`. #[inline(always)] fn support_target_description_xml_override( &mut self, ) -> Option> { None } /// (LLDB extension) Support for overriding the register info specified by /// `Target::Arch`. #[inline(always)] fn support_lldb_register_info_override( &mut self, ) -> Option> { None } /// Support for reading the target's memory map. #[inline(always)] fn support_memory_map(&mut self) -> Option> { None } /// Support for flash memory operations. #[inline(always)] fn support_flash_operations(&mut self) -> Option> { None } /// Support for setting / removing syscall catchpoints. #[inline(always)] fn support_catch_syscalls( &mut self, ) -> Option> { None } /// Support for Host I/O operations. #[inline(always)] fn support_host_io(&mut self) -> Option> { None } /// Support for reading the current exec-file. #[inline(always)] fn support_exec_file(&mut self) -> Option> { None } /// Support for reading the target's Auxillary Vector. #[inline(always)] fn support_auxv(&mut self) -> Option> { None } /// Support for reading a list of libraries for SVR4 (System-V/Unix) /// platforms. #[inline(always)] fn support_libraries_svr4(&mut self) -> Option> { None } /// Support for reading a list of libraries (Windows/generic format). /// /// This is used for targets where library offsets are maintained externally /// (e.g., Windows PE targets). #[inline(always)] fn support_libraries(&mut self) -> Option> { None } /// (LLDB extension) Support for reporting host information. #[inline(always)] fn support_host_info(&mut self) -> Option> { None } /// (LLDB extension) Support for reporting process information. #[inline(always)] fn support_process_info(&mut self) -> Option> { None } /// (LLDB extension) Support for WebAssembly (Wasm)-specific commands. #[inline(always)] fn support_wasm(&mut self) -> Option> { None } } macro_rules! __delegate { (fn $op:ident(&mut $this:ident) $($sig:tt)*) => { fn $op(&mut $this) $($sig)* { (**$this).$op() } }; (fn $op:ident(&$this:ident) $($sig:tt)*) => { fn $op(&$this) $($sig)* { (**$this).$op() } } } macro_rules! __delegate_support { ($ext:ident, $extpath:ident) => { pastey::paste! { __delegate!(fn [](&mut self) -> Option]<'_, Self>>); } }; ($ext:ident) => { __delegate_support!($ext, $ext); }; } macro_rules! impl_dyn_target { ($type:ty) => { impl Target for $type where A: Arch, { type Arch = A; type Error = E; __delegate!(fn base_ops(&mut self) -> ext::base::BaseOps<'_, Self::Arch, Self::Error>); __delegate!(fn guard_rail_implicit_sw_breakpoints(&self) -> bool); __delegate!(fn use_fork_stop_reason(&self) -> bool); __delegate!(fn use_lldb_register_info(&self) -> bool); __delegate!(fn use_no_ack_mode(&self) -> bool); __delegate!(fn use_resume_stub(&self) -> bool); __delegate!(fn use_rle(&self) -> bool); __delegate!(fn use_target_description_xml(&self) -> bool); __delegate!(fn use_vfork_stop_reason(&self) -> bool); __delegate!(fn use_vforkdone_stop_reason(&self) -> bool); __delegate!(fn use_x_lowcase_packet(&self) -> bool); __delegate!(fn use_x_upcase_packet(&self) -> bool); // TODO: (breaking) fix inconsistencies in `support_` naming __delegate_support!(auxv); __delegate_support!(breakpoints); __delegate_support!(catch_syscalls); __delegate_support!(exec_file); __delegate_support!(extended_mode); __delegate!(fn support_flash_operations(&mut self) -> Option>); __delegate_support!(host_info); __delegate_support!(host_io); __delegate_support!(libraries); __delegate!(fn support_libraries_svr4(&mut self) -> Option>); __delegate_support!(lldb_register_info_override); __delegate_support!(memory_map); __delegate_support!(monitor_cmd); __delegate_support!(process_info); __delegate_support!(section_offsets); __delegate_support!(target_description_xml_override); __delegate_support!(tracepoints); __delegate_support!(wasm); } }; } impl_dyn_target!(&mut dyn Target); #[cfg(feature = "alloc")] impl_dyn_target!(alloc::boxed::Box>); ================================================ FILE: src/util/dead_code_marker.rs ================================================ /// NOTE: We don't have a proper black box in stable Rust. This is /// a workaround implementation, that may have a too big performance overhead, /// depending on operation, or it may fail to properly avoid having code /// optimized out. It is good enough that it is used by default. /// /// A function that is opaque to the optimizer, to allow benchmarks to /// pretend to use outputs to assist in avoiding dead-code /// elimination. // copied from https://docs.rs/bencher/0.1.5/src/bencher/lib.rs.html#590-596 #[cfg(feature = "__dead_code_marker")] pub fn black_box(dummy: T) -> T { unsafe { let ret = core::ptr::read_volatile(&dummy); core::mem::forget(dummy); ret } } /// If the block of code which contains this macro doesn't get dead code /// eliminated, this macro ensures that the resulting binary contains a /// easy-to-find static string with the format `"<$feature,$ctx>"`. /// /// In `gdbstub`, this macro makes it easy to see if the Rust compiler was able /// to dead-code-eliminate the packet parsing / handling code associated with /// unimplemented protocol extensions. /// /// e.g: if the target didn't implement the `MonitorCmd` extension, then running /// the unix command `strings | grep " { #[cfg(feature = "__dead_code_marker")] $crate::util::dead_code_marker::black_box(concat!("<", $feature, ",", $ctx, ">")); }; } ================================================ FILE: src/util/managed_vec.rs ================================================ use managed::ManagedSlice; /// Error value indicating insufficient capacity. #[derive(Debug, Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] pub struct CapacityError(pub Element); /// Wraps a ManagedSlice in a vec-like interface. pub struct ManagedVec<'a, 'b, T> { buf: &'b mut ManagedSlice<'a, T>, len: usize, } impl<'a, 'b, T> ManagedVec<'a, 'b, T> { pub fn new_with_idx(buf: &'b mut ManagedSlice<'a, T>, len: usize) -> Self { ManagedVec { buf, len } } pub fn push(&mut self, value: T) -> Result<(), CapacityError> { if self.len < self.buf.len() { self.buf[self.len] = value; self.len += 1; Ok(()) } else { match &mut self.buf { ManagedSlice::Borrowed(_) => Err(CapacityError(value)), #[cfg(feature = "alloc")] ManagedSlice::Owned(buf) => { buf.push(value); Ok(()) } } } } #[cfg(feature = "trace-pkt")] pub fn as_slice<'c: 'b>(&'c self) -> &'b [T] { &self.buf[..self.len] } } ================================================ FILE: src/util/mod.rs ================================================ //! Private utility types used internally within `gdbstub`. //! //! These are all bits of functionality that _could_ exist as their own crates / //! libraries, and do not rely on any `gdbstub` specific infrastructure. pub mod managed_vec; pub(crate) mod dead_code_marker;