1use crate::errno::Errno;
4use crate::sys::signal::Signal;
5use crate::unistd::Pid;
6use crate::Result;
7use cfg_if::cfg_if;
8use libc::{self, c_long, c_void, siginfo_t};
9use std::{mem, ptr};
10
11pub type AddressType = *mut ::libc::c_void;
12
13#[cfg(all(
14 target_os = "linux",
15 any(
16 all(
17 target_arch = "x86_64",
18 any(target_env = "gnu", target_env = "musl")
19 ),
20 all(target_arch = "x86", target_env = "gnu")
21 )
22))]
23use libc::user_regs_struct;
24
25cfg_if! {
26 if #[cfg(any(all(target_os = "linux", target_arch = "s390x"),
27 all(target_os = "linux", target_env = "gnu"),
28 target_env = "uclibc"))] {
29 #[doc(hidden)]
30 pub type RequestType = ::libc::c_uint;
31 } else {
32 #[doc(hidden)]
33 pub type RequestType = ::libc::c_int;
34 }
35}
36
37libc_enum! {
38 #[cfg_attr(not(any(target_env = "musl", target_env = "uclibc", target_os = "android")), repr(u32))]
39 #[cfg_attr(any(target_env = "musl", target_env = "uclibc", target_os = "android"), repr(i32))]
40 #[non_exhaustive]
42 pub enum Request {
43 PTRACE_TRACEME,
44 PTRACE_PEEKTEXT,
45 PTRACE_PEEKDATA,
46 PTRACE_PEEKUSER,
47 PTRACE_POKETEXT,
48 PTRACE_POKEDATA,
49 PTRACE_POKEUSER,
50 PTRACE_CONT,
51 PTRACE_KILL,
52 PTRACE_SINGLESTEP,
53 #[cfg(any(all(target_os = "android", target_pointer_width = "32"),
54 all(target_os = "linux", any(target_env = "musl",
55 target_arch = "mips",
56 target_arch = "mips64",
57 target_arch = "x86_64",
58 target_pointer_width = "32"))))]
59 PTRACE_GETREGS,
60 #[cfg(any(all(target_os = "android", target_pointer_width = "32"),
61 all(target_os = "linux", any(target_env = "musl",
62 target_arch = "mips",
63 target_arch = "mips64",
64 target_arch = "x86_64",
65 target_pointer_width = "32"))))]
66 PTRACE_SETREGS,
67 #[cfg(any(all(target_os = "android", target_pointer_width = "32"),
68 all(target_os = "linux", any(target_env = "musl",
69 target_arch = "mips",
70 target_arch = "mips64",
71 target_arch = "x86_64",
72 target_pointer_width = "32"))))]
73 PTRACE_GETFPREGS,
74 #[cfg(any(all(target_os = "android", target_pointer_width = "32"),
75 all(target_os = "linux", any(target_env = "musl",
76 target_arch = "mips",
77 target_arch = "mips64",
78 target_arch = "x86_64",
79 target_pointer_width = "32"))))]
80 PTRACE_SETFPREGS,
81 PTRACE_ATTACH,
82 PTRACE_DETACH,
83 #[cfg(all(target_os = "linux", any(target_env = "musl",
84 target_arch = "mips",
85 target_arch = "mips64",
86 target_arch = "x86",
87 target_arch = "x86_64")))]
88 PTRACE_GETFPXREGS,
89 #[cfg(all(target_os = "linux", any(target_env = "musl",
90 target_arch = "mips",
91 target_arch = "mips64",
92 target_arch = "x86",
93 target_arch = "x86_64")))]
94 PTRACE_SETFPXREGS,
95 PTRACE_SYSCALL,
96 PTRACE_SETOPTIONS,
97 PTRACE_GETEVENTMSG,
98 PTRACE_GETSIGINFO,
99 PTRACE_SETSIGINFO,
100 #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
101 target_arch = "mips64"))))]
102 PTRACE_GETREGSET,
103 #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
104 target_arch = "mips64"))))]
105 PTRACE_SETREGSET,
106 #[cfg(target_os = "linux")]
107 #[cfg_attr(docsrs, doc(cfg(all())))]
108 PTRACE_SEIZE,
109 #[cfg(target_os = "linux")]
110 #[cfg_attr(docsrs, doc(cfg(all())))]
111 PTRACE_INTERRUPT,
112 #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
113 target_arch = "mips64"))))]
114 PTRACE_LISTEN,
115 #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
116 target_arch = "mips64"))))]
117 PTRACE_PEEKSIGINFO,
118 #[cfg(all(target_os = "linux", target_env = "gnu",
119 any(target_arch = "x86", target_arch = "x86_64")))]
120 PTRACE_SYSEMU,
121 #[cfg(all(target_os = "linux", target_env = "gnu",
122 any(target_arch = "x86", target_arch = "x86_64")))]
123 PTRACE_SYSEMU_SINGLESTEP,
124 }
125}
126
127libc_enum! {
128 #[repr(i32)]
129 #[non_exhaustive]
133 pub enum Event {
134 PTRACE_EVENT_FORK,
136 PTRACE_EVENT_VFORK,
138 PTRACE_EVENT_CLONE,
140 PTRACE_EVENT_EXEC,
142 PTRACE_EVENT_VFORK_DONE,
144 PTRACE_EVENT_EXIT,
147 PTRACE_EVENT_SECCOMP,
149 PTRACE_EVENT_STOP,
152 }
153}
154
155libc_bitflags! {
156 pub struct Options: libc::c_int {
159 PTRACE_O_TRACESYSGOOD;
163 PTRACE_O_TRACEFORK;
165 PTRACE_O_TRACEVFORK;
167 PTRACE_O_TRACECLONE;
169 PTRACE_O_TRACEEXEC;
171 PTRACE_O_TRACEVFORKDONE;
173 PTRACE_O_TRACEEXIT;
176 PTRACE_O_TRACESECCOMP;
179 PTRACE_O_EXITKILL;
182 }
183}
184
185fn ptrace_peek(
186 request: Request,
187 pid: Pid,
188 addr: AddressType,
189 data: *mut c_void,
190) -> Result<c_long> {
191 let ret = unsafe {
192 Errno::clear();
193 libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data)
194 };
195 match Errno::result(ret) {
196 Ok(..) | Err(Errno::UnknownErrno) => Ok(ret),
197 err @ Err(..) => err,
198 }
199}
200
201#[cfg(all(
203 target_os = "linux",
204 any(
205 all(
206 target_arch = "x86_64",
207 any(target_env = "gnu", target_env = "musl")
208 ),
209 all(target_arch = "x86", target_env = "gnu")
210 )
211))]
212pub fn getregs(pid: Pid) -> Result<user_regs_struct> {
213 ptrace_get_data::<user_regs_struct>(Request::PTRACE_GETREGS, pid)
214}
215
216#[cfg(all(
218 target_os = "linux",
219 any(
220 all(
221 target_arch = "x86_64",
222 any(target_env = "gnu", target_env = "musl")
223 ),
224 all(target_arch = "x86", target_env = "gnu")
225 )
226))]
227pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> {
228 let res = unsafe {
229 libc::ptrace(
230 Request::PTRACE_SETREGS as RequestType,
231 libc::pid_t::from(pid),
232 ptr::null_mut::<c_void>(),
233 ®s as *const _ as *const c_void,
234 )
235 };
236 Errno::result(res).map(drop)
237}
238
239fn ptrace_get_data<T>(request: Request, pid: Pid) -> Result<T> {
244 let mut data = mem::MaybeUninit::uninit();
245 let res = unsafe {
246 libc::ptrace(
247 request as RequestType,
248 libc::pid_t::from(pid),
249 ptr::null_mut::<T>(),
250 data.as_mut_ptr() as *const _ as *const c_void,
251 )
252 };
253 Errno::result(res)?;
254 Ok(unsafe { data.assume_init() })
255}
256
257unsafe fn ptrace_other(
258 request: Request,
259 pid: Pid,
260 addr: AddressType,
261 data: *mut c_void,
262) -> Result<c_long> {
263 Errno::result(libc::ptrace(
264 request as RequestType,
265 libc::pid_t::from(pid),
266 addr,
267 data,
268 ))
269 .map(|_| 0)
270}
271
272pub fn setoptions(pid: Pid, options: Options) -> Result<()> {
274 let res = unsafe {
275 libc::ptrace(
276 Request::PTRACE_SETOPTIONS as RequestType,
277 libc::pid_t::from(pid),
278 ptr::null_mut::<c_void>(),
279 options.bits() as *mut c_void,
280 )
281 };
282 Errno::result(res).map(drop)
283}
284
285pub fn getevent(pid: Pid) -> Result<c_long> {
287 ptrace_get_data::<c_long>(Request::PTRACE_GETEVENTMSG, pid)
288}
289
290pub fn getsiginfo(pid: Pid) -> Result<siginfo_t> {
292 ptrace_get_data::<siginfo_t>(Request::PTRACE_GETSIGINFO, pid)
293}
294
295pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> {
297 let ret = unsafe {
298 Errno::clear();
299 libc::ptrace(
300 Request::PTRACE_SETSIGINFO as RequestType,
301 libc::pid_t::from(pid),
302 ptr::null_mut::<c_void>(),
303 sig as *const _ as *const c_void,
304 )
305 };
306 match Errno::result(ret) {
307 Ok(_) => Ok(()),
308 Err(e) => Err(e),
309 }
310}
311
312pub fn traceme() -> Result<()> {
317 unsafe {
318 ptrace_other(
319 Request::PTRACE_TRACEME,
320 Pid::from_raw(0),
321 ptr::null_mut(),
322 ptr::null_mut(),
323 )
324 .map(drop) }
326}
327
328pub fn syscall<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
333 let data = match sig.into() {
334 Some(s) => s as i32 as *mut c_void,
335 None => ptr::null_mut(),
336 };
337 unsafe {
338 ptrace_other(Request::PTRACE_SYSCALL, pid, ptr::null_mut(), data)
339 .map(drop) }
341}
342
343#[cfg(all(
349 target_os = "linux",
350 target_env = "gnu",
351 any(target_arch = "x86", target_arch = "x86_64")
352))]
353pub fn sysemu<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
354 let data = match sig.into() {
355 Some(s) => s as i32 as *mut c_void,
356 None => ptr::null_mut(),
357 };
358 unsafe {
359 ptrace_other(Request::PTRACE_SYSEMU, pid, ptr::null_mut(), data)
360 .map(drop)
361 }
363}
364
365pub fn attach(pid: Pid) -> Result<()> {
369 unsafe {
370 ptrace_other(
371 Request::PTRACE_ATTACH,
372 pid,
373 ptr::null_mut(),
374 ptr::null_mut(),
375 )
376 .map(drop) }
378}
379
380#[cfg(target_os = "linux")]
384#[cfg_attr(docsrs, doc(cfg(all())))]
385pub fn seize(pid: Pid, options: Options) -> Result<()> {
386 unsafe {
387 ptrace_other(
388 Request::PTRACE_SEIZE,
389 pid,
390 ptr::null_mut(),
391 options.bits() as *mut c_void,
392 )
393 .map(drop) }
395}
396
397pub fn detach<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
402 let data = match sig.into() {
403 Some(s) => s as i32 as *mut c_void,
404 None => ptr::null_mut(),
405 };
406 unsafe {
407 ptrace_other(Request::PTRACE_DETACH, pid, ptr::null_mut(), data)
408 .map(drop)
409 }
410}
411
412pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
417 let data = match sig.into() {
418 Some(s) => s as i32 as *mut c_void,
419 None => ptr::null_mut(),
420 };
421 unsafe {
422 ptrace_other(Request::PTRACE_CONT, pid, ptr::null_mut(), data).map(drop)
423 }
425}
426
427#[cfg(target_os = "linux")]
431#[cfg_attr(docsrs, doc(cfg(all())))]
432pub fn interrupt(pid: Pid) -> Result<()> {
433 unsafe {
434 ptrace_other(
435 Request::PTRACE_INTERRUPT,
436 pid,
437 ptr::null_mut(),
438 ptr::null_mut(),
439 )
440 .map(drop)
441 }
442}
443
444pub fn kill(pid: Pid) -> Result<()> {
448 unsafe {
449 ptrace_other(
450 Request::PTRACE_KILL,
451 pid,
452 ptr::null_mut(),
453 ptr::null_mut(),
454 )
455 .map(drop)
456 }
457}
458
459pub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
483 let data = match sig.into() {
484 Some(s) => s as i32 as *mut c_void,
485 None => ptr::null_mut(),
486 };
487 unsafe {
488 ptrace_other(Request::PTRACE_SINGLESTEP, pid, ptr::null_mut(), data)
489 .map(drop)
490 }
491}
492
493#[cfg(all(
500 target_os = "linux",
501 target_env = "gnu",
502 any(target_arch = "x86", target_arch = "x86_64")
503))]
504pub fn sysemu_step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
505 let data = match sig.into() {
506 Some(s) => s as i32 as *mut c_void,
507 None => ptr::null_mut(),
508 };
509 unsafe {
510 ptrace_other(
511 Request::PTRACE_SYSEMU_SINGLESTEP,
512 pid,
513 ptr::null_mut(),
514 data,
515 )
516 .map(drop) }
518}
519
520pub fn read(pid: Pid, addr: AddressType) -> Result<c_long> {
522 ptrace_peek(Request::PTRACE_PEEKDATA, pid, addr, ptr::null_mut())
523}
524
525pub unsafe fn write(
532 pid: Pid,
533 addr: AddressType,
534 data: *mut c_void,
535) -> Result<()> {
536 ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data).map(drop)
537}
538
539pub fn read_user(pid: Pid, offset: AddressType) -> Result<c_long> {
542 ptrace_peek(Request::PTRACE_PEEKUSER, pid, offset, ptr::null_mut())
543}
544
545pub unsafe fn write_user(
553 pid: Pid,
554 offset: AddressType,
555 data: *mut c_void,
556) -> Result<()> {
557 ptrace_other(Request::PTRACE_POKEUSER, pid, offset, data).map(drop)
558}