Continuing after a breakpoint both with single step and cont (but breakpoints still one-use)
This commit is contained in:
parent
bcb61a02f0
commit
5897c9e862
|
|
@ -30,20 +30,26 @@ impl StoppedTarget {
|
||||||
Ok(self.breakpoints.contains_key(&(rip - 1)))
|
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()?;
|
let mut registers = self.get_registers()?;
|
||||||
if self.breakpoints.contains_key(&(registers.rip - 1)) { // We are on a breakpoint, we remove it
|
self.remove_breakpoint(registers.rip - 1)?;
|
||||||
self = self.remove_breakpoint(registers.rip - 1)?;
|
registers.rip -= 1;
|
||||||
registers.rip -= 1;
|
nix::sys::ptrace::setregs(self.pid, registers).map_err(PTraceError)?;
|
||||||
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)?;
|
nix::sys::ptrace::cont(self.pid, None).map_err(PTraceError)?;
|
||||||
Ok(self.to_running())
|
Ok(self.to_running())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stepi(self) -> Result<RunningTarget, PTraceError> {
|
pub fn stepi(mut self) -> Result<RunningTarget, DebugError> {
|
||||||
todo!("Take into account breakpoints also for stepi");
|
if self.on_breakpoint()? {
|
||||||
|
self.breakpoint_remove_and_rewind()?;
|
||||||
|
}
|
||||||
nix::sys::ptrace::step(self.pid, None).map_err(PTraceError)?;
|
nix::sys::ptrace::step(self.pid, None).map_err(PTraceError)?;
|
||||||
Ok(self.to_running())
|
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 🤷
|
#[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) {
|
if self.breakpoints.contains_key(&address) {
|
||||||
return Err(DebugError::DuplicateBreakpoint { 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)?;
|
nix::sys::ptrace::write(self.pid, address as *mut c_void, new_content).map_err(PTraceError)?;
|
||||||
|
|
||||||
self.breakpoints.insert(address, target_byte);
|
self.breakpoints.insert(address, target_byte);
|
||||||
Ok(self)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_endian = "little")] // Same as add_breakpoint
|
#[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) {
|
match self.breakpoints.remove(&address) {
|
||||||
None => Err(DebugError::NonExistingBreakpoint { address }),
|
None => Err(DebugError::NonExistingBreakpoint { address }),
|
||||||
Some(original_byte) => {
|
Some(original_byte) => {
|
||||||
let content = nix::sys::ptrace::read(self.pid, address as *mut c_void).map_err(PTraceError)?;
|
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);
|
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)?;
|
nix::sys::ptrace::write(self.pid, address as *mut c_void, new_content).map_err(PTraceError)?;
|
||||||
Ok(self)
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
src/main.rs
10
src/main.rs
|
|
@ -103,18 +103,20 @@ fn main() -> color_eyre::Result<()> {
|
||||||
Ok(ForkResult::Parent { child: child_pid }) => {
|
Ok(ForkResult::Parent { child: child_pid }) => {
|
||||||
println!("✔️ Created child {child_pid}");
|
println!("✔️ Created child {child_pid}");
|
||||||
|
|
||||||
let target = StoppedTarget::new(child_pid)?;
|
let mut target = StoppedTarget::new(child_pid)?;
|
||||||
println!("✔️ Child ready!");
|
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()?;
|
let target = target.cont()?.wait_for_something()?;
|
||||||
match target {
|
match target {
|
||||||
Either::Left(t) => {
|
Either::Left(t) => {
|
||||||
println!("🔎 rip: {:#x}", t.get_registers()?.rip);
|
|
||||||
if t.on_breakpoint()? {
|
if t.on_breakpoint()? {
|
||||||
println!("🚧 We are on a 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, .. }) => {
|
Either::Right(ExitedTarget { exit_code, .. }) => {
|
||||||
println!("👋 Child exited with code {exit_code}");
|
println!("👋 Child exited with code {exit_code}");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue