addr_symbolizer/
modules.rs

1// Axel '0vercl0k' Souchet - February 23 2024
2//! This module contains the implementation of the [`Module`] type which is used
3//! across the codebase.
4use std::ops::Range;
5
6use crate::misc::Rva;
7
8/// A user or kernel module.
9#[derive(Debug, Default, Clone)]
10pub struct Module {
11    /// Where the module is loaded into virtual memory.
12    pub at: Range<u64>,
13    /// The name of the module.
14    pub name: String,
15}
16
17impl Module {
18    /// Create a [`Module`].
19    pub fn new(name: impl Into<String>, start: u64, end: u64) -> Self {
20        Module {
21            name: name.into(),
22            at: start..end,
23        }
24    }
25
26    /// Calculate an rva from an `addr` contained in this module.
27    #[expect(clippy::cast_possible_truncation)]
28    #[must_use]
29    pub fn rva(&self, addr: u64) -> Rva {
30        debug_assert!(self.at.contains(&addr));
31
32        let offset = addr - self.at.start;
33        assert!(offset <= u32::MAX.into());
34
35        offset as Rva
36    }
37}
38
39/// A list of modules.
40#[derive(Debug, Default)]
41pub struct Modules(Vec<Module>);
42
43impl Modules {
44    /// Create a [`Modules`].
45    #[must_use]
46    pub fn new(mut modules: Vec<Module>) -> Self {
47        // Order the modules by their end addresses.
48        modules.sort_unstable_by_key(|e| e.at.end);
49
50        Self(modules)
51    }
52
53    /// Find the module that contains this address.
54    #[must_use]
55    pub fn find(&self, addr: u64) -> Option<&Module> {
56        // Find the index of the first module that might contain `addr`.
57        let idx = self.0.partition_point(|m| m.at.end <= addr);
58
59        // At this point there's several cases to handle.
60        //
61        // `partition_point` returns the len of the vector if it couldn't
62        // partition in two. This means that `addr` cannot possibly be contained by any
63        // of the modules we have, so we're done.
64        if idx == self.0.len() {
65            return None;
66        }
67
68        // We found the first module that has an end address larger than `addr`. This
69        // doesn't mean the module contains the address though. Imagine `addr` =
70        // `0xdeadbeef`, and `module.at` = `[0xefefefef, 0xefefefef+1]`.
71        let module = &self.0[idx];
72
73        // For this reason, we'll make sure the `addr` is in fact included, otherwise
74        // it's not a match.
75        if module.at.contains(&addr) {
76            Some(module)
77        } else {
78            None
79        }
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn basics() {
89        let modules = Modules::new(vec![
90            Module::new("foo".to_string(), 0x1_000, 0x2_000),
91            Module::new("foobar".to_string(), 0x2_000, 0x3_000),
92            Module::new("bar".to_string(), 0x4_000, 0x5_000),
93        ]);
94
95        assert!(modules.find(1).is_none());
96        assert_eq!(modules.find(0x1_000).unwrap().name, "foo");
97        assert_eq!(modules.find(0x2_000).unwrap().name, "foobar");
98        assert!(modules.find(0x3_000).is_none());
99        assert_eq!(modules.find(0x4_fff).unwrap().name, "bar");
100        assert!(modules.find(0x6_000).is_none());
101    }
102}