diff --git a/src/linux/minidump_writer/mod.rs b/src/linux/minidump_writer/mod.rs index d81b79d6..75a6d6f6 100644 --- a/src/linux/minidump_writer/mod.rs +++ b/src/linux/minidump_writer/mod.rs @@ -7,6 +7,7 @@ use { dso_debug, dumper_cpu_info::CpuInfoError, maps_reader::{MappingInfo, MappingList, MapsReaderError}, + process_inspection::ProcessInspector, process_reader::{CopyFromProcessError, ProcessReader}, serializers::*, thread_info::{ThreadInfo, ThreadInfoError}, @@ -41,8 +42,6 @@ use { #[cfg(target_os = "android")] use super::android::late_process_mappings; -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use super::thread_info; pub use super::auxv::{AuxvType, DirectAuxvDumpInfo}; @@ -82,6 +81,7 @@ pub struct MinidumpWriterConfig { crashing_thread_context: CrashingThreadContext, stop_timeout: Duration, direct_auxv_dump_info: Option, + process_inspector: ProcessInspector, } #[derive(Debug)] @@ -104,6 +104,7 @@ pub struct MinidumpWriter { pub crash_context: Option, pub app_memory: AppMemoryList, pub memory_blocks: Vec, + process_inspector: ProcessInspector, } #[derive(Debug, Clone)] @@ -137,6 +138,7 @@ impl MinidumpWriterConfig { crashing_thread_context: Default::default(), stop_timeout: STOP_TIMEOUT, direct_auxv_dump_info: Default::default(), + process_inspector: ProcessInspector::local(), } } @@ -243,6 +245,7 @@ impl MinidumpWriterConfig { crash_context: self.crash_context, app_memory: self.app_memory, memory_blocks: self.memory_blocks, + process_inspector: self.process_inspector, } } } @@ -515,7 +518,10 @@ impl MinidumpWriter { } /// Suspends a thread by attaching to it. - fn suspend_thread(child: Pid) -> Result<(), WriterError> { + fn suspend_thread( + _process_inspector: &ProcessInspector, + child: Pid, + ) -> Result<(), WriterError> { use WriterError::PtraceAttachError as AttachErr; let pid = nix::unistd::Pid::from_raw(child); @@ -561,7 +567,7 @@ impl MinidumpWriter { // We thus test the stack pointer and exclude any threads that are part of // the seccomp sandbox's trusted code. let skip_thread; - let regs = thread_info::ThreadInfo::getregs(pid.into()); + let regs = _process_inspector.get_gen_regs(pid.into()); if let Ok(regs) = regs { #[cfg(target_arch = "x86_64")] { @@ -592,13 +598,15 @@ impl MinidumpWriter { // If the thread either disappeared before we could attach to it, or if // it was part of the seccomp sandbox's trusted code, it is OK to // silently drop it from the minidump. - self.threads.retain(|x| match Self::suspend_thread(x.tid) { - Ok(()) => true, - Err(e) => { - soft_errors.push(e); - false - } - }); + self.threads.retain( + |x| match Self::suspend_thread(&self.process_inspector, x.tid) { + Ok(()) => true, + Err(e) => { + soft_errors.push(e); + false + } + }, + ); self.threads_suspended = true; @@ -755,7 +763,7 @@ impl MinidumpWriter { return Err(ThreadInfoError::IndexOutOfBounds(index, self.threads.len())); } - ThreadInfo::create(self.process_id, self.threads[index].tid) + ThreadInfo::create(&self.process_inspector, self.threads[index].tid) } // Returns a valid stack pointer and the mapping that contains the stack. diff --git a/src/linux/mod.rs b/src/linux/mod.rs index 68b870be..33c3672f 100644 --- a/src/linux/mod.rs +++ b/src/linux/mod.rs @@ -1,19 +1,24 @@ // `WriterError` is large and clippy doesn't like that, but not a huge deal atm #![allow(clippy::result_large_err)] -#[cfg(target_os = "android")] -mod android; +pub use maps_reader::LINUX_GATE_LIBRARY_NAME; + pub mod app_memory; -pub(crate) mod auxv; pub mod crash_context; -mod dso_debug; -mod dumper_cpu_info; pub mod maps_reader; pub mod minidump_writer; pub mod module_reader; pub mod process_reader; -mod serializers; pub mod thread_info; -pub use maps_reader::LINUX_GATE_LIBRARY_NAME; +pub(crate) mod auxv; + +mod dso_debug; +mod dumper_cpu_info; +mod process_inspection; +mod serializers; + +#[cfg(target_os = "android")] +mod android; + pub type Pid = i32; diff --git a/src/linux/process_inspection/mod.rs b/src/linux/process_inspection/mod.rs new file mode 100644 index 00000000..f7d758e1 --- /dev/null +++ b/src/linux/process_inspection/mod.rs @@ -0,0 +1,141 @@ +use { + core::{ffi::c_void, mem}, + nix::errno::Errno, + regs::*, + std::{ + fs::File, + io::{self, Read}, + path::Path, + }, +}; + +pub mod regs; + +#[cfg(target_env = "gnu")] +type PtraceRequestType = core::ffi::c_uint; + +#[cfg(not(target_env = "gnu"))] +type PtraceRequestType = core::ffi::c_int; + +#[derive(Debug)] +pub struct ProcessInspector { + _private: (), // Placeholder to force API usage for creation +} + +impl ProcessInspector { + pub fn local() -> Self { + ProcessInspector { _private: () } + } + + pub fn read_file(&self, path: impl AsRef) -> io::Result { + File::open(path) + } + + pub fn get_gen_regs(&self, tid: libc::pid_t) -> nix::Result { + getregset(tid).or_else(|_| getregs(tid)) + } + + pub fn get_fp_regs(&self, tid: libc::pid_t) -> nix::Result { + getfpregset(tid).or_else(|_| getfpregs(tid)) + } + + #[cfg(target_arch = "x86")] + pub fn get_fpx_regs(&self, tid: libc::pid_t) -> nix::Result { + const PTRACE_GETFPXREGS: PtraceRequestType = 18; + unsafe { ptrace_getregs::(PTRACE_GETFPXREGS, tid) } + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn ptrace_peekuser( + &self, + pid: libc::pid_t, + addr: usize, + ) -> nix::Result<[u8; mem::size_of::()]> { + // Since ptrace() is vararg, best to explicitly state arg types + let addr: *mut libc::c_void = addr as *mut libc::c_void; + let data: *mut libc::c_void = core::ptr::null_mut(); + Errno::set_raw(0); + let rv = unsafe { libc::ptrace(libc::PTRACE_PEEKUSER, pid, addr, data) }; + if rv == -1 && Errno::last_raw() != 0 { + Err(Errno::last()) + } else { + Ok(rv.to_ne_bytes()) + } + } +} + +fn getregset(_pid: libc::pid_t) -> nix::Result { + #[cfg(target_arch = "arm")] + { + Err(Errno::ENOTSUP) + } + #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))] + { + const NT_PRSTATUS: usize = 1; + ptrace_getregset(NT_PRSTATUS, _pid) + } +} + +fn getregs(pid: libc::pid_t) -> nix::Result { + const PTRACE_GETREGS: PtraceRequestType = 12; + unsafe { ptrace_getregs::(PTRACE_GETREGS, pid) } +} + +fn getfpregset(pid: libc::pid_t) -> nix::Result { + #[cfg(target_arch = "arm")] + { + const NT_ARM_VFP: usize = 0x400; + ptrace_getregset(NT_ARM_VFP, pid) + } + #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))] + { + const NT_PRFPREGSET: usize = 2; + ptrace_getregset(NT_PRFPREGSET, pid) + } +} + +fn getfpregs(_pid: libc::pid_t) -> nix::Result { + #[cfg(target_arch = "arm")] + { + Err(Errno::ENOTSUP) + } + #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))] + { + const PTRACE_GETFPREGS: PtraceRequestType = 14; + unsafe { ptrace_getregs::(PTRACE_GETFPREGS, _pid) } + } +} + +/// Safety: RequestType and T must agree on the size of the returned type +unsafe fn ptrace_getregs(request: PtraceRequestType, pid: libc::pid_t) -> nix::Result { + let mut output = mem::MaybeUninit::::uninit(); + + // Since ptrace() is vararg, best to explicitly state arg types + let addr: *mut c_void = core::ptr::null_mut(); + let data: *mut c_void = output.as_mut_ptr().cast(); + let res = unsafe { libc::ptrace(request, pid, addr, data) }; + Errno::result(res)?; + Ok(unsafe { output.assume_init() }) +} + +fn ptrace_getregset(regset_type: usize, pid: libc::pid_t) -> nix::Result { + let mut output = mem::MaybeUninit::::uninit(); + let mut io = libc::iovec { + iov_base: output.as_mut_ptr().cast(), + iov_len: mem::size_of::(), + }; + + // Since ptrace() is vararg, best to explicitly state arg types + let addr: *mut c_void = regset_type as *mut c_void; + let data: *mut c_void = (&raw mut io).cast(); + let res = unsafe { libc::ptrace(libc::PTRACE_GETREGSET, pid, addr, data) }; + Errno::result(res)?; + + // PTRACE_GETREGSET returns the number of bytes actually read in iov_len. Need to ensure + // all bytes of T are actually initialized + if io.iov_len != mem::size_of::() { + return Err(Errno::EINVAL); + } + + Ok(unsafe { output.assume_init() }) +} diff --git a/src/linux/process_inspection/regs.rs b/src/linux/process_inspection/regs.rs new file mode 100644 index 00000000..b98c8f06 --- /dev/null +++ b/src/linux/process_inspection/regs.rs @@ -0,0 +1,138 @@ +pub use imp::*; + +#[cfg(target_arch = "aarch64")] +mod imp { + pub type GenRegs = libc::user_regs_struct; + pub type FpRegs = user_fpsimd_struct; + + /// https://github.com/rust-lang/libc/pull/2719 + #[repr(C)] + #[derive(Debug)] + pub struct user_fpsimd_struct { + pub vregs: [u128; 32], + pub fpsr: u32, + pub fpcr: u32, + } +} + +#[cfg(target_arch = "arm")] +mod imp { + pub type GenRegs = user_regs_struct; + pub type FpRegs = user_fpregs_struct; + + // Not defined by libc because this works only for cores support VFP + #[repr(C)] + #[derive(Debug, Eq, Hash, PartialEq, Copy, Clone, Default)] + pub struct user_fpregs_struct { + pub fpregs: [u64; 32], + pub fpscr: u32, + } + + #[repr(C)] + #[derive(Debug, Eq, Hash, PartialEq, Copy, Clone, Default)] + pub struct user_regs_struct { + pub uregs: [u32; 18], + } +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +mod imp { + pub type GenRegs = user_regs_struct; + pub type FpRegs = user_fpregs_struct; + + #[cfg(target_arch = "x86")] + pub type FpxRegs = user_fpxregs_struct; + + pub const NUM_DEBUG_REGISTERS: usize = 8; + + #[cfg(target_arch = "x86")] + pub type RegType = u32; + + #[cfg(target_arch = "x86_64")] + pub type RegType = u64; + + #[cfg(all(not(target_os = "android"), target_arch = "x86"))] + pub use libc::user_fpxregs_struct; + #[cfg(not(all(target_os = "android", target_arch = "x86")))] + pub use libc::{user, user_fpregs_struct, user_regs_struct}; + + // Not defined by libc on Android + #[cfg(all(target_os = "android", target_arch = "x86"))] + #[allow(non_camel_case_types)] + #[repr(C)] + pub struct user_regs_struct { + pub ebx: libc::c_long, + pub ecx: libc::c_long, + pub edx: libc::c_long, + pub esi: libc::c_long, + pub edi: libc::c_long, + pub ebp: libc::c_long, + pub eax: libc::c_long, + pub xds: libc::c_long, + pub xes: libc::c_long, + pub xfs: libc::c_long, + pub xgs: libc::c_long, + pub orig_eax: libc::c_long, + pub eip: libc::c_long, + pub xcs: libc::c_long, + pub eflags: libc::c_long, + pub esp: libc::c_long, + pub xss: libc::c_long, + } + + // Not defined by libc on Android + #[cfg(all(target_os = "android", target_arch = "x86"))] + #[allow(non_camel_case_types)] + #[repr(C)] + pub struct user_fpxregs_struct { + pub cwd: libc::c_ushort, + pub swd: libc::c_ushort, + pub twd: libc::c_ushort, + pub fop: libc::c_ushort, + pub fip: libc::c_long, + pub fcs: libc::c_long, + pub foo: libc::c_long, + pub fos: libc::c_long, + pub mxcsr: libc::c_long, + __reserved: libc::c_long, + pub st_space: [libc::c_long; 32], + pub xmm_space: [libc::c_long; 32], + padding: [libc::c_long; 56], + } + + // Not defined by libc on Android + #[cfg(all(target_os = "android", target_arch = "x86"))] + #[allow(non_camel_case_types)] + #[repr(C)] + pub struct user_fpregs_struct { + pub cwd: libc::c_long, + pub swd: libc::c_long, + pub twd: libc::c_long, + pub fip: libc::c_long, + pub fcs: libc::c_long, + pub foo: libc::c_long, + pub fos: libc::c_long, + pub st_space: [libc::c_long; 20], + } + + #[cfg(all(target_os = "android", target_arch = "x86"))] + #[allow(non_camel_case_types)] + #[repr(C)] + pub struct user { + pub regs: user_regs_struct, + pub u_fpvalid: libc::c_long, + pub i387: user_fpregs_struct, + pub u_tsize: libc::c_ulong, + pub u_dsize: libc::c_ulong, + pub u_ssize: libc::c_ulong, + pub start_code: libc::c_ulong, + pub start_stack: libc::c_ulong, + pub signal: libc::c_long, + __reserved: libc::c_int, + pub u_ar0: *mut user_regs_struct, + pub u_fpstate: *mut user_fpregs_struct, + pub magic: libc::c_ulong, + pub u_comm: [libc::c_char; 32], + pub u_debugreg: [libc::c_int; 8], + } +} diff --git a/src/linux/thread_info/aarch64.rs b/src/linux/thread_info/aarch64.rs index 152ba6d9..8d68e099 100644 --- a/src/linux/thread_info/aarch64.rs +++ b/src/linux/thread_info/aarch64.rs @@ -1,20 +1,8 @@ use { - super::{Pid, PtraceRequestType, ThreadInfoError}, + super::{Pid, ProcessInspector, ThreadInfoError, regs::*}, crate::minidump_cpu::{FP_REG_COUNT, GP_REG_COUNT, RawContextCPU}, }; -/// https://github.com/rust-lang/libc/pull/2719 -#[derive(Debug)] -#[allow(non_camel_case_types)] -pub struct user_fpsimd_struct { - pub vregs: [u128; 32], - pub fpsr: u32, - pub fpcr: u32, -} - -type Result = std::result::Result; - -#[cfg(target_arch = "aarch64")] #[derive(Debug)] pub struct ThreadInfoAarch64 { pub stack_pointer: usize, @@ -29,26 +17,6 @@ impl ThreadInfoAarch64 { self.regs.pc as usize } - fn getregset(pid: Pid) -> Result { - const NT_PRSTATUS: usize = 1; - super::ptrace_getregset(NT_PRSTATUS, pid) - } - - fn getregs(pid: Pid) -> Result { - const PTRACE_GETREGS: PtraceRequestType = 12; - unsafe { super::ptrace_getregs::(PTRACE_GETREGS, pid) } - } - - fn getfpregset(pid: Pid) -> Result { - const NT_PRFPREGSET: usize = 2; - super::ptrace_getregset(NT_PRFPREGSET, pid) - } - - fn getfpregs(pid: Pid) -> Result { - const PTRACE_GETFPREGS: PtraceRequestType = 14; - unsafe { super::ptrace_getregs::(PTRACE_GETFPREGS, pid) } - } - pub fn fill_cpu_context(&self, out: &mut RawContextCPU) { out.context_flags = minidump_common::format::ContextFlagsArm64Old::CONTEXT_ARM64_OLD_FULL.bits() as u64; @@ -66,10 +34,14 @@ impl ThreadInfoAarch64 { out.float_regs[..FP_REG_COUNT].copy_from_slice(&self.fpregs.vregs[..FP_REG_COUNT]); } - pub fn create(_pid: Pid, tid: Pid) -> Result { - let (ppid, tgid) = super::get_ppid_and_tgid(tid)?; - let regs = Self::getregset(tid).or_else(|_| Self::getregs(tid))?; - let fpregs = Self::getfpregset(tid).or_else(|_| Self::getfpregs(tid))?; + pub fn create(process_inspector: &ProcessInspector, tid: Pid) -> Result { + let (ppid, tgid) = super::get_ppid_and_tgid(process_inspector, tid)?; + let regs = process_inspector + .get_gen_regs(tid) + .map_err(ThreadInfoError::PtraceError)?; + let fpregs = process_inspector + .get_fp_regs(tid) + .map_err(ThreadInfoError::PtraceError)?; let stack_pointer = regs.sp as usize; diff --git a/src/linux/thread_info/arm.rs b/src/linux/thread_info/arm.rs index 03637840..792c23ba 100644 --- a/src/linux/thread_info/arm.rs +++ b/src/linux/thread_info/arm.rs @@ -1,25 +1,8 @@ use { - super::{Pid, PtraceRequestType, ThreadInfoError}, + super::{Pid, ProcessInspector, ThreadInfoError, regs::*}, crate::minidump_cpu::RawContextCPU, }; -type Result = std::result::Result; - -// Not defined by libc because this works only for cores support VFP -#[allow(non_camel_case_types)] -#[repr(C)] -#[derive(Debug, Eq, Hash, PartialEq, Copy, Clone, Default)] -pub struct user_fpregs_struct { - pub fpregs: [u64; 32], - pub fpscr: u32, -} - -#[repr(C)] -#[derive(Debug, Eq, Hash, PartialEq, Copy, Clone, Default)] -pub struct user_regs_struct { - uregs: [u32; 18], -} - #[derive(Debug)] pub struct ThreadInfoArm { pub stack_pointer: usize, @@ -30,16 +13,6 @@ pub struct ThreadInfoArm { } impl ThreadInfoArm { - fn getfpregs(pid: Pid) -> Result { - const NT_ARM_VFP: usize = 0x400; - super::ptrace_getregset(NT_ARM_VFP, pid) - } - - fn getregs(pid: Pid) -> Result { - const PTRACE_GETREGS: PtraceRequestType = 12; - unsafe { super::ptrace_getregs::(PTRACE_GETREGS, pid) } - } - pub fn get_instruction_pointer(&self) -> usize { self.regs.uregs[15] as usize } @@ -54,10 +27,14 @@ impl ThreadInfoArm { out.float_save.regs = self.fpregs.fpregs; } - pub fn create(_pid: Pid, tid: Pid) -> Result { - let (ppid, tgid) = super::get_ppid_and_tgid(tid)?; - let regs = Self::getregs(tid)?; - let fpregs = Self::getfpregs(tid).unwrap_or(Default::default()); + pub fn create(process_inspector: &ProcessInspector, tid: Pid) -> Result { + let (ppid, tgid) = super::get_ppid_and_tgid(process_inspector, tid)?; + let regs = process_inspector + .get_gen_regs(tid) + .map_err(ThreadInfoError::PtraceError)?; + let fpregs = process_inspector + .get_fp_regs(tid) + .map_err(ThreadInfoError::PtraceError)?; let stack_pointer = regs.uregs[13] as usize; diff --git a/src/linux/thread_info/mod.rs b/src/linux/thread_info/mod.rs index b623cfa0..863fc3a1 100644 --- a/src/linux/thread_info/mod.rs +++ b/src/linux/thread_info/mod.rs @@ -1,17 +1,16 @@ use { - super::{Pid, serializers::*}, + super::{ + Pid, + process_inspection::{ProcessInspector, regs}, + serializers::*, + }, crate::serializers::*, - nix::errno::Errno, std::{ - ffi::c_void, io::{self, BufRead}, path, }, }; -#[cfg(target_arch = "x86_64")] -pub use x86::copy_u32_registers; - type Result = std::result::Result; #[derive(thiserror::Error, Debug, serde::Serialize)] @@ -46,6 +45,9 @@ cfg_if::cfg_if! { if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { mod x86; pub type ThreadInfo = x86::ThreadInfoX86; + + #[cfg(target_arch = "x86_64")] + pub use x86::copy_u32_registers; } else if #[cfg(target_arch = "arm")] { mod arm; pub type ThreadInfo = arm::ThreadInfoArm; @@ -55,18 +57,12 @@ cfg_if::cfg_if! { } } -#[cfg(target_env = "gnu")] -type PtraceRequestType = core::ffi::c_uint; - -#[cfg(not(target_env = "gnu"))] -type PtraceRequestType = core::ffi::c_int; - -fn get_ppid_and_tgid(tid: Pid) -> Result<(Pid, Pid)> { +fn get_ppid_and_tgid(process_inspector: &ProcessInspector, tid: Pid) -> Result<(Pid, Pid)> { let mut ppid = -1; let mut tgid = -1; let status_path = path::PathBuf::from(format!("/proc/{tid}/status")); - let status_file = std::fs::File::open(status_path)?; + let status_file = process_inspector.read_file(status_path)?; for line in io::BufReader::new(status_file).lines() { let l = line?; let start = l @@ -97,37 +93,3 @@ fn get_ppid_and_tgid(tid: Pid) -> Result<(Pid, Pid)> { } Ok((ppid, tgid)) } - -/// Safety: RequestType and T must agree on the size of the returned type -unsafe fn ptrace_getregs(request: PtraceRequestType, pid: libc::pid_t) -> Result { - let mut output = std::mem::MaybeUninit::uninit(); - - // Since ptrace() is vararg, best to explicitly state arg types - let addr: *mut c_void = std::ptr::null_mut(); - let data: *mut c_void = (&raw mut output).cast(); - let res = unsafe { libc::ptrace(request, pid, addr, data) }; - Errno::result(res).map_err(ThreadInfoError::PtraceError)?; - Ok(unsafe { output.assume_init() }) -} - -fn ptrace_getregset(regset_type: usize, pid: libc::pid_t) -> Result { - let mut output = std::mem::MaybeUninit::::uninit(); - let mut io = libc::iovec { - iov_base: output.as_mut_ptr().cast(), - iov_len: std::mem::size_of::(), - }; - - // Since ptrace() is vararg, best to explicitly state arg types - let addr: *mut c_void = regset_type as *mut c_void; - let data: *mut c_void = (&raw mut io).cast(); - let res = unsafe { libc::ptrace(libc::PTRACE_GETREGSET, pid, addr, data) }; - Errno::result(res).map_err(ThreadInfoError::PtraceError)?; - - // PTRACE_GETREGSET returns the number of bytes actually read in iov_len. Need to ensure - // all bytes of T are actually initialized - if io.iov_len != std::mem::size_of::() { - return Err(ThreadInfoError::PtraceError(Errno::EINVAL)); - } - - Ok(unsafe { output.assume_init() }) -} diff --git a/src/linux/thread_info/x86.rs b/src/linux/thread_info/x86.rs index 155ca430..0f2e2d8f 100644 --- a/src/linux/thread_info/x86.rs +++ b/src/linux/thread_info/x86.rs @@ -1,106 +1,10 @@ use { - super::{Pid, PtraceRequestType, ThreadInfoError}, + super::{Pid, ProcessInspector, ThreadInfoError, regs::*}, crate::{minidump_cpu::RawContextCPU, minidump_format::format}, core::mem, - nix::errno::Errno, scroll::Pwrite, }; -#[cfg(all(not(target_os = "android"), target_arch = "x86"))] -use libc::user_fpxregs_struct; -#[cfg(not(all(target_os = "android", target_arch = "x86")))] -use libc::{user, user_fpregs_struct, user_regs_struct}; - -type Result = std::result::Result; - -#[cfg(target_arch = "x86")] -type RegType = u32; - -#[cfg(target_arch = "x86_64")] -type RegType = u64; - -// Not defined by libc on Android -#[cfg(all(target_os = "android", target_arch = "x86"))] -#[allow(non_camel_case_types)] -#[repr(C)] -pub struct user_regs_struct { - pub ebx: libc::c_long, - pub ecx: libc::c_long, - pub edx: libc::c_long, - pub esi: libc::c_long, - pub edi: libc::c_long, - pub ebp: libc::c_long, - pub eax: libc::c_long, - pub xds: libc::c_long, - pub xes: libc::c_long, - pub xfs: libc::c_long, - pub xgs: libc::c_long, - pub orig_eax: libc::c_long, - pub eip: libc::c_long, - pub xcs: libc::c_long, - pub eflags: libc::c_long, - pub esp: libc::c_long, - pub xss: libc::c_long, -} - -// Not defined by libc on Android -#[cfg(all(target_os = "android", target_arch = "x86"))] -#[allow(non_camel_case_types)] -#[repr(C)] -pub struct user_fpxregs_struct { - pub cwd: libc::c_ushort, - pub swd: libc::c_ushort, - pub twd: libc::c_ushort, - pub fop: libc::c_ushort, - pub fip: libc::c_long, - pub fcs: libc::c_long, - pub foo: libc::c_long, - pub fos: libc::c_long, - pub mxcsr: libc::c_long, - __reserved: libc::c_long, - pub st_space: [libc::c_long; 32], - pub xmm_space: [libc::c_long; 32], - padding: [libc::c_long; 56], -} - -// Not defined by libc on Android -#[cfg(all(target_os = "android", target_arch = "x86"))] -#[allow(non_camel_case_types)] -#[repr(C)] -pub struct user_fpregs_struct { - pub cwd: libc::c_long, - pub swd: libc::c_long, - pub twd: libc::c_long, - pub fip: libc::c_long, - pub fcs: libc::c_long, - pub foo: libc::c_long, - pub fos: libc::c_long, - pub st_space: [libc::c_long; 20], -} - -#[cfg(all(target_os = "android", target_arch = "x86"))] -#[allow(non_camel_case_types)] -#[repr(C)] -pub struct user { - pub regs: user_regs_struct, - pub u_fpvalid: libc::c_long, - pub i387: user_fpregs_struct, - pub u_tsize: libc::c_ulong, - pub u_dsize: libc::c_ulong, - pub u_ssize: libc::c_ulong, - pub start_code: libc::c_ulong, - pub start_stack: libc::c_ulong, - pub signal: libc::c_long, - __reserved: libc::c_int, - pub u_ar0: *mut user_regs_struct, - pub u_fpstate: *mut user_fpregs_struct, - pub magic: libc::c_ulong, - pub u_comm: [libc::c_char; 32], - pub u_debugreg: [libc::c_int; 8], -} - -const NUM_DEBUG_REGISTERS: usize = 8; - pub struct ThreadInfoX86 { pub stack_pointer: usize, pub tgid: Pid, // thread group id @@ -113,41 +17,21 @@ pub struct ThreadInfoX86 { } impl ThreadInfoX86 { - fn getregset(pid: Pid) -> Result { - const NT_PRSTATUS: usize = 1; - super::ptrace_getregset(NT_PRSTATUS, pid) - } - - pub fn getregs(pid: Pid) -> Result { - const PTRACE_GETREGS: PtraceRequestType = 12; - unsafe { super::ptrace_getregs::(PTRACE_GETREGS, pid) } - } - - fn getfpregset(pid: Pid) -> Result { - const NT_PRFPREGSET: usize = 2; - super::ptrace_getregset(NT_PRFPREGSET, pid) - } - - fn getfpregs(pid: Pid) -> Result { - const PTRACE_GETFPREGS: PtraceRequestType = 14; - unsafe { super::ptrace_getregs::(PTRACE_GETFPREGS, pid) } - } - - #[cfg(target_arch = "x86")] - fn getfpxregs(pid: Pid) -> Result { - const PTRACE_GETFPXREGS: PtraceRequestType = 18; - unsafe { super::ptrace_getregs::(PTRACE_GETFPXREGS, pid) } - } - - pub fn create(_pid: Pid, tid: Pid) -> Result { - let (ppid, tgid) = super::get_ppid_and_tgid(tid)?; - let regs = Self::getregset(tid).or_else(|_| Self::getregs(tid))?; - let fpregs = Self::getfpregset(tid).or_else(|_| Self::getfpregs(tid))?; + pub fn create(process_inspector: &ProcessInspector, tid: Pid) -> Result { + let (ppid, tgid) = super::get_ppid_and_tgid(process_inspector, tid)?; + let regs = process_inspector + .get_gen_regs(tid) + .map_err(ThreadInfoError::PtraceError)?; + let fpregs = process_inspector + .get_fp_regs(tid) + .map_err(ThreadInfoError::PtraceError)?; #[cfg(target_arch = "x86")] let fpxregs = { if cfg!(target_feature = "fxsr") { - Self::getfpxregs(tid)? + process_inspector + .get_fpx_regs(tid) + .map_err(ThreadInfoError::PtraceError)? } else { unsafe { mem::zeroed() } } @@ -157,7 +41,9 @@ impl ThreadInfoX86 { let debug_offset = mem::offset_of!(user, u_debugreg); for (idx, dreg) in dregs.iter_mut().enumerate() { - let chunk = ptrace_peekuser(tid, debug_offset + idx * mem::size_of::())?; + let chunk = process_inspector + .ptrace_peekuser(tid, debug_offset + idx * mem::size_of::()) + .map_err(ThreadInfoError::PtraceError)?; *dreg = RegType::from_ne_bytes(chunk[0..mem::size_of::()].try_into().unwrap()); } @@ -353,19 +239,6 @@ impl ThreadInfoX86 { } } -fn ptrace_peekuser(pid: libc::pid_t, addr: usize) -> Result<[u8; mem::size_of::()]> { - // Since ptrace() is vararg, best to explicitly state arg types - let addr: *mut libc::c_void = addr as *mut libc::c_void; - let data: *mut libc::c_void = core::ptr::null_mut(); - Errno::set_raw(0); - let rv = unsafe { libc::ptrace(libc::PTRACE_PEEKUSER, pid, addr, data) }; - if rv == -1 && Errno::last_raw() != 0 { - Err(ThreadInfoError::PtraceError(Errno::last())) - } else { - Ok(rv.to_ne_bytes()) - } -} - #[cfg(target_arch = "x86_64")] pub fn copy_u32_registers(dst: &mut [u128], src: &[u32]) { assert_eq!(mem::size_of_val(src), mem::size_of_val(dst));