1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
#![feature(unboxed_closures)] #![feature(fn_traits)] #![feature(const_fn_fn_ptr_basics)] //! Changing function definitions at runtime. //! //! This crate is primarily used to load new function definitions from shared //! object files in an exceedingly easy way. //! //! ## Short Example //! The following shows how //! dead-simple this crate is to use: //! ``` //! // main.rs //! use hotpatch::patchable; //! //! #[patchable] //! fn foo() { } //! //! fn main() -> Result<(), Box<dyn std::error::Error>> { //! foo(); // does nothing //! foo.hotpatch_lib("libsomething.so")?; //! foo(); // does something totally different! //! Ok(()) //! } //! ``` //! What about defining a patch? Also easy: //! ``` //! // lib.rs //! use hotpatch::patch; //! //! #[patch] //! fn foo() { } //! ``` //! For more examples see the [git repo](https://github.com/Shizcow/hotpatch). //! //! ## Features //! For reference, this crate recognizes the following features: //! - `allow-main`: Allow setting `main` as [`#[patchable]`](patchable). Only useful if using `#[start]` or `#[main]`. //! - `redirect-main`: Same as `allow-main` but also generates a stub `#[main]` to call the [`Patchable`](Patchable). //! If you just want to hotpatch `main`, this is probably the right feature. Requires nightly and `#[feature(main)]`. //! - `large-signatures`: Tweaks the variadic generics engine. See [`hotpatch_fn`](Patchable::hotpatch_fn). //! //! ## Warnings //! Under normal operation, this crate provides type saftey, thread saftey, //! namepace saftey, and a whole bunch of other guarentees. However, use of this //! crate is still playing with fire. //! //! The one thing that cannot be checked against is call stack saftey. Because //! [`Patchable`](Patchable) uses [`RwLock`](https://doc.rust-lang.org/std/sync/struct.RwLock.html)s //! the current thread is blocked when trying to hotpatch a function. //! This ensures that an out-of-date function body cannot be ran. However if the //! function being hotpatched is the current function or anywhere within the //! call stack (eg patching a function that called the current function) a //! deadlock will occur. Be careful! //! //! The `try` methods within [`Patchable`](Patchable) provide additional checks //! for this, but may cause other problems in multithreaded environments. //! //! ## Bypassing Thread Saftey //! The previous section mentions being unable to hotpatch currently running functions. //! This is a deliberate saftey feature. However, it can be bypassed by using the //! `force` methods within [`Patchable`](Patchable). This allows multiple //! functions definitions to run at once. This is unsafe, but allows for some really //! interesting things such as hotpatching `main`. use std::sync::RwLock; use simple_error::bail; #[doc(hidden)] pub use once_cell::sync::Lazy; use variadic_generics::*; pub use hotpatch_macros::*; /// Created by [`#[patch]`](patch). Internal use only. /// /// Creates a `#[no_mangle] pub static` instance to be imported in another /// binary by [`Patchable`](Patchable) methods. pub struct HotpatchExport<T> { symbol: &'static str, // field order is important sig: &'static str, ptr: T, } impl<T> HotpatchExport<T> { #[doc(hidden)] pub const fn __new(ptr: T, symbol: &'static str, sig: &'static str) -> Self { Self{symbol, sig, ptr} } } /// Created by [`#[patchable]`](patchable). A functor capable of overwriting its /// own function. pub struct Patchable<Args, Ret> { lazy: Lazy<Option<RwLock<HotpatchImportInternal<Args, Ret>>>>, } #[doc(hidden)] pub struct HotpatchImportInternal<Args, Ret> { current_ptr: Box<dyn Fn(Args) -> Ret + Send + Sync + 'static>, default_ptr: fn(Args) -> Ret, sig: &'static str, lib: Option<libloading::Library>, mpath: &'static str, } impl<Args: 'static, Ret: 'static> HotpatchImportInternal<Args, Ret> { pub fn new(ptr: fn(Args) -> Ret, mpath: &'static str, sig: &'static str) -> Self { Self{current_ptr: Box::new(ptr), default_ptr: ptr, lib: None, sig, mpath: mpath.trim_start_matches(|c| c!=':')} } fn clean(&mut self) -> Result<(), Box<dyn std::error::Error>> { if self.lib.is_some() { self.lib.take().unwrap().close()?; } Ok(()) } pub fn restore_default(&mut self) -> Result<(), Box<dyn std::error::Error>> { self.current_ptr = Box::new(self.default_ptr); self.clean() } pub fn hotpatch_fn<F: Send + Sync + 'static>(&mut self, ptr: F) -> Result<(), Box<dyn std::error::Error>> where F: Fn(Args) -> Ret { self.current_ptr = Box::new(ptr); self.clean() } pub fn hotpatch_lib(&mut self, lib_name: &str) -> Result<(), Box<dyn std::error::Error>> { unsafe { let lib = libloading::Library::new(lib_name)?; let mut i: usize = 0; loop { let symbol_name = format!("{}{}", "__HOTPATCH_EXPORT_", i); let exports: libloading::Symbol<*mut HotpatchExport<fn(Args) -> Ret>> = lib.get(symbol_name.as_bytes()).map_err( |_| format!("Hotpatch for {} failed: symbol not found in library {}", self.mpath, lib_name))?; let export_obj = &**exports; if export_obj.symbol.trim_start_matches(|c| c!=':') == self.mpath { // found the correct symbol if self.sig != export_obj.sig { bail!("Hotpatch for {} failed: symbol found but of wrong type. Expected {} but found {}", self.mpath, self.sig, export_obj.sig); } self.current_ptr = Box::new(export_obj.ptr); self.clean()?; self.lib = Some(lib); break; } i += 1; } } Ok(()) } } // passthrough methods impl<Args: 'static, Ret: 'static> Patchable<Args, Ret> { #[doc(hidden)] pub const fn __new(ptr: fn() -> Option<RwLock<HotpatchImportInternal<Args, Ret>>>) -> Self { Self{lazy: Lazy::new(ptr)} } #[doc(hidden)] pub fn __new_internal(ptr: fn(Args) -> Ret, mpath: &'static str, sig: &'static str) -> Option<RwLock<HotpatchImportInternal<Args, Ret>>> { Some(RwLock::new(HotpatchImportInternal::new(ptr, mpath, sig))) } /// Hotpatch this functor with functionality defined in `lib_name`. /// Will search a shared object `cdylib` file for [`#[patch]`](patch) exports, /// finding the definition that matches module path and signature. /// /// ## Example /// ``` /// #[patchable] /// fn foo() {} /// /// fn main() -> Result<(), Box<dyn std::error::Error>> { /// foo(); // does something /// foo.hotpatch_lib("libtest.so")?; /// foo(); // does something else /// Ok(()) /// } /// ``` pub fn hotpatch_lib(&self, lib_name: &str) -> Result<(), Box<dyn std::error::Error + '_>> { self.lazy.as_ref().unwrap().write()?.hotpatch_lib(lib_name) } /// Like [`hotpatch_lib`](Patchable::hotpatch_lib) but uses /// [`RwLock::try_write`](https://doc.rust-lang.org/std/sync/struct.RwLock.html#method.try_write). pub fn try_hotpatch_lib(&self, lib_name: &str) -> Result<(), Box<dyn std::error::Error + '_>> { self.lazy.as_ref().unwrap().try_write()?.hotpatch_lib(lib_name) } /// Like [`hotpatch_lib`](Patchable::hotpatch_lib) but uses /// unsafe features to completly bypass the /// [`RwLock`](https://doc.rust-lang.org/std/sync/struct.RwLock.html). /// Can be used to patch the current function or parent functions. /// **Use with caution**. pub unsafe fn force_hotpatch_lib(&self, lib_name: &str) -> Result<(), Box<dyn std::error::Error + '_>> { let sref = self as *const Self as *mut Self; let mut rref = (*sref).lazy.take().unwrap(); let reslt = rref.get_mut().unwrap().hotpatch_lib(lib_name); *(*sref).lazy = Some(rref); reslt } /// Hotpatch this functor back to its original definition. /// /// ## Example /// ``` /// #[patchable] /// fn foo() {} /// /// fn main() -> Result<(), Box<dyn std::error::Error>> { /// foo(); // does A /// foo.hotpatch_lib("libtest.so")?; /// foo(); // does B /// foo.restore_default(); /// foo(); // does A again /// Ok(()) /// } /// ``` pub fn restore_default(&self) -> Result<(), Box<dyn std::error::Error + '_>> { self.lazy.as_ref().unwrap().write()?.restore_default() } /// Like [`restore_default`](Patchable::restore_default) but uses /// [`RwLock::try_write`](https://doc.rust-lang.org/std/sync/struct.RwLock.html#method.try_write). pub fn try_restore_default(&self) -> Result<(), Box<dyn std::error::Error + '_>> { self.lazy.as_ref().unwrap().try_write()?.restore_default() } /// Like [`restore_default`](Patchable::restore_default) but uses /// unsafe features to completly bypass the /// [`RwLock`](https://doc.rust-lang.org/std/sync/struct.RwLock.html). /// Can be used to patch the current function or parent functions. /// **Use with caution**. pub unsafe fn force_restore_default(&self) -> Result<(), Box<dyn std::error::Error + '_>> { let sref = self as *const Self as *mut Self; let mut rref = (*sref).lazy.take().unwrap(); let reslt = rref.get_mut().unwrap().restore_default(); *(*sref).lazy = Some(rref); reslt } } #[cfg(doc)] impl<VaGen: 'static, Ret: 'static> Patchable<VaGen, Ret> { /// Hotpatch this functor with functionality defined in `ptr`. /// `ptr` can be a function pointer or `move` closure with the /// same type signature as the functor's function. /// /// ## Example /// ``` /// #[patchable] /// fn foo(_: i32, _: i32, _: i32) {} /// /// fn bar(_: i32, _: i32, _: i32) {} /// /// fn main() -> Result<(), Box<dyn std::error::Error>> { /// foo.hotpatch_fn(bar)?; /// foo.hotpatch_fn(move |a, b, c| println!("{} {} {}", a, b, c))?; /// Ok(()) /// } /// ``` /// /// ## VaArgs Note /// Implementation is defined with the [`variadic_generics`](https://docs.rs/variadic_generics) /// crate. This means /// a macro is used to define a finite but large number of templated inputs. /// If using functions with large numbers of inputs and `hotpatch_fn` does not /// appear to be defined, compile `hotpatch` with the `large-signatures` feature /// to increase the number of supported arguements. /// /// This is the only place where `large_signatures` is needed. Large signature /// functions are supported out of the box for [`hotpatch_lib`](Patchable::hotpatch_lib) and /// [`restore_default`](Patchable::restore_default). pub fn hotpatch_fn<F: Send + Sync + 'static>(&self, ptr: F) -> Result<(), Box<dyn std::error::Error + '_>> where F: Fn(VaGen) -> Ret { // The actual implementation is below } /// Like [`hotpatch_fn`](Patchable::hotpatch_fn) but uses /// [`RwLock::try_write`](https://doc.rust-lang.org/std/sync/struct.RwLock.html#method.try_write). pub fn try_hotpatch_fn<F: Send + Sync + 'static>(&self, ptr: F) -> Result<(), Box<dyn std::error::Error + '_>> where F: Fn(VaGen) -> Ret { // The actual implementation is below } /// Like [`hotpatch_fn`](Patchable::hotpatch_fn) but uses /// unsafe features to completly bypass the /// [`RwLock`](https://doc.rust-lang.org/std/sync/struct.RwLock.html). /// Can be used to patch the current function or parent functions. /// **Use with caution**. pub unsafe fn force_hotpatch_fn<F: Send + Sync + 'static>(&self, ptr: F) -> Result<(), Box<dyn std::error::Error + '_>> where F: Fn(VaGen) -> Ret { // The actual implementation is below } } #[cfg(not(doc))] #[cfg(not(feature = "large-signatures"))] va_expand_with_nil!{ ($va_len:tt) ($($va_idents:ident),*) ($($va_indices:tt),*) impl<$($va_idents: 'static,)* Ret: 'static> Patchable<($($va_idents,)*), Ret> { pub fn hotpatch_fn<F: Send + Sync + 'static>(&self, ptr: F) -> Result<(), Box<dyn std::error::Error + '_>> where F: Fn($($va_idents),*) -> Ret { self.lazy.as_ref().unwrap().write()?.hotpatch_fn(move |args| ptr.call(args)) } pub fn try_hotpatch_fn<F: Send + Sync + 'static>(&self, ptr: F) -> Result<(), Box<dyn std::error::Error + '_>> where F: Fn($($va_idents),*) -> Ret { self.lazy.as_ref().unwrap().try_write()?.hotpatch_fn(move |args| ptr.call(args)) } pub unsafe fn force_hotpatch_fn<F: Send + Sync + 'static>(&self, ptr: F) -> Result<(), Box<dyn std::error::Error + '_>> where F: Fn($($va_idents),*) -> Ret { let sref = self as *const Self as *mut Self; let mut rref = (*sref).lazy.take().unwrap(); let reslt = rref.get_mut().unwrap().hotpatch_fn(move |args| ptr.call(args)); *(*sref).lazy = Some(rref); reslt } } } #[cfg(not(doc))] #[cfg(feature = "large-signatures")] va_expand_more_with_nil!{ ($va_len:tt) ($($va_idents:ident),*) ($($va_indices:tt),*) impl<$($va_idents: 'static,)* Ret: 'static> Patchable<($($va_idents,)*), Ret> { pub fn hotpatch_fn<F: Send + Sync + 'static>(&self, ptr: F) -> Result<(), Box<dyn std::error::Error + '_>> where F: Fn($($va_idents),*) -> Ret { self.lazy.as_ref().unwrap().write()?.hotpatch_fn(move |args| ptr.call(args)) } pub fn try_hotpatch_fn<F: Send + Sync + 'static>(&self, ptr: F) -> Result<(), Box<dyn std::error::Error + '_>> where F: Fn($($va_idents),*) -> Ret { self.lazy.as_ref().unwrap().try_write()?.hotpatch_fn(move |args| ptr.call(args)) } } } impl<Args, Ret> FnOnce<Args> for Patchable<Args, Ret> { type Output = Ret; extern "rust-call" fn call_once(self, args: Args) -> Ret { // When variadic generics are imlemented the following line can be used // to avoid the layer of indirection associated with a function having // a tuple list as an arguement. The current bottleneck is getting the // type bounds for variadic arguements on HotpatchImportInternal. Currently, a single // type arguement tuple is used to give a constant number of arguements. // When variadic template arguements are introduced, the stored function pointer // will be type-aware. //self.lazy.unwrap().read().unwrap().current_ptr.call(args) (self.lazy.as_ref().unwrap().read().unwrap().current_ptr)(args) } } impl<Args, Ret> FnMut<Args> for Patchable<Args, Ret> { extern "rust-call" fn call_mut(&mut self, args: Args) -> Ret { (self.lazy.as_ref().unwrap().read().unwrap().current_ptr)(args) } } impl<Args, Ret> Fn<Args> for Patchable<Args, Ret> { extern "rust-call" fn call(&self, args: Args) -> Ret { (self.lazy.as_ref().unwrap().read().unwrap().current_ptr)(args) } }