Skip to main content

ms_pdb/dbi/
optional_dbg.rs

1//! Decodes the Optional Debug Header Substream.
2//!
3//! This substream contains an array of stream indexes. The order of the array is significant;
4//! each has a specific purpose. They are enumerated by the [`OptionalDebugHeaderStream`] type.
5//!
6//! # References
7//! * <https://llvm.org/docs/PDB/DbiStream.html#id10>
8//! * [`DBGTYPE` in `pdb.h`](https://github.com/microsoft/microsoft-pdb/blob/805655a28bd8198004be2ac27e6e0290121a5e89/langapi/include/pdb.h#L438)
9
10use super::*;
11
12/// Provides access to the Optional Debug Header.
13pub struct OptionalDebugHeader<'a> {
14    /// Raw access to the stream indexes
15    pub stream_indexes: &'a [StreamIndexU16],
16}
17
18impl<'a> OptionalDebugHeader<'a> {
19    /// Parses the Optional Debug Header Substream.
20    pub fn parse(bytes: &'a [u8]) -> anyhow::Result<Self> {
21        let Ok(stream_indexes) = <[StreamIndexU16]>::ref_from_bytes(bytes) else {
22            bail!(
23                "The OptionalDebugHeader has an invalid size. The size is required to be a multiple of 2. Size: {}",
24                bytes.len()
25            );
26        };
27
28        Ok(Self { stream_indexes })
29    }
30
31    /// Gets a stream index, given an index into the Optional Debug Header.
32    pub fn stream_by_index(&self, i: usize) -> Option<u32> {
33        self.stream_indexes.get(i)?.get()
34    }
35
36    /// Gets a stream index, given an identifier for a stream within the Optional Debug Header.
37    pub fn stream(&self, s: OptionalDebugHeaderStream) -> Option<u32> {
38        self.stream_by_index(s as usize)
39    }
40
41    /// The number of stream indexes in the Optional Debug Header Substream.
42    pub fn num_streams(&self) -> usize {
43        self.stream_indexes.len()
44    }
45
46    /// Iterates the streams within the Optional Debug Header. The iterated values are
47    /// `(i, stream)` where `i` is an index into the Optional Debug Header.
48    /// `OptionalDebugHeaderStream::try_from(i)`.
49    pub fn iter_streams(&self) -> IterStreams<'_> {
50        IterStreams {
51            stream_indexes: self.stream_indexes,
52            next: 0,
53        }
54    }
55}
56
57/// Contains the list of Optional Debug Streams.
58#[derive(Clone)]
59pub struct OptionalDebugHeaders {
60    /// Contains stream indices, indexed by OptionalDebugStreamIndex.
61    pub streams: Vec<u16>,
62}
63
64impl OptionalDebugHeaders {
65    /// Iterates the Optional Debug Streams.
66    pub fn iter(&self) -> impl Iterator<Item = (OptionalDebugStream, u32)> + '_ {
67        self.streams.iter().enumerate().filter_map(|(i, &s)| {
68            if s != NIL_STREAM_INDEX {
69                Some((OptionalDebugStream(i as u32), s as u32))
70            } else {
71                None
72            }
73        })
74    }
75}
76
77/// Identifies one of the Optional Debug Streams.
78#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, Ord, PartialOrd)]
79pub struct OptionalDebugStream(pub u32);
80
81#[allow(missing_docs)]
82impl OptionalDebugStream {
83    pub const FPO_DATA: Self = Self(0);
84    pub const EXCEPTION_DATA: Self = Self(1);
85    pub const FIXUP_DATA: Self = Self(2);
86    pub const OMAP_TO_SRC_DATA: Self = Self(3);
87    pub const OMAP_FROM_SRC_DATA: Self = Self(4);
88    pub const SECTION_HEADER_DATA: Self = Self(5);
89    pub const TOKEN_TO_RECORD_ID_MAP: Self = Self(6);
90    pub const XDATA: Self = Self(7);
91    pub const PDATA: Self = Self(8);
92    pub const NEW_FPO_DATA: Self = Self(9);
93    pub const ORIGINAL_SECTION_HEADER_DATA: Self = Self(10);
94}
95
96impl OptionalDebugStream {
97    /// Returns the name of this optional debug stream.
98    pub fn name(&self) -> Option<&'static str> {
99        OPTIONAL_DEBUG_HEADER_STREAM_NAME
100            .get(self.0 as usize)
101            .copied()
102    }
103}
104
105/// Iterates streams
106pub struct IterStreams<'a> {
107    stream_indexes: &'a [StreamIndexU16],
108    next: usize,
109}
110
111impl<'a> Iterator for IterStreams<'a> {
112    type Item = (usize, u32);
113
114    fn next(&mut self) -> Option<Self::Item> {
115        while self.next < self.stream_indexes.len() {
116            let i = self.next;
117            let stream_index_or_nil = self.stream_indexes[i].get();
118            self.next += 1;
119
120            if let Some(stream_index) = stream_index_or_nil {
121                return Some((i, stream_index));
122            }
123        }
124        None
125    }
126}
127
128macro_rules! optional_debug_header_streams {
129    (
130        $(
131            $( #[$a:meta] )*
132            $index:literal, $name:ident, $description:expr;
133        )*
134    ) => {
135        /// Identifies the stream indexes stored in the Optional Debug Header.
136        #[derive(Copy, Clone, Eq, PartialEq, Debug)]
137        #[repr(u8)]
138        #[allow(non_camel_case_types)]
139        #[allow(missing_docs)]
140        pub enum OptionalDebugHeaderStream {
141            $(
142                $( #[$a] )*
143                $name = $index,
144            )*
145        }
146
147        /// The short name (identifier) for each of the names in `OptionalDebugHeaderStream`.
148        pub static OPTIONAL_DEBUG_HEADER_STREAM_NAME: [&str; 11] = [
149            $(
150                stringify!($name),
151            )*
152        ];
153
154        /// The for each of the names in `OptionalDebugHeaderStream`.
155        pub static OPTIONAL_DEBUG_HEADER_STREAM_DESCRIPTION: [&str; 11] = [
156            $(
157                $description,
158            )*
159        ];
160
161        impl TryFrom<usize> for OptionalDebugHeaderStream {
162            type Error = ();
163
164            fn try_from(i: usize) -> std::result::Result<Self, Self::Error> {
165                match i {
166                    $( $index => Ok(Self::$name), )*
167                    _ => Err(()),
168                }
169            }
170        }
171    }
172}
173
174optional_debug_header_streams! {
175    /// Stream contains an array of `FPO_DATA` structures. This contains the relocated contents of
176    /// any `.debug$F` section from any of the linker inputs.
177    0, fpo_data, "";
178    /// Stream contains a debug data directory of type `IMAGE_DEBUG_TYPE_EXCEPTION`.
179    1, exception_data, "";
180    /// Stream contains a debug data directory of type `IMAGE_DEBUG_TYPE_FIXUP`.
181    2, fixup_data, "";
182    /// Stream contains a debug data directory of type `IMAGE_DEBUG_TYPE_OMAP_TO_SRC`.
183    /// This is used for mapping addresses from instrumented code to uninstrumented code.
184    3, omap_to_src_data, "";
185    /// Stream contains a debug data directory of type `IMAGE_DEBUG_TYPE_OMAP_FROM_SRC`.
186    /// This is used for mapping addresses from uninstrumented code to instrumented code.
187    4, omap_from_src_data, "";
188    /// A dump of all section headers from the original executable.
189    5, section_header_data, "";
190    6, token_to_record_id_map, "";
191    /// Exception handler data
192    7, xdata, "";
193    /// Procedure data
194    8, pdata, "";
195    9, new_fpo_data, "";
196    10, original_section_header_data, "";
197}