Custom error class and better error handling in main using thiserror and anyhow
This commit is contained in:
parent
c59e81571c
commit
37ace245b3
|
|
@ -2,11 +2,19 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.98"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bdb"
|
name = "bdb"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"nix",
|
"nix",
|
||||||
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -44,3 +52,58 @@ dependencies = [
|
||||||
"cfg_aliases",
|
"cfg_aliases",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.95"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,5 @@ publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nix = { version = "0.29.0", features = ["process", "ptrace"] }
|
nix = { version = "0.29.0", features = ["process", "ptrace"] }
|
||||||
|
thiserror = "2.0.12"
|
||||||
|
anyhow = "1.0.98"
|
||||||
|
|
@ -1,7 +1,15 @@
|
||||||
use nix::errno::Errno;
|
|
||||||
use nix::sys::wait::{waitid, Id, WaitPidFlag, WaitStatus};
|
use nix::sys::wait::{waitid, Id, WaitPidFlag, WaitStatus};
|
||||||
use nix::unistd::Pid;
|
use nix::unistd::Pid;
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum DebugError {
|
||||||
|
#[error("Error when calling ptrace: {0}")]
|
||||||
|
PTraceError(#[from] nix::errno::Errno),
|
||||||
|
|
||||||
|
#[error("Child stopped with status {0:?}, but was not expecting to catch this one")]
|
||||||
|
UnexpectedWaitStatus(nix::sys::wait::WaitStatus),
|
||||||
|
}
|
||||||
|
|
||||||
pub struct DebugTarget<S: DebugState> {
|
pub struct DebugTarget<S: DebugState> {
|
||||||
state: S,
|
state: S,
|
||||||
}
|
}
|
||||||
|
|
@ -20,12 +28,14 @@ impl DebugState for Stopped {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DebugTarget<Stopped> {
|
impl DebugTarget<Stopped> {
|
||||||
pub fn new(pid: Pid) -> Result<Self, Errno> {
|
pub fn new(pid: Pid) -> Result<Self, DebugError> {
|
||||||
waitid(Id::Pid(pid), WaitPidFlag::WSTOPPED).and(Ok(DebugTarget { state: Stopped { pid } }))
|
waitid(Id::Pid(pid), WaitPidFlag::WSTOPPED)?;
|
||||||
|
Ok(DebugTarget { state: Stopped { pid } })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cont(self) -> Result<DebugTarget<Running>, Errno> {
|
pub fn cont(self) -> Result<DebugTarget<Running>, DebugError> {
|
||||||
nix::sys::ptrace::cont(self.state.pid, None).and(Ok(DebugTarget { state: Running { pid: self.state.pid } }))
|
nix::sys::ptrace::cont(self.state.pid, None)?;
|
||||||
|
Ok(DebugTarget { state: Running { pid: self.state.pid } })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,10 +49,10 @@ impl DebugState for Running {
|
||||||
|
|
||||||
|
|
||||||
impl DebugTarget<Running> {
|
impl DebugTarget<Running> {
|
||||||
pub fn wait_for_exit(self) -> Result<i32, Errno> {
|
pub fn wait_for_exit(self) -> Result<i32, DebugError> {
|
||||||
waitid(Id::Pid(self.state.pid), WaitPidFlag::WEXITED).and_then(|status| match status {
|
match waitid(Id::Pid(self.state.pid), WaitPidFlag::WEXITED)? {
|
||||||
WaitStatus::Exited(_, exit_code) => Ok(exit_code),
|
WaitStatus::Exited(_pid, exit_code) => Ok(exit_code),
|
||||||
_ => Err(Errno::EINVAL), // TODO: custom error type?
|
status => Err(DebugError::UnexpectedWaitStatus(status))
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
55
src/main.rs
55
src/main.rs
|
|
@ -2,16 +2,16 @@ mod child;
|
||||||
mod debug_target;
|
mod debug_target;
|
||||||
|
|
||||||
use crate::debug_target::DebugTarget;
|
use crate::debug_target::DebugTarget;
|
||||||
|
use anyhow::{anyhow, bail};
|
||||||
use nix::libc::user_regs_struct;
|
use nix::libc::user_regs_struct;
|
||||||
use nix::sys::ptrace::*;
|
use nix::sys::ptrace::*;
|
||||||
use nix::sys::signal::Signal::*;
|
use nix::sys::signal::Signal::*;
|
||||||
use nix::sys::wait::{waitid, waitpid, Id, WaitPidFlag, WaitStatus};
|
use nix::sys::wait::{waitid, waitpid, Id, WaitPidFlag, WaitStatus};
|
||||||
use nix::unistd::{fork, ForkResult, Pid};
|
use nix::unistd::{fork, ForkResult, Pid};
|
||||||
use std::ffi::{c_long, c_void, CString};
|
use std::ffi::{c_long, c_void, CString};
|
||||||
use std::process::ExitCode;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn single_step_all(child_pid: Pid) -> ExitCode {
|
fn single_step_all(child_pid: Pid) -> Result<(), anyhow::Error> {
|
||||||
let mut instruction_number = 0;
|
let mut instruction_number = 0;
|
||||||
loop {
|
loop {
|
||||||
let wait_status = waitpid(child_pid, None);
|
let wait_status = waitpid(child_pid, None);
|
||||||
|
|
@ -20,24 +20,23 @@ fn single_step_all(child_pid: Pid) -> ExitCode {
|
||||||
instruction_number += 1;
|
instruction_number += 1;
|
||||||
let regs = getregs(child_pid).unwrap();
|
let regs = getregs(child_pid).unwrap();
|
||||||
println!("🔎 [==> {}] rip= 0x{:016X}, rax = 0x{rax:X} ({rax})", instruction_number, regs.rip, rax = regs.rax);
|
println!("🔎 [==> {}] rip= 0x{:016X}, rax = 0x{rax:X} ({rax})", instruction_number, regs.rip, rax = regs.rax);
|
||||||
step(child_pid, None).unwrap();
|
step(child_pid, None)?;
|
||||||
}
|
}
|
||||||
Ok(WaitStatus::Exited(_, exit_code)) => {
|
Ok(WaitStatus::Exited(_, exit_code)) => {
|
||||||
println!("👋 Child exited with code {exit_code}");
|
println!("👋 Child exited with code {exit_code}");
|
||||||
return ExitCode::SUCCESS;
|
return Ok(());
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
println!("⚠️ Other (unexpected) wait status: {other:?}");
|
bail!("⚠️ Other (unexpected) wait status: {other:?}");
|
||||||
return ExitCode::FAILURE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn breakpoint_fun(child_pid: Pid) -> ExitCode {
|
fn breakpoint_fun(child_pid: Pid) -> Result<(), anyhow::Error> {
|
||||||
println!("⏳️ Waiting for child to be ready");
|
println!("⏳️ Waiting for child to be ready");
|
||||||
waitid(Id::Pid(child_pid), WaitPidFlag::WSTOPPED).unwrap();
|
waitid(Id::Pid(child_pid), WaitPidFlag::WSTOPPED)?;
|
||||||
|
|
||||||
let address: u64 = 0x0000000000401019;
|
let address: u64 = 0x0000000000401019;
|
||||||
println!("🚧 Setting breakpoint at location 0x{address:x}");
|
println!("🚧 Setting breakpoint at location 0x{address:x}");
|
||||||
|
|
@ -53,67 +52,65 @@ fn breakpoint_fun(child_pid: Pid) -> ExitCode {
|
||||||
println!("\t🖍️ Breakpoint set");
|
println!("\t🖍️ Breakpoint set");
|
||||||
|
|
||||||
println!("⚙️ Continuing execution waiting for breakpoint");
|
println!("⚙️ Continuing execution waiting for breakpoint");
|
||||||
cont(child_pid, None).unwrap();
|
cont(child_pid, None)?;
|
||||||
match waitpid(child_pid, None).unwrap() {
|
match waitpid(child_pid, None)? {
|
||||||
WaitStatus::Stopped(_, SIGTRAP) => {
|
WaitStatus::Stopped(_, SIGTRAP) => {
|
||||||
let registers = getregs(child_pid).unwrap();
|
let registers = getregs(child_pid)?;
|
||||||
let breakpoint_addr = registers.rip - 1;
|
let breakpoint_addr = registers.rip - 1;
|
||||||
println!("🛑 Stopped at breakpoint ({:#018x})!", breakpoint_addr);
|
println!("🛑 Stopped at breakpoint ({:#018x})!", breakpoint_addr);
|
||||||
println!("\t🔎 Registers content: {:?}", registers);
|
println!("\t🔎 Registers content: {:?}", registers);
|
||||||
println!("\t🖍️ Restoring instructions to original");
|
println!("\t🖍️ Restoring instructions to original");
|
||||||
write(child_pid, address as *mut c_void, c_long::from_le_bytes(orig_bytes)).expect("breakpoint restore memory");
|
write(child_pid, address as *mut c_void, c_long::from_le_bytes(orig_bytes)).expect("breakpoint restore memory");
|
||||||
println!("\t↪️ Rolling back instruction pointer");
|
println!("\t↪️ Rolling back instruction pointer");
|
||||||
setregs(child_pid, user_regs_struct { rip: breakpoint_addr, ..registers }).unwrap();
|
setregs(child_pid, user_regs_struct { rip: breakpoint_addr, ..registers })?;
|
||||||
println!("\t⚙️ One more instruction");
|
println!("\t⚙️ One more instruction");
|
||||||
step(child_pid, None).unwrap();
|
step(child_pid, None)?;
|
||||||
waitid(Id::Pid(child_pid), WaitPidFlag::WSTOPPED).unwrap();
|
waitid(Id::Pid(child_pid), WaitPidFlag::WSTOPPED)?;
|
||||||
println!("\t⚙️ Continuing execution");
|
println!("\t⚙️ Continuing execution");
|
||||||
cont(child_pid, None).unwrap();
|
cont(child_pid, None)?;
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
println!("⚠️ Other (unexpected) wait status: {other:?}");
|
bail!("⚠️ Other (unexpected) wait status: {other:?}");
|
||||||
return ExitCode::FAILURE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match waitpid(child_pid, None) {
|
match waitpid(child_pid, None) {
|
||||||
Ok(WaitStatus::Exited(_, exit_code)) => {
|
Ok(WaitStatus::Exited(_, exit_code)) => {
|
||||||
println!("👋 Child exited with code {exit_code}");
|
println!("👋 Child exited with code {exit_code}");
|
||||||
ExitCode::SUCCESS
|
Ok(())
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
println!("⚠️ Other (unexpected) wait status: {other:?}");
|
bail!("⚠️ Other (unexpected) wait status: {other:?}");
|
||||||
ExitCode::FAILURE
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> Result<(), anyhow::Error> {
|
||||||
let child_exec_path = CString::new(env!("ASM_PROG_PATH")).unwrap();
|
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).unwrap();
|
let target = DebugTarget::new(child_pid)?;
|
||||||
println!("✔️ Child ready!");
|
println!("✔️ Child ready!");
|
||||||
|
|
||||||
println!("⚙️ Continuing execution");
|
println!("⚙️ Continuing execution");
|
||||||
let target = target.cont().unwrap();
|
let target = target.cont()?;
|
||||||
let exit_code = target.wait_for_exit().unwrap();
|
let exit_code = target.wait_for_exit()?;
|
||||||
|
|
||||||
println!("👋 Child exited with code {exit_code}");
|
println!("👋 Child exited with code {exit_code}");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
|
||||||
// return single_step_all(child_pid);
|
|
||||||
// return breakpoint_fun(child_pid);
|
|
||||||
|
|
||||||
ExitCode::SUCCESS
|
// single_step_all(child_pid)
|
||||||
|
// breakpoint_fun(child_pid)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("❌ Fork failed: {e}");
|
println!("❌ Fork failed: {e}");
|
||||||
ExitCode::FAILURE
|
Err(anyhow!("Fork failed"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue