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}