| // Copyright 2012 Google Inc. |
| // |
| // 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/experimental/pdb_dumper/pdb_module_info_stream_dumper.h" |
| |
| #include "syzygy/common/align.h" |
| #include "syzygy/experimental/pdb_dumper/pdb_dump_util.h" |
| #include "syzygy/experimental/pdb_dumper/pdb_symbol_record_dumper.h" |
| #include "syzygy/pdb/pdb_dbi_stream.h" |
| #include "syzygy/pdb/pdb_stream.h" |
| #include "syzygy/pdb/pdb_stream_reader.h" |
| #include "syzygy/pdb/pdb_symbol_record.h" |
| #include "syzygy/pe/cvinfo_ext.h" |
| |
| namespace pdb { |
| |
| namespace cci = Microsoft_Cci_Pdb; |
| |
| namespace { |
| |
| // Read the file checksum substream from a module info stream. The filenames |
| // used by this module will be stored in a map. |
| // @param file_names The map containing the filenames listed in the name stream |
| // of the PDB. |
| // @param stream The stream containing the checksum substream. |
| // @param length The length of the checksum substream. |
| // @param module_files The map where the filenames should be saved. |
| // @returns true on success, false on error. |
| bool ReadFileChecksums(const OffsetStringMap& file_names, |
| pdb::PdbStreamReaderWithPosition* reader, |
| size_t length, |
| OffsetStringMap* module_files) { |
| DCHECK(reader != NULL); |
| DCHECK(module_files != NULL); |
| size_t base = reader->Position(); |
| size_t end = base + length; |
| common::BinaryStreamParser parser(reader); |
| while (reader->Position() < end) { |
| cci::CV_FileCheckSum checksum = {}; |
| |
| size_t pos = reader->Position() - base; |
| if (!parser.Read(&checksum)) { |
| LOG(ERROR) << "Unable to read file checksum."; |
| return false; |
| } |
| OffsetStringMap::const_iterator it(file_names.find(checksum.name)); |
| if (it == file_names.end()) { |
| LOG(ERROR) << "There is a checksum reference for a file that is not in " |
| << "the list of files used by this module."; |
| return false; |
| } |
| module_files->insert(std::make_pair(pos, it->second)); |
| |
| // Skip the checksum and align. |
| if (!reader->Consume(checksum.len) || !parser.AlignTo(4)) { |
| LOG(ERROR) << "Unable to seek past file checksum."; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| // Dump the line information from a line information substream. |
| // @param file_names The map containing the filenames used by this module. |
| // @param out The output where the data should be dumped. |
| // @param stream The stream containing the line information. |
| // @param length The length of the line information substream. |
| // @param indent_level The indentation level to use. |
| // @returns true on success, false on error. |
| bool DumpLineInfo(const OffsetStringMap& file_names, |
| FILE* out, |
| pdb::PdbStreamReaderWithPosition* reader, |
| size_t length, |
| uint8_t indent_level) { |
| DCHECK(reader != NULL); |
| size_t base = reader->Position(); |
| // Read the header. |
| |
| common::BinaryStreamParser parser(reader); |
| cci::CV_LineSection line_section = {}; |
| if (!parser.Read(&line_section)) { |
| LOG(ERROR) << "Unable to read line section."; |
| return false; |
| } |
| |
| size_t end = base + length; |
| while (reader->Position() < end) { |
| cci::CV_SourceFile source_file = {}; |
| if (!parser.Read(&source_file)) { |
| LOG(ERROR) << "Unable to read source info."; |
| return false; |
| } |
| |
| std::vector<cci::CV_Line> lines(source_file.count); |
| if (lines.size() && !parser.ReadMultiple(lines.size(), &lines)) { |
| LOG(ERROR) << "Unable to read line records."; |
| return false; |
| } |
| |
| std::vector<cci::CV_Column> columns(source_file.count); |
| if ((line_section.flags & cci::CV_LINES_HAVE_COLUMNS) != 0 && |
| !parser.ReadMultiple(columns.size(), &columns)) { |
| LOG(ERROR) << "Unable to read column records."; |
| return false; |
| } |
| |
| OffsetStringMap::const_iterator it(file_names.find(source_file.index)); |
| if (it == file_names.end()) { |
| LOG(ERROR) << "Unable to find an index in the list of filenames used by " |
| << "this module."; |
| return false; |
| } |
| DumpIndentedText(out, |
| indent_level, |
| "Section %d, offset 0x%04X.\n", |
| line_section.sec, |
| line_section.off); |
| for (size_t i = 0; i < lines.size(); ++i) { |
| if (columns[i].offColumnStart != 0) { |
| DumpIndentedText(out, indent_level, |
| "%s(%d, %d): line and column at %d:%04X.\n", |
| it->second.c_str(), |
| lines[i].flags & cci::linenumStart, |
| columns[i].offColumnStart, |
| line_section.sec, |
| line_section.off + lines[i].offset); |
| } else { |
| DumpIndentedText(out, |
| indent_level, |
| "%s(%d): line at %d:%04X.\n", |
| it->second.c_str(), |
| lines[i].flags & cci::linenumStart, |
| line_section.sec, |
| line_section.off + lines[i].offset); |
| } |
| } |
| } |
| return true; |
| } |
| |
| // Dump the line information substream from a module info stream. |
| // @param name_map The map containing the filenames listed in the name stream of |
| // the PDB. |
| // @param out The output where the data should be dumped. |
| // @param stream The stream containing the line information. |
| // @param start The position where the line information start in the stream. |
| // @param lines_bytes The length of the line information substream. |
| // @param indent_level The level of indentation to use. |
| void DumpLines(const OffsetStringMap& name_map, |
| FILE* out, |
| pdb::PdbStream* stream, |
| size_t start, |
| size_t lines_bytes, |
| uint8_t indent_level) { |
| DCHECK(stream != NULL); |
| if (lines_bytes == 0) |
| return; |
| |
| // The line information is arranged as a back-to-back run of {type, len} |
| // prefixed chunks. The types are DEBUG_S_FILECHKSMS and DEBUG_S_LINES. |
| // The first of these provides file names and a file content checksum, where |
| // each record is identified by its index into its chunk (excluding type |
| // and len). |
| pdb::PdbStreamReaderWithPosition reader(start, lines_bytes, stream); |
| common::BinaryStreamParser parser(&reader); |
| OffsetStringMap file_names; |
| while (!reader.AtEnd()) { |
| uint32_t line_info_type = 0; |
| uint32_t length = 0; |
| if (!parser.Read(&line_info_type) || !parser.Read(&length)) { |
| LOG(ERROR) << "Unable to read line info signature."; |
| return; |
| } |
| |
| switch (line_info_type) { |
| case cci::DEBUG_S_FILECHKSMS: |
| if (!ReadFileChecksums(name_map, &reader, length, &file_names)) |
| return; |
| break; |
| case cci::DEBUG_S_LINES: |
| if (!DumpLineInfo(file_names, out, &reader, length, indent_level)) |
| return; |
| break; |
| default: |
| // Skip over for now. |
| DumpIndentedText(out, indent_level, "Unsupported line info type.\n"); |
| DumpIndentedText(out, indent_level + 1, "Type: %d\n", line_info_type); |
| DumpIndentedText(out, indent_level + 1, "Length: %d\n", length); |
| if (!reader.Consume(length)) { |
| LOG(ERROR) << "Failed to skip over unsupported line info type."; |
| return; |
| } |
| break; |
| } |
| } |
| } |
| |
| } // namespace |
| |
| void DumpModuleInfoStream(const DbiModuleInfo& module_info, |
| const OffsetStringMap& name_table, |
| FILE* out, |
| PdbStream* stream) { |
| DCHECK(stream != NULL); |
| uint8_t indent_level = 1; |
| DumpIndentedText(out, |
| indent_level, |
| "Module name: %s\n", |
| module_info.module_name().c_str()); |
| DumpIndentedText(out, |
| indent_level, |
| "Object name: %s\n", |
| module_info.object_name().c_str()); |
| pdb::PdbStreamReaderWithPosition reader(stream); |
| uint32_t type = 0; |
| if (!reader.Read(sizeof(type), &type) || type != cci::C13) { |
| LOG(ERROR) << "Unexpected symbol stream type " << type << "."; |
| return; |
| } |
| SymbolRecordVector symbols; |
| const DbiModuleInfoBase& module_info_base = module_info.module_info_base(); |
| ReadSymbolRecord(stream, sizeof(type), |
| module_info_base.symbol_bytes - sizeof(type), &symbols); |
| DumpIndentedText(out, indent_level + 1, "Symbol records:\n"); |
| DumpSymbolRecords(out, stream, symbols, indent_level + 2); |
| DumpIndentedText(out, indent_level + 1, "Lines:\n"); |
| DumpLines(name_table, |
| out, |
| stream, |
| module_info.module_info_base().symbol_bytes, |
| module_info.module_info_base().lines_bytes, |
| indent_level + 2); |
| } |
| |
| } // namespace pdb |