Skip to content

Commit

Permalink
Implement GDB server using gdbstub
Browse files Browse the repository at this point in the history
  • Loading branch information
mkroening committed Sep 9, 2021
1 parent 62f0471 commit dfd35fb
Show file tree
Hide file tree
Showing 18 changed files with 861 additions and 17 deletions.
60 changes: 51 additions & 9 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ default = []
instrument = ["rftrace", "rftrace-frontend"]

[patch.crates-io]
gdbstub = { git = "https://github.com/daniel5151/gdbstub", branch = "dev/0.6" }
nix = { git = "https://github.com/nix-rust/nix" }
x86_64 = { git = "https://github.com/mkroening/x86_64", branch = "debug-registers" }

[dependencies]
bitflags = "1.3"
Expand All @@ -45,13 +47,16 @@ core_affinity = "0.5"
either = "1.6"
env_logger = "0.9"
envmnt = "0.9"
gdbstub = "0.5"
gdbstub_arch = "0.1"
goblin = { version = "0.4", default-features = false, features = ["elf64", "elf32", "endian_fd", "std"] }
lazy_static = "1.4"
libc = "0.2"
log = "0.4"
raw-cpuid = "10.2"
rustc-serialize = "0.3"
thiserror = "1.0"
x86_64 = "0.14"

rftrace = { version = "0.1", optional = true }
rftrace-frontend = { version = "0.1", optional = true }
Expand Down
2 changes: 2 additions & 0 deletions src/arch/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#[cfg(target_arch = "x86_64")]
pub mod x86_64;
1 change: 1 addition & 0 deletions src/arch/x86_64/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod registers;
120 changes: 120 additions & 0 deletions src/arch/x86_64/registers/debug.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//! Functions to read and write debug registers.

use std::convert::{TryFrom, TryInto};

use gdbstub::target::ext::breakpoints::WatchKind;
use x86_64::{
registers::debug::{
DebugAddressRegisterNumber, Dr7Flags, Dr7Value, HwBreakpointCondition, HwBreakpointSize,
TryFromIntError,
},
VirtAddr,
};

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct HwBreakpoint {
addr: VirtAddr,
size: HwBreakpointSize,
condition: HwBreakpointCondition,
}

impl HwBreakpoint {
pub fn new_breakpoint(addr: u64, kind: usize) -> Result<Self, TryFromIntError> {
Ok(Self {
addr: VirtAddr::new(addr),
size: kind.try_into()?,
condition: HwBreakpointCondition::InstructionExecution,
})
}

pub fn new_watchpoint(addr: u64, len: u64, kind: WatchKind) -> Option<Self> {
let condition = match kind {
WatchKind::Write => Some(HwBreakpointCondition::DataWrites),
WatchKind::Read => None,
WatchKind::ReadWrite => Some(HwBreakpointCondition::DataReadsWrites),
}?;

let ret = Self {
addr: VirtAddr::new(addr),
size: usize::try_from(len).ok()?.try_into().ok()?,
condition,
};

Some(ret)
}
}

#[derive(Clone, Copy, Debug)]
pub struct HwBreakpoints([Option<HwBreakpoint>; 4]);

#[derive(Debug)]
pub struct CapacityExceededError(());

impl HwBreakpoints {
pub const fn new() -> Self {
Self([None; 4])
}

pub fn try_insert(&mut self, hw_breakpoint: HwBreakpoint) -> Result<(), CapacityExceededError> {
if let Some(entry) = self.0.iter_mut().find(|entry| entry.is_none()) {
*entry = Some(hw_breakpoint);
Ok(())
} else {
Err(CapacityExceededError(()))
}
}

pub fn take(&mut self, hw_breakpoint: &HwBreakpoint) -> Option<HwBreakpoint> {
self.0
.iter_mut()
.find(|slot| slot.as_ref() == Some(hw_breakpoint))?
.take()
}

fn control_value(&self) -> Dr7Value {
let dr7_flags = Dr7Flags::LOCAL_EXACT_BREAKPOINT_ENABLE
| Dr7Flags::GLOBAL_EXACT_BREAKPOINT_ENABLE
| Dr7Flags::GENERAL_DETECT_ENABLE;
let mut dr7_value = Dr7Value::from(dr7_flags);

for (i, hw_breakpoint) in
self.0.iter().enumerate().filter_map(|(i, hw_breakpoint)| {
hw_breakpoint.map(|hw_breakpoint| (i, hw_breakpoint))
}) {
let n = DebugAddressRegisterNumber::new(i.try_into().unwrap()).unwrap();

dr7_value
.flags_mut()
.insert(Dr7Flags::global_breakpoint_enable(n));
dr7_value.set_condition(n, hw_breakpoint.condition);
dr7_value.set_size(n, hw_breakpoint.size);
}

dr7_value
}

pub fn registers(self) -> [u64; 8] {
let control_value = self.control_value();
let address = |hw_breakpoint: Option<HwBreakpoint>| {
hw_breakpoint
.map(|hw_breakpoint| hw_breakpoint.addr.as_u64())
.unwrap_or(0)
};
[
address(self.0[0]),
address(self.0[1]),
address(self.0[2]),
address(self.0[3]),
0,
0,
0,
control_value.bits(),
]
}
}

impl Default for HwBreakpoints {
fn default() -> Self {
Self::new()
}
}
2 changes: 2 additions & 0 deletions src/arch/x86_64/registers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#[cfg(target_os = "linux")]
pub mod debug;
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod macros;
#[macro_use]
extern crate log;

mod arch;
pub mod consts;
#[cfg(target_os = "linux")]
pub mod linux;
Expand Down
Loading

0 comments on commit dfd35fb

Please sign in to comment.