1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
use std::collections::BTreeMap;

/// Keeps track of mapped libraries in an address space. Stores a value
/// for each mapping, and allows efficient lookup of that value based on
/// an address.
///
/// A "library" here is a loose term; it could be a normal shared library,
/// or the main binary, but it could also be a synthetic library for JIT
/// code. For normal libraries, there's usually just one mapping per library.
/// For JIT code, you could have many small mappings, one per JIT function,
/// all pointing to the synthetic JIT "library".
#[derive(Debug, Clone)]
pub struct LibMappings<T> {
    /// A BTreeMap of non-overlapping Mappings. The key is the start_avma of the mapping.
    ///
    /// When a new mapping is added, overlapping mappings are removed.
    map: BTreeMap<u64, Mapping<T>>,
}

impl<T> Default for LibMappings<T> {
    fn default() -> Self {
        Self::new()
    }
}

impl<T> LibMappings<T> {
    /// Creates a new empty instance.
    pub fn new() -> Self {
        Self {
            map: BTreeMap::new(),
        }
    }

    /// Add a mapping to this address space. Any existing mappings which overlap with the
    /// new mapping are removed.
    ///
    /// `start_avma` and `end_avma` describe the address range that this mapping
    /// occupies.
    ///
    /// AVMA = "actual virtual memory address"
    ///
    /// `relative_address_at_start` is the "relative address" which corresponds
    /// to `start_avma`, in the library that is mapped in this mapping. This is zero if
    /// `start_avm` is the base address of the library.
    ///
    /// A relative address is a `u32` value which is relative to the library base address.
    /// So you will usually set `relative_address_at_start` to `start_avma - base_avma`.
    ///
    /// For ELF binaries, the base address is the AVMA of the first segment, i.e. the
    /// start_avma of the mapping created by the first ELF `LOAD` command.
    ///
    /// For mach-O binaries, the base address is the vmaddr of the `__TEXT` segment.
    ///
    /// For Windows binaries, the base address is the image load address.
    pub fn add_mapping(
        &mut self,
        start_avma: u64,
        end_avma: u64,
        relative_address_at_start: u32,
        value: T,
    ) {
        let removal_avma_range_start =
            if let Some(mapping_overlapping_with_start_avma) = self.lookup_impl(start_avma) {
                mapping_overlapping_with_start_avma.start_avma
            } else {
                start_avma
            };
        // self.map.drain(removal_avma_range_start..end_avma);
        let overlapping_keys: Vec<u64> = self
            .map
            .range(removal_avma_range_start..end_avma)
            .map(|(start_avma, _)| *start_avma)
            .collect();
        for key in overlapping_keys {
            self.map.remove(&key);
        }

        self.map.insert(
            start_avma,
            Mapping {
                start_avma,
                end_avma,
                relative_address_at_start,
                value,
            },
        );
    }

    /// Remove a mapping which starts at the given address. If found, this returns
    /// the `relative_address_at_start` and the associated value of the mapping.
    pub fn remove_mapping(&mut self, start_avma: u64) -> Option<(u32, T)> {
        self.map
            .remove(&start_avma)
            .map(|m| (m.relative_address_at_start, m.value))
    }

    /// Clear all mappings.
    pub fn clear(&mut self) {
        self.map.clear();
    }

    /// Look up the mapping which covers the given address and return
    /// the stored value.
    pub fn lookup(&self, avma: u64) -> Option<&T> {
        self.lookup_impl(avma).map(|m| &m.value)
    }

    /// Look up the mapping which covers the given address and return
    /// its `Mapping<T>``.
    fn lookup_impl(&self, avma: u64) -> Option<&Mapping<T>> {
        let (_start_avma, last_mapping_starting_at_or_before_avma) =
            self.map.range(..=avma).next_back()?;
        if avma < last_mapping_starting_at_or_before_avma.end_avma {
            Some(last_mapping_starting_at_or_before_avma)
        } else {
            None
        }
    }

    /// Converts an absolute address (AVMA, actual virtual memory address) into
    /// a relative address and the mapping's associated value.
    pub fn convert_address(&self, avma: u64) -> Option<(u32, &T)> {
        let mapping = match self.lookup_impl(avma) {
            Some(mapping) => mapping,
            None => return None,
        };
        let offset_from_mapping_start = (avma - mapping.start_avma) as u32;
        let relative_address = mapping.relative_address_at_start + offset_from_mapping_start;
        Some((relative_address, &mapping.value))
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
struct Mapping<T> {
    start_avma: u64,
    end_avma: u64,
    relative_address_at_start: u32,
    value: T,
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_lib_mappings() {
        let mut m = LibMappings::new();
        m.add_mapping(100, 200, 100, "100..200");
        m.add_mapping(200, 250, 200, "200..250");
        assert_eq!(m.lookup(200), Some(&"200..250"));
        m.add_mapping(180, 220, 180, "180..220");
        assert_eq!(m.lookup(200), Some(&"180..220"));
        assert_eq!(m.lookup(170), None);
        assert_eq!(m.lookup(220), None);
        m.add_mapping(225, 250, 225, "225..250");
        m.add_mapping(255, 270, 255, "255..270");
        m.add_mapping(100, 150, 100, "100..150");
        assert_eq!(m.lookup(90), None);
        assert_eq!(m.lookup(150), None);
        assert_eq!(m.lookup(149), Some(&"100..150"));
        assert_eq!(m.lookup(200), Some(&"180..220"));
        assert_eq!(m.lookup(260), Some(&"255..270"));
    }
}