Skip to main content

imping_runtime/
lib.rs

1//! Runtime support for code generated by imping.
2//!
3//! Generated bindings depend on this crate alone. [`Module`], [`Global`] and
4//! the string types also work without generated code.
5//!
6//! ```no_run
7//! use imping_runtime::{Build, Global, Module};
8//!
9//! static TARGET: Module = Module::new(
10//!     "target.exe",
11//!     &[Build { timestamp: 0x64b8f2a1, image_size: 0xa2b000 }],
12//! );
13//! static GRAVITY: Global<f32> = Global::new(&TARGET, 0x3a1200);
14//!
15//! fn main() -> imping_runtime::Result<()> {
16//!     // SAFETY: this build of `target.exe` keeps an `f32` at RVA 0x3a1200.
17//!     unsafe { TARGET.init() }?;
18//!     GRAVITY.write(4.9);
19//!     Ok(())
20//! }
21//! ```
22//!
23//! # Main items
24//!
25//! - [`Module`]: a module's expected identity; [`Module::init`] checks the
26//!   loaded build and resolves addresses against its base.
27//! - [`Global`]: a typed global variable, read and written safely when it is
28//!   [`Plain`].
29//! - [`Hook`]: the guard a generated function's `hook` returns.
30//! - [`CStrPtr`], [`WStrPtr`], [`CStrBuf`], [`WStrBuf`] and [`WString`]:
31//!   `char` and `wchar_t` strings in the target's memory.
32//! - [`Inherits`] and [`Upcast`]: base classes and pointer upcasts.
33//! - [`Error`]: everything that can go wrong.
34//!
35//! # What stays `unsafe`
36//!
37//! [`Module::init`] is the one promise that the bindings match the running
38//! build. After it, plain globals, hooks, forwarding a hooked call and
39//! functions marked safe need no `unsafe`. Still `unsafe`, as only the caller
40//! knows the target's state: dereferencing a pointer from the target, reading
41//! a string through one, and calling a function not marked safe.
42//!
43//! Dereference once per block rather than once per access:
44//!
45//! ```no_run
46//! # #[repr(C)] struct Entity { health: f32, armor: f32 }
47//! # let player: *mut Entity = std::ptr::null_mut();
48//! // SAFETY: `player` is null or points to a live entity owned by the target.
49//! if let Some(player) = unsafe { player.as_mut() } {
50//!     player.health = 100.0;
51//!     player.armor = 50.0;
52//! }
53//! ```
54//!
55//! # Unloading
56//!
57//! Hooks jump into the mod's code, so drop or disable every [`Hook`] before
58//! the mod is unloaded. After [`Hook::leak`], the mod must never be unloaded.
59//!
60//! # Platforms
61//!
62//! Windows only. Elsewhere the crate compiles, but [`Module::init`] returns
63//! [`Error::UnsupportedPlatform`].
64
65mod backend;
66mod error;
67mod function;
68mod global;
69mod hook;
70mod inherits;
71mod module;
72mod plain;
73mod rtti;
74mod strings;
75mod sys;
76
77pub use crate::error::{Error, Mismatch, Result, VtableMiss};
78pub use crate::global::Global;
79pub use crate::hook::Hook;
80pub use crate::inherits::{Inherits, Upcast};
81pub use crate::module::{Build, Module};
82pub use crate::plain::Plain;
83pub use crate::strings::{CStrBuf, CStrPtr, WStrBuf, WStrPtr, WString};
84
85/// Plumbing for generated bindings; not a stable API, it changes with the
86/// generator.
87#[doc(hidden)]
88pub mod __private {
89    pub use crate::function::{Function, Pattern, Site, VtableSlot};
90    pub use crate::hook::{Slot, install};
91    use crate::{Build, Global, Module};
92
93    /// A module the bindings know several labelled builds of: `build_of[i]` is
94    /// the index into `labels` of `builds[i]`.
95    pub const fn module(
96        name: &'static str,
97        builds: &'static [Build],
98        build_of: &'static [usize],
99        labels: &'static [&'static str],
100    ) -> Module {
101        Module::labelled(name, builds, build_of, labels)
102    }
103
104    /// The loaded build's index into its module's labels, or `None` before
105    /// `init` or for a build none of them is.
106    pub fn build_index(module: &Module) -> Option<usize> {
107        module.build_index()
108    }
109
110    /// A global with an address per labelled build of `module`.
111    pub const fn global<T>(
112        module: &'static Module,
113        name: &'static str,
114        rvas: &'static [Option<u32>],
115    ) -> Global<T> {
116        Global::per_build(module, name, rvas)
117    }
118}