Continuing after a breakpoint both with single step and cont (but breakpoints still one-use)

This commit is contained in:
Elnath 2025-05-18 17:52:41 +02:00
parent bcb61a02f0
commit 5897c9e862
2 changed files with 24 additions and 16 deletions

View File

@ -30,20 +30,26 @@ impl StoppedTarget {
Ok(self.breakpoints.contains_key(&(rip - 1)))
}
pub fn cont(mut self) -> Result<RunningTarget, DebugError> {
fn breakpoint_remove_and_rewind(&mut self) -> Result<(), DebugError> {
let mut registers = self.get_registers()?;
if self.breakpoints.contains_key(&(registers.rip - 1)) { // We are on a breakpoint, we remove it
self = self.remove_breakpoint(registers.rip - 1)?;
registers.rip -= 1;
nix::sys::ptrace::setregs(self.pid, registers).map_err(PTraceError)?;
}
self.remove_breakpoint(registers.rip - 1)?;
registers.rip -= 1;
nix::sys::ptrace::setregs(self.pid, registers).map_err(PTraceError)?;
Ok(())
}
pub fn cont(mut self) -> Result<RunningTarget, DebugError> {
if self.on_breakpoint()? {
self.breakpoint_remove_and_rewind()?;
}
nix::sys::ptrace::cont(self.pid, None).map_err(PTraceError)?;
Ok(self.to_running())
}
pub fn stepi(self) -> Result<RunningTarget, PTraceError> {
todo!("Take into account breakpoints also for stepi");
pub fn stepi(mut self) -> Result<RunningTarget, DebugError> {
if self.on_breakpoint()? {
self.breakpoint_remove_and_rewind()?;
}
nix::sys::ptrace::step(self.pid, None).map_err(PTraceError)?;
Ok(self.to_running())
}
@ -62,7 +68,7 @@ impl StoppedTarget {
}
#[cfg(target_endian = "little")] // With a bit more work it could be implemented on both architectures, but I only have little-endian computers 🤷
pub fn add_breakpoint(mut self, address: u64) -> Result<Self, DebugError> {
pub fn add_breakpoint(&mut self, address: u64) -> Result<(), DebugError> {
if self.breakpoints.contains_key(&address) {
return Err(DebugError::DuplicateBreakpoint { address });
}
@ -72,18 +78,18 @@ impl StoppedTarget {
nix::sys::ptrace::write(self.pid, address as *mut c_void, new_content).map_err(PTraceError)?;
self.breakpoints.insert(address, target_byte);
Ok(self)
Ok(())
}
#[cfg(target_endian = "little")] // Same as add_breakpoint
pub fn remove_breakpoint(mut self, address: u64) -> Result<Self, DebugError> {
pub fn remove_breakpoint(&mut self, address: u64) -> Result<(), DebugError> {
match self.breakpoints.remove(&address) {
None => Err(DebugError::NonExistingBreakpoint { address }),
Some(original_byte) => {
let content = nix::sys::ptrace::read(self.pid, address as *mut c_void).map_err(PTraceError)?;
let new_content = (content & (!0xff as c_long)) | (original_byte as c_long);
nix::sys::ptrace::write(self.pid, address as *mut c_void, new_content).map_err(PTraceError)?;
Ok(self)
Ok(())
}
}
}

View File

@ -103,18 +103,20 @@ fn main() -> color_eyre::Result<()> {
Ok(ForkResult::Parent { child: child_pid }) => {
println!("✔️ Created child {child_pid}");
let target = StoppedTarget::new(child_pid)?;
let mut target = StoppedTarget::new(child_pid)?;
println!("✔️ Child ready!");
let target = target.add_breakpoint(0x401019)?;
let breakpoint1 = 0x401019;
println!("🛑 Adding breakpoint at {breakpoint1:#x}");
target.add_breakpoint(breakpoint1)?;
let target = target.cont()?.wait_for_something()?;
match target {
Either::Left(t) => {
println!("🔎 rip: {:#x}", t.get_registers()?.rip);
if t.on_breakpoint()? {
println!("🚧 We are on a breakpoint!")
}
t.cont()?.wait_for_exit()?;
println!("🔎 rip: {:#x}", t.get_registers()?.rip);
single_step_all(t)?;
}
Either::Right(ExitedTarget { exit_code, .. }) => {
println!("👋 Child exited with code {exit_code}");