Coder Social home page Coder Social logo

gdbstub's Introduction

gdbstub

An implementation of the GDB Remote Server Protocol in Rust, primarily for use in emulators.

gdbstub tries to make as few assumptions as possible about your emulator's architecture, and aims to provide a "drop-in" way to add GDB support into an emulator, without any large refactoring / ownership juggling (hopefully).

Debugging Features

  • Core Protocol
    • Step / Continue
    • Add / Remove Breakpoints
    • Read/Write memory
    • Read/Write/Access Watchpoints (i.e: value breakpoints)
      • implemented, but currently broken
  • Extended Protocol
    • Automatic architecture detection (via target.xml)

There are also a few features which rely on std, which can be enabled by enabling the std feature:

  • An impl Connection for some common std types (notably: TcpStream)
  • Additional logging (outputs protocol responses via trace!)

Future Plans

  • Improve packet-parsing infrastructure
  • Reconsider / Rework Target::Usize
    • i.e: find a "nicer" way to make gdbstub generic across various target pointer sizes
    • Will most-likely switch to using &[u8] instead of a generic type
      • If only const-generics were stable, then I could use generic-sized arrays. One day...
  • Improve multiprocess / multithread / multicore support?
  • Re-architect internals to remove alloc dependency?
    • Current approach has a clear separation between packet parsing and command execution, and requires intermediate allocations for parsed data.
    • Require user to provide their own packet buffers

Example

Disclaimer: gdbstub's API and architecture is still very much in flux, so expect things to change often and "destructively"

This snippet provides a very brief overview of how to use gdbstub.

While I have a few projects which are already using gdbstub, none of them are open-source (at the moment). In the future, I'll try to find some time time to create a more robust (read: compiling) example.

use std::net::{TcpListener, TcpStream};

use gdbstub::{GdbStub, Access, AccessKind, Target, TargetState};

// <your pre-existing emulator>
struct MySystem { /* ... */ }

// `Target` is the fundamental trait of `gdbstub`, wrapping the multitude of different
// emulator implementations behind a single, generic interface which the GdbStub can
// query for information / drive forward in response to incoming GDB commands.
impl Target for MySystem {
    // The target's pointer size
    type Usize = u32;
    // A user-defined error type (for passing-through any internal emulation errors)
    type Error = ();

    // Run the system for a single "step", using the provided callback to log
    // any memory accesses which may have occurred
    fn step(
        &mut self,
        mut log_mem_access: impl FnMut(Access<u32>),
    ) -> Result<TargetState, Self::Error> {
        self.cpu.cycle()?;

        for (read_or_write, addr, val) in self.mem.recent_accesses.drain(..) {
            log_mem_access(Access {
                kind: if read_or_write {
                    AccessKind::Read
                } else {
                    AccessKind::Write
                },
                addr,
                val
            })
        }

        Ok(TargetState::Running)
    }

    // Read-out the CPU's register values in the order specified in the arch's
    // `target.xml` file.
    // e.g: for ARM: binutils-gdb/blob/master/gdb/features/arm/arm-core.xml
    fn read_registers(&mut self, mut push_reg: impl FnMut(&[u8])) {
        // general purpose registers
        for i in 0..13 {
            push_reg(&self.cpu.reg_get(i).to_le_bytes());
        }
        push_reg(&self.cpu.reg_get(reg::SP).to_le_bytes());
        push_reg(&self.cpu.reg_get(reg::LR).to_le_bytes());
        push_reg(&self.cpu.reg_get(reg::PC).to_le_bytes());
        // Floating point registers, unused
        for _ in 0..25 {
            push_reg(&[0, 0, 0, 0]);
        }
        push_reg(&self.cpu.reg_get(reg::CPSR).to_le_bytes());
    }

    fn read_pc(&mut self) -> u32 {
        self.cpu.reg_get(reg::PC)
    }

    // read the specified memory addresses from the target
    fn read_addrs(&mut self, addr: std::ops::Range<u32>, mut push_byte: impl FnMut(u8)) {
        for addr in addr {
            push_byte(self.mem.r8(addr))
        }
    }

    // write data to the specified memory addresses
    fn write_addrs(&mut self, mut get_addr_val: impl FnMut() -> Option<(u32, u8)>) {
        while let Some((addr, val)) = get_addr_val() {
            self.mem.w8(addr, val);
        }
    }

    // there are a few other optional methods which can be implemented to enable
    // some more advanced functionality (e.g: automatic arch detection).
    // See the docs for details.
}


fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Your existing setup code...
    let mut system = MySystem::new()?;
    // ...

    // e.g: using a TcpStream for the GDB connection
    let sockaddr = format!("localhost:{}", 9001);
    eprintln!("Waiting for a GDB connection on {:?}...", sockaddr);
    let sock = TcpListener::bind(sockaddr)?;
    let (stream, addr) = sock.accept()?;
    eprintln!("Debugger connected from {}", addr);

    // At this point, it's possible to connect to the emulator using
    // `gdb-multiarch -iex "target remote localhost:9001"`

    // Hand the connection off to the GdbStub.
    let debugger = GdbStub::new(stream);

    // Instead of taking ownership of the system, GdbStub takes a &mut, yielding ownership once the debugging session is closed, or an error occurs.
    let system_result = match debugger.run(&mut system) {
        Ok(state) => {
            eprintln!("Disconnected from GDB. Target state: {:?}", state);
            Ok(())
        }
        Err(gdbstub::Error::TargetError(e)) => Err(e),
        Err(e) => return Err(e.into()),
    };

    eprintln!("{:?}", system_result);
}

Using gdbstub on actual hardware

While the target use-case for gdbstub is emulation, the crate is no_std compatible (albeit with a dependency on alloc), which means it should be possible to use in embedded contexts as well.

At the moment, this is not a supported use-case, and has not been tested. Please let me know if you've had any success using gdbstub on actual hardware!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.