imping_ir/function.rs
1//! Functions, their calling conventions, parameters and parameter storage.
2
3use crate::TypeId;
4use serde::{Deserialize, Serialize};
5
6/// A function in a module.
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
8#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
9pub struct Function {
10 /// The function's name, without namespace. Overloads may share a name.
11 pub name: String,
12 /// The namespace path, outermost first (e.g. `["Engine", "Entity"]`). Empty
13 /// for a free function.
14 #[serde(default, skip_serializing_if = "Vec::is_empty")]
15 pub namespace: Vec<String>,
16 /// The function's address, relative to the module's image base.
17 #[serde(with = "crate::hex::int")]
18 #[cfg_attr(feature = "schema", schemars(with = "String"))]
19 pub rva: u32,
20 /// The calling convention.
21 pub calling_convention: CallingConvention,
22 /// The return type (the `void` primitive for no return value).
23 pub return_type: TypeId,
24 /// The parameters, in order.
25 #[serde(default)]
26 pub parameters: Vec<Parameter>,
27 /// Whether the function is variadic.
28 #[serde(default)]
29 pub variadic: bool,
30 /// Whether the parameters use non-standard storage, given by their
31 /// [`Storage`], instead of the calling convention's defaults.
32 #[serde(default)]
33 pub custom_storage: bool,
34 /// Free-form tags attached to the function (e.g. in Ghidra).
35 #[serde(default, skip_serializing_if = "Vec::is_empty")]
36 pub tags: Vec<String>,
37 /// The bytes expected at the start of the function, used to verify the
38 /// build before hooking. Empty if none were recorded.
39 #[serde(
40 default,
41 skip_serializing_if = "Vec::is_empty",
42 with = "crate::hex::bytes"
43 )]
44 #[cfg_attr(feature = "schema", schemars(with = "String"))]
45 pub verification: Vec<u8>,
46 /// The same bytes as `verification`, with the ones that hold addresses as
47 /// `??`, e.g. `"48 8d 05 ?? ?? ?? ?? c3"`. Checks the function is still the
48 /// same code where its addresses may have changed. Empty if none were
49 /// recorded.
50 #[serde(default, skip_serializing_if = "String::is_empty")]
51 pub shape: String,
52 /// A byte pattern that finds the function when the bytes at `rva` don't
53 /// match `verification`, as after an update to the module.
54 #[serde(default, skip_serializing_if = "Option::is_none")]
55 pub pattern: Option<Pattern>,
56}
57
58/// A byte pattern that finds a [`Function`] by its code instead of its address.
59#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
60#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
61pub struct Pattern {
62 /// Hex bytes separated by spaces, with `??` or `?` for any byte, e.g.
63 /// `"48 89 5c 24 ?? 57"`. It must match exactly once in the module's code.
64 pub bytes: String,
65 /// Bytes from the start of the match to the function, or to the call when
66 /// `follow_call` is set. Negative steps back.
67 #[serde(default, skip_serializing_if = "is_zero")]
68 pub offset: i32,
69 /// Whether there's a `call` or `jmp` to the function at `offset`, rather
70 /// than the function itself.
71 #[serde(default, skip_serializing_if = "is_false")]
72 pub follow_call: bool,
73}
74
75fn is_zero(value: &i32) -> bool {
76 *value == 0
77}
78
79fn is_false(value: &bool) -> bool {
80 !*value
81}
82
83/// A function's calling convention.
84///
85/// On x64 there is a single convention, [`Win64`](Self::Win64); the others
86/// apply to x86.
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
88#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
89#[serde(rename_all = "lowercase")]
90pub enum CallingConvention {
91 /// x86 `__cdecl`.
92 Cdecl,
93 /// x86 `__stdcall`.
94 Stdcall,
95 /// x86 `__fastcall`.
96 Fastcall,
97 /// x86 `__thiscall`.
98 Thiscall,
99 /// The single x64 convention.
100 Win64,
101}
102
103/// A function parameter.
104#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
105#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
106pub struct Parameter {
107 /// The parameter's name, if known.
108 #[serde(default, skip_serializing_if = "Option::is_none")]
109 pub name: Option<String>,
110 /// The parameter's type.
111 #[serde(rename = "type")]
112 pub type_id: TypeId,
113 /// Where the parameter is passed.
114 pub storage: Storage,
115}
116
117/// Where a [`Parameter`] is passed.
118#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
119#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
120#[serde(tag = "kind", rename_all = "snake_case")]
121pub enum Storage {
122 /// Passed in a register (e.g. `"rcx"`, `"xmm0"`).
123 Register {
124 /// The register name, lowercase.
125 value: String,
126 },
127 /// Passed on the stack, at the given byte offset.
128 Stack {
129 /// The byte offset of the parameter.
130 offset: i64,
131 },
132}