1 : // Copyright 2012 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/pdb/pdb_dbi_stream.h"
16 :
17 : #include "base/strings/stringprintf.h"
18 : #include "syzygy/common/align.h"
19 : #include "syzygy/pdb/pdb_constants.h"
20 : #include "syzygy/pdb/pdb_stream.h"
21 : #include "syzygy/pdb/pdb_stream_reader.h"
22 : #include "syzygy/pdb/pdb_util.h"
23 :
24 : namespace pdb {
25 :
26 E : bool DbiModuleInfo::Read(common::BinaryStreamParser* parser) {
27 E : DCHECK(parser != nullptr);
28 :
29 : if (!parser->Read(&module_info_base_) || !parser->ReadString(&module_name_) ||
30 E : !parser->ReadString(&object_name_) || !parser->AlignTo(4)) {
31 i : LOG(ERROR) << "Unable to read module information.";
32 i : return false;
33 : }
34 :
35 E : return true;
36 E : }
37 :
38 : // Reads the header from the Dbi stream of the PDB.
39 E : bool DbiStream::ReadDbiHeaders(pdb::PdbStream* stream) {
40 E : DCHECK(stream != NULL);
41 :
42 E : if (!stream->ReadBytesAt(0, sizeof(header_), &header_)) {
43 i : LOG(ERROR) << "Unable to read the header of the Dbi Stream.";
44 i : return false;
45 : }
46 :
47 E : size_t dbg_header_offs = pdb::GetDbiDbgHeaderOffset(header_);
48 E : if (!stream->ReadBytesAt(dbg_header_offs, sizeof(dbg_header_),
49 : &dbg_header_)) {
50 i : LOG(ERROR) << "Unable to read Dbg header of the Dbi Stream.";
51 i : return false;
52 : }
53 :
54 E : return true;
55 E : }
56 :
57 : // Reads the module info substream from the Dbi stream of the PDB.
58 E : bool DbiStream::ReadDbiModuleInfo(pdb::PdbStream* stream) {
59 E : DCHECK(stream != NULL);
60 :
61 : // This substream starts just after the Dbi header in the Dbi stream.
62 E : size_t module_start = sizeof(pdb::DbiHeader);
63 :
64 E : pdb::PdbStreamReaderWithPosition reader(module_start, header_.gp_modi_size,
65 : stream);
66 E : common::BinaryStreamParser parser(&reader);
67 : // Read each module info block.
68 E : while (!reader.AtEnd()) {
69 E : DbiModuleInfo module_info;
70 E : if (!module_info.Read(&parser))
71 i : return false;
72 E : modules_.push_back(module_info);
73 E : }
74 :
75 E : if (!reader.AtEnd()) {
76 i : LOG(ERROR) << "Module info substream of the Dbi stream is not valid.";
77 i : return false;
78 : }
79 :
80 E : return true;
81 E : }
82 :
83 : // Reads the section contribs substream from the Dbi stream of the PDB.
84 E : bool DbiStream::ReadDbiSectionContribs(pdb::PdbStream* stream) {
85 E : DCHECK(stream != NULL);
86 :
87 E : size_t section_contribs_start = sizeof(pdb::DbiHeader) + header_.gp_modi_size;
88 E : pdb::PdbStreamReaderWithPosition reader(
89 : section_contribs_start, header_.section_contribution_size, stream);
90 E : common::BinaryStreamParser parser(&reader);
91 E : uint32_t signature = 0;
92 E : if (!parser.Read(&signature)) {
93 i : LOG(ERROR) << "Unable to seek to section contributions substream.";
94 i : return false;
95 : }
96 :
97 E : if (signature != kPdbDbiSectionContribsSignature) {
98 E : LOG(ERROR) << "Unexpected signature for the section contribs substream. "
99 : << "Expected "
100 : << base::StringPrintf("0x%08X", kPdbDbiSectionContribsSignature)
101 : << ", read "
102 : << base::StringPrintf("0x%08X", signature) << ".";
103 E : return false;
104 : }
105 :
106 : size_t section_contrib_count =
107 E : (header_.section_contribution_size - reader.Position()) /
108 : sizeof(DbiSectionContrib);
109 :
110 E : if (!parser.ReadMultiple(section_contrib_count, §ion_contribs_)) {
111 i : LOG(ERROR) << "Unable to read section contributions.";
112 i : return false;
113 : }
114 :
115 E : if (!reader.AtEnd()) {
116 i : LOG(ERROR) << "Section contribs substream of the Dbi stream is not valid.";
117 i : return false;
118 : }
119 :
120 E : return true;
121 E : }
122 :
123 : // Reads the section map substream from the Dbi stream of the PDB.
124 E : bool DbiStream::ReadDbiSectionMap(pdb::PdbStream* stream) {
125 E : DCHECK(stream != NULL);
126 :
127 : size_t section_map_start = sizeof(pdb::DbiHeader)
128 : + header_.gp_modi_size
129 E : + header_.section_contribution_size;
130 E : pdb::PdbStreamReaderWithPosition reader(section_map_start,
131 : header_.section_map_size, stream);
132 E : common::BinaryStreamParser parser(&reader);
133 E : uint16_t number_of_sections = 0;
134 E : if (!parser.Read(&number_of_sections)) {
135 i : LOG(ERROR) << "Unable to read the length of the section map in the Dbi "
136 : << "stream.";
137 i : return false;
138 : }
139 :
140 : // The number of section appears to be present twice. This check ensure that
141 : // the value are always equals. If it's not it'll give us a sample to
142 : // understand what's this value.
143 E : uint16_t number_of_sections_copy = 0;
144 E : if (!parser.Read(&number_of_sections_copy)) {
145 i : LOG(ERROR) << "Unable to read the copy of the length of the section map in "
146 : << "the Dbi stream.";
147 i : return false;
148 : }
149 :
150 E : if (number_of_sections != number_of_sections_copy) {
151 i : LOG(ERROR) << "Mismatched values for the length of the section map ("
152 : << number_of_sections << " vs "<< number_of_sections_copy
153 : << ").";
154 i : return false;
155 : }
156 :
157 E : while (!reader.AtEnd()) {
158 : DbiSectionMapItem section_map_item;
159 E : if (!parser.Read(§ion_map_item)) {
160 i : LOG(ERROR) << "Failed to read a section map item";
161 i : return false;
162 : }
163 :
164 E : section_map_[section_map_item.section_number] = section_map_item;
165 E : }
166 :
167 E : if (section_map_.size() != number_of_sections) {
168 i : LOG(ERROR) << "Unexpected number of sections in the section map (expected "
169 : << number_of_sections << ", read " << section_map_.size()
170 : << ").";
171 i : return false;
172 : }
173 :
174 E : if (!reader.AtEnd()) {
175 i : LOG(ERROR) << "Section map substream of the Dbi stream is not valid.";
176 i : return false;
177 : }
178 :
179 E : return true;
180 E : }
181 :
182 : // Reads the file info substream from the Dbi stream of the PDB.
183 : // The structure of this substream is:
184 : // Header | File-blocks table | Offset table | Name table.
185 : // - The header contains the number of entries in the File-blocks table (16
186 : // bits) followed by the number of entries in the offset table (16 bits). You
187 : // have to multiply each size by 4 to obtain the size in bytes.
188 : // - The file-blocks table is divided in 2 parts. The first part contains the
189 : // starting index of each block (16 bits) and the second one contains
190 : // the length of these blocks. These value refer to the offset table. It
191 : // seems that there's always a last block with a starting value equal to the
192 : // length of the offset table and a length of 0 at the end of this table.
193 : // - The offset table contains offsets to the beginning of file names in the
194 : // name table. These offsets are relative to the beginning of the name table.
195 : // - The name table contain all the filenames used in this substream.
196 E : bool DbiStream::ReadDbiFileInfo(pdb::PdbStream* stream) {
197 E : DCHECK(stream != NULL);
198 :
199 : size_t file_info_start = sizeof(pdb::DbiHeader)
200 : + header_.gp_modi_size
201 : + header_.section_contribution_size
202 E : + header_.section_map_size;
203 E : pdb::PdbStreamReaderWithPosition reader(file_info_start,
204 : header_.file_info_size, stream);
205 E : common::BinaryStreamParser parser(&reader);
206 :
207 E : uint16_t file_blocks_table_size = 0;
208 E : uint16_t offset_table_size = 0;
209 E : if (!parser.Read(&file_blocks_table_size) ||
210 : !parser.Read(&offset_table_size)) {
211 i : LOG(ERROR) << "Unable to read the header of the file info substream.";
212 i : return false;
213 : }
214 :
215 : // Calculate the starting address of the different sections of this substream.
216 E : size_t file_blocks_table_start = file_info_start + reader.Position();
217 : size_t offset_table_start =
218 E : file_blocks_table_start + file_blocks_table_size * sizeof(uint32_t);
219 :
220 E : if (!ReadDbiFileInfoBlocks(stream,
221 : file_blocks_table_size,
222 : file_blocks_table_start,
223 : offset_table_start)) {
224 i : return false;
225 : }
226 :
227 : size_t name_table_start =
228 E : offset_table_start + offset_table_size * sizeof(uint32_t);
229 E : size_t file_info_end = file_info_start + header_.file_info_size;
230 :
231 : // Read the name table in this substream.
232 E : if (!ReadDbiFileNameTable(stream,
233 : name_table_start,
234 : file_info_end)) {
235 i : return false;
236 : }
237 :
238 E : return true;
239 E : }
240 :
241 : bool DbiStream::ReadDbiFileInfoBlocks(pdb::PdbStream* stream,
242 : uint16_t file_blocks_table_size,
243 : size_t file_blocks_table_start,
244 E : size_t offset_table_start) {
245 E : file_info_.first.resize(file_blocks_table_size);
246 :
247 : // The block info data is composed of two parallel arrays in the stream.
248 : // The first array is an array of uint16_t block start positions, and the
249 : // second one is an array of uint16_t lengths.
250 : // Each {start, length} pair notes the location and length of variable-length
251 : // array of uint16_t identifiers. These identifiers in turn then point to
252 : // locations in the file name table, and so identify a file name.
253 : // This is done because there's a lot of repetition in the file name data, as
254 : // every compilation unit ends up including a subset of the same files.
255 :
256 : // Create a reader over the start position array.
257 E : pdb::PdbStreamReaderWithPosition start_reader(
258 : file_blocks_table_start, file_blocks_table_size * sizeof(uint16_t),
259 : stream);
260 E : common::BinaryStreamParser start_parser(&start_reader);
261 :
262 : // Create a reader over the length array.
263 : size_t file_block_length_start =
264 E : file_blocks_table_start + file_blocks_table_size * sizeof(uint16_t);
265 E : pdb::PdbStreamReaderWithPosition length_reader(
266 : file_block_length_start, file_blocks_table_size * sizeof(uint16_t),
267 : stream);
268 E : common::BinaryStreamParser length_parser(&length_reader);
269 :
270 : // Read information about each block of the file info substream.
271 E : for (int i = 0; i < file_blocks_table_size; ++i) {
272 E : uint16_t block_start = 0;
273 E : uint16_t block_length = 0;
274 E : if (!start_parser.Read(&block_start) ||
275 : !length_parser.Read(&block_length)) {
276 i : LOG(ERROR) << "Unable to read the file info substream.";
277 i : return false;
278 : }
279 :
280 E : pdb::PdbStreamReaderWithPosition block_reader(
281 : offset_table_start + block_start * sizeof(uint32_t),
282 : block_length * sizeof(uint32_t), stream);
283 E : common::BinaryStreamParser block_parser(&block_reader);
284 : // Fill the file list.
285 E : if (!block_parser.ReadMultiple(block_length, &file_info_.first.at(i))) {
286 i : LOG(ERROR) << "Unable to read the file info substream.";
287 i : return false;
288 : }
289 E : }
290 :
291 E : return true;
292 E : }
293 :
294 : // It would be useful to move this code to a more generic function if we see
295 : // this structure somewhere else in the PDB.
296 : bool DbiStream::ReadDbiFileNameTable(pdb::PdbStream* stream,
297 : size_t name_table_start,
298 E : size_t name_table_end) {
299 E : DCHECK_LE(name_table_start, name_table_end);
300 E : pdb::PdbStreamReaderWithPosition reader(
301 : name_table_start, name_table_end - name_table_start, stream);
302 E : common::BinaryStreamParser parser(&reader);
303 :
304 E : while (!reader.AtEnd()) {
305 E : std::string filename;
306 E : size_t pos = reader.Position();
307 E : if (!parser.ReadString(&filename)) {
308 i : LOG(ERROR) << "Unable to read the name table of the file info substream.";
309 i : return false;
310 : }
311 E : file_info_.second.insert(std::make_pair(pos, filename));
312 E : }
313 :
314 E : if (!reader.AtEnd()) {
315 i : LOG(ERROR) << "File info substream of the Dbi stream is not valid.";
316 i : return false;
317 : }
318 :
319 E : return true;
320 E : }
321 :
322 E : bool DbiStream::ReadDbiECInfo(pdb::PdbStream* stream) {
323 : // It's important to note that the ec_info_size field appears after the
324 : // dbg_header_size field in the header of this stream but the EC info
325 : // substream is located before the DbgHeader substream.
326 : size_t ec_info_start = sizeof(pdb::DbiHeader)
327 : + header_.gp_modi_size
328 : + header_.section_contribution_size
329 : + header_.section_map_size
330 : + header_.file_info_size
331 E : + header_.ts_map_size;
332 E : size_t ec_info_end = ec_info_start + header_.ec_info_size;
333 :
334 E : return ReadStringTable(stream,
335 : "EC info",
336 : ec_info_start,
337 : ec_info_end,
338 : &ec_info_vector_);
339 E : }
340 :
341 E : bool DbiStream::Read(pdb::PdbStream* stream ) {
342 E : DCHECK(stream != NULL);
343 :
344 E : if (!ReadDbiHeaders(stream))
345 i : return false;
346 :
347 E : if (!ReadDbiModuleInfo(stream))
348 i : return false;
349 :
350 E : if (!ReadDbiSectionContribs(stream))
351 E : return false;
352 :
353 E : if (!ReadDbiSectionMap(stream))
354 i : return false;
355 :
356 E : if (!ReadDbiFileInfo(stream))
357 i : return false;
358 :
359 E : if (header_.ts_map_size != 0) {
360 i : LOG(ERROR) << "The length of the TS map is expected to be null but we've "
361 : << "read a length of " << header_.ts_map_size << ".";
362 i : return false;
363 : }
364 :
365 E : if (!ReadDbiECInfo(stream))
366 i : return false;
367 :
368 E : return true;
369 E : }
370 :
371 : } // namespace pdb
|