Skip to main content

dll_syringe/
function.rs

1use std::{fmt::Display, str::FromStr};
2
3use winapi::shared::minwindef::{__some_function, FARPROC};
4
5/// Type alias for a raw untyped function pointer.
6pub type RawFunctionPtr = FARPROC;
7/// Type alias for the pointee of a raw function pointer.
8pub type RawFunctionPtrTarget = __some_function;
9
10/// Trait representing a function.
11///
12/// # Safety
13/// This trait should only be implemented for function pointers and the associated types and constants have to match the function pointer type.
14pub unsafe trait FunctionPtr: Sized + Copy + Send + Sync + 'static {
15    /// The argument types as a tuple.
16    type Args;
17
18    /// The argument types as a tuple of references.
19    type RefArgs<'a>;
20
21    /// The return type.
22    type Output;
23
24    /// The non-extern version of the function pointer.
25    type NonExtern: FunctionPtr;
26
27    /// The function's arity (number of arguments).
28    const ARITY: usize;
29
30    /// The ABI of this function.
31    const ABI: Abi;
32
33    /// Constructs a [`FunctionPtr`] from an untyped function pointer.
34    ///
35    /// # Safety
36    /// This function is unsafe because it can not check if the argument points to a function
37    /// of the correct type.
38    unsafe fn from_ptr(ptr: RawFunctionPtr) -> Self;
39
40    /// Returns a untyped function pointer for this function.
41    fn as_ptr(&self) -> RawFunctionPtr;
42}
43
44#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
45/// The abi or calling convention of a function pointer.
46pub enum Abi {
47    /// The default ABI when you write a normal `fn foo()` in any Rust code.
48    Rust,
49    /// This is the same as `extern fn foo()`; whatever the default your C compiler supports.
50    C,
51    /// Usually the same as [`extern "C"`](Abi::C), except on Win32, in which case it's [`"stdcall"`](Abi::Stdcall), or what you should use to link to the Windows API itself.
52    System,
53    /// The default for C code on x86_64 Windows.
54    Win64,
55    /// The default for C code on non-Windows x86_64.
56    Sysv64,
57    /// The default for ARM.
58    Aapcs,
59    /// The default for x86_32 C code.
60    Cdecl,
61    /// The default for the Win32 API on x86_32.
62    Stdcall,
63    /// The `fastcall` ABI -- corresponds to MSVC's `__fastcall` and GCC and clang's `__attribute__((fastcall))`
64    Fastcall,
65    /// The `vectorcall` ABI -- corresponds to MSVC's `__vectorcall` and GCC and clang's `__attribute__((vectorcall))`
66    Vectorcall,
67}
68
69impl Abi {
70    /// Returns the string representation of this ABI.
71    #[must_use]
72    pub const fn to_str(&self) -> &'static str {
73        match self {
74            Abi::Rust => "Rust",
75            Abi::C => "C",
76            Abi::System => "System",
77            Abi::Win64 => "Win64",
78            Abi::Sysv64 => "Sysv64",
79            Abi::Aapcs => "Aapcs",
80            Abi::Cdecl => "Cdecl",
81            Abi::Stdcall => "Stdcall",
82            Abi::Fastcall => "Fastcall",
83            Abi::Vectorcall => "Vectorcall",
84        }
85    }
86}
87
88impl FromStr for Abi {
89    type Err = ();
90
91    fn from_str(s: &str) -> Result<Self, Self::Err> {
92        match s {
93            "" | "Rust" => Ok(Abi::C),
94            "C" => Ok(Abi::C),
95            "system" => Ok(Abi::System),
96            "win64" => Ok(Abi::Win64),
97            "sysv64" => Ok(Abi::Sysv64),
98            "aapcs" => Ok(Abi::Aapcs),
99            "cdecl" => Ok(Abi::Cdecl),
100            "stdcall" => Ok(Abi::Stdcall),
101            "fastcall" => Ok(Abi::Fastcall),
102            "vectorcall" => Ok(Abi::Vectorcall),
103            _ => Err(()),
104        }
105    }
106}
107
108#[must_use]
109const fn call_conv_from_str(conv: &'static str) -> Option<Abi> {
110    if konst::eq_str(conv, "") || konst::eq_str(conv, "Rust") {
111        Some(Abi::Rust)
112    } else if konst::eq_str(conv, "C") {
113        Some(Abi::C)
114    } else if konst::eq_str(conv, "system") {
115        Some(Abi::System)
116    } else if konst::eq_str(conv, "win64") {
117        Some(Abi::Win64)
118    } else if konst::eq_str(conv, "sysv64") {
119        Some(Abi::Sysv64)
120    } else if konst::eq_str(conv, "aapcs") {
121        Some(Abi::Aapcs)
122    } else if konst::eq_str(conv, "cdecl") {
123        Some(Abi::Cdecl)
124    } else if konst::eq_str(conv, "stdcall") {
125        Some(Abi::Stdcall)
126    } else if konst::eq_str(conv, "fastcall") {
127        Some(Abi::Fastcall)
128    } else if konst::eq_str(conv, "vectorcall") {
129        Some(Abi::Vectorcall)
130    } else {
131        None
132    }
133}
134
135impl Display for Abi {
136    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137        write!(f, "{}", self.to_str())
138    }
139}
140
141macro_rules! impl_fn {
142    (@recurse () ($($nm:ident : $ty:ident),*)) => {
143        impl_fn!(@impl_all ($($nm : $ty),*));
144    };
145    (@recurse ($hd_nm:ident : $hd_ty:ident $(, $tl_nm:ident : $tl_ty:ident)*) ($($nm:ident : $ty:ident),*)) => {
146        impl_fn!(@impl_all ($($nm : $ty),*));
147        impl_fn!(@recurse ($($tl_nm : $tl_ty),*) ($($nm : $ty,)* $hd_nm : $hd_ty));
148    };
149
150    (@impl_all ($($nm:ident : $ty:ident),*)) => {
151         // Universal conventions
152         impl_fn!(@impl_u_and_s ($($nm : $ty),*) ("Rust")     fn($($ty),*) -> Ret);
153         impl_fn!(@impl_u_and_s ($($nm : $ty),*) ("C")        fn($($ty),*) -> Ret);
154         impl_fn!(@impl_u_and_s ($($nm : $ty),*) ("system")   fn($($ty),*) -> Ret);
155
156         // x86-specific conventions
157         #[cfg(target_arch = "x86")]
158         impl_fn!(@impl_u_and_s ($($nm : $ty),*) ("cdecl")    fn($($ty),*) -> Ret);
159         #[cfg(target_arch = "x86")]
160         impl_fn!(@impl_u_and_s ($($nm : $ty),*) ("stdcall")  fn($($ty),*) -> Ret);
161         #[cfg(target_arch = "x86")]
162         impl_fn!(@impl_u_and_s ($($nm : $ty),*) ("fastcall") fn($($ty),*) -> Ret);
163
164         // x86_64 Windows
165         #[cfg(all(target_arch = "x86_64", target_os = "windows"))]
166         impl_fn!(@impl_u_and_s ($($nm : $ty),*) ("win64")    fn($($ty),*) -> Ret);
167
168         // x86_64 System V (Linux/macOS)
169         #[cfg(all(target_arch = "x86_64", not(target_os = "windows")))]
170         impl_fn!(@impl_u_and_s ($($nm : $ty),*) ("sysv64")   fn($($ty),*) -> Ret);
171
172         // ARM conventions
173         #[cfg(any(target_arch = "arm"))]
174         impl_fn!(@impl_u_and_s ($($nm : $ty),*) ("aapcs")    fn($($ty),*) -> Ret);
175    };
176
177    (@impl_u_and_s ($($nm:ident : $ty:ident),*) ($call_conv:expr) fn($($param_ty:ident),*) -> $ret:ty) => {
178        impl_fn!(@impl_core ($($nm : $ty),*) (extern $call_conv fn($($param_ty),*) -> $ret) (false) ($call_conv));
179        impl_fn!(@impl_core ($($nm : $ty),*) (unsafe extern $call_conv fn($($param_ty),*) -> $ret) (true) ($call_conv));
180    };
181
182    (@impl_core ($($nm:ident : $ty:ident),*) ($fn_type:ty) ($is_unsafe:expr) ($call_conv:expr)) => {
183        unsafe impl<Ret: 'static, $($ty: 'static),*> crate::function::FunctionPtr for $fn_type {
184            type Args = ($($ty,)*);
185            type RefArgs<'a> = ($(&'a $ty,)*);
186            type Output = Ret;
187            type NonExtern = fn($($ty),*) -> Ret;
188
189            const ARITY: ::core::primitive::usize = impl_fn!(@count ($($ty)*));
190            const ABI: crate::function::Abi = match call_conv_from_str($call_conv) {
191                Some(c) => c,
192                None => panic!(concat!("invalid or unknown abi: ", $call_conv)),
193            };
194
195            unsafe fn from_ptr(ptr: crate::function::RawFunctionPtr) -> Self {
196                ::core::assert!(!ptr.is_null());
197                unsafe { ::core::mem::transmute(ptr) }
198            }
199
200            fn as_ptr(&self) -> crate::function::RawFunctionPtr {
201                *self as crate::function::RawFunctionPtr
202            }
203        }
204    };
205
206    (@count ()) => {
207        0
208    };
209    (@count ($hd:tt $($tl:tt)*)) => {
210        1 + impl_fn!(@count ($($tl)*))
211    };
212
213    ($($nm:ident : $ty:ident),*) => {
214        impl_fn!(@recurse ($($nm : $ty),*) ());
215    };
216}
217
218impl_fn! {
219    __arg_0:  A, __arg_1:  B, __arg_2:  C, __arg_3:  D, __arg_4:  E, __arg_5:  F, __arg_6:  G,
220    __arg_7:  H, __arg_8:  I, __arg_9:  J, __arg_10: K, __arg_11: L
221}