diff --git a/crates/linux/process-backend/src/lib.rs b/crates/linux/process-backend/src/lib.rs index 014394c9..38b513e2 100644 --- a/crates/linux/process-backend/src/lib.rs +++ b/crates/linux/process-backend/src/lib.rs @@ -5,6 +5,7 @@ use core::ffi::CStr; use regs::*; pub use drop_fail_handler::Handler as DropFailHandler; +pub use wrapper::Stat; #[macro_use] mod drop_fail_handler; @@ -44,7 +45,7 @@ pub trait Backend: core::fmt::Debug { path: &CStr, offset: u64, ) -> Result>; - fn stat_file(&self, path: &CStr) -> Result; + fn stat_file(&self, path: &CStr) -> Result; fn read_file<'a>(&'a self, path: &CStr) -> Result>; fn read_dir<'a>(&'a self, path: &CStr) -> Result>; fn read_link(&self, path: &CStr, buf: &mut [u8]) -> Result; diff --git a/crates/linux/process-backend/src/local/error.rs b/crates/linux/process-backend/src/local/error.rs index 79feefc3..00b46634 100644 --- a/crates/linux/process-backend/src/local/error.rs +++ b/crates/linux/process-backend/src/local/error.rs @@ -36,6 +36,8 @@ pub enum Error { NotSupported, #[error("failed to get registers: {0}")] GetRegistersFailed(c_int), + #[error("unexpected size for register set. Expected: {0}, actual: {0}")] + UnexpectedRegisterSetSize(usize, usize), #[error("failed to map memory: {0}")] MMapfailed(c_int), #[error("the start position of a mapping is past its end position")] diff --git a/crates/linux/process-backend/src/local/mod.rs b/crates/linux/process-backend/src/local/mod.rs index 4dbcf076..4b727b5c 100644 --- a/crates/linux/process-backend/src/local/mod.rs +++ b/crates/linux/process-backend/src/local/mod.rs @@ -1,5 +1,5 @@ use crate::{ - ProcessReaderKind, + ProcessReaderKind, Stat, regs::*, wrapper::{OwnedFd, errno, set_errno}, }; @@ -20,12 +20,6 @@ mod syscall_invoker; pub type Result = core::result::Result; -#[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 Backend { pid: pid_t, @@ -113,11 +107,13 @@ impl Backend { MappedModuleMemoryReader::new(&mut self.syscall_invoker.borrow_mut(), path, offset) } - pub fn stat_file(&self, path: &CStr) -> Result { + pub fn stat_file(&self, path: &CStr) -> Result { let mut output = unsafe { mem::zeroed::() }; self.standard_syscall(|| unsafe { libc::stat(path.as_ptr(), &mut output) }) .map_err(Error::StatFailed)?; - Ok(output) + Ok(Stat { + st_mode: output.st_mode, + }) } pub fn read_file(&self, path: &CStr) -> Result { @@ -152,17 +148,15 @@ impl Backend { } pub fn get_gen_regs(&self, tid: libc::pid_t) -> Result { - self.getregset(tid).or_else(|_| self.getregs(tid)) + self.get_regs::(tid) } - pub fn get_fp_regs(&self, tid: libc::pid_t) -> Result { - self.getfpregset(tid).or_else(|_| self.getfpregs(tid)) + self.get_regs::(tid) } #[cfg(target_arch = "x86")] pub fn get_fpx_regs(&self, tid: libc::pid_t) -> Result { - const PTRACE_GETFPXREGS: PtraceRequestType = 18; - unsafe { self.ptrace_getregs::(PTRACE_GETFPXREGS, tid) } + self.get_regs::(tid) } #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -204,87 +198,59 @@ impl Backend { .map_err(Error::OpenFileFailed) } - fn getregset(&self, _tid: libc::pid_t) -> Result { - #[cfg(target_arch = "arm")] - { - Err(Error::NotSupported) - } - #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))] - { - const NT_PRSTATUS: usize = 1; - self.ptrace_getregset(NT_PRSTATUS, _tid) - } - } - - fn getregs(&self, tid: libc::pid_t) -> Result { - const PTRACE_GETREGS: PtraceRequestType = 12; - unsafe { self.ptrace_getregs::(PTRACE_GETREGS, tid) } - } - - fn getfpregset(&self, tid: libc::pid_t) -> Result { - #[cfg(target_arch = "arm")] - { - const NT_ARM_VFP: usize = 0x400; - self.ptrace_getregset(NT_ARM_VFP, tid) - } - #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))] - { - const NT_PRFPREGSET: usize = 2; - self.ptrace_getregset(NT_PRFPREGSET, tid) - } + fn get_regs(&self, tid: libc::pid_t) -> Result { + self.ptrace_getregset::(tid).or_else(|e| { + if T::LEGACY_REQUEST.is_some() { + self.ptrace_getregs_legacy::(tid) + } else { + Err(e) + } + }) } - fn getfpregs(&self, _tid: libc::pid_t) -> Result { - #[cfg(target_arch = "arm")] - { - Err(Error::NotSupported) - } - #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))] - { - const PTRACE_GETFPREGS: PtraceRequestType = 14; - unsafe { self.ptrace_getregs::(PTRACE_GETFPREGS, _tid) } - } - } + fn ptrace_getregs_legacy(&self, tid: libc::pid_t) -> Result { + let Some(request) = T::LEGACY_REQUEST else { + return Err(Error::NotSupported); + }; - /// Safety: RequestType and T must agree on the size of the returned type - unsafe fn ptrace_getregs(&self, request: PtraceRequestType, tid: libc::pid_t) -> Result { - let mut output = mem::MaybeUninit::::uninit(); + let mut output = T::Output::default(); self.standard_syscall(|| unsafe { ptrace( request, tid, core::ptr::null_mut(), - output.as_mut_ptr().cast(), + (&raw mut output).cast(), ) }) .map_err(Error::GetRegistersFailed)?; - Ok(unsafe { output.assume_init() }) + Ok(output) } - fn ptrace_getregset(&self, regset_type: usize, tid: libc::pid_t) -> Result { - let mut output = mem::MaybeUninit::::uninit(); + fn ptrace_getregset(&self, tid: libc::pid_t) -> Result { + let output_size = size_of::(); + assert!(T::KERNEL_SIZE <= output_size); + + let mut output = T::Output::default(); let mut io = libc::iovec { - iov_base: output.as_mut_ptr().cast(), - iov_len: mem::size_of::(), + iov_base: (&raw mut output).cast(), + iov_len: T::KERNEL_SIZE, }; self.standard_syscall(|| unsafe { ptrace( libc::PTRACE_GETREGSET, tid, - regset_type as *mut _, + T::NOTE.0 as *mut _, (&raw mut io).cast(), ) }) .map_err(Error::GetRegistersFailed)?; - // 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::() { - Err(Error::GetRegistersFailed(libc::EINVAL))?; + if T::KERNEL_SIZE != io.iov_len { + return Err(Error::UnexpectedRegisterSetSize(T::KERNEL_SIZE, io.iov_len)); } - Ok(unsafe { output.assume_init() }) + Ok(output) } fn ptrace_detach(&self, tid: libc::pid_t) -> Result<()> { @@ -359,7 +325,7 @@ impl crate::Backend for Backend { Backend::map_module_into_memory(self, path, offset).map_err(crate::Error::Local) } - fn stat_file(&self, path: &CStr) -> crate::Result { + fn stat_file(&self, path: &CStr) -> crate::Result { Backend::stat_file(self, path).map_err(crate::Error::Local) } diff --git a/crates/linux/process-backend/src/regs.rs b/crates/linux/process-backend/src/regs.rs index b98c8f06..d5c52fa5 100644 --- a/crates/linux/process-backend/src/regs.rs +++ b/crates/linux/process-backend/src/regs.rs @@ -1,138 +1,451 @@ +// We define our own versions of the various constants/register-holding structures from the ptrace +// API of Linux for a few reasons: +// +// 1. The versions of them defined in the `libc` crate don't provide `Deserialize` or `Serialize` +// implementations, and we need that - So we'd have to maintain parallel versions of these +// structs anyway. +// +// 2. A lot of these structures/constants vary slightly by name and availability between glibc, +// bionic and musl, even though the kernel itself supports them. Doing it ourselves helps us +// maintain more consistency. +// +// 3. All of these structures are basically plain-old-data types under the hood, so we can avoid +// using MaybeUninit by deriving a `Default` implementation for them - something the `libc` +// crate doesn't do because it doesn't generalize to all types in libc. +// +// All information was obtained directly from the Linux Kernel source code, with a few +// modifications like explicitly-declared padding, and consistent use of fixed-width unsigned +// integer types. +// +// Kernel names will be used for these structs, and links to the relevant code in the kernel are +// included. +#![allow(unused)] + +#[cfg(target_env = "gnu")] +pub(crate) type PtraceRequestType = core::ffi::c_uint; + +#[cfg(not(target_env = "gnu"))] +pub(crate) type PtraceRequestType = core::ffi::c_int; + +/// A kernel-defined constant that names a register set as part of a call to +/// ptrace(PTRACE_GETREGSET) +pub(crate) struct RegSetNote(pub(crate) usize); + +/// Describes a register-set buffer that may be populated by `ptrace`. +/// +/// # Safety +/// +/// Implementors must guarantee all of the following: +/// +/// 1. `NOTE` identifies a `PTRACE_GETREGSET` register set whose first +/// `KERNEL_SIZE` bytes have the layout of the corresponding prefix +/// of `Output`. +/// +/// 2. If `LEGACY_REQUEST` is `Some`, that request writes the same register +/// representation, writes no more than `KERNEL_SIZE` bytes, and uses the +/// fourth `ptrace` argument as the output buffer. +/// +/// 3. `KERNEL_SIZE <= size_of::()`. +/// +/// 4. Every byte pattern the kernel may leave in the first `KERNEL_SIZE` +/// bytes, combined with the untouched bytes of `Output::default()`, +/// must constitute a valid `Output`, including when the syscall fails +/// after a partial write. +/// +/// 5. `Output` must not require drop glue. +pub(crate) unsafe trait PtraceRegisterSet { + const NOTE: RegSetNote; + /// Legacy ptrace request to fall back on if PTRACE_GETREGSET fails + const LEGACY_REQUEST: Option; + const KERNEL_SIZE: usize; + type Output: Copy + Default; +} + +pub(crate) enum GenRegsTag {} +pub(crate) enum FpRegsTag {} + +// https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/include/uapi/linux/elf.h?h=v7.1.8 +const NT_PRSTATUS: RegSetNote = RegSetNote(1); +const NT_PRFPREG: RegSetNote = RegSetNote(2); +const NT_ARM_VFP: RegSetNote = RegSetNote(0x400); +const NT_PRXFPREG: RegSetNote = RegSetNote(0x46e62b7f); + pub use imp::*; +// https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm64/kernel/ptrace.c?h=v7.1.8#n1614 #[cfg(target_arch = "aarch64")] mod imp { - pub type GenRegs = libc::user_regs_struct; - pub type FpRegs = user_fpsimd_struct; + use super::*; + + pub type GenRegs = user_pt_regs; + pub type FpRegs = user_fpsimd_state; - /// https://github.com/rust-lang/libc/pull/2719 + // https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm64/include/uapi/asm/ptrace.h?h=v7.1.8#n88 #[repr(C)] - #[derive(Debug)] - pub struct user_fpsimd_struct { + #[derive(Clone, Copy, Debug, Default, serde::Deserialize, serde::Serialize)] + pub struct user_pt_regs { + pub regs: [u64; 31], + pub sp: u64, + pub pc: u64, + pub pstate: u64, + } + + // https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm64/include/uapi/asm/ptrace.h?h=v7.1.8#n95 + #[repr(C)] + #[derive(Clone, Copy, Debug, Default, serde::Deserialize, serde::Serialize)] + pub struct user_fpsimd_state { pub vregs: [u128; 32], pub fpsr: u32, pub fpcr: u32, + #[serde(skip)] + pub __reserved: [u32; 2], + } + + unsafe impl PtraceRegisterSet for GenRegsTag { + const NOTE: RegSetNote = NT_PRSTATUS; + const LEGACY_REQUEST: Option = None; + const KERNEL_SIZE: usize = size_of::(); + type Output = user_pt_regs; } + + unsafe impl PtraceRegisterSet for FpRegsTag { + const NOTE: RegSetNote = NT_PRFPREG; + const LEGACY_REQUEST: Option = None; + const KERNEL_SIZE: usize = size_of::(); + type Output = user_fpsimd_state; + } + + const _: () = { + assert!(size_of::() == 272); + assert!(align_of::() == 8); + assert!(size_of::() == 528); + assert!(align_of::() == 16); + }; } +// https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm/kernel/ptrace.c?h=v7.1.8#n678 #[cfg(target_arch = "arm")] mod imp { - pub type GenRegs = user_regs_struct; - pub type FpRegs = user_fpregs_struct; + use super::*; + + pub type GenRegs = pt_regs; + pub type FpRegs = user_vfp; - // Not defined by libc because this works only for cores support VFP + // https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm/include/uapi/asm/ptrace.h?h=v7.1.8 + const PTRACE_GETREGS: PtraceRequestType = 12; + const PTRACE_GETVFPREGS: PtraceRequestType = 27; + + // https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm/include/uapi/asm/ptrace.h?h=v7.1.8#n149 + const ARM_VFPREGS_SIZE: usize = 32 * 8 /*fpregs*/ + 4 /*fpscr*/; + + // https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm/include/asm/user.h?h=v7.1.8#n85 + // + // Note: This struct has 4 bytes of padding at the end due to its alignment requirements, but + // the kernel does not write those 4 bytes as part of a ptrace call - it will only write 260 + // bytes and not 264 (the size of this struct with trailing padding). + // + // We capture that by setting the `KERNEL_SIZE` to `ARM_VFPREGS_SIZE` instead of + // `size_of::()`. #[repr(C)] - #[derive(Debug, Eq, Hash, PartialEq, Copy, Clone, Default)] - pub struct user_fpregs_struct { + #[derive(Clone, Copy, Debug, Default, serde::Deserialize, serde::Serialize)] + pub struct user_vfp { pub fpregs: [u64; 32], pub fpscr: u32, } + // https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm/include/asm/ptrace.h?h=v7.1.8#n16 #[repr(C)] - #[derive(Debug, Eq, Hash, PartialEq, Copy, Clone, Default)] - pub struct user_regs_struct { + #[derive(Clone, Copy, Debug, Default, serde::Deserialize, serde::Serialize)] + pub struct pt_regs { pub uregs: [u32; 18], } + + unsafe impl PtraceRegisterSet for GenRegsTag { + const NOTE: RegSetNote = NT_PRSTATUS; + const LEGACY_REQUEST: Option = Some(PTRACE_GETREGS); + const KERNEL_SIZE: usize = size_of::(); + type Output = pt_regs; + } + + unsafe impl PtraceRegisterSet for FpRegsTag { + const NOTE: RegSetNote = NT_ARM_VFP; + const LEGACY_REQUEST: Option = Some(PTRACE_GETVFPREGS); + const KERNEL_SIZE: usize = ARM_VFPREGS_SIZE; + type Output = user_vfp; + } + + const _: () = { + assert!(size_of::() == 72); + assert!(align_of::() == 4); + assert!(size_of::() == 264); + assert!(align_of::() == 8); + }; } +// https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/x86/kernel/ptrace.c?h=v7.1.8#n1298 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] mod imp { - pub type GenRegs = user_regs_struct; - pub type FpRegs = user_fpregs_struct; + use super::*; - #[cfg(target_arch = "x86")] - pub type FpxRegs = user_fpxregs_struct; + // https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/x86/include/uapi/asm/ptrace-abi.h?h=v7.1.8 + const PTRACE_GETREGS: PtraceRequestType = 12; + const PTRACE_GETFPREGS: PtraceRequestType = 14; pub const NUM_DEBUG_REGISTERS: usize = 8; + pub use subarch::*; + #[cfg(target_arch = "x86")] - pub type RegType = u32; + mod subarch { + use super::*; + + pub type GenRegs = user_regs_struct; + pub type FpRegs = user_i387_ia32_struct; + pub type FpxRegs = fxregs_state; + pub type RegType = u32; + + pub(crate) enum FpxRegsTag {} + + // https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/x86/include/uapi/asm/ptrace-abi.h?h=v7.1.8 + const PTRACE_GETFPXREGS: PtraceRequestType = 18; + + // Obtained by running: + // println!("{}", std::mem::offset_of!(libc::user, u_debugreg)); + pub const USER_STRUCT_DEBUGREG_OFFSET: usize = 252; + + // https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/x86/include/asm/user_32.h?h=v7.1.8#n78 + #[repr(C)] + #[derive(Clone, Copy, Debug, Default, serde::Deserialize, serde::Serialize)] + pub struct user_regs_struct { + pub ebx: u32, + pub ecx: u32, + pub edx: u32, + pub esi: u32, + pub edi: u32, + pub ebp: u32, + pub eax: u32, + pub ds: u32, + pub es: u32, + pub fs: u32, + pub gs: u32, + pub orig_eax: u32, + pub eip: u32, + pub cs: u32, + pub eflags: u32, + pub esp: u32, + pub ss: u32, + } + + // https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/x86/include/asm/user32.h?h=v7.1.8#n8 + #[repr(C)] + #[derive(Clone, Copy, Debug, Default, serde::Deserialize, serde::Serialize)] + pub struct user_i387_ia32_struct { + pub cwd: u32, + pub swd: u32, + pub twd: u32, + pub fip: u32, + pub fcs: u32, + pub foo: u32, + pub fos: u32, + pub st_space: [u32; 20], + } + + // https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/x86/include/asm/fpu/types.h?h=v7.1.8#n36 + #[repr(C, align(16))] + #[derive(Clone, Copy, Debug, Default, serde::Deserialize, serde::Serialize)] + pub struct fxregs_state { + pub cwd: u16, + pub swd: u16, + pub twd: u16, + pub fop: u16, + pub fip: u32, + pub fcs: u32, + pub foo: u32, + pub fos: u32, + pub mxcsr: u32, + pub mxcsr_mask: u32, + pub st_space: [u32; 32], + pub xmm_space: super::XmmSpace, + #[serde(skip)] + pub __padding: [u32; 12], + #[serde(skip)] + pub __padding1: [u32; 12], + } + + unsafe impl PtraceRegisterSet for GenRegsTag { + const NOTE: RegSetNote = NT_PRSTATUS; + const LEGACY_REQUEST: Option = Some(PTRACE_GETREGS); + const KERNEL_SIZE: usize = size_of::(); + type Output = user_regs_struct; + } + + unsafe impl PtraceRegisterSet for FpRegsTag { + const NOTE: RegSetNote = NT_PRFPREG; + const LEGACY_REQUEST: Option = Some(PTRACE_GETFPREGS); + const KERNEL_SIZE: usize = size_of::(); + type Output = user_i387_ia32_struct; + } + + unsafe impl PtraceRegisterSet for FpxRegsTag { + const NOTE: RegSetNote = NT_PRXFPREG; + const LEGACY_REQUEST: Option = Some(PTRACE_GETFPXREGS); + const KERNEL_SIZE: usize = size_of::(); + type Output = fxregs_state; + } + + const _: () = { + assert!(size_of::() == 68); + assert!(align_of::() == 4); + assert!(size_of::() == 108); + assert!(align_of::() == 4); + assert!(size_of::() == 512); + assert!(align_of::() == 16); + }; + } #[cfg(target_arch = "x86_64")] - pub type RegType = u64; + mod subarch { + use super::*; - #[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}; + pub type GenRegs = user_regs_struct; + pub type FpRegs = fxregs_state; + pub 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, + // Obtained by running: + // println!("{}", std::mem::offset_of!(libc::user, u_debugreg)); + pub const USER_STRUCT_DEBUGREG_OFFSET: usize = 848; + + // https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/x86/include/asm/user_64.h?h=v7.1.8#n69 + #[repr(C)] + #[derive(Clone, Copy, Debug, Default, serde::Deserialize, serde::Serialize)] + pub struct user_regs_struct { + pub r15: u64, + pub r14: u64, + pub r13: u64, + pub r12: u64, + pub rbp: u64, + pub rbx: u64, + pub r11: u64, + pub r10: u64, + pub r9: u64, + pub r8: u64, + pub rax: u64, + pub rcx: u64, + pub rdx: u64, + pub rsi: u64, + pub rdi: u64, + pub orig_rax: u64, + pub rip: u64, + pub cs: u64, + pub eflags: u64, + pub rsp: u64, + pub ss: u64, + pub fs_base: u64, + pub gs_base: u64, + pub ds: u64, + pub es: u64, + pub fs: u64, + pub gs: u64, + } + + // https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/x86/include/asm/fpu/types.h?h=v7.1.8#n36 + #[repr(C, align(16))] + #[derive(Clone, Copy, Debug, Default, serde::Deserialize, serde::Serialize)] + pub struct fxregs_state { + pub cwd: u16, + pub swd: u16, + pub twd: u16, + pub fop: u16, + pub rip: u64, + pub rdp: u64, + pub mxcsr: u32, + pub mxcsr_mask: u32, + pub st_space: [u32; 32], + pub xmm_space: super::XmmSpace, + #[serde(skip)] + pub __padding: [u32; 12], + #[serde(skip)] + pub __padding1: [u32; 12], + } + + unsafe impl PtraceRegisterSet for GenRegsTag { + const NOTE: RegSetNote = NT_PRSTATUS; + const LEGACY_REQUEST: Option = Some(PTRACE_GETREGS); + const KERNEL_SIZE: usize = size_of::(); + type Output = user_regs_struct; + } + + unsafe impl PtraceRegisterSet for FpRegsTag { + const NOTE: RegSetNote = NT_PRFPREG; + const LEGACY_REQUEST: Option = Some(PTRACE_GETFPREGS); + const KERNEL_SIZE: usize = size_of::(); + type Output = fxregs_state; + } + + const _: () = { + assert!(size_of::() == 216); + assert!(align_of::() == 8); + assert!(size_of::() == 512); + assert!(align_of::() == 16); + }; } - // 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], + // std and serde don't implement Default, Deserialize, and Serialize traits for arrays longer + // than 32 elements, so we have to hand-code them for the XMM array + + #[derive(Clone, Copy, Debug)] + #[repr(transparent)] + pub struct XmmSpace(pub [u32; 64]); + + impl Default for XmmSpace { + fn default() -> XmmSpace { + XmmSpace([0; 64]) + } } - // 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], + impl serde::Serialize for XmmSpace { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeTuple; + let mut tup = serializer.serialize_tuple(64)?; + for element in self.0.iter() { + tup.serialize_element::(element)?; + } + tup.end() + } } - #[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], + impl<'de> serde::Deserialize<'de> for XmmSpace { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct Visitor; + + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = XmmSpace; + + fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(formatter, "a u32 array of length 64") + } + + fn visit_seq>( + self, + mut seq: A, + ) -> Result { + let mut arr = [0u32; 64]; + for (idx, element) in arr.iter_mut().enumerate() { + *element = seq + .next_element::()? + .ok_or(::invalid_length(idx, &self))?; + } + Ok(XmmSpace(arr)) + } + } + + deserializer.deserialize_tuple(64, Visitor) + } } } diff --git a/crates/linux/process-backend/src/wrapper.rs b/crates/linux/process-backend/src/wrapper.rs index 26a8de7e..fef8c44e 100644 --- a/crates/linux/process-backend/src/wrapper.rs +++ b/crates/linux/process-backend/src/wrapper.rs @@ -1,4 +1,5 @@ use core::ffi::c_int; +use serde::{Deserialize, Serialize}; #[derive(Debug)] pub(crate) struct OwnedFd(c_int); @@ -22,6 +23,11 @@ impl Drop for OwnedFd { } } +#[derive(Clone, Copy, Debug, Deserialize, Serialize)] +pub struct Stat { + pub st_mode: u32, +} + pub(crate) fn errno() -> c_int { unsafe { *errno_location() } } diff --git a/src/linux/process_inspection/mod.rs b/src/linux/process_inspection/mod.rs index 82a51676..97ea5bbb 100644 --- a/src/linux/process_inspection/mod.rs +++ b/src/linux/process_inspection/mod.rs @@ -1,7 +1,7 @@ use super as linux; use crate::module_reader::{ModuleMemoryReadError, ReadError, ReadModuleMemory}; use linux::maps_reader; -use process_backend::{ProcessReader as _, local, regs::*}; +use process_backend::{ProcessReader as _, Stat, local, regs::*}; use process_reader::{CopyFromProcessError, ProcessReader, ProcessReaderBackend}; use std::{ borrow::Cow, @@ -43,7 +43,7 @@ pub trait ProcessInspector: core::fmt::Debug { path: PathBuf, offset: u64, ) -> Result>; - fn stat_file(&self, path: PathBuf) -> Result; + fn stat_file(&self, path: PathBuf) -> Result; fn read_file<'a>(&'a self, path: PathBuf) -> Result>; fn read_dir<'a>(&'a self, path: PathBuf) -> Result>; fn read_link(&self, path: PathBuf) -> Result; @@ -96,7 +96,7 @@ impl ProcessInspector for B { Ok(MappedModuleMemoryReader(Box::new(reader))) } - fn stat_file(&self, path: PathBuf) -> Result { + fn stat_file(&self, path: PathBuf) -> Result { let c_path = CString::new(path.into_os_string().into_vec()).unwrap(); B::stat_file(self, &c_path).map_err(Error::Backend) } diff --git a/src/linux/thread_info/aarch64.rs b/src/linux/thread_info/aarch64.rs index 858b13b3..670bd3ad 100644 --- a/src/linux/thread_info/aarch64.rs +++ b/src/linux/thread_info/aarch64.rs @@ -8,8 +8,8 @@ pub struct ThreadInfoAarch64 { pub stack_pointer: usize, pub tgid: Pid, // thread group id pub ppid: Pid, // parent process - pub regs: libc::user_regs_struct, - pub fpregs: user_fpsimd_struct, + pub regs: GenRegs, + pub fpregs: FpRegs, } impl ThreadInfoAarch64 { diff --git a/src/linux/thread_info/arm.rs b/src/linux/thread_info/arm.rs index 514af28c..6296e239 100644 --- a/src/linux/thread_info/arm.rs +++ b/src/linux/thread_info/arm.rs @@ -8,8 +8,8 @@ pub struct ThreadInfoArm { pub stack_pointer: usize, pub tgid: Pid, // thread group id pub ppid: Pid, // parent process - pub regs: user_regs_struct, - pub fpregs: user_fpregs_struct, + pub regs: GenRegs, + pub fpregs: FpRegs, } impl ThreadInfoArm { diff --git a/src/linux/thread_info/x86.rs b/src/linux/thread_info/x86.rs index 007f940e..78d90ecc 100644 --- a/src/linux/thread_info/x86.rs +++ b/src/linux/thread_info/x86.rs @@ -9,11 +9,11 @@ pub struct ThreadInfoX86 { pub stack_pointer: usize, pub tgid: Pid, // thread group id pub ppid: Pid, // parent process - pub regs: user_regs_struct, - pub fpregs: user_fpregs_struct, + pub regs: GenRegs, + pub fpregs: FpRegs, pub dregs: [RegType; NUM_DEBUG_REGISTERS], #[cfg(target_arch = "x86")] - pub fpxregs: user_fpxregs_struct, + pub fpxregs: FpxRegs, } impl ThreadInfoX86 { @@ -42,7 +42,7 @@ impl ThreadInfoX86 { let mut dregs: [RegType; NUM_DEBUG_REGISTERS] = [0; NUM_DEBUG_REGISTERS]; - let debug_offset = mem::offset_of!(user, u_debugreg); + let debug_offset = USER_STRUCT_DEBUGREG_OFFSET; for (idx, dreg) in dregs.iter_mut().enumerate() { let chunk = process_inspector .ptrace_peekuser(debug_offset + idx * mem::size_of::()) @@ -130,19 +130,19 @@ impl ThreadInfoX86 { let mut float_save = crate::minidump_cpu::FloatStateCPU { control_word: fs.cwd, status_word: fs.swd, - tag_word: fs.ftw as u8, + tag_word: fs.twd as u8, error_opcode: fs.fop, error_offset: fs.rip as u32, data_offset: fs.rdp as u32, error_selector: 0, // We don't have this. data_selector: 0, // We don't have this. mx_csr: fs.mxcsr, - mx_csr_mask: fs.mxcr_mask, + mx_csr_mask: fs.mxcsr_mask, ..Default::default() }; copy_u32_registers(&mut float_save.float_registers, &fs.st_space); - copy_u32_registers(&mut float_save.xmm_registers, &fs.xmm_space); + copy_u32_registers(&mut float_save.xmm_registers, &fs.xmm_space.0); out.float_save .pwrite_with(float_save, 0, scroll::Endian::Little) @@ -154,41 +154,41 @@ impl ThreadInfoX86 { pub fn fill_cpu_context(&self, out: &mut RawContextCPU) { out.context_flags = format::ContextFlagsX86::CONTEXT_X86_ALL.bits(); - out.dr0 = self.dregs[0] as u32; - out.dr3 = self.dregs[3] as u32; - out.dr1 = self.dregs[1] as u32; - out.dr2 = self.dregs[2] as u32; + out.dr0 = self.dregs[0]; + out.dr3 = self.dregs[3]; + out.dr1 = self.dregs[1]; + out.dr2 = self.dregs[2]; // 4 and 5 deliberatly omitted because they aren't included in the minidump // format. - out.dr6 = self.dregs[6] as u32; - out.dr7 = self.dregs[7] as u32; - - out.gs = self.regs.xgs as u32; - out.fs = self.regs.xfs as u32; - out.es = self.regs.xes as u32; - out.ds = self.regs.xds as u32; - - out.edi = self.regs.edi as u32; - out.esi = self.regs.esi as u32; - out.ebx = self.regs.ebx as u32; - out.edx = self.regs.edx as u32; - out.ecx = self.regs.ecx as u32; - out.eax = self.regs.eax as u32; - - out.ebp = self.regs.ebp as u32; - out.eip = self.regs.eip as u32; - out.cs = self.regs.xcs as u32; - out.eflags = self.regs.eflags as u32; - out.esp = self.regs.esp as u32; - out.ss = self.regs.xss as u32; - - out.float_save.control_word = self.fpregs.cwd as u32; - out.float_save.status_word = self.fpregs.swd as u32; - out.float_save.tag_word = self.fpregs.twd as u32; - out.float_save.error_offset = self.fpregs.fip as u32; - out.float_save.error_selector = self.fpregs.fcs as u32; - out.float_save.data_offset = self.fpregs.foo as u32; - out.float_save.data_selector = self.fpregs.fos as u32; + out.dr6 = self.dregs[6]; + out.dr7 = self.dregs[7]; + + out.gs = self.regs.gs; + out.fs = self.regs.fs; + out.es = self.regs.es; + out.ds = self.regs.ds; + + out.edi = self.regs.edi; + out.esi = self.regs.esi; + out.ebx = self.regs.ebx; + out.edx = self.regs.edx; + out.ecx = self.regs.ecx; + out.eax = self.regs.eax; + + out.ebp = self.regs.ebp; + out.eip = self.regs.eip; + out.cs = self.regs.cs; + out.eflags = self.regs.eflags; + out.esp = self.regs.esp; + out.ss = self.regs.ss; + + out.float_save.control_word = self.fpregs.cwd; + out.float_save.status_word = self.fpregs.swd; + out.float_save.tag_word = self.fpregs.twd; + out.float_save.error_offset = self.fpregs.fip; + out.float_save.error_selector = self.fpregs.fcs; + out.float_save.data_offset = self.fpregs.foo; + out.float_save.data_selector = self.fpregs.fos; { let ra = &mut out.float_save.register_area; @@ -235,7 +235,7 @@ impl ThreadInfoX86 { debug_assert_eq!(offset, 160); - for val in &self.fpxregs.xmm_space { + for val in &self.fpxregs.xmm_space.0 { write_er!(val); } }