Skip to main content

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 a module that contains this address.
54    #[must_use]
55    pub fn by_addr(&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    /// Find a module by name.
83    #[must_use]
84    pub fn by_name(&self, name: &str) -> Option<&Module> {
85        self.0.iter().find(|module| module.name == name)
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn basics() {
95        let modules = Modules::new(vec![
96            Module::new("foo".to_string(), 0x1_000, 0x2_000),
97            Module::new("foobar".to_string(), 0x2_000, 0x3_000),
98            Module::new("bar".to_string(), 0x4_000, 0x5_000),
99        ]);
100
101        assert!(modules.by_addr(1).is_none());
102        assert_eq!(modules.by_addr(0x1_000).unwrap().name, "foo");
103        assert_eq!(modules.by_name("foo").unwrap().at.start, 0x1_000);
104        // assert_eq!(modules.by_name("Foo").unwrap().at.start, 0x1_000);
105        assert_eq!(modules.by_addr(0x2_000).unwrap().name, "foobar");
106        assert_eq!(modules.by_name("foobar").unwrap().at.start, 0x2_000);
107        // assert_eq!(modules.by_name("Foobar").unwrap().at.start, 0x2_000);
108        assert!(modules.by_addr(0x3_000).is_none());
109        assert!(modules.by_name("bleh").is_none());
110        assert_eq!(modules.by_addr(0x4_fff).unwrap().name, "bar");
111        assert_eq!(modules.by_name("bar").unwrap().at.start, 0x4_000);
112        assert!(modules.by_addr(0x6_000).is_none());
113    }
114}