isr_macros/symbols.rs
1use crate::Error;
2
3/// A symbol descriptor.
4#[derive(Debug, Clone)]
5pub struct SymbolDescriptor {
6 /// The virtual address offset of the symbol.
7 pub offset: u64,
8}
9
10impl TryFrom<SymbolDescriptor> for u64 {
11 type Error = Error;
12
13 fn try_from(value: SymbolDescriptor) -> Result<Self, Self::Error> {
14 Ok(value.offset)
15 }
16}
17
18//
19//
20//
21
22pub trait IntoSymbol<T> {
23 type Error;
24
25 fn into_symbol(self) -> Result<T, Error>;
26}
27
28impl IntoSymbol<u64> for Result<SymbolDescriptor, Error> {
29 type Error = Error;
30
31 fn into_symbol(self) -> Result<u64, Error> {
32 self?.try_into()
33 }
34}
35
36impl IntoSymbol<Option<u64>> for Result<SymbolDescriptor, Error> {
37 type Error = Error;
38
39 fn into_symbol(self) -> Result<Option<u64>, Error> {
40 match self {
41 Ok(symbol) => Ok(Some(symbol.try_into()?)),
42 Err(_) => Ok(None),
43 }
44 }
45}
46
47/// Defines a set of symbols.
48///
49/// This macro simplifies the process of defining symbols for later use
50/// with the `isr` crate, enabling type-safe access to symbol addresses
51/// and offsets. It generates a struct with fields corresponding to
52/// the defined symbols.
53///
54/// # Usage
55///
56/// ```rust
57/// # use isr::{
58/// # cache::{Codec as _, JsonCodec},
59/// # macros::symbols,
60/// # };
61/// #
62/// symbols! {
63/// #[derive(Debug)]
64/// pub struct Symbols {
65/// PsActiveProcessHead: u64,
66///
67/// // Optional symbols might be missing from profile.
68/// PsInitialSystemProcess: Option<u64>,
69/// NonExistentSymbol: Option<u64>,
70///
71/// // Provide aliases when symbols might have different names across builds.
72/// #[isr(alias = "KiSystemCall64Shadow")]
73/// KiSystemCall64: u64,
74///
75/// // Multiple aliases for a symbol.
76/// #[isr(alias = ["_NtOpenFile@24", "NtOpenFile"])]
77/// NtOpenFile: u64, // Address of the NtOpenFile function
78/// }
79/// }
80///
81/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
82/// // Use the profile of a Windows 10.0.18362.356 kernel.
83/// # let profile = JsonCodec::decode(include_bytes!(
84/// # concat!(
85/// # "../../../",
86/// # "tests/data/cache/",
87/// # "windows/ntkrnlmp.pdb/ce7ffb00c20b87500211456b3e905c471/profile.json"
88/// # )
89/// # ))?;
90/// let symbols = Symbols::new(&profile)?;
91/// assert_eq!(symbols.PsActiveProcessHead, 0x437BC0);
92/// assert_eq!(symbols.PsInitialSystemProcess, Some(0x5733A0));
93/// assert_eq!(symbols.NonExistentSymbol, None);
94/// # Ok(())
95/// # }
96/// ```
97///
98/// # Attributes
99///
100/// - `#[isr(alias = <alias>)]`: Specifies an alternative name for the symbol.
101/// This is useful when the symbol has different names across different OS
102/// builds or versions.
103///
104/// - `#[isr(override = <override>)]`: Overrides the symbol name with a custom
105/// name. This is useful when the symbol name should be different from the
106/// field name.
107///
108/// `<alias>` and `<override>` can be a single literal or an array
109/// of literals, e.g.:
110/// - `#[isr(alias = "alternative_name")]`
111/// - `#[isr(alias = ["name1", "name2", ...])]`
112///
113/// The generated struct provides a `new` method that takes a reference to
114/// a [`Profile`] and returns a `Result` containing the populated struct or
115/// an error if any symbols are not found.
116///
117/// [`Profile`]: isr_core::Profile
118#[macro_export]
119macro_rules! symbols {
120 (
121 $(#[$symbols_attrs:meta])*
122 $vis:vis struct $name:ident {
123 $(
124 $(#[isr($($isr_attr:tt)*)])?
125 $fname:ident: $ftype:ty
126 ),+ $(,)?
127 }
128 ) => {
129 $(#[$symbols_attrs])*
130 #[allow(non_camel_case_types, non_snake_case, missing_docs)]
131 $vis struct $name {
132 $($vis $fname: $ftype),+
133 }
134
135 impl $name {
136 /// Creates a new symbol instance.
137 $vis fn new(profile: &$crate::__private::Profile) -> Result<Self, $crate::Error> {
138 use $crate::__private::IntoSymbol as _;
139
140 Ok(Self {
141 $(
142 $fname: $crate::symbols!(@assign
143 profile,
144 $fname,
145 [$($($isr_attr)*)?]
146 ).into_symbol()?,
147 )*
148 })
149 }
150 }
151 };
152
153 (@assign
154 $profile:ident,
155 $fname:ident,
156 []
157 ) => {{
158 use $crate::__private::ProfileExt as _;
159
160 $profile
161 .find_symbol_descriptor(stringify!($fname))
162 }};
163
164 (@assign
165 $profile:ident,
166 $fname:ident,
167 [alias = $alias:literal]
168 ) => {{
169 use $crate::__private::ProfileExt as _;
170
171 $profile
172 .find_symbol_descriptor(stringify!($fname))
173 .or_else(|_| $profile
174 .find_symbol_descriptor($alias)
175 )
176 }};
177
178 (@assign
179 $profile:ident,
180 $fname:ident,
181 [alias = [$($alias:literal),+ $(,)?]]
182 ) => {{
183 use $crate::__private::ProfileExt as _;
184
185 $profile
186 .find_symbol_descriptor(stringify!($fname))
187 $(
188 .or_else(|_| $profile
189 .find_symbol_descriptor($alias)
190 )
191 )+
192 }};
193
194 (@assign
195 $profile:ident,
196 $fname:ident,
197 [override = $override:literal]
198 ) => {{
199 use $crate::__private::ProfileExt as _;
200
201 $profile
202 .find_symbol_descriptor($override)
203 }};
204
205 (@assign
206 $profile:ident,
207 $fname:ident,
208 [override = [$($override:literal),+ $(,)?]]
209 ) => {{
210 use $crate::__private::ProfileExt as _;
211
212 Err($crate::Error::symbol_not_found(stringify!($fname)))
213 $(
214 .or_else(|_| $profile
215 .find_symbol_descriptor($override)
216 )
217 )+
218 }};
219}