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}