diff --git a/Cargo.lock b/Cargo.lock index 83f2d51..0aab0d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -150,6 +150,16 @@ dependencies = [ "winapi 0.2.8", ] +[[package]] +name = "proc-macro-crate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fdbd1df62156fbc5945f4762632564d7d038153091c3fcf1067f6aef7cff92" +dependencies = [ + "thiserror", + "toml", +] + [[package]] name = "proc-macro2" version = "1.0.29" @@ -181,10 +191,16 @@ dependencies = [ ] [[package]] -name = "skidscan" -version = "0.1.1" +name = "serde" +version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89c52ddc5d6dde2bbb702e353d78c059cbbe07f15189ead0b7ac7da49b44bacc" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" + +[[package]] +name = "skidscan" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ddb509ef676cf26d1eac42416142360a15862275770dfada57e9b150244912" dependencies = [ "libc", "skidscan-macros", @@ -193,10 +209,11 @@ dependencies = [ [[package]] name = "skidscan-macros" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c98a622ae309bfbfe69e99bbf9e8ce23efc15817eda4bda1369bbb3eea668f83" +checksum = "881ef2150f0e4e2a855bb661a3ecd621ab1b6aa07b34aef941d1bd37b4267e8d" dependencies = [ + "proc-macro-crate", "syn", ] @@ -217,6 +234,35 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "thiserror" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + [[package]] name = "typenum" version = "1.14.0" diff --git a/gmod/src/hax.rs b/gmod/src/hax.rs index ace999b..0d2ebbd 100644 --- a/gmod/src/hax.rs +++ b/gmod/src/hax.rs @@ -1,4 +1,5 @@ #[macro_export] +/// Common pattern for detouring. macro_rules! __vtable_offset { ($name:ident = { win64: $win64:literal, @@ -22,7 +23,8 @@ macro_rules! __vtable_offset { } #[macro_export] -macro_rules! __vtable_func { +/// Common pattern for detouring. +macro_rules! __gmod_func { ($ty:ident = extern fn($($ident:ident: $arg:ty),*) $(-> $rtn:ty)?) => { #[cfg(target_pointer_width = "64")] pub type $ty = extern "fastcall" fn($($ident: $arg),*) $(-> $rtn)?; @@ -36,6 +38,7 @@ macro_rules! __vtable_func { } #[macro_export] +/// Common pattern for detouring. macro_rules! __hook_func { ($ty:ident = extern fn $fn:ident($($ident:ident: $arg:ty),*) $(-> $rtn:ty)? $code:block) => { #[cfg(target_pointer_width = "64")] @@ -56,4 +59,50 @@ macro_rules! __hook_func { #[cfg(all(target_os = "linux", target_pointer_width = "32"))] extern "C" fn $fn($($ident: $arg),*) $(-> $rtn)? $code }; +} + +#[macro_export] +/// Common pattern for detouring. +macro_rules! find_gmod_signature { + (($library:ident, $library_path:ident), @EXPORT = $export:literal) => { + $library.get($export).ok().map(|func: Symbol<'static, _>| *func) + }; + + (($library:ident, $library_path:ident), @SIG = $sig:literal) => { + $crate::sigscan::signature!($sig).scan_module($library_path).ok().map(|x| std::mem::transmute(x)) + }; + + (($library:ident, $library_path:ident) -> { + win64_x86_64: [$($win64_x86_64:tt)+], + win32_x86_64: [$($win32_x86_64:tt)+], + + linux64_x86_64: [$($linux64_x86_64:tt)+], + linux32_x86_64: [$($linux32_x86_64:tt)+], + + win32: [$($win32:tt)+], + linux32: [$($linux32:tt)+], + }) => {{ + let x86_64 = $crate::is_x86_64(); + if x86_64 { + #[cfg(all(target_os = "windows", target_pointer_width = "64"))] { + $crate::find_gmod_signature!(($library, $library_path), $($win64_x86_64)+) + } + #[cfg(all(target_os = "windows", target_pointer_width = "32"))] { + $crate::find_gmod_signature!(($library, $library_path), $($win32_x86_64)+) + } + #[cfg(all(target_os = "linux", target_pointer_width = "64"))] { + $crate::find_gmod_signature!(($library, $library_path), $($linux64_x86_64)+) + } + #[cfg(all(target_os = "linux", target_pointer_width = "32"))] { + $crate::find_gmod_signature!(($library, $library_path), $($linux32_x86_64)+) + } + } else { + #[cfg(target_os = "windows")] { + $crate::find_gmod_signature!(($library, $library_path), $($win32)+) + } + #[cfg(target_os = "linux")] { + $crate::find_gmod_signature!(($library, $library_path), $($linux32)+) + } + } + }} } \ No newline at end of file diff --git a/gmod/src/lib.rs b/gmod/src/lib.rs index 403f61b..876203b 100644 --- a/gmod/src/lib.rs +++ b/gmod/src/lib.rs @@ -14,4 +14,104 @@ pub mod lua; pub mod msgc; /// Advanced dark magic utilities -pub mod hax; \ No newline at end of file +pub mod hax; + +/// Returns whether this client is running the x86-64 branch +/// +/// Current implementation checks the LuaJIT version exported by lua_shared +pub fn is_x86_64() -> bool { + lua::LUA_SHARED.x86_64 +} + +/// Opens & returns a shared library loaded by Garry's Mod using the raw path to the module. +/// +/// # Example +/// ```no_run +/// // This would only work on Windows x86-64 branch in 64-bit mode +/// let (engine, engine_path): (gmod::libloading::Library, &'static str) = open_library_srv!("bin/win64/engine.dll").expect("Failed to open engine.dll!"); +/// println!("Opened engine.dll from: {}", engine_path); +/// ``` +#[macro_export] +macro_rules! open_library_raw { + ($($path:literal),+) => { + ::gmod::libloading::Library::new(concat!($($path),+)).map(|lib| (lib, concat!($($path),+))) + } +} + +/// Opens & returns a shared library loaded by Garry's Mod, in "server mode" (will prioritize _srv.so on Linux main branch) +/// +/// Respects 32-bit/64-bit main/x86-64 branches and finds the correct library. +/// +/// # Example +/// ```no_run +/// let (engine, engine_path): (gmod::libloading::Library, &'static str) = open_library_srv!("engine").expect("Failed to open engine.dll!"); +/// println!("Opened engine.dll from: {}", engine_path); +/// ``` +#[macro_export] +macro_rules! open_library_srv { + ($name:literal) => {{ + #[cfg(not(all(any(target_os = "windows", target_os = "linux"), any(target_pointer_width = "32", target_pointer_width = "64"))))] { + compile_error!("Unsupported platform"); + } + + #[cfg(all(target_os = "windows", target_pointer_width = "64"))] { + ::gmod::open_library_raw!("bin/win64/", $name, ".dll") + } + #[cfg(all(target_os = "windows", target_pointer_width = "32"))] { + ::gmod::open_library_raw!("bin/", $name, ".dll") + .or_else(|_| ::gmod::open_library_raw!("garrysmod/bin/", $name, ".dll")) + } + + #[cfg(all(target_os = "linux", target_pointer_width = "64"))] { + ::gmod::open_library_raw!("bin/linux64/", $name, ".so") + .or_else(|_| ::gmod::open_library_raw!("bin/linux64/lib", $name, ".so")) + } + #[cfg(all(target_os = "linux", target_pointer_width = "32"))] { + ::gmod::open_library_raw!("bin/linux32/", $name, ".so") + .or_else(|_| ::gmod::open_library_raw!("bin/linux32/lib", $name, ".so")) + .or_else(|_| ::gmod::open_library_raw!("bin/", $name, "_srv.so")) + .or_else(|_| ::gmod::open_library_raw!("bin/lib", $name, "_srv.so")) + .or_else(|_| ::gmod::open_library_raw!("bin/", $name, ".so")) + .or_else(|_| ::gmod::open_library_raw!("bin/lib", $name, ".so")) + } + }}; +} + +/// Opens & returns a shared library loaded by Garry's Mod. You are most likely looking for `open_library_srv!`, as this will prioritize non-_srv.so libraries on Linux main branch. +/// +/// Respects 32-bit/64-bit main/x86-64 branches and finds the correct library. +/// +/// # Example +/// ```no_run +/// let (engine, engine_path): (gmod::libloading::Library, &'static str) = open_library!("engine").expect("Failed to open engine.dll!"); +/// println!("Opened engine.dll from: {}", engine_path); +/// ``` +#[macro_export] +macro_rules! open_library { + ($name:literal) => {{ + #[cfg(not(all(any(target_os = "windows", target_os = "linux"), any(target_pointer_width = "32", target_pointer_width = "64"))))] { + compile_error!("Unsupported platform"); + } + + #[cfg(all(target_os = "windows", target_pointer_width = "64"))] { + ::gmod::open_library_raw!("bin/win64/", $name, ".dll") + } + #[cfg(all(target_os = "windows", target_pointer_width = "32"))] { + ::gmod::open_library_raw!("bin/", $name, ".dll") + .or_else(|_| ::gmod::open_library_raw!("garrysmod/bin/", $name, ".dll")) + } + + #[cfg(all(target_os = "linux", target_pointer_width = "64"))] { + ::gmod::open_library_raw!("bin/linux64/", $name, ".so") + .or_else(|_| ::gmod::open_library_raw!("bin/linux64/lib", $name, ".so")) + } + #[cfg(all(target_os = "linux", target_pointer_width = "32"))] { + ::gmod::open_library_raw!("bin/linux32/", $name, ".so") + .or_else(|_| ::gmod::open_library_raw!("bin/linux32/lib", $name, ".so")) + .or_else(|_| ::gmod::open_library_raw!("bin/", $name, ".so")) + .or_else(|_| ::gmod::open_library_raw!("bin/lib", $name, ".so")) + .or_else(|_| ::gmod::open_library_raw!("bin/", $name, "_srv.so")) + .or_else(|_| ::gmod::open_library_raw!("bin/lib", $name, "_srv.so")) + } + }}; +} \ No newline at end of file diff --git a/gmod/src/lua/import.rs b/gmod/src/lua/import.rs index bece999..1c3c833 100644 --- a/gmod/src/lua/import.rs +++ b/gmod/src/lua/import.rs @@ -69,10 +69,12 @@ impl LuaError { } lazy_static::lazy_static! { - pub(super) static ref LUA_SHARED: LuaShared = LuaShared::import(); + pub(crate) static ref LUA_SHARED: LuaShared = LuaShared::import(); } -pub(super) struct LuaShared { +pub(crate) struct LuaShared { + pub x86_64: bool, + pub lual_loadfile: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, path: LuaString) -> i32>, pub lual_loadstring: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, path: LuaString) -> i32>, pub lua_getfield: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, index: i32, k: LuaString)>, @@ -132,6 +134,8 @@ impl LuaShared { } Self { + x86_64: library.get::(b"luaJIT_version_2_1_0_beta3\0").is_ok(), + lual_loadfile: find_symbol!("luaL_loadfile"), lual_loadstring: find_symbol!("luaL_loadstring"), lua_getfield: find_symbol!("lua_getfield"),