dll_syringe_payload_utils/
lib.rs

1#![warn(unsafe_op_in_unsafe_fn)]
2//! Utilities for building payloads for [`dll-syringe`](https://docs.rs/dll-syringe/latest/dll_syringe/).
3
4/// A macro for defining functions to be called using [`RemoteProcedure`](https://docs.rs/dll-syringe/latest/dll_syringe/struct.RemoteProcedure.html) from [`dll-syringe`](https://docs.rs/dll-syringe/latest/dll_syringe/)
5/// in a more ergonomic manner.
6///
7/// # Example
8/// ```
9/// dll_syringe_payload_utils::remote_procedure! {
10///     fn add(a: i32, b: i32) -> i32 {
11///         a + b
12///     }
13/// }
14/// #
15/// # let args = (1, 2);
16/// # let mut result = 0;
17/// # unsafe { add(&args, &mut result) };
18/// # assert_eq!(result, 3);
19/// ```
20///
21/// will generate an equivalent function to
22///
23/// ```
24/// #[no_mangle]
25/// pub unsafe extern "system" fn add(args: *const (i32, i32), result: *mut i32) {
26///     unsafe { *result = (*args).0 + (*args).1 }
27/// }
28/// #
29/// # let args = (1, 2);
30/// # let mut result = 0;
31/// # unsafe { add(&args, &mut result) };
32/// # assert_eq!(result, 3);
33/// ```
34#[macro_export]
35macro_rules! remote_procedure {
36    ($(pub)? fn $fn:ident ( $($name:ident : $type:ty),* )
37        $body:block
38    ) => {
39        $crate::remote_procedure! {
40            pub fn $fn ( $($name : $type),* ) -> () $body
41        }
42    };
43    ($(pub)? fn $fn:ident ( $($name:ident : $type:ty),* ) -> $ret:ty
44        $body:block
45    ) => {
46        #[no_mangle]
47        pub unsafe extern "system" fn $fn ( __args: *const ($($type ,)*), __result: *mut $ret ) {
48            fn __inner ( $($name : $type),* ) -> $ret $body
49
50            let ($($name ,)*) = unsafe { ::core::ptr::read(__args) };
51            unsafe { ::core::ptr::write(__result, __inner($($name ,)*)) };
52        }
53    };
54}
55
56#[cfg(test)]
57mod tests {
58    use std::{mem::MaybeUninit, ptr};
59
60    #[test]
61    fn does_not_drop_uninit_result() {
62        pub struct NoImplicitDrop(u32);
63
64        impl Drop for NoImplicitDrop {
65            fn drop(&mut self) {
66                assert_ne!(self.0, 0, "dropped before being initialized");
67            }
68        }
69
70        impl NoImplicitDrop {
71            fn new() -> Self {
72                Self(1)
73            }
74        }
75
76        remote_procedure! {
77            fn add() -> NoImplicitDrop {
78                NoImplicitDrop::new()
79            }
80        }
81
82        let mut result = MaybeUninit::uninit();
83        unsafe { add(&(), result.as_mut_ptr()) };
84    }
85
86    #[test]
87    fn works_with_byte_buf() {
88        remote_procedure! {
89            fn pass_bytes(_arg: &[u8]) {
90            }
91        }
92    }
93
94    #[test]
95    fn takes_ownership_of_arg() {
96        pub struct NoCopy(u32);
97
98        impl Drop for NoCopy {
99            fn drop(&mut self) {}
100        }
101
102        static mut STORAGE: Option<NoCopy> = None;
103
104        remote_procedure! {
105            fn takes_ownership(arg: NoCopy) {
106                unsafe { STORAGE = Some(arg) }
107            }
108        }
109
110        let mut arg = NoCopy(1);
111        let arg_ptr = &mut arg as *mut NoCopy;
112        unsafe { takes_ownership(arg_ptr.cast(), &mut ()) };
113        unsafe { ptr::write(arg_ptr, NoCopy(2)) };
114
115        assert_eq!(unsafe { STORAGE.take() }.unwrap().0, 1);
116    }
117}