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 any(target_arch = "x86_64", target_arch = "aarch64"),
18 any(target_env = "gnu", target_env = "musl")
19 ),
20 all(target_arch = "x86", target_env = "gnu"),
21 all(target_arch = "riscv64", target_env = "gnu"),
22 ),
23))]
24use libc::user_regs_struct;
25
26cfg_if! {
27 if #[cfg(any(all(target_os = "linux", target_arch = "s390x"),
28 all(target_os = "linux", target_env = "gnu"),
29 target_env = "uclibc"))] {
30 #[doc(hidden)]
31 pub type RequestType = ::libc::c_uint;
32 } else {
33 #[doc(hidden)]
34 pub type RequestType = ::libc::c_int;
35 }
36}
37
38libc_enum! {
39 #[cfg_attr(not(any(target_env = "musl", target_env = "uclibc", target_os = "android", target_env = "ohos")), repr(u32))]
40 #[cfg_attr(any(target_env = "musl", target_env = "uclibc", target_os = "android", target_env = "ohos"), repr(i32))]
41 #[non_exhaustive]
43 pub enum Request {
44 PTRACE_TRACEME,
45 PTRACE_PEEKTEXT,
46 PTRACE_PEEKDATA,
47 PTRACE_PEEKUSER,
48 PTRACE_POKETEXT,
49 PTRACE_POKEDATA,
50 PTRACE_POKEUSER,
51 PTRACE_CONT,
52 PTRACE_KILL,
53 PTRACE_SINGLESTEP,
54 #[cfg(any(all(target_os = "android", target_pointer_width = "32"),
55 all(target_os = "linux", any(target_env = "musl",
56 target_env = "ohos",
57 target_arch = "mips",
58 target_arch = "mips32r6",
59 target_arch = "mips64",
60 target_arch = "mips64r6",
61 target_arch = "x86_64",
62 target_pointer_width = "32"))))]
63 PTRACE_GETREGS,
64 #[cfg(any(all(target_os = "android", target_pointer_width = "32"),
65 all(target_os = "linux", any(target_env = "musl",
66 target_env = "ohos",
67 target_arch = "mips",
68 target_arch = "mips32r6",
69 target_arch = "mips64",
70 target_arch = "mips64r6",
71 target_arch = "x86_64",
72 target_pointer_width = "32"))))]
73 PTRACE_SETREGS,
74 #[cfg(any(all(target_os = "android", target_pointer_width = "32"),
75 all(target_os = "linux", any(target_env = "musl",
76 target_env = "ohos",
77 target_arch = "mips",
78 target_arch = "mips32r6",
79 target_arch = "mips64",
80 target_arch = "mips64r6",
81 target_arch = "x86_64",
82 target_pointer_width = "32"))))]
83 PTRACE_GETFPREGS,
84 #[cfg(any(all(target_os = "android", target_pointer_width = "32"),
85 all(target_os = "linux", any(target_env = "musl",
86 target_env = "ohos",
87 target_arch = "mips",
88 target_arch = "mips32r6",
89 target_arch = "mips64",
90 target_arch = "mips64r6",
91 target_arch = "x86_64",
92 target_pointer_width = "32"))))]
93 PTRACE_SETFPREGS,
94 PTRACE_ATTACH,
95 PTRACE_DETACH,
96 #[cfg(all(target_os = "linux", any(target_env = "musl",
97 target_env = "ohos",
98 target_arch = "mips",
99 target_arch = "mips32r6",
100 target_arch = "mips64",
101 target_arch = "mips64r6",
102 target_arch = "x86",
103 target_arch = "x86_64")))]
104 PTRACE_GETFPXREGS,
105 #[cfg(all(target_os = "linux", any(target_env = "musl",
106 target_env = "ohos",
107 target_arch = "mips",
108 target_arch = "mips32r6",
109 target_arch = "mips64",
110 target_arch = "mips64r6",
111 target_arch = "x86",
112 target_arch = "x86_64")))]
113 PTRACE_SETFPXREGS,
114 PTRACE_SYSCALL,
115 PTRACE_SETOPTIONS,
116 PTRACE_GETEVENTMSG,
117 PTRACE_GETSIGINFO,
118 PTRACE_SETSIGINFO,
119 #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
120 target_arch = "mips32r6",
121 target_arch = "mips64",
122 target_arch = "mips64r6"))))]
123 PTRACE_GETREGSET,
124 #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
125 target_arch = "mips32r6",
126 target_arch = "mips64",
127 target_arch = "mips64r6"))))]
128 PTRACE_SETREGSET,
129 #[cfg(target_os = "linux")]
130 PTRACE_SEIZE,
131 #[cfg(target_os = "linux")]
132 PTRACE_INTERRUPT,
133 #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
134 target_arch = "mips32r6",
135 target_arch = "mips64",
136 target_arch = "mips64r6"))))]
137 PTRACE_LISTEN,
138 #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
139 target_arch = "mips32r6",
140 target_arch = "mips64",
141 target_arch = "mips64r6"))))]
142 PTRACE_PEEKSIGINFO,
143 #[cfg(all(target_os = "linux", target_env = "gnu",
144 any(target_arch = "x86", target_arch = "x86_64")))]
145 PTRACE_SYSEMU,
146 #[cfg(all(target_os = "linux", target_env = "gnu",
147 any(target_arch = "x86", target_arch = "x86_64")))]
148 PTRACE_SYSEMU_SINGLESTEP,
149 #[cfg(all(target_os = "linux", target_env = "gnu"))]
150 PTRACE_GET_SYSCALL_INFO,
151 }
152}
153
154libc_enum! {
155 #[repr(i32)]
156 #[non_exhaustive]
160 pub enum Event {
161 PTRACE_EVENT_FORK,
163 PTRACE_EVENT_VFORK,
165 PTRACE_EVENT_CLONE,
167 PTRACE_EVENT_EXEC,
169 PTRACE_EVENT_VFORK_DONE,
171 PTRACE_EVENT_EXIT,
174 PTRACE_EVENT_SECCOMP,
176 PTRACE_EVENT_STOP,
179 }
180}
181
182#[cfg(all(
183 target_os = "linux",
184 any(
185 all(
186 target_env = "gnu",
187 any(
188 target_arch = "x86_64",
189 target_arch = "x86",
190 target_arch = "aarch64",
191 target_arch = "riscv64",
192 )
193 ),
194 all(
195 target_env = "musl",
196 target_arch = "aarch64",
197 )
198 ),
199))]
200libc_enum! {
201 #[repr(i32)]
202 #[non_exhaustive]
204 pub enum RegisterSetValue {
205 NT_PRSTATUS,
206 NT_PRFPREG,
207 NT_PRPSINFO,
208 NT_TASKSTRUCT,
209 NT_AUXV,
210 }
211}
212
213#[cfg(all(
214 target_os = "linux",
215 any(
216 all(
217 target_env = "gnu",
218 any(
219 target_arch = "x86_64",
220 target_arch = "x86",
221 target_arch = "aarch64",
222 target_arch = "riscv64",
223 )
224 ),
225 all(
226 target_env = "musl",
227 target_arch = "aarch64",
228 )
229 ),
230))]
231pub unsafe trait RegisterSet {
239 const VALUE: RegisterSetValue;
241
242 type Regs;
244}
245
246
247#[cfg(all(
248 target_os = "linux",
249 any(
250 all(
251 target_env = "gnu",
252 any(
253 target_arch = "x86_64",
254 target_arch = "x86",
255 target_arch = "aarch64",
256 target_arch = "riscv64",
257 )
258 ),
259 all(
260 target_env = "musl",
261 target_arch = "aarch64",
262 )
263 ),
264))]
265pub mod regset {
267 use super::*;
268
269 #[derive(Debug, Clone, Copy)]
270 pub enum NT_PRSTATUS {}
272
273 unsafe impl RegisterSet for NT_PRSTATUS {
274 const VALUE: RegisterSetValue = RegisterSetValue::NT_PRSTATUS;
275 type Regs = user_regs_struct;
276 }
277
278 #[derive(Debug, Clone, Copy)]
279 pub enum NT_PRFPREG {}
281
282 unsafe impl RegisterSet for NT_PRFPREG {
283 const VALUE: RegisterSetValue = RegisterSetValue::NT_PRFPREG;
284 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
285 type Regs = libc::user_fpregs_struct;
286 #[cfg(target_arch = "aarch64")]
287 type Regs = libc::user_fpsimd_struct;
288 #[cfg(target_arch = "riscv64")]
289 type Regs = libc::__riscv_mc_d_ext_state;
290 }
291}
292
293libc_bitflags! {
294 pub struct Options: libc::c_int {
297 PTRACE_O_TRACESYSGOOD;
301 PTRACE_O_TRACEFORK;
303 PTRACE_O_TRACEVFORK;
305 PTRACE_O_TRACECLONE;
307 PTRACE_O_TRACEEXEC;
309 PTRACE_O_TRACEVFORKDONE;
311 PTRACE_O_TRACEEXIT;
314 PTRACE_O_TRACESECCOMP;
317 PTRACE_O_EXITKILL;
320 }
321}
322
323fn ptrace_peek(
324 request: Request,
325 pid: Pid,
326 addr: AddressType,
327 data: *mut c_void,
328) -> Result<c_long> {
329 let ret = unsafe {
330 Errno::clear();
331 libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data)
332 };
333 match Errno::result(ret) {
334 Ok(..) | Err(Errno::UnknownErrno) => Ok(ret),
335 err @ Err(..) => err,
336 }
337}
338
339#[cfg(all(
347 target_os = "linux",
348 any(
349 all(
350 target_arch = "x86_64",
351 any(target_env = "gnu", target_env = "musl")
352 ),
353 all(target_arch = "x86", target_env = "gnu")
354 )
355))]
356pub fn getregs(pid: Pid) -> Result<user_regs_struct> {
357 ptrace_get_data::<user_regs_struct>(Request::PTRACE_GETREGS, pid)
358}
359
360#[cfg(all(
368 target_os = "linux",
369 any(
370 all(
371 target_arch = "aarch64",
372 any(target_env = "gnu", target_env = "musl")
373 ),
374 all(target_arch = "riscv64", target_env = "gnu")
375 )
376))]
377pub fn getregs(pid: Pid) -> Result<user_regs_struct> {
378 getregset::<regset::NT_PRSTATUS>(pid)
379}
380
381#[cfg(all(
383 target_os = "linux",
384 any(
385 all(
386 target_env = "gnu",
387 any(
388 target_arch = "x86_64",
389 target_arch = "x86",
390 target_arch = "aarch64",
391 target_arch = "riscv64"
392 )
393 ),
394 all(target_env = "musl", target_arch = "aarch64")
395 )
396))]
397pub fn getregset<S: RegisterSet>(pid: Pid) -> Result<S::Regs> {
398 let request = Request::PTRACE_GETREGSET;
399 let mut data = mem::MaybeUninit::<S::Regs>::uninit();
400 let mut iov = libc::iovec {
401 iov_base: data.as_mut_ptr().cast(),
402 iov_len: mem::size_of::<S::Regs>(),
403 };
404 unsafe {
405 ptrace_other(
406 request,
407 pid,
408 S::VALUE as i32 as AddressType,
409 (&mut iov as *mut libc::iovec).cast(),
410 )?;
411 };
412 Ok(unsafe { data.assume_init() })
413}
414
415#[cfg(all(
423 target_os = "linux",
424 any(
425 all(
426 target_arch = "x86_64",
427 any(target_env = "gnu", target_env = "musl")
428 ),
429 all(target_arch = "x86", target_env = "gnu")
430 )
431))]
432pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> {
433 let res = unsafe {
434 libc::ptrace(
435 Request::PTRACE_SETREGS as RequestType,
436 libc::pid_t::from(pid),
437 ptr::null_mut::<c_void>(),
438 ®s as *const user_regs_struct as *const c_void,
439 )
440 };
441 Errno::result(res).map(drop)
442}
443
444#[cfg(all(
452 target_os = "linux",
453 any(
454 all(
455 target_env = "gnu",
456 any(target_arch = "aarch64", target_arch = "riscv64")
457 ),
458 all(target_env = "musl", target_arch = "aarch64")
459 )
460))]
461pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> {
462 setregset::<regset::NT_PRSTATUS>(pid, regs)
463}
464
465#[cfg(all(
467 target_os = "linux",
468 any(
469 all(
470 target_env = "gnu",
471 any(
472 target_arch = "x86_64",
473 target_arch = "x86",
474 target_arch = "aarch64",
475 target_arch = "riscv64"
476 )
477 ),
478 all(target_env = "musl", target_arch = "aarch64")
479 )
480))]
481pub fn setregset<S: RegisterSet>(pid: Pid, mut regs: S::Regs) -> Result<()> {
482 let mut iov = libc::iovec {
483 iov_base: (&mut regs as *mut S::Regs).cast(),
484 iov_len: mem::size_of::<S::Regs>(),
485 };
486 unsafe {
487 ptrace_other(
488 Request::PTRACE_SETREGSET,
489 pid,
490 S::VALUE as i32 as AddressType,
491 (&mut iov as *mut libc::iovec).cast(),
492 )?;
493 }
494 Ok(())
495}
496
497fn ptrace_get_data<T>(request: Request, pid: Pid) -> Result<T> {
502 let mut data = mem::MaybeUninit::<T>::uninit();
503 let res = unsafe {
504 libc::ptrace(
505 request as RequestType,
506 libc::pid_t::from(pid),
507 std::mem::size_of::<T>(),
508 data.as_mut_ptr(),
509 )
510 };
511 Errno::result(res)?;
512 Ok(unsafe { data.assume_init() })
513}
514
515unsafe fn ptrace_other(
516 request: Request,
517 pid: Pid,
518 addr: AddressType,
519 data: *mut c_void,
520) -> Result<c_long> {
521 unsafe {
522 Errno::result(libc::ptrace(
523 request as RequestType,
524 libc::pid_t::from(pid),
525 addr,
526 data,
527 ))
528 .map(|_| 0)
529 }
530}
531
532pub fn setoptions(pid: Pid, options: Options) -> Result<()> {
534 let res = unsafe {
535 libc::ptrace(
536 Request::PTRACE_SETOPTIONS as RequestType,
537 libc::pid_t::from(pid),
538 ptr::null_mut::<c_void>(),
539 options.bits() as *mut c_void,
540 )
541 };
542 Errno::result(res).map(drop)
543}
544
545pub fn getevent(pid: Pid) -> Result<c_long> {
547 ptrace_get_data::<c_long>(Request::PTRACE_GETEVENTMSG, pid)
548}
549
550pub fn getsiginfo(pid: Pid) -> Result<siginfo_t> {
552 ptrace_get_data::<siginfo_t>(Request::PTRACE_GETSIGINFO, pid)
553}
554
555pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> {
557 let ret = unsafe {
558 Errno::clear();
559 libc::ptrace(
560 Request::PTRACE_SETSIGINFO as RequestType,
561 libc::pid_t::from(pid),
562 ptr::null_mut::<c_void>(),
563 sig as *const _ as *const c_void,
564 )
565 };
566 match Errno::result(ret) {
567 Ok(_) => Ok(()),
568 Err(e) => Err(e),
569 }
570}
571
572#[cfg(all(target_os = "linux", target_env = "gnu"))]
575pub fn syscall_info(pid: Pid) -> Result<libc::ptrace_syscall_info> {
576 ptrace_get_data::<libc::ptrace_syscall_info>(Request::PTRACE_GET_SYSCALL_INFO, pid)
577}
578
579pub fn traceme() -> Result<()> {
584 unsafe {
585 ptrace_other(
586 Request::PTRACE_TRACEME,
587 Pid::from_raw(0),
588 ptr::null_mut(),
589 ptr::null_mut(),
590 )
591 .map(drop) }
593}
594
595pub fn syscall<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
600 let data = match sig.into() {
601 Some(s) => s as i32 as *mut c_void,
602 None => ptr::null_mut(),
603 };
604 unsafe {
605 ptrace_other(Request::PTRACE_SYSCALL, pid, ptr::null_mut(), data)
606 .map(drop) }
608}
609
610#[cfg(all(
616 target_os = "linux",
617 target_env = "gnu",
618 any(target_arch = "x86", target_arch = "x86_64")
619))]
620pub fn sysemu<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
621 let data = match sig.into() {
622 Some(s) => s as i32 as *mut c_void,
623 None => ptr::null_mut(),
624 };
625 unsafe {
626 ptrace_other(Request::PTRACE_SYSEMU, pid, ptr::null_mut(), data)
627 .map(drop)
628 }
630}
631
632pub fn attach(pid: Pid) -> Result<()> {
636 unsafe {
637 ptrace_other(
638 Request::PTRACE_ATTACH,
639 pid,
640 ptr::null_mut(),
641 ptr::null_mut(),
642 )
643 .map(drop) }
645}
646
647#[cfg(target_os = "linux")]
651pub fn seize(pid: Pid, options: Options) -> Result<()> {
652 unsafe {
653 ptrace_other(
654 Request::PTRACE_SEIZE,
655 pid,
656 ptr::null_mut(),
657 options.bits() as *mut c_void,
658 )
659 .map(drop) }
661}
662
663pub fn detach<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
668 let data = match sig.into() {
669 Some(s) => s as i32 as *mut c_void,
670 None => ptr::null_mut(),
671 };
672 unsafe {
673 ptrace_other(Request::PTRACE_DETACH, pid, ptr::null_mut(), data)
674 .map(drop)
675 }
676}
677
678pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
683 let data = match sig.into() {
684 Some(s) => s as i32 as *mut c_void,
685 None => ptr::null_mut(),
686 };
687 unsafe {
688 ptrace_other(Request::PTRACE_CONT, pid, ptr::null_mut(), data).map(drop)
689 }
691}
692
693#[cfg(target_os = "linux")]
697pub fn interrupt(pid: Pid) -> Result<()> {
698 unsafe {
699 ptrace_other(
700 Request::PTRACE_INTERRUPT,
701 pid,
702 ptr::null_mut(),
703 ptr::null_mut(),
704 )
705 .map(drop)
706 }
707}
708
709pub fn kill(pid: Pid) -> Result<()> {
713 unsafe {
714 ptrace_other(
715 Request::PTRACE_KILL,
716 pid,
717 ptr::null_mut(),
718 ptr::null_mut(),
719 )
720 .map(drop)
721 }
722}
723
724pub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
748 let data = match sig.into() {
749 Some(s) => s as i32 as *mut c_void,
750 None => ptr::null_mut(),
751 };
752 unsafe {
753 ptrace_other(Request::PTRACE_SINGLESTEP, pid, ptr::null_mut(), data)
754 .map(drop)
755 }
756}
757
758#[cfg(all(
765 target_os = "linux",
766 target_env = "gnu",
767 any(target_arch = "x86", target_arch = "x86_64")
768))]
769pub fn sysemu_step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
770 let data = match sig.into() {
771 Some(s) => s as i32 as *mut c_void,
772 None => ptr::null_mut(),
773 };
774 unsafe {
775 ptrace_other(
776 Request::PTRACE_SYSEMU_SINGLESTEP,
777 pid,
778 ptr::null_mut(),
779 data,
780 )
781 .map(drop) }
783}
784
785pub fn read(pid: Pid, addr: AddressType) -> Result<c_long> {
788 ptrace_peek(Request::PTRACE_PEEKDATA, pid, addr, ptr::null_mut())
789}
790
791#[allow(clippy::not_unsafe_ptr_arg_deref)]
794pub fn write(pid: Pid, addr: AddressType, data: c_long) -> Result<()> {
795 unsafe {
796 ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data as *mut c_void)
800 .map(drop)
801 }
802}
803
804pub fn read_user(pid: Pid, offset: AddressType) -> Result<c_long> {
807 ptrace_peek(Request::PTRACE_PEEKUSER, pid, offset, ptr::null_mut())
808}
809
810#[allow(clippy::not_unsafe_ptr_arg_deref)]
813pub fn write_user(pid: Pid, offset: AddressType, data: c_long) -> Result<()> {
814 unsafe {
815 ptrace_other(Request::PTRACE_POKEUSER, pid, offset, data as *mut c_void)
819 .map(drop)
820 }
821}