1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
use crate::errors::ThreadInfoError;
use nix::{errno::Errno, sys::ptrace, unistd};
use std::{
    io::{self, BufRead},
    path,
};

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

pub type Pid = i32;

cfg_if::cfg_if! {
    if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
        mod x86;
        pub type ThreadInfo = x86::ThreadInfoX86;
    } else if #[cfg(target_arch = "arm")] {
        mod arm;
        pub type ThreadInfo = arm::ThreadInfoArm;
    } else if #[cfg(target_arch = "aarch64")] {
        mod aarch64;
        pub type ThreadInfo = aarch64::ThreadInfoAarch64;
    } else if #[cfg(target_arch = "mips")] {
        mod mips;
        pub type ThreadInfo = mips::ThreadInfoMips;
    }
}

#[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
}

#[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);

        let to_copy = std::cmp::min(dst.len(), src.len());
        dst[..to_copy].copy_from_slice(&src[..to_copy]);
    }
}

trait CommonThreadInfo {
    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/{}/status", tid));
        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,
            }
        }
        if ppid == -1 || tgid == -1 {
            return Err(ThreadInfoError::InvalidPid(
                format!("/proc/{}/status", tid),
                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>(
        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() })
    }

    /// 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 _,
            )
        };
        Errno::result(res)?;
        Ok(unsafe { data.assume_init() })
    }

    /// COPY FROM CRATE nix BECAUSE ITS NOT PUBLIC
    fn ptrace_peek(
        request: ptrace::RequestType,
        pid: 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,
        }
    }
}
impl ThreadInfo {
    pub fn create(pid: Pid, tid: Pid) -> std::result::Result<Self, ThreadInfoError> {
        Self::create_impl(pid, tid)
    }
}