use cfg_if::cfg_if;
use std::{mem, ptr};
use crate::{Error, Result};
use crate::errno::Errno;
use libc::{self, c_void, c_long, siginfo_t};
use crate::unistd::Pid;
use crate::sys::signal::Signal;
pub type AddressType = *mut ::libc::c_void;
#[cfg(all(
target_os = "linux",
any(all(target_arch = "x86_64",
any(target_env = "gnu", target_env = "musl")),
all(target_arch = "x86", target_env = "gnu"))
))]
use libc::user_regs_struct;
cfg_if! {
if #[cfg(any(all(target_os = "linux", target_arch = "s390x"),
all(target_os = "linux", target_env = "gnu")))] {
#[doc(hidden)]
pub type RequestType = ::libc::c_uint;
} else {
#[doc(hidden)]
pub type RequestType = ::libc::c_int;
}
}
libc_enum!{
#[cfg_attr(not(any(target_env = "musl", target_os = "android")), repr(u32))]
#[cfg_attr(any(target_env = "musl", target_os = "android"), repr(i32))]
pub enum Request {
PTRACE_TRACEME,
PTRACE_PEEKTEXT,
PTRACE_PEEKDATA,
PTRACE_PEEKUSER,
PTRACE_POKETEXT,
PTRACE_POKEDATA,
PTRACE_POKEUSER,
PTRACE_CONT,
PTRACE_KILL,
PTRACE_SINGLESTEP,
#[cfg(any(all(target_os = "android", target_pointer_width = "32"),
all(target_os = "linux", any(target_env = "musl",
target_arch = "mips",
target_arch = "mips64",
target_arch = "x86_64",
target_pointer_width = "32"))))]
PTRACE_GETREGS,
#[cfg(any(all(target_os = "android", target_pointer_width = "32"),
all(target_os = "linux", any(target_env = "musl",
target_arch = "mips",
target_arch = "mips64",
target_arch = "x86_64",
target_pointer_width = "32"))))]
PTRACE_SETREGS,
#[cfg(any(all(target_os = "android", target_pointer_width = "32"),
all(target_os = "linux", any(target_env = "musl",
target_arch = "mips",
target_arch = "mips64",
target_arch = "x86_64",
target_pointer_width = "32"))))]
PTRACE_GETFPREGS,
#[cfg(any(all(target_os = "android", target_pointer_width = "32"),
all(target_os = "linux", any(target_env = "musl",
target_arch = "mips",
target_arch = "mips64",
target_arch = "x86_64",
target_pointer_width = "32"))))]
PTRACE_SETFPREGS,
PTRACE_ATTACH,
PTRACE_DETACH,
#[cfg(all(target_os = "linux", any(target_env = "musl",
target_arch = "mips",
target_arch = "mips64",
target_arch = "x86",
target_arch = "x86_64")))]
PTRACE_GETFPXREGS,
#[cfg(all(target_os = "linux", any(target_env = "musl",
target_arch = "mips",
target_arch = "mips64",
target_arch = "x86",
target_arch = "x86_64")))]
PTRACE_SETFPXREGS,
PTRACE_SYSCALL,
PTRACE_SETOPTIONS,
PTRACE_GETEVENTMSG,
PTRACE_GETSIGINFO,
PTRACE_SETSIGINFO,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips64"))))]
PTRACE_GETREGSET,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips64"))))]
PTRACE_SETREGSET,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips64"))))]
PTRACE_SEIZE,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips64"))))]
PTRACE_INTERRUPT,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips64"))))]
PTRACE_LISTEN,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips64"))))]
PTRACE_PEEKSIGINFO,
}
}
libc_enum!{
#[repr(i32)]
pub enum Event {
PTRACE_EVENT_FORK,
PTRACE_EVENT_VFORK,
PTRACE_EVENT_CLONE,
PTRACE_EVENT_EXEC,
PTRACE_EVENT_VFORK_DONE,
PTRACE_EVENT_EXIT,
PTRACE_EVENT_SECCOMP,
}
}
libc_bitflags! {
pub struct Options: libc::c_int {
PTRACE_O_TRACESYSGOOD;
PTRACE_O_TRACEFORK;
PTRACE_O_TRACEVFORK;
PTRACE_O_TRACECLONE;
PTRACE_O_TRACEEXEC;
PTRACE_O_TRACEVFORKDONE;
PTRACE_O_TRACEEXIT;
PTRACE_O_TRACESECCOMP;
#[cfg(any(target_os = "android", target_os = "linux"))]
PTRACE_O_EXITKILL;
}
}
fn ptrace_peek(request: Request, pid: Pid, addr: AddressType, data: *mut c_void) -> Result<c_long> {
let ret = unsafe {
Errno::clear();
libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data)
};
match Errno::result(ret) {
Ok(..) | Err(Error::Sys(Errno::UnknownErrno)) => Ok(ret),
err @ Err(..) => err,
}
}
#[cfg(all(
target_os = "linux",
any(all(target_arch = "x86_64",
any(target_env = "gnu", target_env = "musl")),
all(target_arch = "x86", target_env = "gnu"))
))]
pub fn getregs(pid: Pid) -> Result<user_regs_struct> {
ptrace_get_data::<user_regs_struct>(Request::PTRACE_GETREGS, pid)
}
#[cfg(all(
target_os = "linux",
any(all(target_arch = "x86_64",
any(target_env = "gnu", target_env = "musl")),
all(target_arch = "x86", target_env = "gnu"))
))]
pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> {
let res = unsafe {
libc::ptrace(Request::PTRACE_SETREGS as RequestType,
libc::pid_t::from(pid),
ptr::null_mut::<c_void>(),
®s as *const _ as *const c_void)
};
Errno::result(res).map(drop)
}
fn ptrace_get_data<T>(request: Request, pid: Pid) -> Result<T> {
let mut data = mem::MaybeUninit::uninit();
let res = unsafe {
libc::ptrace(request as RequestType,
libc::pid_t::from(pid),
ptr::null_mut::<T>(),
data.as_mut_ptr() as *const _ as *const c_void)
};
Errno::result(res)?;
Ok(unsafe{ data.assume_init() })
}
unsafe fn ptrace_other(request: Request, pid: Pid, addr: AddressType, data: *mut c_void) -> Result<c_long> {
Errno::result(libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data)).map(|_| 0)
}
pub fn setoptions(pid: Pid, options: Options) -> Result<()> {
let res = unsafe {
libc::ptrace(Request::PTRACE_SETOPTIONS as RequestType,
libc::pid_t::from(pid),
ptr::null_mut::<c_void>(),
options.bits() as *mut c_void)
};
Errno::result(res).map(drop)
}
pub fn getevent(pid: Pid) -> Result<c_long> {
ptrace_get_data::<c_long>(Request::PTRACE_GETEVENTMSG, pid)
}
pub fn getsiginfo(pid: Pid) -> Result<siginfo_t> {
ptrace_get_data::<siginfo_t>(Request::PTRACE_GETSIGINFO, pid)
}
pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> {
let ret = unsafe{
Errno::clear();
libc::ptrace(Request::PTRACE_SETSIGINFO as RequestType,
libc::pid_t::from(pid),
ptr::null_mut::<c_void>(),
sig as *const _ as *const c_void)
};
match Errno::result(ret) {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
}
pub fn traceme() -> Result<()> {
unsafe {
ptrace_other(
Request::PTRACE_TRACEME,
Pid::from_raw(0),
ptr::null_mut(),
ptr::null_mut(),
).map(drop)
}
}
pub fn syscall<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as i32 as *mut c_void,
None => ptr::null_mut(),
};
unsafe {
ptrace_other(
Request::PTRACE_SYSCALL,
pid,
ptr::null_mut(),
data,
).map(drop)
}
}
pub fn attach(pid: Pid) -> Result<()> {
unsafe {
ptrace_other(
Request::PTRACE_ATTACH,
pid,
ptr::null_mut(),
ptr::null_mut(),
).map(drop)
}
}
#[cfg(all(target_os = "linux", not(any(target_arch = "mips", target_arch = "mips64"))))]
pub fn seize(pid: Pid, options: Options) -> Result<()> {
unsafe {
ptrace_other(
Request::PTRACE_SEIZE,
pid,
ptr::null_mut(),
options.bits() as *mut c_void,
).map(drop)
}
}
pub fn detach<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as i32 as *mut c_void,
None => ptr::null_mut(),
};
unsafe {
ptrace_other(
Request::PTRACE_DETACH,
pid,
ptr::null_mut(),
data
).map(drop)
}
}
pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as i32 as *mut c_void,
None => ptr::null_mut(),
};
unsafe {
ptrace_other(Request::PTRACE_CONT, pid, ptr::null_mut(), data).map(drop)
}
}
pub fn kill(pid: Pid) -> Result<()> {
unsafe {
ptrace_other(Request::PTRACE_KILL, pid, ptr::null_mut(), ptr::null_mut()).map(drop)
}
}
pub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as i32 as *mut c_void,
None => ptr::null_mut(),
};
unsafe {
ptrace_other(Request::PTRACE_SINGLESTEP, pid, ptr::null_mut(), data).map(drop)
}
}
pub fn read(pid: Pid, addr: AddressType) -> Result<c_long> {
ptrace_peek(Request::PTRACE_PEEKDATA, pid, addr, ptr::null_mut())
}
pub unsafe fn write(
pid: Pid,
addr: AddressType,
data: *mut c_void) -> Result<()>
{
ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data).map(drop)
}