use super::memory;
use crate::error::{Error, Result};
use crate::{alloc, arch, util};
use std::cell::UnsafeCell;
use std::fmt;
use std::sync::atomic::{AtomicBool, Ordering};
pub struct Detour {
#[allow(dead_code)]
relay: Option<alloc::ExecutableMemory>,
trampoline: alloc::ExecutableMemory,
patcher: UnsafeCell<arch::Patcher>,
enabled: AtomicBool,
}
impl Detour {
pub unsafe fn new(target: *const (), detour: *const ()) -> Result<Self> {
if target == detour {
Err(Error::SameAddress)?;
}
let mut pool = memory::POOL.lock().unwrap();
if !util::is_executable_address(target)? || !util::is_executable_address(detour)? {
Err(Error::NotExecutable)?;
}
let margin = arch::meta::prolog_margin(target);
let trampoline = arch::Trampoline::new(target, margin)?;
let relay = if let Some(emitter) = arch::meta::relay_builder(target, detour)? {
Some(memory::allocate_pic(&mut pool, &emitter, target)?)
} else {
None
};
let detour = relay
.as_ref()
.map(|code| code.as_ptr() as *const ())
.unwrap_or(detour);
Ok(Detour {
patcher: UnsafeCell::new(arch::Patcher::new(
target,
detour,
trampoline.prolog_size(),
)?),
trampoline: memory::allocate_pic(&mut pool, trampoline.emitter(), target)?,
enabled: AtomicBool::default(),
relay,
})
}
pub unsafe fn enable(&self) -> Result<()> {
self.toggle(true)
}
pub unsafe fn disable(&self) -> Result<()> {
self.toggle(false)
}
pub fn is_enabled(&self) -> bool {
self.enabled.load(Ordering::SeqCst)
}
pub fn trampoline(&self) -> &() {
unsafe {
(self.trampoline.as_ptr() as *const ())
.as_ref()
.expect("trampoline should not be null")
}
}
unsafe fn toggle(&self, enabled: bool) -> Result<()> {
let _guard = memory::POOL.lock().unwrap();
if self.enabled.load(Ordering::SeqCst) == enabled {
return Ok(());
}
let _handle = {
let area = (*self.patcher.get()).area();
region::protect_with_handle(
area.as_ptr(),
area.len(),
region::Protection::READ_WRITE_EXECUTE,
)
}?;
(*self.patcher.get()).toggle(enabled);
self.enabled.store(enabled, Ordering::SeqCst);
Ok(())
}
}
impl Drop for Detour {
fn drop(&mut self) {
debug_assert!(unsafe { self.disable().is_ok() });
}
}
impl fmt::Debug for Detour {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Detour {{ enabled: {}, trampoline: {:?} }}",
self.is_enabled(),
self.trampoline()
)
}
}
unsafe impl Send for Detour {}
unsafe impl Sync for Detour {}