Modifed debug target type representation
This commit is contained in:
parent
aa661db532
commit
88072597a1
|
|
@ -1,8 +1,8 @@
|
||||||
|
use crate::syscall_info::{syscall_info, SyscallInfo, SyscallInfoError};
|
||||||
use either::{Either, Left, Right};
|
use either::{Either, Left, Right};
|
||||||
use nix::libc::user_regs_struct;
|
use nix::libc::user_regs_struct;
|
||||||
use nix::sys::wait::{waitid, Id, WaitPidFlag, WaitStatus};
|
use nix::sys::wait::{waitid, Id, WaitPidFlag, WaitStatus};
|
||||||
use nix::unistd::Pid;
|
use nix::unistd::Pid;
|
||||||
use crate::syscall_info::{SyscallInfo, SyscallInfoError, syscall_info};
|
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
#[error("Error when calling ptrace: {0}")]
|
#[error("Error when calling ptrace: {0}")]
|
||||||
|
|
@ -24,85 +24,82 @@ pub enum DebugError {
|
||||||
UnexpectedWaitStatus(nix::sys::wait::WaitStatus),
|
UnexpectedWaitStatus(nix::sys::wait::WaitStatus),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DebugTarget<S: DebugState> {
|
pub struct StoppedTarget {
|
||||||
state: S,
|
pub pid: Pid,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait DebugState {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn pid(&self) -> Pid;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Stopped {
|
|
||||||
pid: Pid,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DebugState for Stopped {
|
|
||||||
fn pid(&self) -> Pid { self.pid }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
impl DebugTarget<Stopped> {
|
impl StoppedTarget {
|
||||||
|
fn to_running(self) -> RunningTarget {
|
||||||
|
RunningTarget { pid: self.pid }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new(pid: Pid) -> Result<Self, DebugError> {
|
pub fn new(pid: Pid) -> Result<Self, DebugError> {
|
||||||
waitid(Id::Pid(pid), WaitPidFlag::WSTOPPED).map_err(WaitError)?;
|
waitid(Id::Pid(pid), WaitPidFlag::WSTOPPED).map_err(WaitError)?;
|
||||||
// Needed for waiting on syscalls and apparently also for getting syscall info (can not get it to work without this)
|
// Needed for waiting on syscalls and apparently also for getting syscall info (can not get it to work without this)
|
||||||
nix::sys::ptrace::setoptions(pid, nix::sys::ptrace::Options::PTRACE_O_TRACESYSGOOD).map_err(PTraceError)?;
|
nix::sys::ptrace::setoptions(pid, nix::sys::ptrace::Options::PTRACE_O_TRACESYSGOOD).map_err(PTraceError)?;
|
||||||
Ok(DebugTarget { state: Stopped { pid } })
|
Ok(Self { pid })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cont(self) -> Result<DebugTarget<Running>, PTraceError> {
|
pub fn cont(self) -> Result<RunningTarget, PTraceError> {
|
||||||
nix::sys::ptrace::cont(self.state.pid, None).map_err(PTraceError)?;
|
nix::sys::ptrace::cont(self.pid, None).map_err(PTraceError)?;
|
||||||
Ok(DebugTarget { state: Running { pid: self.state.pid } })
|
Ok(self.to_running())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stepi(self) -> Result<DebugTarget<Running>, PTraceError> {
|
pub fn stepi(self) -> Result<RunningTarget, PTraceError> {
|
||||||
nix::sys::ptrace::step(self.state.pid, None).map_err(PTraceError)?;
|
nix::sys::ptrace::step(self.pid, None).map_err(PTraceError)?;
|
||||||
Ok(DebugTarget { state: Running { pid: self.state.pid } })
|
Ok(self.to_running())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cont_syscall(self) -> Result<DebugTarget<Running>, PTraceError> {
|
pub fn cont_syscall(self) -> Result<RunningTarget, PTraceError> {
|
||||||
nix::sys::ptrace::syscall(self.state.pid, None).map_err(PTraceError)?;
|
nix::sys::ptrace::syscall(self.pid, None).map_err(PTraceError)?;
|
||||||
Ok(DebugTarget { state: Running { pid: self.state.pid } })
|
Ok(self.to_running())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_registers(&self) -> Result<user_regs_struct, PTraceError> {
|
pub fn get_registers(&self) -> Result<user_regs_struct, PTraceError> {
|
||||||
Ok(nix::sys::ptrace::getregs(self.state.pid)?)
|
Ok(nix::sys::ptrace::getregs(self.pid)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_syscall_info(&self) -> Result<SyscallInfo, SyscallInfoError> {
|
pub fn get_syscall_info(&self) -> Result<SyscallInfo, SyscallInfoError> {
|
||||||
Ok(syscall_info(self.state.pid.as_raw())?)
|
Ok(syscall_info(self.pid.as_raw())?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Running {
|
pub struct RunningTarget {
|
||||||
pid: Pid,
|
pub pid: Pid,
|
||||||
}
|
|
||||||
|
|
||||||
impl DebugState for Running {
|
|
||||||
fn pid(&self) -> Pid { self.pid }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
impl DebugTarget<Running> {
|
impl RunningTarget {
|
||||||
pub fn wait_for_something(self) -> Result<Either<DebugTarget<Stopped>, i32>, DebugError> {
|
fn to_stopped(self) -> StoppedTarget {
|
||||||
match waitid(Id::Pid(self.state.pid), WaitPidFlag::WSTOPPED | WaitPidFlag::WEXITED).map_err(WaitError)? {
|
StoppedTarget { pid: self.pid }
|
||||||
WaitStatus::Exited(_pid, exit_code) => Ok(Right(exit_code)),
|
}
|
||||||
WaitStatus::PtraceEvent(..) | WaitStatus::PtraceSyscall(..) => Ok(Left(DebugTarget { state: Stopped { pid: self.state.pid } })),
|
|
||||||
|
pub fn wait_for_something(self) -> Result<Either<StoppedTarget, ExitedTarget>, DebugError> {
|
||||||
|
match waitid(Id::Pid(self.pid), WaitPidFlag::WSTOPPED | WaitPidFlag::WEXITED).map_err(WaitError)? {
|
||||||
|
WaitStatus::Exited(_pid, exit_code) => Ok(Right(ExitedTarget { exit_code, was_pid: self.pid })),
|
||||||
|
WaitStatus::PtraceEvent(..) | WaitStatus::PtraceSyscall(..) => Ok(Left(self.to_stopped())),
|
||||||
status => Err(DebugError::UnexpectedWaitStatus(status)),
|
status => Err(DebugError::UnexpectedWaitStatus(status)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait_for_syscall(self) -> Result<DebugTarget<Stopped>, DebugError> {
|
pub fn wait_for_syscall(self) -> Result<StoppedTarget, DebugError> {
|
||||||
match waitid(Id::Pid(self.state.pid), WaitPidFlag::WSTOPPED).map_err(WaitError)? {
|
match waitid(Id::Pid(self.pid), WaitPidFlag::WSTOPPED).map_err(WaitError)? {
|
||||||
WaitStatus::PtraceSyscall(..) => Ok(DebugTarget { state: Stopped { pid: self.state.pid } }),
|
WaitStatus::PtraceSyscall(..) => Ok(self.to_stopped()),
|
||||||
status => Err(DebugError::UnexpectedWaitStatus(status)),
|
status => Err(DebugError::UnexpectedWaitStatus(status)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait_for_exit(self) -> Result<i32, DebugError> {
|
pub fn wait_for_exit(self) -> Result<ExitedTarget, DebugError> {
|
||||||
match waitid(Id::Pid(self.state.pid), WaitPidFlag::WEXITED).map_err(WaitError)? {
|
match waitid(Id::Pid(self.pid), WaitPidFlag::WEXITED).map_err(WaitError)? {
|
||||||
WaitStatus::Exited(_pid, exit_code) => Ok(exit_code),
|
WaitStatus::Exited(_pid, exit_code) => Ok(ExitedTarget { exit_code, was_pid: self.pid }),
|
||||||
status => Err(DebugError::UnexpectedWaitStatus(status))
|
status => Err(DebugError::UnexpectedWaitStatus(status))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ExitedTarget {
|
||||||
|
pub exit_code: i32,
|
||||||
|
pub was_pid: Pid,
|
||||||
|
}
|
||||||
15
src/main.rs
15
src/main.rs
|
|
@ -3,7 +3,7 @@ mod debug_target;
|
||||||
mod syscall_info;
|
mod syscall_info;
|
||||||
mod syscall_names;
|
mod syscall_names;
|
||||||
|
|
||||||
use crate::debug_target::{DebugTarget, Stopped};
|
use crate::debug_target::{ExitedTarget, StoppedTarget};
|
||||||
use color_eyre::eyre::eyre;
|
use color_eyre::eyre::eyre;
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use nix::libc::user_regs_struct;
|
use nix::libc::user_regs_struct;
|
||||||
|
|
@ -14,7 +14,7 @@ use nix::unistd::{fork, ForkResult, Pid};
|
||||||
use std::ffi::{c_long, c_void, CString};
|
use std::ffi::{c_long, c_void, CString};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn single_step_all(mut target: DebugTarget<Stopped>) -> color_eyre::Result<()> {
|
fn single_step_all(mut target: StoppedTarget) -> color_eyre::Result<()> {
|
||||||
let mut instruction_number = 0;
|
let mut instruction_number = 0;
|
||||||
loop {
|
loop {
|
||||||
instruction_number += 1;
|
instruction_number += 1;
|
||||||
|
|
@ -22,7 +22,7 @@ fn single_step_all(mut target: DebugTarget<Stopped>) -> color_eyre::Result<()> {
|
||||||
println!("🔎 [==> {}] rip= {:#016x}, rax = 0x{rax:x} ({rax})", instruction_number, regs.rip, rax = regs.rax);
|
println!("🔎 [==> {}] rip= {:#016x}, rax = 0x{rax:x} ({rax})", instruction_number, regs.rip, rax = regs.rax);
|
||||||
match target.stepi()?.wait_for_something()? {
|
match target.stepi()?.wait_for_something()? {
|
||||||
Either::Left(t) => target = t,
|
Either::Left(t) => target = t,
|
||||||
Either::Right(exit_code) => {
|
Either::Right(ExitedTarget { exit_code, was_pid: _was_pid }) => {
|
||||||
println!("👋 Child exited with code {exit_code}");
|
println!("👋 Child exited with code {exit_code}");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
@ -31,14 +31,14 @@ fn single_step_all(mut target: DebugTarget<Stopped>) -> color_eyre::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn strace(mut target: DebugTarget<Stopped>) -> color_eyre::Result<()> {
|
fn strace(mut target: StoppedTarget) -> color_eyre::Result<()> {
|
||||||
loop {
|
loop {
|
||||||
match target.cont_syscall()?.wait_for_something()? {
|
match target.cont_syscall()?.wait_for_something()? {
|
||||||
Either::Left(t) => {
|
Either::Left(t) => {
|
||||||
println!("{:?}", t.get_syscall_info());
|
println!("{:?}", t.get_syscall_info());
|
||||||
target = t;
|
target = t;
|
||||||
}
|
}
|
||||||
Either::Right(exit_code) => {
|
Either::Right(ExitedTarget { exit_code, was_pid: _was_pid }) => {
|
||||||
println!("👋 Child exited with code {exit_code}");
|
println!("👋 Child exited with code {exit_code}");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
@ -100,16 +100,17 @@ fn breakpoint_fun(child_pid: Pid) -> color_eyre::Result<()> {
|
||||||
|
|
||||||
fn main() -> color_eyre::Result<()> {
|
fn main() -> color_eyre::Result<()> {
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
let child_exec_path = CString::new(env!("C_PROG_PATH"))?;
|
let child_exec_path = CString::new(env!("ASM_PROG_PATH"))?;
|
||||||
|
|
||||||
match unsafe { fork() } {
|
match unsafe { fork() } {
|
||||||
Ok(ForkResult::Child) => child::starti(child_exec_path),
|
Ok(ForkResult::Child) => child::starti(child_exec_path),
|
||||||
Ok(ForkResult::Parent { child: child_pid }) => {
|
Ok(ForkResult::Parent { child: child_pid }) => {
|
||||||
println!("✔️ Created child {child_pid}");
|
println!("✔️ Created child {child_pid}");
|
||||||
|
|
||||||
let target = DebugTarget::new(child_pid)?;
|
let target = StoppedTarget::new(child_pid)?;
|
||||||
println!("✔️ Child ready!");
|
println!("✔️ Child ready!");
|
||||||
|
|
||||||
|
|
||||||
// println!("🔎 rip: {:#x}", target.get_registers()?.rip);
|
// println!("🔎 rip: {:#x}", target.get_registers()?.rip);
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue