1 use cfg_if::cfg_if;
2 use crate::errno::Errno;
3 use libc::{self, c_int};
4 use std::ptr;
5 use crate::sys::signal::Signal;
6 use crate::unistd::Pid;
7 use crate::Result;
8
9 pub type RequestType = c_int;
10
11 cfg_if! {
12 if #[cfg(any(target_os = "dragonfly",
13 target_os = "freebsd",
14 target_os = "macos",
15 target_os = "openbsd"))] {
16 #[doc(hidden)]
17 pub type AddressType = *mut ::libc::c_char;
18 } else {
19 #[doc(hidden)]
20 pub type AddressType = *mut ::libc::c_void;
21 }
22 }
23
24 libc_enum! {
25 #[repr(i32)]
26 /// Ptrace Request enum defining the action to be taken.
27 pub enum Request {
28 PT_TRACE_ME,
29 PT_READ_I,
30 PT_READ_D,
31 #[cfg(target_os = "macos")]
32 PT_READ_U,
33 PT_WRITE_I,
34 PT_WRITE_D,
35 #[cfg(target_os = "macos")]
36 PT_WRITE_U,
37 PT_CONTINUE,
38 PT_KILL,
39 #[cfg(any(any(target_os = "dragonfly",
40 target_os = "freebsd",
41 target_os = "macos"),
42 all(target_os = "openbsd", target_arch = "x86_64"),
43 all(target_os = "netbsd", any(target_arch = "x86_64",
44 target_arch = "powerpc"))))]
45 PT_STEP,
46 PT_ATTACH,
47 PT_DETACH,
48 #[cfg(target_os = "macos")]
49 PT_SIGEXC,
50 #[cfg(target_os = "macos")]
51 PT_THUPDATE,
52 #[cfg(target_os = "macos")]
53 PT_ATTACHEXC
54 }
55 }
56
ptrace_other( request: Request, pid: Pid, addr: AddressType, data: c_int, ) -> Result<c_int>57 unsafe fn ptrace_other(
58 request: Request,
59 pid: Pid,
60 addr: AddressType,
61 data: c_int,
62 ) -> Result<c_int> {
63 Errno::result(libc::ptrace(
64 request as RequestType,
65 libc::pid_t::from(pid),
66 addr,
67 data,
68 )).map(|_| 0)
69 }
70
71 /// Sets the process as traceable, as with `ptrace(PT_TRACEME, ...)`
72 ///
73 /// Indicates that this process is to be traced by its parent.
74 /// This is the only ptrace request to be issued by the tracee.
traceme() -> Result<()>75 pub fn traceme() -> Result<()> {
76 unsafe { ptrace_other(Request::PT_TRACE_ME, Pid::from_raw(0), ptr::null_mut(), 0).map(drop) }
77 }
78
79 /// Attach to a running process, as with `ptrace(PT_ATTACH, ...)`
80 ///
81 /// Attaches to the process specified by `pid`, making it a tracee of the calling process.
attach(pid: Pid) -> Result<()>82 pub fn attach(pid: Pid) -> Result<()> {
83 unsafe { ptrace_other(Request::PT_ATTACH, pid, ptr::null_mut(), 0).map(drop) }
84 }
85
86 /// Detaches the current running process, as with `ptrace(PT_DETACH, ...)`
87 ///
88 /// Detaches from the process specified by `pid` allowing it to run freely, optionally delivering a
89 /// signal specified by `sig`.
detach<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()>90 pub fn detach<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
91 let data = match sig.into() {
92 Some(s) => s as c_int,
93 None => 0,
94 };
95 unsafe {
96 ptrace_other(Request::PT_DETACH, pid, ptr::null_mut(), data).map(drop)
97 }
98 }
99
100 /// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)`
101 ///
102 /// Continues the execution of the process with PID `pid`, optionally
103 /// delivering a signal specified by `sig`.
cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()>104 pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
105 let data = match sig.into() {
106 Some(s) => s as c_int,
107 None => 0,
108 };
109 unsafe {
110 // Ignore the useless return value
111 ptrace_other(Request::PT_CONTINUE, pid, 1 as AddressType, data).map(drop)
112 }
113 }
114
115 /// Issues a kill request as with `ptrace(PT_KILL, ...)`
116 ///
117 /// This request is equivalent to `ptrace(PT_CONTINUE, ..., SIGKILL);`
kill(pid: Pid) -> Result<()>118 pub fn kill(pid: Pid) -> Result<()> {
119 unsafe {
120 ptrace_other(Request::PT_KILL, pid, 0 as AddressType, 0).map(drop)
121 }
122 }
123
124 /// Move the stopped tracee process forward by a single step as with
125 /// `ptrace(PT_STEP, ...)`
126 ///
127 /// Advances the execution of the process with PID `pid` by a single step optionally delivering a
128 /// signal specified by `sig`.
129 ///
130 /// # Example
131 /// ```rust
132 /// use nix::sys::ptrace::step;
133 /// use nix::unistd::Pid;
134 /// use nix::sys::signal::Signal;
135 /// use nix::sys::wait::*;
136 /// fn main() {
137 /// // If a process changes state to the stopped state because of a SIGUSR1
138 /// // signal, this will step the process forward and forward the user
139 /// // signal to the stopped process
140 /// match waitpid(Pid::from_raw(-1), None) {
141 /// Ok(WaitStatus::Stopped(pid, Signal::SIGUSR1)) => {
142 /// let _ = step(pid, Signal::SIGUSR1);
143 /// }
144 /// _ => {},
145 /// }
146 /// }
147 /// ```
148 #[cfg(
149 any(
150 any(target_os = "dragonfly", target_os = "freebsd", target_os = "macos"),
151 all(target_os = "openbsd", target_arch = "x86_64"),
152 all(target_os = "netbsd",
153 any(target_arch = "x86_64", target_arch = "powerpc")
154 )
155 )
156 )]
step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()>157 pub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
158 let data = match sig.into() {
159 Some(s) => s as c_int,
160 None => 0,
161 };
162 unsafe { ptrace_other(Request::PT_STEP, pid, ptr::null_mut(), data).map(drop) }
163 }
164
165 /// Reads a word from a processes memory at the given address
read(pid: Pid, addr: AddressType) -> Result<c_int>166 pub fn read(pid: Pid, addr: AddressType) -> Result<c_int> {
167 unsafe {
168 // Traditionally there was a difference between reading data or
169 // instruction memory but not in modern systems.
170 ptrace_other(Request::PT_READ_D, pid, addr, 0)
171 }
172 }
173
174 /// Writes a word into the processes memory at the given address
write(pid: Pid, addr: AddressType, data: c_int) -> Result<()>175 pub fn write(pid: Pid, addr: AddressType, data: c_int) -> Result<()> {
176 unsafe { ptrace_other(Request::PT_WRITE_D, pid, addr, data).map(drop) }
177 }
178