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)] pthread_self() -> Pthread20 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 #[non_exhaustive] 28 pub enum Request { 29 PT_TRACE_ME, 30 PT_READ_I, pthread_kill<T: Into<Option<Signal>>>(thread: Pthread, signal: T) -> Result<()>31 PT_READ_D, 32 #[cfg(target_os = "macos")] 33 PT_READ_U, 34 PT_WRITE_I, 35 PT_WRITE_D, 36 #[cfg(target_os = "macos")] 37 PT_WRITE_U, 38 PT_CONTINUE, 39 PT_KILL, 40 #[cfg(any(any(target_os = "dragonfly", 41 target_os = "freebsd", 42 target_os = "macos"), 43 all(target_os = "openbsd", target_arch = "x86_64"), 44 all(target_os = "netbsd", any(target_arch = "x86_64", 45 target_arch = "powerpc"))))] 46 PT_STEP, 47 PT_ATTACH, 48 PT_DETACH, 49 #[cfg(target_os = "macos")] 50 PT_SIGEXC, 51 #[cfg(target_os = "macos")] 52 PT_THUPDATE, 53 #[cfg(target_os = "macos")] 54 PT_ATTACHEXC 55 } 56 } 57 58 unsafe fn ptrace_other( 59 request: Request, 60 pid: Pid, 61 addr: AddressType, 62 data: c_int, 63 ) -> Result<c_int> { 64 Errno::result(libc::ptrace( 65 request as RequestType, 66 libc::pid_t::from(pid), 67 addr, 68 data, 69 )).map(|_| 0) 70 } 71 72 /// Sets the process as traceable, as with `ptrace(PT_TRACEME, ...)` 73 /// 74 /// Indicates that this process is to be traced by its parent. 75 /// This is the only ptrace request to be issued by the tracee. 76 pub fn traceme() -> Result<()> { 77 unsafe { ptrace_other(Request::PT_TRACE_ME, Pid::from_raw(0), ptr::null_mut(), 0).map(drop) } 78 } 79 80 /// Attach to a running process, as with `ptrace(PT_ATTACH, ...)` 81 /// 82 /// Attaches to the process specified by `pid`, making it a tracee of the calling process. 83 pub fn attach(pid: Pid) -> Result<()> { 84 unsafe { ptrace_other(Request::PT_ATTACH, pid, ptr::null_mut(), 0).map(drop) } 85 } 86 87 /// Detaches the current running process, as with `ptrace(PT_DETACH, ...)` 88 /// 89 /// Detaches from the process specified by `pid` allowing it to run freely, optionally delivering a 90 /// signal specified by `sig`. 91 pub fn detach<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> { 92 let data = match sig.into() { 93 Some(s) => s as c_int, 94 None => 0, 95 }; 96 unsafe { 97 ptrace_other(Request::PT_DETACH, pid, ptr::null_mut(), data).map(drop) 98 } 99 } 100 101 /// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)` 102 /// 103 /// Continues the execution of the process with PID `pid`, optionally 104 /// delivering a signal specified by `sig`. 105 pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> { 106 let data = match sig.into() { 107 Some(s) => s as c_int, 108 None => 0, 109 }; 110 unsafe { 111 // Ignore the useless return value 112 ptrace_other(Request::PT_CONTINUE, pid, 1 as AddressType, data).map(drop) 113 } 114 } 115 116 /// Issues a kill request as with `ptrace(PT_KILL, ...)` 117 /// 118 /// This request is equivalent to `ptrace(PT_CONTINUE, ..., SIGKILL);` 119 pub fn kill(pid: Pid) -> Result<()> { 120 unsafe { 121 ptrace_other(Request::PT_KILL, pid, 0 as AddressType, 0).map(drop) 122 } 123 } 124 125 /// Move the stopped tracee process forward by a single step as with 126 /// `ptrace(PT_STEP, ...)` 127 /// 128 /// Advances the execution of the process with PID `pid` by a single step optionally delivering a 129 /// signal specified by `sig`. 130 /// 131 /// # Example 132 /// ```rust 133 /// use nix::sys::ptrace::step; 134 /// use nix::unistd::Pid; 135 /// use nix::sys::signal::Signal; 136 /// use nix::sys::wait::*; 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 #[cfg( 148 any( 149 any(target_os = "dragonfly", target_os = "freebsd", target_os = "macos"), 150 all(target_os = "openbsd", target_arch = "x86_64"), 151 all(target_os = "netbsd", 152 any(target_arch = "x86_64", target_arch = "powerpc") 153 ) 154 ) 155 )] 156 pub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> { 157 let data = match sig.into() { 158 Some(s) => s as c_int, 159 None => 0, 160 }; 161 unsafe { ptrace_other(Request::PT_STEP, pid, ptr::null_mut(), data).map(drop) } 162 } 163 164 /// Reads a word from a processes memory at the given address 165 pub fn read(pid: Pid, addr: AddressType) -> Result<c_int> { 166 unsafe { 167 // Traditionally there was a difference between reading data or 168 // instruction memory but not in modern systems. 169 ptrace_other(Request::PT_READ_D, pid, addr, 0) 170 } 171 } 172 173 /// Writes a word into the processes memory at the given address 174 pub fn write(pid: Pid, addr: AddressType, data: c_int) -> Result<()> { 175 unsafe { ptrace_other(Request::PT_WRITE_D, pid, addr, data).map(drop) } 176 } 177