1use 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 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 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 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 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 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 _) {}; 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 sys_ptrace::PTRACE_SYSCALL if session.first => syscall::PTRACE_STOP_PRE_SYSCALL,
180 sys_ptrace::PTRACE_SYSCALL => syscall,
181 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 _, 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, gs_base: 0, ds: 0, es: 0, fs: 0, gs: 0, };
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 _, rcx: c_regs.rcx as _,
245 rdx: c_regs.rdx as _,
246 rsi: c_regs.rsi as _,
247 rdi: c_regs.rdi as _,
248 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 };
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 unimplemented!("inner_ptrace not implemented on riscv64");
277}
278
279impl PalPtrace for Sys {
280 #[allow(unused_unsafe)] 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}