ms_pdb/dbi/
optional_dbg.rs

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
//! Decodes the Optional Debug Header Substream.
//!
//! This substream contains an array of stream indexes. The order of the array is significant;
//! each has a specific purpose. They are enumerated by the [`OptionalDebugHeaderStream`] type.
//!
//! # References
//! * <https://llvm.org/docs/PDB/DbiStream.html#id10>
//! * [`DBGTYPE` in `pdb.h`](https://github.com/microsoft/microsoft-pdb/blob/805655a28bd8198004be2ac27e6e0290121a5e89/langapi/include/pdb.h#L438)

use super::*;

/// Provides access to the Optional Debug Header.
pub struct OptionalDebugHeader<'a> {
    /// Raw access to the stream indexes
    pub stream_indexes: &'a [StreamIndexU16],
}

impl<'a> OptionalDebugHeader<'a> {
    /// Parses the Optional Debug Header Substream.
    pub fn parse(bytes: &'a [u8]) -> anyhow::Result<Self> {
        let Ok(stream_indexes) = <[StreamIndexU16]>::ref_from_bytes(bytes) else {
            bail!("The OptionalDebugHeader has an invalid size. The size is required to be a multiple of 2. Size: {}",
                bytes.len());
        };

        Ok(Self { stream_indexes })
    }

    /// Gets a stream index, given an index into the Optional Debug Header.
    pub fn stream_by_index(&self, i: usize) -> Option<u32> {
        self.stream_indexes.get(i)?.get()
    }

    /// Gets a stream index, given an identifier for a stream within the Optional Debug Header.
    pub fn stream(&self, s: OptionalDebugHeaderStream) -> Option<u32> {
        self.stream_by_index(s as usize)
    }

    /// The number of stream indexes in the Optional Debug Header Substream.
    pub fn num_streams(&self) -> usize {
        self.stream_indexes.len()
    }

    /// Iterates the streams within the Optional Debug Header. The iterated values are
    /// `(i, stream)` where `i` is an index into the Optional Debug Header.
    /// `OptionalDebugHeaderStream::try_from(i)`.
    pub fn iter_streams(&self) -> IterStreams<'_> {
        IterStreams {
            stream_indexes: self.stream_indexes,
            next: 0,
        }
    }
}

/// Iterates streams
pub struct IterStreams<'a> {
    stream_indexes: &'a [StreamIndexU16],
    next: usize,
}

impl<'a> Iterator for IterStreams<'a> {
    type Item = (usize, u32);

    fn next(&mut self) -> Option<Self::Item> {
        while self.next < self.stream_indexes.len() {
            let i = self.next;
            let stream_index_or_nil = self.stream_indexes[i].get();
            self.next += 1;

            if let Some(stream_index) = stream_index_or_nil {
                return Some((i, stream_index));
            }
        }
        None
    }
}

macro_rules! optional_debug_header_streams {
    (
        $(
            $( #[$a:meta] )*
            $index:literal, $name:ident, $description:expr;
        )*
    ) => {
        /// Identifies the stream indexes stored in the Optional Debug Header.
        #[derive(Copy, Clone, Eq, PartialEq, Debug)]
        #[repr(u8)]
        #[allow(non_camel_case_types)]
        #[allow(missing_docs)]
        pub enum OptionalDebugHeaderStream {
            $(
                $( #[$a] )*
                $name = $index,
            )*
        }

        /// The short name (identifier) for each of the names in `OptionalDebugHeaderStream`.
        pub static OPTIONAL_DEBUG_HEADER_STREAM_NAME: [&str; 11] = [
            $(
                stringify!($name),
            )*
        ];

        /// The for each of the names in `OptionalDebugHeaderStream`.
        pub static OPTIONAL_DEBUG_HEADER_STREAM_DESCRIPTION: [&str; 11] = [
            $(
                $description,
            )*
        ];

        impl TryFrom<usize> for OptionalDebugHeaderStream {
            type Error = ();

            fn try_from(i: usize) -> std::result::Result<Self, Self::Error> {
                match i {
                    $( $index => Ok(Self::$name), )*
                    _ => Err(()),
                }
            }
        }
    }
}

optional_debug_header_streams! {
    /// Stream contains an array of `FPO_DATA` structures. This contains the relocated contents of
    /// any `.debug$F` section from any of the linker inputs.
    0, fpo_data, "";
    /// Stream contains a debug data directory of type `IMAGE_DEBUG_TYPE_EXCEPTION`.
    1, exception_data, "";
    /// Stream contains a debug data directory of type `IMAGE_DEBUG_TYPE_FIXUP`.
    2, fixup_data, "";
    /// Stream contains a debug data directory of type `IMAGE_DEBUG_TYPE_OMAP_TO_SRC`.
    /// This is used for mapping addresses from instrumented code to uninstrumented code.
    3, omap_to_src_data, "";
    /// Stream contains a debug data directory of type `IMAGE_DEBUG_TYPE_OMAP_FROM_SRC`.
    /// This is used for mapping addresses from uninstrumented code to instrumented code.
    4, omap_from_src_data, "";
    /// A dump of all section headers from the original executable.
    5, section_header_data, "";
    6, token_to_record_id_map, "";
    /// Exception handler data
    7, xdata, "";
    /// Procedure data
    8, pdata, "";
    9, new_fpo_data, "";
    10, original_section_header_data, "";
}