pdb/
pdbi.rs

1// Copyright 2017 pdb Developers
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use std::convert::TryInto;
9use std::mem;
10
11use uuid::Uuid;
12
13use crate::common::*;
14use crate::dbi::HeaderVersion;
15use crate::msf::*;
16
17/// A PDB info stream header parsed from a stream.
18///
19/// The [PDB information stream] contains the GUID and age fields that can be used to
20/// verify that a PDB file matches a specific binary, as well a list of named PDB streams
21/// with their stream indices.
22///
23/// [PDB information stream]: http://llvm.org/docs/PDB/PdbStream.html
24#[derive(Debug)]
25pub struct PDBInformation<'s> {
26    /// The version of the PDB format in use.
27    pub version: HeaderVersion,
28    /// A 32-bit timestamp.
29    pub signature: u32,
30    /// The number of times this PDB file has been written.
31    ///
32    /// This number is bumped by the linker and other tools every time the PDB is modified. It does
33    /// not necessarily correspond to the age declared in the image. Consider using
34    /// [`DebugInformation::age`](crate::DebugInformation::age) for a better match.
35    ///
36    /// This PDB matches an image, if the `guid` values match and the PDB age is equal or higher
37    /// than the image's age.
38    pub age: u32,
39    /// A `Uuid` generated when this PDB file was created that should uniquely identify it.
40    pub guid: Uuid,
41    /// The offset of the start of the stream name data within the stream.
42    pub names_offset: usize,
43    /// The size of the stream name data, in bytes.
44    pub names_size: usize,
45    stream: Stream<'s>,
46}
47
48impl<'s> PDBInformation<'s> {
49    /// Parses a `PDBInformation` from raw stream data.
50    pub(crate) fn parse(stream: Stream<'s>) -> Result<Self> {
51        let (version, signature, age, guid, names_size, names_offset) = {
52            let mut buf = stream.parse_buffer();
53            let version = From::from(buf.parse_u32()?);
54            let signature = buf.parse_u32()?;
55            let age = buf.parse_u32()?;
56            let guid = Uuid::from_fields(
57                buf.parse_u32()?,
58                buf.parse_u16()?,
59                buf.parse_u16()?,
60                buf.take(8)?.try_into().unwrap(),
61            );
62            let names_size = buf.parse_u32()? as usize;
63            let names_offset = buf.pos();
64            (version, signature, age, guid, names_size, names_offset)
65        };
66
67        Ok(PDBInformation {
68            version,
69            signature,
70            age,
71            guid,
72            names_size,
73            names_offset,
74            stream,
75        })
76    }
77
78    /// Get a `StreamNames` object that can be used to iterate over named streams contained
79    /// within the PDB file.
80    ///
81    /// This can be used to look up certain PDB streams by name.
82    ///
83    /// # Example
84    ///
85    /// ```
86    /// # use pdb::FallibleIterator;
87    /// #
88    /// # fn test() -> pdb::Result<()> {
89    /// let file = std::fs::File::open("fixtures/self/foo.pdb")?;
90    /// let mut pdb = pdb::PDB::open(file)?;
91    /// let info = pdb.pdb_information()?;
92    /// let names = info.stream_names()?;
93    /// let mut v: Vec<_> = names.iter().map(|n| n.name.to_string()).collect();
94    /// v.sort();
95    /// assert_eq!(&v, &["mystream", "/LinkInfo", "/names", "/src/headerblock"]);
96    /// # Ok(())
97    /// # }
98    /// ```
99    pub fn stream_names(&self) -> Result<StreamNames<'_>> {
100        // The names map is part of the PDB info stream that provides a mapping from stream names to
101        // stream indicies. Its [format on disk](1) is somewhat complicated, consisting of a block of
102        // data comprising the names as null-terminated C strings, followed by a map of stream indices
103        // to the offset of their names within the names block.
104        //
105        // [The map itself](2) is stored as a 32-bit count of the number of entries, followed by a
106        // 32-bit value that gives the number of bytes taken up by the entries themselves, followed by
107        // two sets: one for names that are present in this PDB, and one for names that have been
108        // deleted, followed by the map entries, each of which is a pair of 32-bit values consisting of
109        // an offset into the names block and a stream ID.
110        //
111        // [The two sets](3) are each stored as a [bit array](4), which consists of a 32-bit count, and
112        // then that many 32-bit words containing the bits in the array.
113        //
114        // [1]: https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/include/nmtni.h#L76
115        // [2]: https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/include/map.h#L474
116        // [3]: https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/include/iset.h#L62
117        // [4]: https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/include/array.h#L209
118
119        let mut names = vec![];
120        let mut buf = self.stream.parse_buffer();
121
122        // Seek forward to the name map.
123        buf.take(self.names_offset + self.names_size)?;
124        let count = buf.parse_u32()?;
125        // We don't actually use most of these.
126        let _entries_size = buf.parse_u32()?;
127        let ok_words = buf.parse_u32()?;
128        let _ok_bits = buf.take(ok_words as usize * mem::size_of::<u32>())?;
129        let deleted_words = buf.parse_u32()?;
130        let _deleted_bits = buf.take(deleted_words as usize * mem::size_of::<u32>())?;
131
132        // Skip over the header here.
133        let mut names_reader = self.stream.parse_buffer();
134        names_reader.take(self.names_offset)?;
135        // And take just the name data.
136        let names_buf = names_reader.take(self.names_size)?;
137        for _ in 0..count {
138            let name_offset = buf.parse_u32()? as usize;
139            let stream_id = StreamIndex(buf.parse_u32()? as u16);
140            let name = ParseBuffer::from(&names_buf[name_offset..]).parse_cstring()?;
141            names.push(StreamName { name, stream_id });
142        }
143
144        Ok(StreamNames { names })
145    }
146}
147
148/// A named stream contained within the PDB file.
149#[derive(Debug)]
150pub struct StreamName<'n> {
151    /// The stream's name.
152    pub name: RawString<'n>,
153    /// The index of this stream.
154    pub stream_id: StreamIndex,
155}
156
157/// A list of named streams contained within the PDB file.
158///
159/// Call [`StreamNames::iter`] to iterate over the names. The iterator produces [`StreamName`]
160/// objects.
161#[derive(Debug)]
162pub struct StreamNames<'s> {
163    /// The list of streams and their names.
164    names: Vec<StreamName<'s>>,
165}
166
167/// An iterator over [`StreamName`]s.
168pub type NameIter<'a, 'n> = std::slice::Iter<'a, StreamName<'n>>;
169
170impl<'s> StreamNames<'s> {
171    /// Return an iterator over named streams and their stream indices.
172    #[inline]
173    pub fn iter(&self) -> NameIter<'_, 's> {
174        self.names.iter()
175    }
176}
177
178impl<'a, 's> IntoIterator for &'a StreamNames<'s> {
179    type Item = &'a StreamName<'s>;
180    type IntoIter = NameIter<'a, 's>;
181
182    #[inline]
183    fn into_iter(self) -> Self::IntoIter {
184        self.names.iter()
185    }
186}