| // Copyright 2012 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "syzygy/pdb/pdb_dbi_stream.h" |
| |
| #include "base/strings/stringprintf.h" |
| #include "syzygy/common/align.h" |
| #include "syzygy/pdb/pdb_constants.h" |
| #include "syzygy/pdb/pdb_stream.h" |
| #include "syzygy/pdb/pdb_stream_reader.h" |
| #include "syzygy/pdb/pdb_util.h" |
| |
| namespace pdb { |
| |
| bool DbiModuleInfo::Read(common::BinaryStreamParser* parser) { |
| DCHECK(parser != nullptr); |
| |
| if (!parser->Read(&module_info_base_) || !parser->ReadString(&module_name_) || |
| !parser->ReadString(&object_name_) || !parser->AlignTo(4)) { |
| LOG(ERROR) << "Unable to read module information."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Reads the header from the Dbi stream of the PDB. |
| bool DbiStream::ReadDbiHeaders(pdb::PdbStream* stream) { |
| DCHECK(stream != NULL); |
| |
| if (!stream->ReadBytesAt(0, sizeof(header_), &header_)) { |
| LOG(ERROR) << "Unable to read the header of the Dbi Stream."; |
| return false; |
| } |
| |
| size_t dbg_header_offs = pdb::GetDbiDbgHeaderOffset(header_); |
| if (!stream->ReadBytesAt(dbg_header_offs, sizeof(dbg_header_), |
| &dbg_header_)) { |
| LOG(ERROR) << "Unable to read Dbg header of the Dbi Stream."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Reads the module info substream from the Dbi stream of the PDB. |
| bool DbiStream::ReadDbiModuleInfo(pdb::PdbStream* stream) { |
| DCHECK(stream != NULL); |
| |
| // This substream starts just after the Dbi header in the Dbi stream. |
| size_t module_start = sizeof(pdb::DbiHeader); |
| |
| pdb::PdbStreamReaderWithPosition reader(module_start, header_.gp_modi_size, |
| stream); |
| common::BinaryStreamParser parser(&reader); |
| // Read each module info block. |
| while (!reader.AtEnd()) { |
| DbiModuleInfo module_info; |
| if (!module_info.Read(&parser)) |
| return false; |
| modules_.push_back(module_info); |
| } |
| |
| if (!reader.AtEnd()) { |
| LOG(ERROR) << "Module info substream of the Dbi stream is not valid."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Reads the section contribs substream from the Dbi stream of the PDB. |
| bool DbiStream::ReadDbiSectionContribs(pdb::PdbStream* stream) { |
| DCHECK(stream != NULL); |
| |
| size_t section_contribs_start = sizeof(pdb::DbiHeader) + header_.gp_modi_size; |
| pdb::PdbStreamReaderWithPosition reader( |
| section_contribs_start, header_.section_contribution_size, stream); |
| common::BinaryStreamParser parser(&reader); |
| uint32_t signature = 0; |
| if (!parser.Read(&signature)) { |
| LOG(ERROR) << "Unable to seek to section contributions substream."; |
| return false; |
| } |
| |
| if (signature != kPdbDbiSectionContribsSignature) { |
| LOG(ERROR) << "Unexpected signature for the section contribs substream. " |
| << "Expected " |
| << base::StringPrintf("0x%08X", kPdbDbiSectionContribsSignature) |
| << ", read " |
| << base::StringPrintf("0x%08X", signature) << "."; |
| return false; |
| } |
| |
| size_t section_contrib_count = |
| (header_.section_contribution_size - reader.Position()) / |
| sizeof(DbiSectionContrib); |
| |
| if (!parser.ReadMultiple(section_contrib_count, §ion_contribs_)) { |
| LOG(ERROR) << "Unable to read section contributions."; |
| return false; |
| } |
| |
| if (!reader.AtEnd()) { |
| LOG(ERROR) << "Section contribs substream of the Dbi stream is not valid."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Reads the section map substream from the Dbi stream of the PDB. |
| bool DbiStream::ReadDbiSectionMap(pdb::PdbStream* stream) { |
| DCHECK(stream != NULL); |
| |
| size_t section_map_start = sizeof(pdb::DbiHeader) |
| + header_.gp_modi_size |
| + header_.section_contribution_size; |
| pdb::PdbStreamReaderWithPosition reader(section_map_start, |
| header_.section_map_size, stream); |
| common::BinaryStreamParser parser(&reader); |
| uint16_t number_of_sections = 0; |
| if (!parser.Read(&number_of_sections)) { |
| LOG(ERROR) << "Unable to read the length of the section map in the Dbi " |
| << "stream."; |
| return false; |
| } |
| |
| // The number of section appears to be present twice. This check ensure that |
| // the value are always equals. If it's not it'll give us a sample to |
| // understand what's this value. |
| uint16_t number_of_sections_copy = 0; |
| if (!parser.Read(&number_of_sections_copy)) { |
| LOG(ERROR) << "Unable to read the copy of the length of the section map in " |
| << "the Dbi stream."; |
| return false; |
| } |
| |
| if (number_of_sections != number_of_sections_copy) { |
| LOG(ERROR) << "Mismatched values for the length of the section map (" |
| << number_of_sections << " vs "<< number_of_sections_copy |
| << ")."; |
| return false; |
| } |
| |
| while (!reader.AtEnd()) { |
| DbiSectionMapItem section_map_item; |
| if (!parser.Read(§ion_map_item)) { |
| LOG(ERROR) << "Failed to read a section map item"; |
| return false; |
| } |
| |
| section_map_[section_map_item.section_number] = section_map_item; |
| } |
| |
| if (section_map_.size() != number_of_sections) { |
| LOG(ERROR) << "Unexpected number of sections in the section map (expected " |
| << number_of_sections << ", read " << section_map_.size() |
| << ")."; |
| return false; |
| } |
| |
| if (!reader.AtEnd()) { |
| LOG(ERROR) << "Section map substream of the Dbi stream is not valid."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Reads the file info substream from the Dbi stream of the PDB. |
| // The structure of this substream is: |
| // Header | File-blocks table | Offset table | Name table. |
| // - The header contains the number of entries in the File-blocks table (16 |
| // bits) followed by the number of entries in the offset table (16 bits). You |
| // have to multiply each size by 4 to obtain the size in bytes. |
| // - The file-blocks table is divided in 2 parts. The first part contains the |
| // starting index of each block (16 bits) and the second one contains |
| // the length of these blocks. These value refer to the offset table. It |
| // seems that there's always a last block with a starting value equal to the |
| // length of the offset table and a length of 0 at the end of this table. |
| // - The offset table contains offsets to the beginning of file names in the |
| // name table. These offsets are relative to the beginning of the name table. |
| // - The name table contain all the filenames used in this substream. |
| bool DbiStream::ReadDbiFileInfo(pdb::PdbStream* stream) { |
| DCHECK(stream != NULL); |
| |
| size_t file_info_start = sizeof(pdb::DbiHeader) |
| + header_.gp_modi_size |
| + header_.section_contribution_size |
| + header_.section_map_size; |
| pdb::PdbStreamReaderWithPosition reader(file_info_start, |
| header_.file_info_size, stream); |
| common::BinaryStreamParser parser(&reader); |
| |
| uint16_t file_blocks_table_size = 0; |
| uint16_t offset_table_size = 0; |
| if (!parser.Read(&file_blocks_table_size) || |
| !parser.Read(&offset_table_size)) { |
| LOG(ERROR) << "Unable to read the header of the file info substream."; |
| return false; |
| } |
| |
| // Calculate the starting address of the different sections of this substream. |
| size_t file_blocks_table_start = file_info_start + reader.Position(); |
| size_t offset_table_start = |
| file_blocks_table_start + file_blocks_table_size * sizeof(uint32_t); |
| |
| if (!ReadDbiFileInfoBlocks(stream, |
| file_blocks_table_size, |
| file_blocks_table_start, |
| offset_table_start)) { |
| return false; |
| } |
| |
| size_t name_table_start = |
| offset_table_start + offset_table_size * sizeof(uint32_t); |
| size_t file_info_end = file_info_start + header_.file_info_size; |
| |
| // Read the name table in this substream. |
| if (!ReadDbiFileNameTable(stream, |
| name_table_start, |
| file_info_end)) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool DbiStream::ReadDbiFileInfoBlocks(pdb::PdbStream* stream, |
| uint16_t file_blocks_table_size, |
| size_t file_blocks_table_start, |
| size_t offset_table_start) { |
| file_info_.first.resize(file_blocks_table_size); |
| |
| // The block info data is composed of two parallel arrays in the stream. |
| // The first array is an array of uint16_t block start positions, and the |
| // second one is an array of uint16_t lengths. |
| // Each {start, length} pair notes the location and length of variable-length |
| // array of uint16_t identifiers. These identifiers in turn then point to |
| // locations in the file name table, and so identify a file name. |
| // This is done because there's a lot of repetition in the file name data, as |
| // every compilation unit ends up including a subset of the same files. |
| |
| // Create a reader over the start position array. |
| pdb::PdbStreamReaderWithPosition start_reader( |
| file_blocks_table_start, file_blocks_table_size * sizeof(uint16_t), |
| stream); |
| common::BinaryStreamParser start_parser(&start_reader); |
| |
| // Create a reader over the length array. |
| size_t file_block_length_start = |
| file_blocks_table_start + file_blocks_table_size * sizeof(uint16_t); |
| pdb::PdbStreamReaderWithPosition length_reader( |
| file_block_length_start, file_blocks_table_size * sizeof(uint16_t), |
| stream); |
| common::BinaryStreamParser length_parser(&length_reader); |
| |
| // Read information about each block of the file info substream. |
| for (int i = 0; i < file_blocks_table_size; ++i) { |
| uint16_t block_start = 0; |
| uint16_t block_length = 0; |
| if (!start_parser.Read(&block_start) || |
| !length_parser.Read(&block_length)) { |
| LOG(ERROR) << "Unable to read the file info substream."; |
| return false; |
| } |
| |
| pdb::PdbStreamReaderWithPosition block_reader( |
| offset_table_start + block_start * sizeof(uint32_t), |
| block_length * sizeof(uint32_t), stream); |
| common::BinaryStreamParser block_parser(&block_reader); |
| // Fill the file list. |
| if (!block_parser.ReadMultiple(block_length, &file_info_.first.at(i))) { |
| LOG(ERROR) << "Unable to read the file info substream."; |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| // It would be useful to move this code to a more generic function if we see |
| // this structure somewhere else in the PDB. |
| bool DbiStream::ReadDbiFileNameTable(pdb::PdbStream* stream, |
| size_t name_table_start, |
| size_t name_table_end) { |
| DCHECK_LE(name_table_start, name_table_end); |
| pdb::PdbStreamReaderWithPosition reader( |
| name_table_start, name_table_end - name_table_start, stream); |
| common::BinaryStreamParser parser(&reader); |
| |
| while (!reader.AtEnd()) { |
| std::string filename; |
| size_t pos = reader.Position(); |
| if (!parser.ReadString(&filename)) { |
| LOG(ERROR) << "Unable to read the name table of the file info substream."; |
| return false; |
| } |
| file_info_.second.emplace(static_cast<uint32_t>(pos), filename); |
| } |
| |
| if (!reader.AtEnd()) { |
| LOG(ERROR) << "File info substream of the Dbi stream is not valid."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool DbiStream::ReadDbiECInfo(pdb::PdbStream* stream) { |
| // It's important to note that the ec_info_size field appears after the |
| // dbg_header_size field in the header of this stream but the EC info |
| // substream is located before the DbgHeader substream. |
| size_t ec_info_start = sizeof(pdb::DbiHeader) |
| + header_.gp_modi_size |
| + header_.section_contribution_size |
| + header_.section_map_size |
| + header_.file_info_size |
| + header_.ts_map_size; |
| size_t ec_info_end = ec_info_start + header_.ec_info_size; |
| |
| return ReadStringTable(stream, |
| "EC info", |
| ec_info_start, |
| ec_info_end, |
| &ec_info_vector_); |
| } |
| |
| bool DbiStream::Read(pdb::PdbStream* stream ) { |
| DCHECK(stream != NULL); |
| |
| if (!ReadDbiHeaders(stream)) |
| return false; |
| |
| if (!ReadDbiModuleInfo(stream)) |
| return false; |
| |
| if (!ReadDbiSectionContribs(stream)) |
| return false; |
| |
| if (!ReadDbiSectionMap(stream)) |
| return false; |
| |
| if (!ReadDbiFileInfo(stream)) |
| return false; |
| |
| if (header_.ts_map_size != 0) { |
| LOG(ERROR) << "The length of the TS map is expected to be null but we've " |
| << "read a length of " << header_.ts_map_size << "."; |
| return false; |
| } |
| |
| if (!ReadDbiECInfo(stream)) |
| return false; |
| |
| return true; |
| } |
| |
| } // namespace pdb |