Skip to main content

isr_core/
lib.rs

1//! ISR core library.
2
3pub mod schema;
4pub mod visit;
5
6use crate::schema::{
7    ArchivedArchitecture, ArchivedArray, ArchivedBase, ArchivedBitfield, ArchivedEnum,
8    ArchivedEnumRef, ArchivedField, ArchivedPointer, ArchivedProfile, ArchivedStruct,
9    ArchivedStructKind, ArchivedStructRef, ArchivedType, ArchivedVariant,
10};
11
12/// A typed view over an archived profile.
13pub struct Profile<'a> {
14    inner: &'a ArchivedProfile,
15    rva_to_symbol: Vec<Symbol<'a>>,
16}
17
18/// A named symbol resolved to its relative virtual address.
19#[derive(Debug, Clone, Copy)]
20pub struct Symbol<'a> {
21    /// Symbol name.
22    pub name: &'a str,
23
24    /// Relative virtual address from the module base.
25    pub rva: u64,
26}
27
28impl<'a> Profile<'a> {
29    /// Constructs a typed view over an archived profile.
30    pub fn from_archived(archived: &'a ArchivedProfile) -> Self {
31        let mut rva_to_symbol = archived
32            .symbols
33            .iter()
34            .map(|(name, &rva)| Symbol {
35                name: name.as_ref(),
36                rva: rva.into(),
37            })
38            .collect::<Vec<_>>();
39        rva_to_symbol.sort_unstable_by_key(|entry| entry.rva);
40
41        Self {
42            inner: archived,
43            rva_to_symbol,
44        }
45    }
46
47    /// Returns the architecture of the profile.
48    pub fn architecture(&self) -> Architecture {
49        Architecture::from_archived(&self.inner.architecture)
50    }
51
52    /// Returns the enums.
53    pub fn enums(&self) -> impl ExactSizeIterator<Item = Enum<'a>> {
54        self.inner.enums.iter().map(|(name, inner)| Enum {
55            name: name.as_ref(),
56            inner,
57        })
58    }
59
60    /// Returns the structs.
61    pub fn structs(&self) -> impl ExactSizeIterator<Item = Struct<'a>> {
62        self.inner.structs.iter().map(|(name, inner)| Struct {
63            name: name.as_ref(),
64            inner,
65        })
66    }
67
68    /// Returns the symbols.
69    pub fn symbols(&self) -> impl ExactSizeIterator<Item = Symbol<'a>> {
70        self.inner.symbols.iter().map(|(name, &rva)| Symbol {
71            name: name.as_ref(),
72            rva: rva.into(),
73        })
74    }
75    /// Returns the size of a given type in bytes.
76    pub fn type_size(&self, ty: Type<'a>) -> Option<u64> {
77        match ty {
78            Type::Base(v) => Some(v.size()),
79            Type::Enum(v) => self.enum_size(v.name()),
80            Type::Struct(v) => self.struct_size(v.name()),
81            Type::Array(v) => self
82                .type_size(v.subtype())
83                .map(|subtype_size| subtype_size * v.dims().product::<u64>()),
84            Type::Pointer(v) => Some(v.size()),
85            Type::Bitfield(v) => self.type_size(v.subtype()),
86            Type::Function => Some(self.pointer_size()),
87        }
88    }
89
90    /// Returns the size of an enum type in bytes.
91    pub fn enum_size(&self, name: &str) -> Option<u64> {
92        let ty = Type::from_archived(&self.inner.enums.get(name)?.subtype);
93        self.type_size(ty)
94    }
95
96    /// Returns the size of a struct type in bytes.
97    pub fn struct_size(&self, name: &str) -> Option<u64> {
98        self.inner.structs.get(name).map(|udt| udt.size.into())
99    }
100
101    /// Returns the size of a pointer in bytes.
102    pub fn pointer_size(&self) -> u64 {
103        match self.architecture() {
104            Architecture::X86 | Architecture::Arm32 => 4,
105            Architecture::Amd64 | Architecture::Arm64 => 8,
106            Architecture::Unknown => 0,
107        }
108    }
109
110    /// Finds an enum by name.
111    pub fn find_enum(&self, type_name: &str) -> Option<Enum<'a>> {
112        self.inner
113            .enums
114            .get_key_value(type_name)
115            .map(|(name, inner)| Enum {
116                name: name.as_ref(),
117                inner,
118            })
119    }
120
121    /// Finds a struct by name.
122    pub fn find_struct(&self, type_name: &str) -> Option<Struct<'a>> {
123        self.inner
124            .structs
125            .get_key_value(type_name)
126            .map(|(name, inner)| Struct {
127                name: name.as_ref(),
128                inner,
129            })
130    }
131
132    /// Finds a symbol by name.
133    pub fn find_symbol(&self, symbol_name: &str) -> Option<u64> {
134        self.inner.symbols.get(symbol_name).map(Into::into)
135    }
136
137    /// Returns the symbol whose RVA is closest to `rva` without exceeding it.
138    ///
139    /// Useful for resolving an address to the containing function.
140    pub fn lookup_symbol(&self, rva: u64) -> Option<Symbol<'a>> {
141        let idx = match self.rva_to_symbol.binary_search_by_key(&rva, |e| e.rva) {
142            Ok(i) => i,
143            Err(0) => return None,
144            Err(i) => i - 1,
145        };
146
147        Some(self.rva_to_symbol[idx])
148    }
149}
150
151/// Target CPU architecture.
152#[allow(missing_docs)]
153#[derive(Debug, Clone, Copy, PartialEq, Eq)]
154pub enum Architecture {
155    Unknown,
156    X86,
157    Amd64,
158    Arm32,
159    Arm64,
160}
161
162impl Architecture {
163    fn from_archived(value: &ArchivedArchitecture) -> Self {
164        match value {
165            ArchivedArchitecture::Unknown => Self::Unknown,
166            ArchivedArchitecture::X86 => Self::X86,
167            ArchivedArchitecture::Amd64 => Self::Amd64,
168            ArchivedArchitecture::Arm32 => Self::Arm32,
169            ArchivedArchitecture::Arm64 => Self::Arm64,
170        }
171    }
172}
173
174/// A named enum type in a profile.
175#[derive(Debug, Clone, Copy)]
176pub struct Enum<'a> {
177    name: &'a str,
178    inner: &'a ArchivedEnum,
179}
180
181impl<'a> Enum<'a> {
182    /// Returns the name of the enum.
183    pub fn name(&self) -> &'a str {
184        self.name
185    }
186
187    /// Returns the underlying integer type of the enum.
188    pub fn subtype(&self) -> Type<'a> {
189        Type::from_archived(&self.inner.subtype)
190    }
191
192    /// Returns the variants of the enum with their discriminant values.
193    pub fn fields(&self) -> impl ExactSizeIterator<Item = (&'a str, Variant)> {
194        self.inner
195            .fields
196            .iter()
197            .map(|(name, variant)| (name.as_ref(), Variant::from_archived(variant)))
198    }
199}
200
201/// Discriminant value of an enum variant.
202#[allow(missing_docs)]
203#[derive(Debug, Clone, Copy, PartialEq, Eq)]
204pub enum Variant {
205    U8(u8),
206    U16(u16),
207    U32(u32),
208    U64(u64),
209    U128(u128),
210    I8(i8),
211    I16(i16),
212    I32(i32),
213    I64(i64),
214    I128(i128),
215}
216
217impl Variant {
218    fn from_archived(value: &ArchivedVariant) -> Self {
219        match value {
220            ArchivedVariant::U8(v) => Self::U8(*v),
221            ArchivedVariant::U16(v) => Self::U16(v.into()),
222            ArchivedVariant::U32(v) => Self::U32(v.into()),
223            ArchivedVariant::U64(v) => Self::U64(v.into()),
224            ArchivedVariant::U128(v) => Self::U128(v.into()),
225            ArchivedVariant::I8(v) => Self::I8(*v),
226            ArchivedVariant::I16(v) => Self::I16(v.into()),
227            ArchivedVariant::I32(v) => Self::I32(v.into()),
228            ArchivedVariant::I64(v) => Self::I64(v.into()),
229            ArchivedVariant::I128(v) => Self::I128(v.into()),
230        }
231    }
232}
233
234/// Struct type.
235#[derive(Debug, Clone, Copy)]
236pub struct Struct<'a> {
237    name: &'a str,
238    inner: &'a ArchivedStruct,
239}
240
241impl<'a> Struct<'a> {
242    /// Returns the name of the struct.
243    pub fn name(&self) -> &'a str {
244        self.name
245    }
246
247    /// Returns the kind of the struct.
248    pub fn kind(&self) -> StructKind {
249        StructKind::from_archived(&self.inner.kind)
250    }
251
252    /// Returns the size of the struct in bytes.
253    pub fn size(&self) -> u64 {
254        self.inner.size.into()
255    }
256
257    /// Finds a field by name.
258    pub fn field(&self, name: &str) -> Option<Field<'a>> {
259        self.inner
260            .fields
261            .get_key_value(name)
262            .map(|(name, field)| Field {
263                name: name.as_ref(),
264                inner: field,
265            })
266    }
267
268    /// Returns the fields of the struct.
269    pub fn fields(&self) -> impl ExactSizeIterator<Item = Field<'a>> {
270        self.inner.fields.iter().map(|(name, inner)| Field {
271            name: name.as_ref(),
272            inner,
273        })
274    }
275}
276
277/// Struct kind.
278#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
279pub enum StructKind {
280    /// A `struct`.
281    Struct,
282
283    /// A `class`.
284    Class,
285
286    /// A `union`.
287    Union,
288
289    /// An `interface`.
290    Interface,
291}
292
293impl StructKind {
294    fn from_archived(value: &ArchivedStructKind) -> Self {
295        match value {
296            ArchivedStructKind::Struct => Self::Struct,
297            ArchivedStructKind::Class => Self::Class,
298            ArchivedStructKind::Union => Self::Union,
299            ArchivedStructKind::Interface => Self::Interface,
300        }
301    }
302}
303
304/// Struct field.
305#[derive(Debug, Clone, Copy)]
306pub struct Field<'a> {
307    name: &'a str,
308    inner: &'a ArchivedField,
309}
310
311impl<'a> Field<'a> {
312    /// Returns the name of the field.
313    pub fn name(&self) -> &'a str {
314        self.name
315    }
316
317    /// Returns the offset of the field in bytes.
318    pub fn offset(&self) -> u64 {
319        self.inner.offset.into()
320    }
321
322    /// Returns the type of the field.
323    pub fn ty(&self) -> Type<'a> {
324        Type::from_archived(&self.inner.ty)
325    }
326}
327
328/// Type.
329#[derive(Debug, Clone, Copy)]
330pub enum Type<'a> {
331    /// Base type.
332    Base(Base),
333
334    /// Enum type.
335    Enum(EnumRef<'a>),
336
337    /// Struct type.
338    Struct(StructRef<'a>),
339
340    /// Array type.
341    Array(Array<'a>),
342
343    /// Pointer type.
344    Pointer(Pointer<'a>),
345
346    /// Bitfield type.
347    Bitfield(Bitfield<'a>),
348
349    /// Function type.
350    Function,
351}
352
353impl<'a> Type<'a> {
354    fn from_archived(value: &'a ArchivedType) -> Self {
355        match value {
356            ArchivedType::Base(inner) => Self::Base(Base::from_archived(inner)),
357            ArchivedType::Enum(inner) => Self::Enum(EnumRef { inner }),
358            ArchivedType::Struct(inner) => Self::Struct(StructRef { inner }),
359            ArchivedType::Array(inner) => Self::Array(Array { inner }),
360            ArchivedType::Pointer(inner) => Self::Pointer(Pointer { inner }),
361            ArchivedType::Bitfield(inner) => Self::Bitfield(Bitfield { inner }),
362            ArchivedType::Function => Self::Function,
363        }
364    }
365}
366
367/// Base type.
368#[allow(missing_docs)]
369#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
370pub enum Base {
371    /// Void type.
372    Void,
373
374    /// Boolean type.
375    Bool,
376
377    /// Character types.
378    Char8,
379    Char16,
380    Char32,
381
382    /// Signed integer types.
383    I8,
384    I16,
385    I32,
386    I64,
387    I128,
388
389    /// Unsigned integer types.
390    U8,
391    U16,
392    U32,
393    U64,
394    U128,
395
396    /// Floating-point types.
397    F8,
398    F16,
399    F32,
400    F64,
401    F128,
402}
403
404impl Base {
405    fn from_archived(value: &ArchivedBase) -> Self {
406        match value {
407            ArchivedBase::Void => Self::Void,
408
409            ArchivedBase::Bool => Self::Bool,
410
411            ArchivedBase::Char8 => Self::Char8,
412            ArchivedBase::Char16 => Self::Char16,
413            ArchivedBase::Char32 => Self::Char32,
414
415            ArchivedBase::I8 => Self::I8,
416            ArchivedBase::I16 => Self::I16,
417            ArchivedBase::I32 => Self::I32,
418            ArchivedBase::I64 => Self::I64,
419            ArchivedBase::I128 => Self::I128,
420
421            ArchivedBase::U8 => Self::U8,
422            ArchivedBase::U16 => Self::U16,
423            ArchivedBase::U32 => Self::U32,
424            ArchivedBase::U64 => Self::U64,
425            ArchivedBase::U128 => Self::U128,
426
427            ArchivedBase::F8 => Self::F8,
428            ArchivedBase::F16 => Self::F16,
429            ArchivedBase::F32 => Self::F32,
430            ArchivedBase::F64 => Self::F64,
431            ArchivedBase::F128 => Self::F128,
432        }
433    }
434
435    /// Returns the size of the base type in bytes.
436    pub fn size(&self) -> u64 {
437        match self {
438            Self::Void => 0,
439            Self::Char8 | Self::I8 | Self::U8 | Self::F8 | Self::Bool => 1,
440            Self::Char16 | Self::I16 | Self::U16 | Self::F16 => 2,
441            Self::Char32 | Self::I32 | Self::U32 | Self::F32 => 4,
442            Self::I64 | Self::U64 | Self::F64 => 8,
443            Self::I128 | Self::U128 | Self::F128 => 16,
444        }
445    }
446}
447
448/// An enum reference.
449#[derive(Debug, Clone, Copy)]
450pub struct EnumRef<'a> {
451    inner: &'a ArchivedEnumRef,
452}
453
454impl<'a> EnumRef<'a> {
455    /// Returns the name of the enum.
456    pub fn name(&self) -> &'a str {
457        &self.inner.name
458    }
459}
460
461/// A struct reference.
462#[derive(Debug, Clone, Copy)]
463pub struct StructRef<'a> {
464    inner: &'a ArchivedStructRef,
465}
466
467impl<'a> StructRef<'a> {
468    /// Returns the name of the struct.
469    pub fn name(&self) -> &'a str {
470        &self.inner.name
471    }
472}
473
474/// Array type.
475#[derive(Debug, Clone, Copy)]
476pub struct Array<'a> {
477    inner: &'a ArchivedArray,
478}
479
480impl<'a> Array<'a> {
481    /// Returns the subtype of the array.
482    pub fn subtype(&self) -> Type<'a> {
483        Type::from_archived(self.inner.subtype.as_ref())
484    }
485
486    /// Returns the dimensions of the array.
487    pub fn dims(&self) -> impl Iterator<Item = u64> + use<'a> {
488        self.inner.dims.iter().map(|&dim| dim.to_native())
489    }
490}
491
492/// Bitfield type.
493#[derive(Debug, Clone, Copy)]
494pub struct Bitfield<'a> {
495    inner: &'a ArchivedBitfield,
496}
497
498impl<'a> Bitfield<'a> {
499    /// Returns the subtype of the bitfield.
500    pub fn subtype(&self) -> Type<'a> {
501        Type::from_archived(self.inner.subtype.as_ref())
502    }
503
504    /// Returns the length of the bitfield in bits.
505    pub fn bit_length(&self) -> u64 {
506        self.inner.bit_length.into()
507    }
508
509    /// Returns the starting bit position within the subtype.
510    pub fn bit_position(&self) -> u64 {
511        self.inner.bit_position.into()
512    }
513}
514
515/// Pointer type.
516#[derive(Debug, Clone, Copy)]
517pub struct Pointer<'a> {
518    inner: &'a ArchivedPointer,
519}
520
521impl<'a> Pointer<'a> {
522    /// Returns the subtype of the pointer.
523    pub fn subtype(&self) -> Type<'a> {
524        Type::from_archived(self.inner.subtype.as_ref())
525    }
526
527    /// Returns the size of the pointer in bytes.
528    pub fn size(&self) -> u64 {
529        self.inner.size.into()
530    }
531}