Skip to main content

relibc/platform/redox/
ptrace.rs

1//! Note: This module is not going to be clean. We're not going to be
2//! able to follow the specs 100%. Linux ptrace is very, very,
3//! different to Redox. Many people agree that Linux ptrace is bad, so
4//! we are NOT going to bend our API for the sake of
5//! compatibility. So, this module will be a hellhole.
6
7use super::super::{ERRNO, PalPtrace, Sys, types::*};
8#[cfg(target_arch = "x86_64")]
9use super::super::{Pal, PalSignal};
10#[cfg(target_arch = "x86_64")]
11use crate::header::arch_x64_user::user_regs_struct;
12use crate::{
13    c_str::{CStr, CString},
14    error::Errno,
15    fs::File,
16    header::{
17        errno::{self as errnoh, EIO},
18        fcntl,
19    },
20    io,
21    raw_cell::RawCell,
22    sync::Mutex,
23};
24#[cfg(target_arch = "x86_64")]
25use crate::{
26    header::{signal, sys_ptrace},
27    io::prelude::*,
28};
29
30use alloc::collections::{BTreeMap, btree_map::Entry};
31#[cfg(target_arch = "x86_64")]
32use core::mem;
33#[cfg(target_arch = "x86_64")]
34use syscall;
35
36pub struct Session {
37    pub first: bool,
38    pub fpregs: File,
39    pub mem: File,
40    pub regs: File,
41    pub tracer: File,
42}
43pub struct State {
44    pub sessions: Mutex<BTreeMap<pid_t, Session>>,
45}
46impl State {
47    fn new() -> Self {
48        Self {
49            sessions: Mutex::new(BTreeMap::new()),
50        }
51    }
52}
53
54#[thread_local]
55static STATE: RawCell<Option<State>> = RawCell::new(None);
56
57pub fn init_state() -> &'static State {
58    // Safe due to STATE being thread_local (TODO: is it though?)
59    unsafe {
60        if STATE.unsafe_ref().is_none() {
61            STATE.unsafe_set(Some(State::new()));
62        }
63        let state_ptr = STATE.unsafe_ref().as_ref().unwrap() as *const State;
64        &*state_ptr
65    }
66}
67pub fn is_traceme(pid: pid_t) -> bool {
68    // Skip special PIDs (<=0)
69    if pid <= 0 {
70        return false;
71    }
72    File::open(
73        CStr::borrow(&CString::new(format!("/scheme/chan/ptrace-relibc/{}/traceme", pid)).unwrap()),
74        fcntl::O_PATH,
75    )
76    .is_ok()
77}
78pub fn get_session(
79    sessions: &mut BTreeMap<pid_t, Session>,
80    pid: pid_t,
81) -> io::Result<&mut Session> {
82    const NEW_FLAGS: c_int = fcntl::O_RDWR | fcntl::O_CLOEXEC;
83
84    match sessions.entry(pid) {
85        Entry::Vacant(entry) => {
86            if is_traceme(pid) {
87                Ok(entry.insert(Session {
88                    first: true,
89                    tracer: File::open(
90                        CStr::borrow(&CString::new(format!("/scheme/proc/{}/trace", pid)).unwrap()),
91                        NEW_FLAGS,
92                    )?,
93                    mem: File::open(
94                        CStr::borrow(&CString::new(format!("/scheme/proc/{}/mem", pid)).unwrap()),
95                        NEW_FLAGS,
96                    )?,
97                    regs: File::open(
98                        CStr::borrow(
99                            &CString::new(format!("/scheme/proc/{}/regs/int", pid)).unwrap(),
100                        ),
101                        NEW_FLAGS,
102                    )?,
103                    fpregs: File::open(
104                        CStr::borrow(
105                            &CString::new(format!("/scheme/proc/{}/regs/float", pid)).unwrap(),
106                        ),
107                        NEW_FLAGS,
108                    )?,
109                }))
110            } else {
111                ERRNO.set(errnoh::ESRCH);
112                Err(io::last_os_error())
113            }
114        }
115        Entry::Occupied(entry) => Ok(entry.into_mut()),
116    }
117}
118
119#[cfg(target_arch = "aarch64")]
120unsafe fn inner_ptrace(
121    request: c_int,
122    pid: pid_t,
123    addr: *mut c_void,
124    data: *mut c_void,
125) -> io::Result<c_int> {
126    //TODO: aarch64
127    unimplemented!("inner_ptrace not implemented on aarch64");
128}
129
130#[cfg(target_arch = "x86")]
131unsafe fn inner_ptrace(
132    request: c_int,
133    pid: pid_t,
134    addr: *mut c_void,
135    data: *mut c_void,
136) -> io::Result<c_int> {
137    //TODO: x86
138    unimplemented!("inner_ptrace not implemented on x86");
139}
140
141#[cfg(target_arch = "x86_64")]
142unsafe fn inner_ptrace(
143    request: c_int,
144    pid: pid_t,
145    addr: *mut c_void,
146    data: *mut c_void,
147) -> io::Result<c_int> {
148    let state = init_state();
149
150    if request == sys_ptrace::PTRACE_TRACEME {
151        // Mark this child as traced, parent will check for this marker file
152        let pid = Sys::getpid();
153        mem::forget(File::open(
154            CStr::borrow(
155                &CString::new(format!("/scheme/chan/ptrace-relibc/{}/traceme", pid)).unwrap(),
156            ),
157            fcntl::O_CREAT | fcntl::O_PATH | fcntl::O_EXCL,
158        )?);
159        return Ok(0);
160    }
161
162    let mut sessions = state.sessions.lock();
163    let session = get_session(&mut sessions, pid)?;
164
165    match request {
166        sys_ptrace::PTRACE_CONT
167        | sys_ptrace::PTRACE_SINGLESTEP
168        | sys_ptrace::PTRACE_SYSCALL
169        | sys_ptrace::PTRACE_SYSEMU
170        | sys_ptrace::PTRACE_SYSEMU_SINGLESTEP => {
171            if let Ok(()) = Sys::kill(pid, signal::SIGCONT as _) {}; // TODO handle error
172
173            // TODO: Translate errors
174            let syscall = syscall::PTRACE_STOP_PRE_SYSCALL | syscall::PTRACE_STOP_POST_SYSCALL;
175            (&mut &session.tracer).write(&match request {
176                sys_ptrace::PTRACE_CONT => syscall::PtraceFlags::empty(),
177                sys_ptrace::PTRACE_SINGLESTEP => syscall::PTRACE_STOP_SINGLESTEP,
178                // Skip the first post-syscall when connected
179                sys_ptrace::PTRACE_SYSCALL if session.first => syscall::PTRACE_STOP_PRE_SYSCALL,
180                sys_ptrace::PTRACE_SYSCALL => syscall,
181                // Skip the first post-syscall when connected
182                sys_ptrace::PTRACE_SYSEMU if session.first => {
183                    syscall::PTRACE_FLAG_IGNORE | syscall::PTRACE_STOP_PRE_SYSCALL
184                }
185                sys_ptrace::PTRACE_SYSEMU => syscall::PTRACE_FLAG_IGNORE | syscall,
186                sys_ptrace::PTRACE_SYSEMU_SINGLESTEP => {
187                    syscall::PTRACE_FLAG_IGNORE | syscall::PTRACE_STOP_SINGLESTEP
188                }
189                _ => unreachable!("unhandled ptrace request type {}", request),
190            })?;
191
192            session.first = false;
193            Ok(0)
194        }
195        sys_ptrace::PTRACE_GETREGS => {
196            let c_regs = unsafe { &mut *(data as *mut user_regs_struct) };
197            let mut redox_regs = syscall::IntRegisters::default();
198            (&mut &session.regs).read(&mut redox_regs)?;
199            *c_regs = user_regs_struct {
200                r15: redox_regs.r15 as _,
201                r14: redox_regs.r14 as _,
202                r13: redox_regs.r13 as _,
203                r12: redox_regs.r12 as _,
204                rbp: redox_regs.rbp as _,
205                rbx: redox_regs.rbx as _,
206                r11: redox_regs.r11 as _,
207                r10: redox_regs.r10 as _,
208                r9: redox_regs.r9 as _,
209                r8: redox_regs.r8 as _,
210                rax: redox_regs.rax as _,
211                rcx: redox_regs.rcx as _,
212                rdx: redox_regs.rdx as _,
213                rsi: redox_regs.rsi as _,
214                rdi: redox_regs.rdi as _,
215                orig_rax: redox_regs.rax as _, // redox_regs.orig_rax as _,
216                rip: redox_regs.rip as _,
217                cs: redox_regs.cs as _,
218                eflags: redox_regs.rflags as _,
219                rsp: redox_regs.rsp as _,
220                ss: redox_regs.ss as _,
221                fs_base: 0, // fs_base: redox_regs.fs_base as _,
222                gs_base: 0, // gs_base: redox_regs.gs_base as _,
223                ds: 0,      // ds: redox_regs.ds as _,
224                es: 0,      // es: redox_regs.es as _,
225                fs: 0,      // fs: redox_regs.fs as _,
226                gs: 0,      // gs: redox_regs.gs as _,
227            };
228            Ok(0)
229        }
230        sys_ptrace::PTRACE_SETREGS => {
231            let c_regs = unsafe { &*(data as *mut user_regs_struct) };
232            let redox_regs = syscall::IntRegisters {
233                r15: c_regs.r15 as _,
234                r14: c_regs.r14 as _,
235                r13: c_regs.r13 as _,
236                r12: c_regs.r12 as _,
237                rbp: c_regs.rbp as _,
238                rbx: c_regs.rbx as _,
239                r11: c_regs.r11 as _,
240                r10: c_regs.r10 as _,
241                r9: c_regs.r9 as _,
242                r8: c_regs.r8 as _,
243                rax: c_regs.orig_rax as _, // c_regs.rax as _,
244                rcx: c_regs.rcx as _,
245                rdx: c_regs.rdx as _,
246                rsi: c_regs.rsi as _,
247                rdi: c_regs.rdi as _,
248                // orig_rax: c_regs.orig_rax as _,
249                rip: c_regs.rip as _,
250                cs: c_regs.cs as _,
251                rflags: c_regs.eflags as _,
252                rsp: c_regs.rsp as _,
253                ss: c_regs.ss as _,
254                // fs_base: c_regs.fs_base as _,
255                // gs_base: c_regs.gs_base as _,
256                // ds: c_regs.ds as _,
257                // es: c_regs.es as _,
258                // fs: c_regs.fs as _,
259                // gs: c_regs.gs as _,
260            };
261            (&mut &session.regs).write(&redox_regs)?;
262            Ok(0)
263        }
264        _ => unimplemented!(),
265    }
266}
267
268#[cfg(target_arch = "riscv64")]
269fn inner_ptrace(
270    request: c_int,
271    pid: pid_t,
272    addr: *mut c_void,
273    data: *mut c_void,
274) -> io::Result<c_int> {
275    //TODO: riscv64
276    unimplemented!("inner_ptrace not implemented on riscv64");
277}
278
279impl PalPtrace for Sys {
280    #[allow(unused_unsafe)] // keeping inner unsafe fails x86_64, removing fails riscv cross-build
281    unsafe fn ptrace(
282        request: c_int,
283        pid: pid_t,
284        addr: *mut c_void,
285        data: *mut c_void,
286    ) -> Result<c_int, Errno> {
287        unsafe { inner_ptrace(request, pid, addr, data) }
288            .map_err(|err| Errno(err.raw_os_error().unwrap_or(EIO)))
289    }
290}