1use super::memory;
2use crate::error::{Error, Result};
3use crate::{alloc, arch, util};
4use std::cell::UnsafeCell;
5use std::fmt;
6use std::sync::atomic::{AtomicBool, Ordering};
7
8pub struct Detour {
13 #[allow(dead_code)]
14 relay: Option<alloc::ExecutableMemory>,
15 trampoline: alloc::ExecutableMemory,
16 patcher: UnsafeCell<arch::Patcher>,
17 enabled: AtomicBool,
18}
19
20impl Detour {
21 pub unsafe fn new(target: *const (), detour: *const ()) -> Result<Self> {
22 if target == detour {
23 Err(Error::SameAddress)?;
24 }
25
26 let mut pool = memory::POOL.lock().unwrap();
28
29 if !util::is_executable_address(target)? || !util::is_executable_address(detour)? {
30 Err(Error::NotExecutable)?;
31 }
32
33 let margin = arch::meta::prolog_margin(target);
35 let trampoline = arch::Trampoline::new(target, margin)?;
36
37 let relay = if let Some(emitter) = arch::meta::relay_builder(target, detour)? {
39 Some(memory::allocate_pic(&mut pool, &emitter, target)?)
40 } else {
41 None
42 };
43
44 let detour = relay
46 .as_ref()
47 .map(|code| code.as_ptr() as *const ())
48 .unwrap_or(detour);
49
50 Ok(Detour {
51 patcher: UnsafeCell::new(arch::Patcher::new(
52 target,
53 detour,
54 trampoline.prolog_size(),
55 )?),
56 trampoline: memory::allocate_pic(&mut pool, trampoline.emitter(), target)?,
57 enabled: AtomicBool::default(),
58 relay,
59 })
60 }
61
62 pub unsafe fn enable(&self) -> Result<()> {
64 self.toggle(true)
65 }
66
67 pub unsafe fn disable(&self) -> Result<()> {
69 self.toggle(false)
70 }
71
72 pub fn is_enabled(&self) -> bool {
74 self.enabled.load(Ordering::SeqCst)
75 }
76
77 pub fn trampoline(&self) -> &() {
79 unsafe {
80 (self.trampoline.as_ptr() as *const ())
81 .as_ref()
82 .expect("trampoline should not be null")
83 }
84 }
85
86 unsafe fn toggle(&self, enabled: bool) -> Result<()> {
88 let _guard = memory::POOL.lock().unwrap();
89
90 if self.enabled.load(Ordering::SeqCst) == enabled {
91 return Ok(());
92 }
93
94 let _handle = {
96 let area = (*self.patcher.get()).area();
97 region::protect_with_handle(
98 area.as_ptr(),
99 area.len(),
100 region::Protection::READ_WRITE_EXECUTE,
101 )
102 }?;
103
104 (*self.patcher.get()).toggle(enabled);
106 self.enabled.store(enabled, Ordering::SeqCst);
107 Ok(())
108 }
109}
110
111impl Drop for Detour {
112 fn drop(&mut self) {
114 debug_assert!(unsafe { self.disable().is_ok() });
115 }
116}
117
118impl fmt::Debug for Detour {
119 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
121 write!(
122 f,
123 "Detour {{ enabled: {}, trampoline: {:?} }}",
124 self.is_enabled(),
125 self.trampoline()
126 )
127 }
128}
129
130unsafe impl Send for Detour {}
131unsafe impl Sync for Detour {}