static_key/
static_match.rs1use std::cell::Cell;
2
3use crate::patch::TextGuard;
4
5pub struct StaticKey<T: 'static> {
11 callsites: Cell<Option<&'static CallSite<T>>>,
13 value: Cell<T>,
14}
15
16unsafe impl<T: Send> Send for StaticKey<T> {}
17unsafe impl<T: Send> Sync for StaticKey<T> {}
18
19impl<T> core::fmt::Debug for StaticKey<T> {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 f.debug_struct("StaticKey").finish()
22 }
23}
24
25#[doc(hidden)]
27#[repr(C)]
28pub struct CallSite<T: 'static> {
29 key: &'static StaticKey<T>,
30 address: *mut u8,
32 matcher: fn(&T) -> usize,
33 next: Cell<Option<&'static CallSite<T>>>,
34 labels: [u32; 0],
35}
36
37unsafe impl<T: Send> Send for CallSite<T> {}
38unsafe impl<T: Send> Sync for CallSite<T> {}
39
40impl<T> CallSite<T> {
41 #[cfg(target_arch = "x86_64")]
42 fn update(&self, text: &mut TextGuard, value: &T) {
44 let callee = (self.matcher)(&value);
45
46 let mut patch;
47 if callee == usize::MAX {
48 patch = [0x0f, 0x1f, 0x44, 0x00, 0x00];
50 } else {
51 let offset = unsafe { *self.labels.as_ptr().add(callee) };
52 patch = [0xe9, 0x00, 0x00, 0x00, 0x00];
53 patch[1..].copy_from_slice(&offset.to_le_bytes());
54 }
55
56 unsafe {
57 crate::patch::replace_instruction(text, self.address, &patch);
58 }
59 }
60
61 #[cfg(target_arch = "riscv64")]
62 fn update(&self, text: &mut TextGuard, value: &T) {
64 let callee = (self.matcher)(&value);
65
66 let patch;
67 if callee == usize::MAX {
68 patch = 0x00000013;
70 } else {
71 let offset = unsafe { *self.labels.as_ptr().add(callee) };
72 patch = (offset >> 20 & 1) << 31
73 | (offset >> 1 & 0x3FF) << 21
74 | (offset >> 11 & 1) << 20
75 | (offset >> 12 & 0xFF) << 12
76 | 0x6F;
77 }
78
79 unsafe {
80 crate::patch::replace_instruction(text, self.address, &patch.to_ne_bytes());
81 }
82 }
83
84 pub unsafe extern "C" fn register(&'static self) {
86 let mut text = crate::patch::lock_text();
87
88 #[cfg(target_arch = "x86_64")]
89 let insn_len = 5;
90
91 #[cfg(target_arch = "riscv64")]
92 let insn_len = 4;
93
94 unsafe { crate::patch::register_code(&mut text, self.address, insn_len) };
96
97 self.next.set(self.key.callsites.get());
99 self.key.callsites.set(Some(self));
100 self.update(&mut text, unsafe { &*self.key.value.as_ptr() });
101
102 unsafe { text.skip_sync() };
105 }
106}
107
108impl<T> StaticKey<T> {
109 #[doc(hidden)]
110 #[inline]
111 pub const unsafe fn new(state: T) -> Self {
112 Self {
113 value: Cell::new(state),
114 callsites: Cell::new(None),
115 }
116 }
117
118 pub fn with<R>(&self, callback: impl FnOnce(&T) -> R) -> R {
125 let _text = crate::patch::lock_text();
126 callback(unsafe { &*self.value.as_ptr() })
127 }
128
129 pub fn get(&self) -> T
136 where
137 T: Copy,
138 {
139 self.with(|x| *x)
140 }
141
142 pub fn set(&self, value: T) {
149 let mut text = crate::patch::lock_text();
150 let mut callsite = self.callsites.get();
151 while let Some(site) = callsite {
152 site.update(&mut text, &value);
153 callsite = site.next.get();
154 }
155 self.value.set(value);
156 }
157}
158
159#[macro_export]
182macro_rules! static_key {
183 ($vis:vis $name: ident: $ty:ty = $init_value:expr) => {
184 $vis static $name: $crate::StaticKey<$ty> = {
185 let value: $ty = $init_value;
186 unsafe { $crate::StaticKey::new(value) }
187 };
188 };
189}
190
191#[doc(hidden)]
192#[macro_export]
193#[cfg(target_arch = "x86_64")]
194macro_rules! with_arch {
195 ($callback: ident, $($tt:tt)*) => {
196 $crate::$callback!("x86_64", $($tt)*)
197 };
198}
199
200#[doc(hidden)]
201#[macro_export]
202#[cfg(target_arch = "riscv64")]
203macro_rules! with_arch {
204 ($callback: path, $($tt:tt)*) => {
205 $crate::$callback!("riscv64", $($tt)*)
206 };
207}
208
209#[macro_export]
241macro_rules! static_match {
242 ($($tt:tt)*) => {
243 $crate::with_arch!(parse_static_match, $crate; $($tt)*);
244 };
245}