1use crate::regs::*;
2use core::{
3 cell::RefCell,
4 ffi::{CStr, c_int, c_long, c_void},
5 mem, ptr,
6};
7use libc::pid_t;
8use syscall_invoker::SyscallInvoker;
9
10pub use self::{error::Error, module_reader::MappedModuleMemoryReader};
11
12mod error;
13mod module_reader;
14mod syscall_invoker;
15
16#[cfg(target_env = "gnu")]
17type PtraceRequestType = core::ffi::c_uint;
18
19#[cfg(not(target_env = "gnu"))]
20type PtraceRequestType = core::ffi::c_int;
21
22#[derive(Debug)]
23pub struct Backend {
24 pid: pid_t,
25 syscall_invoker: RefCell<SyscallInvoker>,
26}
27
28impl Backend {
29 pub fn new(pid: libc::pid_t) -> Self {
30 Self {
31 pid,
32 syscall_invoker: Default::default(),
33 }
34 }
35 pub fn process_reader(&self) -> ProcessReader {
36 ProcessReader(process_reader::ProcessReader::new(self.pid))
37 }
38 pub fn stop_process(&self) -> Result<(), Error> {
39 self.standard_syscall(|| unsafe { libc::kill(self.pid, libc::SIGSTOP) })
40 .map_err(Error::SigStopFailed)?;
41 Ok(())
42 }
43
44 pub fn continue_process(&self) -> Result<(), Error> {
45 self.standard_syscall(|| unsafe { libc::kill(self.pid, libc::SIGCONT) })
46 .map_err(Error::SigContFailed)?;
47 Ok(())
48 }
49
50 pub fn suspend_thread(&self, tid: libc::pid_t) -> Result<(), Error> {
51 self.standard_syscall(|| unsafe {
52 ptrace(libc::PTRACE_ATTACH, tid, ptr::null_mut(), ptr::null_mut())
53 })
54 .map_err(Error::PtraceAttachFailed)?;
55
56 loop {
57 let mut status = 0;
58 if let Err(e) =
59 self.standard_syscall(|| unsafe { libc::waitpid(tid, &mut status, libc::__WALL) })
60 {
61 if e == libc::EINTR {
62 continue;
63 }
64 self.ptrace_detach(tid)?;
65 Err(Error::WaitPidFailed(e))?;
66 }
67
68 if !libc::WIFSTOPPED(status) {
69 Err(Error::UnexpectedStatus(status))?;
70 }
71
72 let signal = libc::WSTOPSIG(status);
73
74 if signal == libc::SIGSTOP {
78 break;
79 }
80
81 self.standard_syscall(|| unsafe {
84 ptrace(libc::PTRACE_CONT, tid, ptr::null_mut(), signal as *mut _)
85 })
86 .map_err(|e| Error::ReinjectFailed(signal, e))?;
87 }
88
89 Ok(())
90 }
91
92 pub fn resume_thread(&self, tid: libc::pid_t) -> Result<(), Error> {
93 self.ptrace_detach(tid)
94 }
95
96 pub fn map_module_into_memory(
97 &self,
98 path: &CStr,
99 offset: u64,
100 ) -> Result<MappedModuleMemoryReader, Error> {
101 MappedModuleMemoryReader::new(&mut self.syscall_invoker.borrow_mut(), path, offset)
102 }
103
104 pub fn stat_file(&self, path: &CStr) -> Result<libc::stat, Error> {
105 let mut output = unsafe { mem::zeroed::<libc::stat>() };
106 self.standard_syscall(|| unsafe { libc::stat(path.as_ptr(), &mut output) })
107 .map_err(Error::StatFailed)?;
108 Ok(output)
109 }
110
111 pub fn read_file(&self, path: &CStr) -> Result<FileReader, Error> {
112 self.open_file(path).map(FileReader)
113 }
114
115 pub fn read_dir(&self, path: &CStr) -> Result<DirReader, Error> {
116 self.special_syscall(|| unsafe {
117 let dirp = libc::opendir(path.as_ptr());
118 if dirp.is_null() {
119 return Err(());
120 }
121 Ok(dirp)
122 })
123 .map(|dirp| DirReader { dirp, eof: false })
124 .map_err(Error::OpenDirFailed)
125 }
126
127 pub fn read_link(&self, path: &CStr, buf: &mut [u8]) -> Result<usize, Error> {
128 let bytes_read = self
129 .standard_syscall(|| unsafe {
130 libc::readlink(path.as_ptr(), buf.as_mut_ptr().cast(), buf.len())
131 })
132 .map_err(Error::ReadLinkFailed)?;
133
134 let bytes_read = usize::try_from(bytes_read).unwrap();
135 if bytes_read == buf.len() {
136 Err(Error::BufferTooSmall)?;
137 }
138
139 Ok(bytes_read)
140 }
141
142 pub fn get_gen_regs(&self, tid: libc::pid_t) -> Result<GenRegs, Error> {
143 self.getregset(tid).or_else(|_| self.getregs(tid))
144 }
145
146 pub fn get_fp_regs(&self, tid: libc::pid_t) -> Result<FpRegs, Error> {
147 self.getfpregset(tid).or_else(|_| self.getfpregs(tid))
148 }
149
150 #[cfg(target_arch = "x86")]
151 pub fn get_fpx_regs(&self, tid: libc::pid_t) -> Result<FpxRegs, Error> {
152 const PTRACE_GETFPXREGS: PtraceRequestType = 18;
153 unsafe { self.ptrace_getregs::<FpxRegs>(PTRACE_GETFPXREGS, tid) }
154 }
155
156 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
157 pub fn ptrace_peekuser(
158 &self,
159 pid: libc::pid_t,
160 addr: usize,
161 ) -> Result<[u8; mem::size_of::<libc::c_long>()], Error> {
162 self.special_syscall(|| unsafe {
163 set_errno(0);
164 let rv = ptrace(
165 libc::PTRACE_PEEKUSER,
166 pid,
167 addr as *mut _,
168 core::ptr::null_mut(),
169 );
170 if rv == -1 && errno() != 0 {
171 return Err(());
172 }
173 Ok(rv.to_ne_bytes())
174 })
175 .map_err(Error::PtracePeekUserFailed)
176 }
177
178 pub fn process_reader_for_virtual_mem(&self) -> ProcessReader {
179 ProcessReader(process_reader::ProcessReader::for_virtual_mem(self.pid))
180 }
181
182 pub fn process_reader_for_file(&self) -> Result<ProcessReader, Error> {
183 process_reader::ProcessReader::for_file(self.pid)
184 .map(ProcessReader)
185 .map_err(Error::ProcessReader)
186 }
187
188 pub fn process_reader_for_ptrace(&self) -> ProcessReader {
189 ProcessReader(process_reader::ProcessReader::for_ptrace(self.pid))
190 }
191
192 fn open_file(&self, path: &CStr) -> Result<OwnedFd, Error> {
193 self.standard_syscall(|| unsafe {
194 libc::open(path.as_ptr(), libc::O_RDONLY | libc::O_CLOEXEC, 0)
195 })
196 .map(|fd| unsafe { OwnedFd::new(fd) })
197 .map_err(Error::OpenFileFailed)
198 }
199
200 fn getregset(&self, _pid: libc::pid_t) -> Result<GenRegs, Error> {
201 #[cfg(target_arch = "arm")]
202 {
203 Err(Error::NotSupported)
204 }
205 #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
206 {
207 const NT_PRSTATUS: usize = 1;
208 self.ptrace_getregset(NT_PRSTATUS, _pid)
209 }
210 }
211
212 fn getregs(&self, pid: libc::pid_t) -> Result<GenRegs, Error> {
213 const PTRACE_GETREGS: PtraceRequestType = 12;
214 unsafe { self.ptrace_getregs::<GenRegs>(PTRACE_GETREGS, pid) }
215 }
216
217 fn getfpregset(&self, pid: libc::pid_t) -> Result<FpRegs, Error> {
218 #[cfg(target_arch = "arm")]
219 {
220 const NT_ARM_VFP: usize = 0x400;
221 self.ptrace_getregset(NT_ARM_VFP, pid)
222 }
223 #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
224 {
225 const NT_PRFPREGSET: usize = 2;
226 self.ptrace_getregset(NT_PRFPREGSET, pid)
227 }
228 }
229
230 fn getfpregs(&self, _pid: libc::pid_t) -> Result<FpRegs, Error> {
231 #[cfg(target_arch = "arm")]
232 {
233 Err(Error::NotSupported)
234 }
235 #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
236 {
237 const PTRACE_GETFPREGS: PtraceRequestType = 14;
238 unsafe { self.ptrace_getregs::<FpRegs>(PTRACE_GETFPREGS, _pid) }
239 }
240 }
241
242 unsafe fn ptrace_getregs<T>(
244 &self,
245 request: PtraceRequestType,
246 pid: libc::pid_t,
247 ) -> Result<T, Error> {
248 let mut output = mem::MaybeUninit::<T>::uninit();
249 self.standard_syscall(|| unsafe {
250 ptrace(
251 request,
252 pid,
253 core::ptr::null_mut(),
254 output.as_mut_ptr().cast(),
255 )
256 })
257 .map_err(Error::GetRegistersFailed)?;
258 Ok(unsafe { output.assume_init() })
259 }
260
261 fn ptrace_getregset<T>(&self, regset_type: usize, pid: libc::pid_t) -> Result<T, Error> {
262 let mut output = mem::MaybeUninit::<T>::uninit();
263 let mut io = libc::iovec {
264 iov_base: output.as_mut_ptr().cast(),
265 iov_len: mem::size_of::<T>(),
266 };
267
268 self.standard_syscall(|| unsafe {
269 ptrace(
270 libc::PTRACE_GETREGSET,
271 pid,
272 regset_type as *mut _,
273 (&raw mut io).cast(),
274 )
275 })
276 .map_err(Error::GetRegistersFailed)?;
277
278 if io.iov_len != mem::size_of::<T>() {
281 Err(Error::GetRegistersFailed(libc::EINVAL))?;
282 }
283
284 Ok(unsafe { output.assume_init() })
285 }
286
287 fn ptrace_detach(&self, tid: libc::pid_t) -> Result<(), Error> {
288 self.standard_syscall(|| unsafe {
289 ptrace(libc::PTRACE_DETACH, tid, ptr::null_mut(), ptr::null_mut())
290 })
291 .map_err(Error::PtraceDetachFailed)?;
292 Ok(())
293 }
294
295 fn standard_syscall<T, F>(&self, f: F) -> Result<T, c_int>
296 where
297 F: FnOnce() -> T,
298 T: From<i8> + core::cmp::PartialEq,
299 {
300 self.syscall_invoker.borrow_mut().invoke_standard(f)
301 }
302
303 fn special_syscall<T, F>(&self, f: F) -> Result<T, c_int>
304 where
305 F: FnOnce() -> Result<T, ()>,
306 {
307 self.syscall_invoker.borrow_mut().invoke(f)
308 }
309
310 #[cfg(feature = "testing")]
311 pub fn fail_one_syscall_with(&self, errno: c_int) {
312 self.syscall_invoker
313 .borrow_mut()
314 .fail_one_syscall_with(errno);
315 }
316}
317
318#[derive(Debug)]
319pub struct FileReader(OwnedFd);
320
321impl FileReader {
322 pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
323 let rv = unsafe { libc::read(self.0.as_raw_fd(), buf.as_mut_ptr().cast(), buf.len()) };
324 if rv == -1 {
325 return Err(Error::ReadFileFailed(errno()));
326 }
327 Ok(rv.try_into().unwrap())
328 }
329 pub fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize, Error> {
330 let rv = unsafe {
331 libc::pread(
332 self.0.as_raw_fd(),
333 buf.as_mut_ptr().cast(),
334 buf.len(),
335 offset.try_into().unwrap(),
336 )
337 };
338 if rv == -1 {
339 return Err(Error::ReadFileFailed(errno()));
340 }
341 Ok(rv.try_into().unwrap())
342 }
343}
344
345#[derive(Debug)]
346pub struct DirReader {
347 dirp: *mut libc::DIR,
348 eof: bool,
349}
350
351impl DirReader {
352 pub fn read_name(&mut self) -> Result<Option<&[u8]>, Error> {
353 if self.eof {
354 return Ok(None);
355 }
356
357 loop {
358 set_errno(0);
359 let dirent = unsafe { libc::readdir(self.dirp) };
360 if dirent.is_null() {
361 if errno() == 0 {
362 self.eof = true;
363 return Ok(None);
364 }
365 return Err(Error::ReadDirFailed(errno()));
366 }
367
368 let name_bytes =
374 unsafe { CStr::from_ptr((&raw const (*dirent).d_name).cast()).to_bytes() };
375
376 if name_bytes == b"." || name_bytes == b".." {
377 continue;
378 }
379
380 return Ok(Some(name_bytes));
381 }
382 }
383}
384
385impl Drop for DirReader {
386 fn drop(&mut self) {
387 let rv = unsafe { libc::closedir(self.dirp) };
388 if rv == -1 {
389 log::debug!("failed to close directory: {}", errno());
390 }
391 }
392}
393
394#[derive(Debug)]
395pub struct ProcessReader(process_reader::ProcessReader);
396
397impl ProcessReader {
398 pub fn read_at(&self, address: usize, buf: &mut [u8]) -> Result<usize, Error> {
399 self.0.read_at(address, buf).map_err(Error::ProcessReader)
400 }
401}
402
403#[derive(Debug)]
404struct OwnedFd(c_int);
405
406impl OwnedFd {
407 pub unsafe fn new(fd: c_int) -> Self {
409 Self(fd)
410 }
411 pub fn as_raw_fd(&self) -> c_int {
412 self.0
413 }
414}
415
416impl Drop for OwnedFd {
417 fn drop(&mut self) {
418 let rv = unsafe { libc::close(self.0) };
419 if rv == -1 {
420 log::error!("failed to close file: {}", errno());
421 }
422 }
423}
424
425unsafe fn ptrace(
428 request: PtraceRequestType,
429 pid: libc::pid_t,
430 addr: *mut c_void,
431 data: *mut c_void,
432) -> c_long {
433 unsafe { libc::ptrace(request, pid, addr, data) }
434}
435
436fn errno() -> c_int {
437 unsafe { *errno_location() }
438}
439
440fn set_errno(value: c_int) {
441 unsafe {
442 *errno_location() = value;
443 }
444}
445
446#[cfg(target_os = "android")]
447fn errno_location() -> *mut c_int {
448 unsafe { libc::__errno() }
449}
450
451#[cfg(not(target_os = "android"))]
452fn errno_location() -> *mut c_int {
453 unsafe { libc::__errno_location() }
454}