Skip to main content

ms_pdb/lines/
subsection.rs

1//! Iteration logic for subsections
2
3#[cfg(test)]
4use pretty_hex::PrettyHex;
5
6use super::*;
7
8/// Iterator state for subsections
9pub struct SubsectionIter<'a> {
10    rest: &'a [u8],
11}
12
13impl<'a> SubsectionIter<'a> {
14    /// Start iteration
15    pub fn new(rest: &'a [u8]) -> Self {
16        Self { rest }
17    }
18
19    /// The remaining unparsed data.
20    pub fn rest(&self) -> &'a [u8] {
21        self.rest
22    }
23}
24
25impl<'a> HasRestLen for SubsectionIter<'a> {
26    fn rest_len(&self) -> usize {
27        self.rest.len()
28    }
29}
30
31/// Iterator state for subsections with mutable access
32pub struct SubsectionIterMut<'a> {
33    rest: &'a mut [u8],
34}
35
36impl<'a> SubsectionIterMut<'a> {
37    /// Begins iteration
38    pub fn new(rest: &'a mut [u8]) -> Self {
39        Self { rest }
40    }
41
42    /// The remaining unparsed data.
43    pub fn rest(&self) -> &[u8] {
44        self.rest
45    }
46}
47
48impl<'a> HasRestLen for SubsectionIterMut<'a> {
49    fn rest_len(&self) -> usize {
50        self.rest.len()
51    }
52}
53
54/// A reference to one subsection
55pub struct Subsection<'a> {
56    /// The kind of data in this subsection.
57    pub kind: SubsectionKind,
58    /// The contents of the subsection.
59    pub data: &'a [u8],
60}
61
62/// A reference to one subsection, with mutable access
63pub struct SubsectionMut<'a> {
64    /// The kind of data in this subsection.
65    pub kind: SubsectionKind,
66    /// The contents of the subsection.
67    pub data: &'a mut [u8],
68}
69
70/// The header of a subsection.
71#[derive(IntoBytes, FromBytes, KnownLayout, Immutable, Unaligned)]
72#[repr(C)]
73pub struct SubsectionHeader {
74    /// The kind of data in this subsection.
75    pub kind: U32<LE>,
76    /// The size of the subsection, in bytes. This value does not count the size of the `kind` field.
77    pub size: U32<LE>,
78}
79
80impl<'a> Iterator for SubsectionIter<'a> {
81    type Item = Subsection<'a>;
82
83    fn next(&mut self) -> Option<Self::Item> {
84        if self.rest.is_empty() {
85            return None;
86        }
87
88        let mut p = Parser::new(self.rest);
89        let header: &SubsectionHeader = if let Ok(h) = p.get::<SubsectionHeader>() {
90            h
91        } else {
92            warn!(
93                "Failed to decode subsection data (incomplete header)!  rest_len = {}",
94                self.rest.len()
95            );
96            return None;
97        };
98        let size = header.size.get() as usize;
99
100        let data = if let Ok(d) = p.bytes(size) {
101            d
102        } else {
103            warn!(
104                "Failed to decode subsection data (incomplete payload)!  rest_len = {}",
105                self.rest.len()
106            );
107            return None;
108        };
109
110        // If 'size' is not 4-byte aligned, then skip the alignment bytes.
111        let alignment_len = (4 - (size & 3)) & 3;
112        let _ = p.skip(alignment_len);
113
114        self.rest = p.into_rest();
115
116        Some(Subsection {
117            kind: SubsectionKind(header.kind.get()),
118            data,
119        })
120    }
121}
122
123impl<'a> Iterator for SubsectionIterMut<'a> {
124    type Item = SubsectionMut<'a>;
125
126    fn next(&mut self) -> Option<Self::Item> {
127        if self.rest.is_empty() {
128            return None;
129        }
130
131        let mut p = ParserMut::new(core::mem::take(&mut self.rest));
132        let header: &SubsectionHeader = p.get::<SubsectionHeader>().ok()?;
133        let size = header.size.get() as usize;
134        let data = p.bytes_mut(size).ok()?;
135
136        let alignment_len = (4 - (size & 3)) & 3;
137        let _ = p.skip(alignment_len);
138
139        self.rest = p.into_rest();
140
141        Some(SubsectionMut {
142            kind: SubsectionKind(header.kind.get()),
143            data,
144        })
145    }
146}
147
148// Test that empty input or malformed line data (too little data) does not cause the iterator to fail.
149// The iterator will return `None`.
150#[test]
151fn empty_or_malformed_input() {
152    static CASES: &[(&str, &[u8])] = &[
153        ("empty input", &[]),
154        ("incomplete subsection_kind", &[0xf1, 0]),
155        ("incomplete subsection_size", &[0xf1, 0, 0, 0, 0xff, 0xff]),
156        (
157            "incomplete subsection_data",
158            &[
159                0xf1, 0, 0, 0, // subsection_Kind
160                0, 0, 1, 0, // subsection_size (0x10000)
161            ],
162        ),
163    ];
164
165    for &(case_name, case_data) in CASES.iter() {
166        // Test the subsection iterator
167        println!("case: {}\n{:?}", case_name, case_data.hex_dump());
168        let ld = LineData::new(case_data);
169        assert_eq!(ld.subsections().count(), 0);
170        assert!(ld.find_checksums().is_none());
171        assert!(ld.find_checksums_bytes().is_none());
172        ld.iter_name_index(|_name| panic!("should never be called"))
173            .unwrap();
174
175        // Do the same thing with a mutable iterator.
176        let mut case_data_mut = case_data.to_vec();
177        let mut ld = LineDataMut::new(&mut case_data_mut);
178        assert_eq!(ld.subsections_mut().count(), 0);
179    }
180}
181
182/// Test the alignment padding code in the subsection iterator.
183#[test]
184fn test_subsection_alignment() {
185    const PAD: u8 = 0xaa;
186
187    #[rustfmt::skip]
188    static DATA: &[u8] = &[
189                                // -----subsection 0 -----
190        0xf4, 0, 0, 0,          // subsection_kind: DEBUG_S_FILECHKSMS
191        0x2, 0, 0, 0,           // subsection_size (unaligned len = 2)
192        0xab, 0xcd,             // subsection_data
193        PAD, PAD,               // 2 padding bytes
194                                // ----- subsection 1 -----
195        0xf5, 0, 0, 0,          // subsection_kind: FRAMEDATA
196        7, 0, 0, 0,             // subsection_size: 7 (unaligned len = 3)
197        1, 2, 3, 4, 5, 6, 7,    // subsection_data
198        PAD,                    // 1 padding byte
199                                // ----- subsection 2 -----
200        0xf6, 0, 0, 0,          // subsection_kind: INLINEELINES
201        8, 0, 0, 0,             // subsection_size: 8 (unaligned len = 0)
202        8, 7, 6, 5, 4, 3, 2, 1, // subsection_data
203                                // no padding bytes
204                                // ----- subsection 3 -----
205        0xf7, 0, 0, 0,          // subsection_kind: CROSSSCOPEIMPORTS
206        5, 0, 0, 0,             // subsection_size: 5 (unaligned len = 1)
207        10, 11, 12, 13, 14,     // subsection_data
208        PAD, PAD, PAD,          // 3 padding bytes
209
210
211    ];
212
213    // Test SubsectionsIter
214    {
215        let mut iter = LineData::new(DATA).subsections();
216
217        let sub0 = iter.next().unwrap();
218        assert_eq!(sub0.kind, SubsectionKind::FILE_CHECKSUMS);
219        assert_eq!(sub0.data, &[0xab, 0xcd]);
220
221        let sub1 = iter.next().unwrap();
222        assert_eq!(sub1.kind, SubsectionKind::FRAMEDATA);
223        assert_eq!(sub1.data, &[1, 2, 3, 4, 5, 6, 7]);
224
225        let sub2 = iter.next().unwrap();
226        assert_eq!(sub2.kind, SubsectionKind::INLINEELINES);
227        assert_eq!(sub2.data, &[8, 7, 6, 5, 4, 3, 2, 1]);
228
229        let sub3 = iter.next().unwrap();
230        assert_eq!(sub3.kind, SubsectionKind::CROSSSCOPEIMPORTS);
231        assert_eq!(sub3.data, &[10, 11, 12, 13, 14]);
232
233        assert!(iter.rest().is_empty());
234    }
235
236    // Test SubsectionIterMut
237    // We repeat the tests because we can't do generics over mutability, and the implementations of
238    // SubsectionIter and SubsectionIterMut
239    {
240        let mut data_mut = DATA.to_vec();
241        let mut iter = SubsectionIterMut::new(&mut data_mut);
242
243        let sub0 = iter.next().unwrap();
244        assert_eq!(sub0.kind, SubsectionKind::FILE_CHECKSUMS);
245        assert_eq!(sub0.data, &[0xab, 0xcd]);
246
247        let sub1 = iter.next().unwrap();
248        assert_eq!(sub1.kind, SubsectionKind::FRAMEDATA);
249        assert_eq!(sub1.data, &[1, 2, 3, 4, 5, 6, 7]);
250
251        let sub2 = iter.next().unwrap();
252        assert_eq!(sub2.kind, SubsectionKind::INLINEELINES);
253        assert_eq!(sub2.data, &[8, 7, 6, 5, 4, 3, 2, 1]);
254
255        let sub3 = iter.next().unwrap();
256        assert_eq!(sub3.kind, SubsectionKind::CROSSSCOPEIMPORTS);
257        assert_eq!(sub3.data, &[10, 11, 12, 13, 14]);
258
259        assert!(iter.rest().is_empty());
260    }
261}