blob: 1758cdc53766868dfe01acd0c21c405b29a944b0 [file] [log] [blame]
// 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, &section_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(&section_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