Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 11 additions & 30 deletions src/linux/thread_info/aarch64.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use {
super::{CommonThreadInfo, NT_Elf, Pid, ThreadInfoError},
super::{Pid, PtraceRequestType, ThreadInfoError},
crate::minidump_cpu::{FP_REG_COUNT, GP_REG_COUNT, RawContextCPU},
nix::sys::ptrace,
};

/// https://github.com/rust-lang/libc/pull/2719
Expand All @@ -25,47 +24,29 @@ pub struct ThreadInfoAarch64 {
pub fpregs: user_fpsimd_struct,
}

impl CommonThreadInfo for ThreadInfoAarch64 {}

impl ThreadInfoAarch64 {
pub fn get_instruction_pointer(&self) -> usize {
self.regs.pc as usize
}

// nix currently doesn't support PTRACE_GETREGSET, so we have to do it ourselves
fn getregset(pid: Pid) -> Result<libc::user_regs_struct> {
Self::ptrace_get_data_via_io(
0x4204 as ptrace::RequestType, // PTRACE_GETREGSET
Some(NT_Elf::NT_PRSTATUS),
nix::unistd::Pid::from_raw(pid),
)
const NT_PRSTATUS: usize = 1;
Comment thread
gabrielesvelto marked this conversation as resolved.
super::ptrace_getregset(NT_PRSTATUS, pid)
}

fn getregs(pid: Pid) -> Result<libc::user_regs_struct> {
// TODO: nix restricts PTRACE_GETREGS to arm android for some reason
Self::ptrace_get_data(
12 as ptrace::RequestType, // PTRACE_GETREGS
None,
nix::unistd::Pid::from_raw(pid),
)
const PTRACE_GETREGS: PtraceRequestType = 12;
unsafe { super::ptrace_getregs::<libc::user_regs_struct>(PTRACE_GETREGS, pid) }
}

// nix currently doesn't support PTRACE_GETREGSET, so we have to do it ourselves
fn getfpregset(pid: Pid) -> Result<user_fpsimd_struct> {
Self::ptrace_get_data_via_io(
0x4204 as ptrace::RequestType, // PTRACE_GETREGSET
Some(NT_Elf::NT_PRFPREGSET),
nix::unistd::Pid::from_raw(pid),
)
const NT_PRFPREGSET: usize = 2;
super::ptrace_getregset(NT_PRFPREGSET, pid)
}

// nix currently doesn't support PTRACE_GETFPREGS, so we have to do it ourselves
fn getfpregs(pid: Pid) -> Result<user_fpsimd_struct> {
Self::ptrace_get_data(
14 as ptrace::RequestType, // PTRACE_GETFPREGS
None,
nix::unistd::Pid::from_raw(pid),
)
const PTRACE_GETFPREGS: PtraceRequestType = 14;
unsafe { super::ptrace_getregs::<user_fpsimd_struct>(PTRACE_GETFPREGS, pid) }
}

pub fn fill_cpu_context(&self, out: &mut RawContextCPU) {
Expand All @@ -85,8 +66,8 @@ impl ThreadInfoAarch64 {
out.float_regs[..FP_REG_COUNT].copy_from_slice(&self.fpregs.vregs[..FP_REG_COUNT]);
}

pub fn create_impl(_pid: Pid, tid: Pid) -> Result<Self> {
let (ppid, tgid) = Self::get_ppid_and_tgid(tid)?;
pub fn create(_pid: Pid, tid: Pid) -> Result<Self> {
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))?;

Expand Down
25 changes: 7 additions & 18 deletions src/linux/thread_info/arm.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use {
super::{CommonThreadInfo, NT_Elf, Pid, ThreadInfoError},
super::{Pid, PtraceRequestType, ThreadInfoError},
crate::minidump_cpu::RawContextCPU,
nix::sys::ptrace,
};

type Result<T> = std::result::Result<T, ThreadInfoError>;
Expand Down Expand Up @@ -30,25 +29,15 @@ pub struct ThreadInfoArm {
pub fpregs: user_fpregs_struct,
}

impl CommonThreadInfo for ThreadInfoArm {}

impl ThreadInfoArm {
// nix currently doesn't support PTRACE_GETFPREGS, so we have to do it ourselves
fn getfpregs(pid: Pid) -> Result<user_fpregs_struct> {
Self::ptrace_get_data_via_io(
0x4204 as ptrace::RequestType, // PTRACE_GETREGSET
Some(NT_Elf::NT_ARM_VFP),
nix::unistd::Pid::from_raw(pid),
)
const NT_ARM_VFP: usize = 0x400;
super::ptrace_getregset(NT_ARM_VFP, pid)
}

// nix currently doesn't support PTRACE_GETREGS, so we have to do it ourselves
fn getregs(pid: Pid) -> Result<user_regs_struct> {
Self::ptrace_get_data::<user_regs_struct>(
ptrace::Request::PTRACE_GETREGS as ptrace::RequestType,
None,
nix::unistd::Pid::from_raw(pid),
)
const PTRACE_GETREGS: PtraceRequestType = 12;
Comment thread
gabrielesvelto marked this conversation as resolved.
unsafe { super::ptrace_getregs::<user_regs_struct>(PTRACE_GETREGS, pid) }
}

pub fn get_instruction_pointer(&self) -> usize {
Expand All @@ -65,8 +54,8 @@ impl ThreadInfoArm {
out.float_save.regs = self.fpregs.fpregs;
}

pub fn create_impl(_pid: Pid, tid: Pid) -> Result<Self> {
let (ppid, tgid) = Self::get_ppid_and_tgid(tid)?;
pub fn create(_pid: Pid, tid: Pid) -> Result<Self> {
let (ppid, tgid) = super::get_ppid_and_tgid(tid)?;
let regs = Self::getregs(tid)?;
let fpregs = Self::getfpregs(tid).unwrap_or(Default::default());

Expand Down
198 changes: 70 additions & 128 deletions src/linux/thread_info/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
use {
super::{Pid, serializers::*},
crate::serializers::*,
nix::{errno::Errno, sys::ptrace},
nix::errno::Errno,
std::{
ffi::c_void,
io::{self, BufRead},
path,
},
};

#[cfg(target_arch = "x86_64")]
pub use x86::copy_u32_registers;

type Result<T> = std::result::Result<T, ThreadInfoError>;

#[derive(thiserror::Error, Debug, serde::Serialize)]
Expand All @@ -30,7 +34,7 @@ pub enum ThreadInfoError {
),
#[error("nix::ptrace() error")]
PtraceError(
#[from]
#[source]
#[serde(serialize_with = "serialize_nix_error")]
nix::Error,
),
Expand All @@ -51,141 +55,79 @@ cfg_if::cfg_if! {
}
}

#[derive(Debug)]
#[allow(non_camel_case_types, dead_code)]
enum NT_Elf {
NT_NONE = 0,
NT_PRSTATUS = 1,
NT_PRFPREGSET = 2,
//NT_PRPSINFO = 3,
//NT_TASKSTRUCT = 4,
//NT_AUXV = 6,
NT_ARM_VFP = 0x400, // ARM VFP/NEON registers
}

#[cfg(target_arch = "x86_64")]
#[inline]
pub fn copy_u32_registers(dst: &mut [u128], src: &[u32]) {
// SAFETY: We are copying a block of memory from ptrace as u32s to the u128
// format of minidump-common
unsafe {
let dst: &mut [u8] =
std::slice::from_raw_parts_mut(dst.as_mut_ptr().cast(), dst.len() * 16);
let src: &[u8] = std::slice::from_raw_parts(src.as_ptr().cast(), src.len() * 4);
#[cfg(target_env = "gnu")]
type PtraceRequestType = core::ffi::c_uint;

let to_copy = std::cmp::min(dst.len(), src.len());
dst[..to_copy].copy_from_slice(&src[..to_copy]);
}
}
#[cfg(not(target_env = "gnu"))]
type PtraceRequestType = core::ffi::c_int;

trait CommonThreadInfo {
fn get_ppid_and_tgid(tid: Pid) -> Result<(Pid, Pid)> {
let mut ppid = -1;
let mut tgid = -1;
fn get_ppid_and_tgid(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)?;
for line in io::BufReader::new(status_file).lines() {
let l = line?;
let start = l
.get(0..6)
.ok_or_else(|| ThreadInfoError::InvalidProcStatusFile(tid, l.clone()))?;
match start {
"Tgid:\t" => {
tgid = l
.get(6..)
.ok_or_else(|| ThreadInfoError::InvalidProcStatusFile(tid, l.clone()))?
.parse::<Pid>()?;
}
"PPid:\t" => {
ppid = l
.get(6..)
.ok_or_else(|| ThreadInfoError::InvalidProcStatusFile(tid, l.clone()))?
.parse::<Pid>()?;
}
_ => continue,
let status_path = path::PathBuf::from(format!("/proc/{tid}/status"));
let status_file = std::fs::File::open(status_path)?;
for line in io::BufReader::new(status_file).lines() {
let l = line?;
let start = l
.get(0..6)
.ok_or_else(|| ThreadInfoError::InvalidProcStatusFile(tid, l.clone()))?;
match start {
"Tgid:\t" => {
tgid = l
.get(6..)
.ok_or_else(|| ThreadInfoError::InvalidProcStatusFile(tid, l.clone()))?
.parse::<Pid>()?;
}
"PPid:\t" => {
ppid = l
.get(6..)
.ok_or_else(|| ThreadInfoError::InvalidProcStatusFile(tid, l.clone()))?
.parse::<Pid>()?;
}
_ => continue,
Comment thread
marti4d marked this conversation as resolved.
}
if ppid == -1 || tgid == -1 {
return Err(ThreadInfoError::InvalidPid(
format!("/proc/{tid}/status"),
ppid,
tgid,
));
}
Ok((ppid, tgid))
}

/// SLIGHTLY MODIFIED COPY FROM CRATE nix
/// Function for ptrace requests that return values from the data field.
/// Some ptrace get requests populate structs or larger elements than `c_long`
/// and therefore use the data field to return values. This function handles these
/// requests.
fn ptrace_get_data<T>(
Comment thread
marti4d marked this conversation as resolved.
request: ptrace::RequestType,
flag: Option<NT_Elf>,
pid: nix::unistd::Pid,
) -> Result<T> {
let mut data = std::mem::MaybeUninit::uninit();
let res = unsafe {
libc::ptrace(
request,
libc::pid_t::from(pid),
flag.unwrap_or(NT_Elf::NT_NONE),
data.as_mut_ptr(),
)
};
Errno::result(res)?;
Ok(unsafe { data.assume_init() })
if ppid == -1 || tgid == -1 {
return Err(ThreadInfoError::InvalidPid(
format!("/proc/{tid}/status"),
ppid,
tgid,
));
}
Ok((ppid, tgid))
}

/// SLIGHTLY MODIFIED COPY FROM CRATE nix
/// Function for ptrace requests that return values from the data field.
/// Some ptrace get requests populate structs or larger elements than `c_long`
/// and therefore use the data field to return values. This function handles these
/// requests.
fn ptrace_get_data_via_io<T>(
request: ptrace::RequestType,
flag: Option<NT_Elf>,
pid: nix::unistd::Pid,
) -> Result<T> {
let mut data = std::mem::MaybeUninit::<T>::uninit();
let io = libc::iovec {
iov_base: data.as_mut_ptr().cast(),
iov_len: std::mem::size_of::<T>(),
};
let res = unsafe {
libc::ptrace(
request,
libc::pid_t::from(pid),
flag.unwrap_or(NT_Elf::NT_NONE),
&io as *const _,
Comment thread
marti4d marked this conversation as resolved.
)
};
Errno::result(res)?;
Ok(unsafe { data.assume_init() })
}
/// Safety: RequestType and T must agree on the size of the returned type
unsafe fn ptrace_getregs<T>(request: PtraceRequestType, pid: libc::pid_t) -> Result<T> {
let mut output = std::mem::MaybeUninit::uninit();

/// COPY FROM CRATE nix BECAUSE ITS NOT PUBLIC
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn ptrace_peek(
request: ptrace::RequestType,
pid: nix::unistd::Pid,
addr: ptrace::AddressType,
data: *mut libc::c_void,
) -> nix::Result<libc::c_long> {
let ret = unsafe {
Errno::clear();
libc::ptrace(request, libc::pid_t::from(pid), addr, data)
};
match Errno::result(ret) {
Ok(..) | Err(Errno::UnknownErrno) => Ok(ret),
err @ Err(..) => err,
}
}
// 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() })
}
impl ThreadInfo {
pub fn create(pid: Pid, tid: Pid) -> std::result::Result<Self, ThreadInfoError> {
Self::create_impl(pid, tid)

fn ptrace_getregset<T>(regset_type: usize, pid: libc::pid_t) -> Result<T> {
let mut output = std::mem::MaybeUninit::<T>::uninit();
let mut io = libc::iovec {
iov_base: output.as_mut_ptr().cast(),
iov_len: std::mem::size_of::<T>(),
};

// 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::<T>() {
return Err(ThreadInfoError::PtraceError(Errno::EINVAL));
}

Ok(unsafe { output.assume_init() })
}
Loading
Loading