ms_pdb/dbi/
modules.rs

1//! DBI Modules Substream
2
3use super::*;
4use crate::StreamIndexU16;
5use bstr::BStr;
6use ms_codeview::HasRestLen;
7use std::mem::take;
8use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
9
10/// The header of a Module Info record. Module Info records are stored in the DBI stream.
11///
12/// See `dbi.h`, `MODI_60_Persist`
13#[derive(Unaligned, IntoBytes, FromBytes, Immutable, KnownLayout, Clone, Debug)]
14#[repr(C)]
15pub struct ModuleInfoFixed {
16    /// This appears to be a module index field, but it is not always set.
17    ///
18    /// In some PDBs, we see this field being set to the zero-based index of this Module Info record
19    /// in the DBI Modules Substream.  In other PDBs, this value is 0.  Set this to 0.
20    pub unused1: U32<LE>,
21
22    /// This module's first section contribution.
23    pub section_contrib: SectionContribEntry,
24
25    /// Various flags
26    ///
27    /// * bit 0: set to 1 if this module has been written since DBI opened
28    /// * bit 1: set to 1 if this module has EC symbolic information
29    /// * bits 2-7: not used
30    /// * bits 8-15: index into TSM list for this mods server
31    pub flags: U16<LE>,
32
33    /// Stream index of the Module Stream for this module, which contains the symbols and line data
34    /// for this module. If this is 0xffff, then this module does not have a module info stream.
35    pub stream: StreamIndexU16,
36
37    /// Specifies the size of the symbols substream within the Module Stream.
38    pub sym_byte_size: U32<LE>,
39
40    /// Specifies the length of the C11 Line Data in a Module Information Stream.
41    /// C11 line data is obsolete and is not supported.
42    pub c11_byte_size: U32<LE>,
43
44    /// Specifies the length of the C13 Line Data in a Module Information Stream.
45    pub c13_byte_size: U32<LE>,
46
47    /// Number of files contributing to this module.
48    pub source_file_count: U16<LE>,
49
50    /// Alignment padding.
51    pub padding: [u8; 2],
52
53    /// Do not read. Set to 0 when encoding.
54    pub unused2: U32<LE>,
55
56    /// Unknown; possible that this relates to Edit-and-Continue.
57    pub source_file_name_index: U32<LE>,
58
59    /// Unknown; possible that this relates to Edit-and-Continue.
60    pub pdb_file_path_name_index: U32<LE>,
61}
62
63impl ModuleInfoFixed {
64    /// Gets the stream for this module, if any. This stream contains the symbol data and C13 Line
65    /// Data for the module.
66    pub fn stream(&self) -> Option<u32> {
67        self.stream.get()
68    }
69}
70
71/// Holds or refers to the data of a substream within a Module Info record.
72#[derive(Clone)]
73pub struct ModInfoSubstream<D: AsRef<[u8]>> {
74    /// The substream data.
75    pub substream_data: D,
76}
77
78impl<D: AsRef<[u8]>> ModInfoSubstream<D> {
79    /// Iterates the Module Info records contained within the DBI Stream.
80    pub fn iter(&self) -> IterModuleInfo<'_> {
81        IterModuleInfo {
82            rest: self.substream_data.as_ref(),
83        }
84    }
85}
86
87/// An in-memory representation of a Module Info record.
88///
89/// The `IterModInfo` iterator produces these items.
90#[allow(missing_docs)]
91pub struct ModuleInfo<'a> {
92    pub header: &'a ModuleInfoFixed,
93    pub module_name: &'a BStr,
94    pub obj_file: &'a BStr,
95}
96
97/// A mutable view of a Module Info record.
98#[allow(missing_docs)]
99pub struct ModuleInfoMut<'a> {
100    pub header: &'a mut ModuleInfoFixed,
101    pub module_name: &'a BStr,
102    pub obj_file: &'a BStr,
103}
104
105impl<'a> ModuleInfo<'a> {
106    /// The name of the module.
107    ///
108    /// * For simple object files, this is the same as `file_name()`.
109    /// * For DLL import libraries, this is the name of the DLL, e.g. `KernelBase.dll`.
110    /// * For static libraries, this is the name (and possibly path) of the object file within the
111    ///   static library, not the static library itself.
112    pub fn module_name(&self) -> &'a BStr {
113        self.module_name
114    }
115
116    /// The file name of this module.
117    ///
118    /// * For individual `*.obj` files that are passed directly to the linker (not in a static
119    ///   library), this is the filename.
120    /// * For static libraries, this is the `*.lib` file, not the modules within it.
121    /// * For DLL import libraries, this is the import library, e.g. `KernelBase.lib`.
122    pub fn obj_file(&self) -> &'a BStr {
123        self.obj_file
124    }
125
126    /// The header of this Module Info record.
127    pub fn header(&self) -> &'a ModuleInfoFixed {
128        self.header
129    }
130
131    /// The stream index of the stream which contains the symbols defined by this module.
132    ///
133    /// Some modules do not have a symbol stream. In that case, this function will return `None`.
134    pub fn stream(&self) -> Option<u32> {
135        self.header.stream()
136    }
137
138    /// Gets the size in bytes of the C11 Line Data.
139    pub fn c11_size(&self) -> u32 {
140        self.header.c11_byte_size.get()
141    }
142
143    /// Gets the size in bytes of the C13 Line Data.
144    pub fn c13_size(&self) -> u32 {
145        self.header.c13_byte_size.get()
146    }
147
148    /// Gets the size in bytes of the symbol stream for this module. This value includes the size
149    /// of the 4-byte symbol stream header.
150    pub fn sym_size(&self) -> u32 {
151        self.header.sym_byte_size.get()
152    }
153}
154
155/// Iterates module info records
156pub struct IterModuleInfo<'a> {
157    rest: &'a [u8],
158}
159
160impl<'a> IterModuleInfo<'a> {
161    #[allow(missing_docs)]
162    pub fn new(data: &'a [u8]) -> Self {
163        Self { rest: data }
164    }
165
166    /// Returns the data in the iterator that has not yet been parsed.
167    pub fn rest(&self) -> &'a [u8] {
168        self.rest
169    }
170}
171
172impl<'a> HasRestLen for IterModuleInfo<'a> {
173    fn rest_len(&self) -> usize {
174        self.rest.len()
175    }
176}
177
178impl<'a> Iterator for IterModuleInfo<'a> {
179    type Item = ModuleInfo<'a>;
180
181    fn next(&mut self) -> Option<Self::Item> {
182        if self.rest.is_empty() {
183            return None;
184        }
185
186        let mut p = Parser::new(self.rest);
187
188        let len_before = p.len();
189        let header: &ModuleInfoFixed = p.get().ok()?;
190        let module_name = p.strz().ok()?;
191        let obj_file = p.strz().ok()?;
192
193        // Each ModInfo structures is variable-length. It ends with two NUL-terminated strings.
194        // However, the ModInfo structures have an alignment requirement, so if the strings
195        // did not land us on an aligned boundary, we have to skip a few bytes to restore
196        // alignment.
197
198        // Find the number of bytes that were used for this structure.
199        let mod_record_bytes = len_before - p.len();
200        let alignment = (4 - (mod_record_bytes & 3)) & 3;
201        p.bytes(alignment).ok()?;
202
203        // Save iterator position.
204        self.rest = p.into_rest();
205
206        Some(ModuleInfo {
207            header,
208            module_name,
209            obj_file,
210        })
211    }
212}
213
214/// Mutable iterator
215pub struct IterModuleInfoMut<'a> {
216    rest: &'a mut [u8],
217}
218
219impl<'a> IterModuleInfoMut<'a> {
220    #[allow(missing_docs)]
221    pub fn new(data: &'a mut [u8]) -> Self {
222        Self { rest: data }
223    }
224}
225
226impl<'a> Iterator for IterModuleInfoMut<'a> {
227    type Item = ModuleInfoMut<'a>;
228
229    fn next(&mut self) -> Option<Self::Item> {
230        if self.rest.is_empty() {
231            return None;
232        }
233
234        // TODO: note that we steal the byte slice, which means that
235        // if anything goes wrong, we'll never put it back.
236        let mut p = ParserMut::new(take(&mut self.rest));
237
238        let len_before = p.len();
239        let header: &mut ModuleInfoFixed = p.get_mut().ok()?;
240        let module_name = p.strz().ok()?;
241        let obj_file = p.strz().ok()?;
242
243        // Each ModInfo structures is variable-length. It ends with two NUL-terminated strings.
244        // However, the ModInfo structures have an alignment requirement, so if the strings
245        // did not land us on an aligned boundary, we have to skip a few bytes to restore
246        // alignment.
247
248        // Find the number of bytes that were used for this structure.
249        let mod_record_bytes = len_before - p.len();
250        let alignment = (4 - (mod_record_bytes & 3)) & 3;
251        p.bytes(alignment).ok()?;
252
253        // Save iterator position.
254        self.rest = p.into_rest();
255        Some(ModuleInfoMut {
256            header,
257            module_name,
258            obj_file,
259        })
260    }
261}