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!("The OptionalDebugHeader has an invalid size. The size is required to be a multiple of 2. Size: {}",
23                bytes.len());
24        };
25
26        Ok(Self { stream_indexes })
27    }
28
29    /// Gets a stream index, given an index into the Optional Debug Header.
30    pub fn stream_by_index(&self, i: usize) -> Option<u32> {
31        self.stream_indexes.get(i)?.get()
32    }
33
34    /// Gets a stream index, given an identifier for a stream within the Optional Debug Header.
35    pub fn stream(&self, s: OptionalDebugHeaderStream) -> Option<u32> {
36        self.stream_by_index(s as usize)
37    }
38
39    /// The number of stream indexes in the Optional Debug Header Substream.
40    pub fn num_streams(&self) -> usize {
41        self.stream_indexes.len()
42    }
43
44    /// Iterates the streams within the Optional Debug Header. The iterated values are
45    /// `(i, stream)` where `i` is an index into the Optional Debug Header.
46    /// `OptionalDebugHeaderStream::try_from(i)`.
47    pub fn iter_streams(&self) -> IterStreams<'_> {
48        IterStreams {
49            stream_indexes: self.stream_indexes,
50            next: 0,
51        }
52    }
53}
54
55/// Iterates streams
56pub struct IterStreams<'a> {
57    stream_indexes: &'a [StreamIndexU16],
58    next: usize,
59}
60
61impl<'a> Iterator for IterStreams<'a> {
62    type Item = (usize, u32);
63
64    fn next(&mut self) -> Option<Self::Item> {
65        while self.next < self.stream_indexes.len() {
66            let i = self.next;
67            let stream_index_or_nil = self.stream_indexes[i].get();
68            self.next += 1;
69
70            if let Some(stream_index) = stream_index_or_nil {
71                return Some((i, stream_index));
72            }
73        }
74        None
75    }
76}
77
78macro_rules! optional_debug_header_streams {
79    (
80        $(
81            $( #[$a:meta] )*
82            $index:literal, $name:ident, $description:expr;
83        )*
84    ) => {
85        /// Identifies the stream indexes stored in the Optional Debug Header.
86        #[derive(Copy, Clone, Eq, PartialEq, Debug)]
87        #[repr(u8)]
88        #[allow(non_camel_case_types)]
89        #[allow(missing_docs)]
90        pub enum OptionalDebugHeaderStream {
91            $(
92                $( #[$a] )*
93                $name = $index,
94            )*
95        }
96
97        /// The short name (identifier) for each of the names in `OptionalDebugHeaderStream`.
98        pub static OPTIONAL_DEBUG_HEADER_STREAM_NAME: [&str; 11] = [
99            $(
100                stringify!($name),
101            )*
102        ];
103
104        /// The for each of the names in `OptionalDebugHeaderStream`.
105        pub static OPTIONAL_DEBUG_HEADER_STREAM_DESCRIPTION: [&str; 11] = [
106            $(
107                $description,
108            )*
109        ];
110
111        impl TryFrom<usize> for OptionalDebugHeaderStream {
112            type Error = ();
113
114            fn try_from(i: usize) -> std::result::Result<Self, Self::Error> {
115                match i {
116                    $( $index => Ok(Self::$name), )*
117                    _ => Err(()),
118                }
119            }
120        }
121    }
122}
123
124optional_debug_header_streams! {
125    /// Stream contains an array of `FPO_DATA` structures. This contains the relocated contents of
126    /// any `.debug$F` section from any of the linker inputs.
127    0, fpo_data, "";
128    /// Stream contains a debug data directory of type `IMAGE_DEBUG_TYPE_EXCEPTION`.
129    1, exception_data, "";
130    /// Stream contains a debug data directory of type `IMAGE_DEBUG_TYPE_FIXUP`.
131    2, fixup_data, "";
132    /// Stream contains a debug data directory of type `IMAGE_DEBUG_TYPE_OMAP_TO_SRC`.
133    /// This is used for mapping addresses from instrumented code to uninstrumented code.
134    3, omap_to_src_data, "";
135    /// Stream contains a debug data directory of type `IMAGE_DEBUG_TYPE_OMAP_FROM_SRC`.
136    /// This is used for mapping addresses from uninstrumented code to instrumented code.
137    4, omap_from_src_data, "";
138    /// A dump of all section headers from the original executable.
139    5, section_header_data, "";
140    6, token_to_record_id_map, "";
141    /// Exception handler data
142    7, xdata, "";
143    /// Procedure data
144    8, pdata, "";
145    9, new_fpo_data, "";
146    10, original_section_header_data, "";
147}