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}