1216 lines
44 KiB
C++

//===-- LVCodeViewReader.cpp ----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This implements the LVCodeViewReader class.
//
//===----------------------------------------------------------------------===//
#include "llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h"
#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h"
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h"
#include "llvm/DebugInfo/LogicalView/Core/LVLine.h"
#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"
#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
#include "llvm/DebugInfo/PDB/Native/LinePrinter.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/DebugInfo/PDB/Native/RawConstants.h"
#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FormatAdapters.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/WithColor.h"
using namespace llvm;
using namespace llvm::codeview;
using namespace llvm::logicalview;
using namespace llvm::msf;
using namespace llvm::object;
using namespace llvm::pdb;
#define DEBUG_TYPE "CodeViewReader"
StringRef LVCodeViewReader::getSymbolKindName(SymbolKind Kind) {
switch (Kind) {
#define SYMBOL_RECORD(EnumName, EnumVal, Name) \
case EnumName: \
return #EnumName;
#include "llvm/DebugInfo/CodeView/CodeViewSymbols.def"
default:
return "UnknownSym";
}
llvm_unreachable("Unknown SymbolKind::Kind");
}
std::string LVCodeViewReader::formatRegisterId(RegisterId Register,
CPUType CPU) {
#define RETURN_CASE(Enum, X, Ret) \
case Enum::X: \
return Ret;
if (CPU == CPUType::ARMNT) {
switch (Register) {
#define CV_REGISTERS_ARM
#define CV_REGISTER(name, val) RETURN_CASE(RegisterId, name, #name)
#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def"
#undef CV_REGISTER
#undef CV_REGISTERS_ARM
default:
break;
}
} else if (CPU == CPUType::ARM64) {
switch (Register) {
#define CV_REGISTERS_ARM64
#define CV_REGISTER(name, val) RETURN_CASE(RegisterId, name, #name)
#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def"
#undef CV_REGISTER
#undef CV_REGISTERS_ARM64
default:
break;
}
} else {
switch (Register) {
#define CV_REGISTERS_X86
#define CV_REGISTER(name, val) RETURN_CASE(RegisterId, name, #name)
#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def"
#undef CV_REGISTER
#undef CV_REGISTERS_X86
default:
break;
}
}
return "formatUnknownEnum(Id)";
}
void LVCodeViewReader::printRelocatedField(StringRef Label,
const coff_section *CoffSection,
uint32_t RelocOffset,
uint32_t Offset,
StringRef *RelocSym) {
StringRef SymStorage;
StringRef &Symbol = RelocSym ? *RelocSym : SymStorage;
if (!resolveSymbolName(CoffSection, RelocOffset, Symbol))
W.printSymbolOffset(Label, Symbol, Offset);
else
W.printHex(Label, RelocOffset);
}
void LVCodeViewReader::getLinkageName(const coff_section *CoffSection,
uint32_t RelocOffset, uint32_t Offset,
StringRef *RelocSym) {
StringRef SymStorage;
StringRef &Symbol = RelocSym ? *RelocSym : SymStorage;
if (resolveSymbolName(CoffSection, RelocOffset, Symbol))
Symbol = "";
}
Expected<StringRef>
LVCodeViewReader::getFileNameForFileOffset(uint32_t FileOffset,
const SymbolGroup *SG) {
if (SG) {
Expected<StringRef> Filename = SG->getNameFromChecksums(FileOffset);
if (!Filename) {
consumeError(Filename.takeError());
return StringRef("");
}
return *Filename;
}
// The file checksum subsection should precede all references to it.
if (!CVFileChecksumTable.valid() || !CVStringTable.valid())
return createStringError(object_error::parse_failed, getFileName());
VarStreamArray<FileChecksumEntry>::Iterator Iter =
CVFileChecksumTable.getArray().at(FileOffset);
// Check if the file checksum table offset is valid.
if (Iter == CVFileChecksumTable.end())
return createStringError(object_error::parse_failed, getFileName());
Expected<StringRef> NameOrErr = CVStringTable.getString(Iter->FileNameOffset);
if (!NameOrErr)
return createStringError(object_error::parse_failed, getFileName());
return *NameOrErr;
}
Error LVCodeViewReader::printFileNameForOffset(StringRef Label,
uint32_t FileOffset,
const SymbolGroup *SG) {
Expected<StringRef> NameOrErr = getFileNameForFileOffset(FileOffset, SG);
if (!NameOrErr)
return NameOrErr.takeError();
W.printHex(Label, *NameOrErr, FileOffset);
return Error::success();
}
void LVCodeViewReader::cacheRelocations() {
for (const SectionRef &Section : getObj().sections()) {
const coff_section *CoffSection = getObj().getCOFFSection(Section);
auto &RM = RelocMap[CoffSection];
for (const RelocationRef &Relocacion : Section.relocations())
RM.push_back(Relocacion);
// Sort relocations by address.
llvm::sort(RM, [](RelocationRef L, RelocationRef R) {
return L.getOffset() < R.getOffset();
});
}
}
// Given a section and an offset into this section the function returns the
// symbol used for the relocation at the offset.
Error LVCodeViewReader::resolveSymbol(const coff_section *CoffSection,
uint64_t Offset, SymbolRef &Sym) {
const auto &Relocations = RelocMap[CoffSection];
basic_symbol_iterator SymI = getObj().symbol_end();
for (const RelocationRef &Relocation : Relocations) {
uint64_t RelocationOffset = Relocation.getOffset();
if (RelocationOffset == Offset) {
SymI = Relocation.getSymbol();
break;
}
}
if (SymI == getObj().symbol_end())
return make_error<StringError>("Unknown Symbol", inconvertibleErrorCode());
Sym = *SymI;
return ErrorSuccess();
}
// Given a section and an offset into this section the function returns the
// name of the symbol used for the relocation at the offset.
Error LVCodeViewReader::resolveSymbolName(const coff_section *CoffSection,
uint64_t Offset, StringRef &Name) {
SymbolRef Symbol;
if (Error E = resolveSymbol(CoffSection, Offset, Symbol))
return E;
Expected<StringRef> NameOrErr = Symbol.getName();
if (!NameOrErr)
return NameOrErr.takeError();
Name = *NameOrErr;
return ErrorSuccess();
}
// CodeView and DWARF can have references to compiler generated elements,
// used for initialization. The MSVC includes in the PDBs, internal compile
// units, associated with the MS runtime support. We mark them as 'system'
// and they are printed only if the command line option 'internal=system'.
bool LVCodeViewReader::isSystemEntry(LVElement *Element, StringRef Name) const {
Name = Name.empty() ? Element->getName() : Name;
auto Find = [=](const char *String) -> bool { return Name.contains(String); };
auto Starts = [=](const char *Pattern) -> bool {
return Name.starts_with(Pattern);
};
auto CheckExclude = [&]() -> bool {
if (Starts("__") || Starts("_PMD") || Starts("_PMFN"))
return true;
if (Find("_s__"))
return true;
if (Find("_CatchableType") || Find("_TypeDescriptor"))
return true;
if (Find("Intermediate\\vctools"))
return true;
if (Find("$initializer$") || Find("dynamic initializer"))
return true;
if (Find("`vftable'") || Find("_GLOBAL__sub"))
return true;
return false;
};
bool Excluded = CheckExclude();
if (Excluded)
Element->setIsSystem();
return Excluded;
}
Error LVCodeViewReader::collectInlineeInfo(
DebugInlineeLinesSubsectionRef &Lines, const llvm::pdb::SymbolGroup *SG) {
for (const InlineeSourceLine &Line : Lines) {
TypeIndex TIInlinee = Line.Header->Inlinee;
uint32_t LineNumber = Line.Header->SourceLineNum;
uint32_t FileOffset = Line.Header->FileID;
LLVM_DEBUG({
DictScope S(W, "InlineeSourceLine");
LogicalVisitor.printTypeIndex("Inlinee", TIInlinee, StreamTPI);
if (Error Err = printFileNameForOffset("FileID", FileOffset, SG))
return Err;
W.printNumber("SourceLineNum", LineNumber);
if (Lines.hasExtraFiles()) {
W.printNumber("ExtraFileCount", Line.ExtraFiles.size());
ListScope ExtraFiles(W, "ExtraFiles");
for (const ulittle32_t &FID : Line.ExtraFiles)
if (Error Err = printFileNameForOffset("FileID", FID, SG))
return Err;
}
});
Expected<StringRef> NameOrErr = getFileNameForFileOffset(FileOffset, SG);
if (!NameOrErr)
return NameOrErr.takeError();
LogicalVisitor.addInlineeInfo(TIInlinee, LineNumber, *NameOrErr);
}
return Error::success();
}
Error LVCodeViewReader::traverseInlineeLines(StringRef Subsection) {
BinaryStreamReader SR(Subsection, llvm::endianness::little);
DebugInlineeLinesSubsectionRef Lines;
if (Error E = Lines.initialize(SR))
return createStringError(errorToErrorCode(std::move(E)), getFileName());
return collectInlineeInfo(Lines);
}
Error LVCodeViewReader::createLines(
const FixedStreamArray<LineNumberEntry> &LineNumbers, LVAddress Addendum,
uint32_t Segment, uint32_t Begin, uint32_t Size, uint32_t NameIndex,
const SymbolGroup *SG) {
LLVM_DEBUG({
uint32_t End = Begin + Size;
W.getOStream() << formatv("{0:x-4}:{1:x-8}-{2:x-8}\n", Segment, Begin, End);
});
for (const LineNumberEntry &Line : LineNumbers) {
if (Line.Offset >= Size)
return createStringError(object_error::parse_failed, getFileName());
LineInfo LI(Line.Flags);
LLVM_DEBUG({
W.getOStream() << formatv(
"{0} {1:x-8}\n", utostr(LI.getStartLine()),
fmt_align(Begin + Line.Offset, AlignStyle::Right, 8, '0'));
});
// The 'processLines()' function will move each created logical line
// to its enclosing logical scope, using the debug ranges information
// and they will be released when its scope parent is deleted.
LVLineDebug *LineDebug = createLineDebug();
CULines.push_back(LineDebug);
LVAddress Address = linearAddress(Segment, Begin + Line.Offset);
LineDebug->setAddress(Address + Addendum);
if (LI.isAlwaysStepInto())
LineDebug->setIsAlwaysStepInto();
else if (LI.isNeverStepInto())
LineDebug->setIsNeverStepInto();
else
LineDebug->setLineNumber(LI.getStartLine());
if (LI.isStatement())
LineDebug->setIsNewStatement();
Expected<StringRef> NameOrErr = getFileNameForFileOffset(NameIndex, SG);
if (!NameOrErr)
return NameOrErr.takeError();
LineDebug->setFilename(*NameOrErr);
}
return Error::success();
}
Error LVCodeViewReader::initializeFileAndStringTables(
BinaryStreamReader &Reader) {
while (Reader.bytesRemaining() > 0 &&
(!CVFileChecksumTable.valid() || !CVStringTable.valid())) {
// The section consists of a number of subsection in the following format:
// |SubSectionType|SubSectionSize|Contents...|
uint32_t SubType, SubSectionSize;
if (Error E = Reader.readInteger(SubType))
return createStringError(errorToErrorCode(std::move(E)), getFileName());
if (Error E = Reader.readInteger(SubSectionSize))
return createStringError(errorToErrorCode(std::move(E)), getFileName());
StringRef Contents;
if (Error E = Reader.readFixedString(Contents, SubSectionSize))
return createStringError(errorToErrorCode(std::move(E)), getFileName());
BinaryStreamRef ST(Contents, llvm::endianness::little);
switch (DebugSubsectionKind(SubType)) {
case DebugSubsectionKind::FileChecksums:
if (Error E = CVFileChecksumTable.initialize(ST))
return createStringError(errorToErrorCode(std::move(E)), getFileName());
break;
case DebugSubsectionKind::StringTable:
if (Error E = CVStringTable.initialize(ST))
return createStringError(errorToErrorCode(std::move(E)), getFileName());
break;
default:
break;
}
uint32_t PaddedSize = alignTo(SubSectionSize, 4);
if (Error E = Reader.skip(PaddedSize - SubSectionSize))
return createStringError(errorToErrorCode(std::move(E)), getFileName());
}
return Error::success();
}
Error LVCodeViewReader::loadTypeServer(TypeServer2Record &TS) {
LLVM_DEBUG({
W.printString("Guid", formatv("{0}", TS.getGuid()).str());
W.printNumber("Age", TS.getAge());
W.printString("Name", TS.getName());
});
SmallString<128> ServerName(TS.getName());
BuffOrErr = MemoryBuffer::getFile(ServerName);
if (BuffOrErr.getError()) {
// The server name does not exist. Try in the same directory as the
// input file.
ServerName = createAlternativePath(ServerName);
BuffOrErr = MemoryBuffer::getFile(ServerName);
if (BuffOrErr.getError()) {
// For the error message, use the original type server name.
return createStringError(errc::bad_file_descriptor,
"File '%s' does not exist.",
TS.getName().str().c_str());
}
}
MemBuffer = std::move(BuffOrErr.get());
// Check if the buffer corresponds to a PDB file.
assert(identify_magic((*MemBuffer).getBuffer()) == file_magic::pdb &&
"Invalid PDB file.");
if (Error Err = loadDataForPDB(PDB_ReaderType::Native, ServerName, Session))
return createStringError(errorToErrorCode(std::move(Err)), "%s",
ServerName.c_str());
PdbSession.reset(static_cast<NativeSession *>(Session.release()));
PDBFile &Pdb = PdbSession->getPDBFile();
// Just because a file with a matching name was found and it was an actual
// PDB file doesn't mean it matches. For it to match the InfoStream's GUID
// must match the GUID specified in the TypeServer2 record.
Expected<InfoStream &> expectedInfo = Pdb.getPDBInfoStream();
if (!expectedInfo || expectedInfo->getGuid() != TS.getGuid())
return createStringError(errc::invalid_argument, "signature_out_of_date");
// The reader needs to switch to a type server, to process the types from
// the server. We need to keep the original input source, as reading other
// sections will require the input associated with the loaded object file.
TypeServer = std::make_shared<InputFile>(&Pdb);
LogicalVisitor.setInput(TypeServer);
LazyRandomTypeCollection &Types = types();
LazyRandomTypeCollection &Ids = ids();
if (Error Err = traverseTypes(Pdb, Types, Ids))
return Err;
return Error::success();
}
Error LVCodeViewReader::loadPrecompiledObject(PrecompRecord &Precomp,
CVTypeArray &CVTypesObj) {
LLVM_DEBUG({
W.printHex("Count", Precomp.getTypesCount());
W.printHex("Signature", Precomp.getSignature());
W.printString("PrecompFile", Precomp.getPrecompFilePath());
});
SmallString<128> ServerName(Precomp.getPrecompFilePath());
BuffOrErr = MemoryBuffer::getFile(ServerName);
if (BuffOrErr.getError()) {
// The server name does not exist. Try in the directory as the input file.
ServerName = createAlternativePath(ServerName);
if (BuffOrErr.getError()) {
// For the error message, use the original type server name.
return createStringError(errc::bad_file_descriptor,
"File '%s' does not exist.",
Precomp.getPrecompFilePath().str().c_str());
}
}
MemBuffer = std::move(BuffOrErr.get());
Expected<std::unique_ptr<Binary>> BinOrErr = createBinary(*MemBuffer);
if (errorToErrorCode(BinOrErr.takeError()))
return createStringError(errc::not_supported,
"Binary object format in '%s' is not supported.",
ServerName.c_str());
Binary &BinaryObj = *BinOrErr.get();
if (!BinaryObj.isCOFF())
return createStringError(errc::not_supported, "'%s' is not a COFF object.",
ServerName.c_str());
Builder = std::make_unique<AppendingTypeTableBuilder>(BuilderAllocator);
// The MSVC precompiled header object file, should contain just a single
// ".debug$P" section.
COFFObjectFile &Obj = *cast<COFFObjectFile>(&BinaryObj);
for (const SectionRef &Section : Obj.sections()) {
Expected<StringRef> SectionNameOrErr = Section.getName();
if (!SectionNameOrErr)
return SectionNameOrErr.takeError();
if (*SectionNameOrErr == ".debug$P") {
Expected<StringRef> DataOrErr = Section.getContents();
if (!DataOrErr)
return DataOrErr.takeError();
uint32_t Magic;
if (Error Err = consume(*DataOrErr, Magic))
return Err;
if (Magic != COFF::DEBUG_SECTION_MAGIC)
return errorCodeToError(object_error::parse_failed);
ReaderPrecomp = std::make_unique<BinaryStreamReader>(
*DataOrErr, llvm::endianness::little);
cantFail(
ReaderPrecomp->readArray(CVTypesPrecomp, ReaderPrecomp->getLength()));
// Append all the type records up to the LF_ENDPRECOMP marker and
// check if the signatures match.
for (const CVType &Type : CVTypesPrecomp) {
ArrayRef<uint8_t> TypeData = Type.data();
if (Type.kind() == LF_ENDPRECOMP) {
EndPrecompRecord EndPrecomp = cantFail(
TypeDeserializer::deserializeAs<EndPrecompRecord>(TypeData));
if (Precomp.getSignature() != EndPrecomp.getSignature())
return createStringError(errc::invalid_argument, "no matching pch");
break;
}
Builder->insertRecordBytes(TypeData);
}
// Done processing .debug$P, break out of section loop.
break;
}
}
// Append all the type records, skipping the first record which is the
// reference to the precompiled header object information.
for (const CVType &Type : CVTypesObj) {
ArrayRef<uint8_t> TypeData = Type.data();
if (Type.kind() != LF_PRECOMP)
Builder->insertRecordBytes(TypeData);
}
// Set up a type stream that refers to the added type records.
Builder->ForEachRecord(
[&](TypeIndex TI, const CVType &Type) { TypeArray.push_back(Type); });
ItemStream =
std::make_unique<BinaryItemStream<CVType>>(llvm::endianness::little);
ItemStream->setItems(TypeArray);
TypeStream.setUnderlyingStream(*ItemStream);
PrecompHeader =
std::make_shared<LazyRandomTypeCollection>(TypeStream, TypeArray.size());
// Change the original input source to use the collected type records.
LogicalVisitor.setInput(PrecompHeader);
LazyRandomTypeCollection &Types = types();
LazyRandomTypeCollection &Ids = ids();
LVTypeVisitor TDV(W, &LogicalVisitor, Types, Ids, StreamTPI,
LogicalVisitor.getShared());
return visitTypeStream(Types, TDV);
}
Error LVCodeViewReader::traverseTypeSection(StringRef SectionName,
const SectionRef &Section) {
LLVM_DEBUG({
ListScope D(W, "CodeViewTypes");
W.printNumber("Section", SectionName, getObj().getSectionID(Section));
});
Expected<StringRef> DataOrErr = Section.getContents();
if (!DataOrErr)
return DataOrErr.takeError();
uint32_t Magic;
if (Error Err = consume(*DataOrErr, Magic))
return Err;
if (Magic != COFF::DEBUG_SECTION_MAGIC)
return errorCodeToError(object_error::parse_failed);
// Get the first type record. It will indicate if this object uses a type
// server (/Zi) or a PCH file (/Yu).
CVTypeArray CVTypes;
BinaryStreamReader Reader(*DataOrErr, llvm::endianness::little);
cantFail(Reader.readArray(CVTypes, Reader.getLength()));
CVTypeArray::Iterator FirstType = CVTypes.begin();
// The object was compiled with /Zi. It uses types from a type server PDB.
if (FirstType->kind() == LF_TYPESERVER2) {
TypeServer2Record TS = cantFail(
TypeDeserializer::deserializeAs<TypeServer2Record>(FirstType->data()));
return loadTypeServer(TS);
}
// The object was compiled with /Yc or /Yu. It uses types from another
// object file with a matching signature.
if (FirstType->kind() == LF_PRECOMP) {
PrecompRecord Precomp = cantFail(
TypeDeserializer::deserializeAs<PrecompRecord>(FirstType->data()));
return loadPrecompiledObject(Precomp, CVTypes);
}
LazyRandomTypeCollection &Types = types();
LazyRandomTypeCollection &Ids = ids();
Types.reset(*DataOrErr, 100);
LVTypeVisitor TDV(W, &LogicalVisitor, Types, Ids, StreamTPI,
LogicalVisitor.getShared());
return visitTypeStream(Types, TDV);
}
Error LVCodeViewReader::traverseTypes(PDBFile &Pdb,
LazyRandomTypeCollection &Types,
LazyRandomTypeCollection &Ids) {
// Traverse types (TPI and IPI).
auto VisitTypes = [&](LazyRandomTypeCollection &Types,
LazyRandomTypeCollection &Ids,
SpecialStream StreamIdx) -> Error {
LVTypeVisitor TDV(W, &LogicalVisitor, Types, Ids, StreamIdx,
LogicalVisitor.getShared());
return visitTypeStream(Types, TDV);
};
Expected<TpiStream &> StreamTpiOrErr = Pdb.getPDBTpiStream();
if (!StreamTpiOrErr)
return StreamTpiOrErr.takeError();
TpiStream &StreamTpi = *StreamTpiOrErr;
StreamTpi.buildHashMap();
LLVM_DEBUG({
W.getOStream() << formatv("Showing {0:N} TPI records\n",
StreamTpi.getNumTypeRecords());
});
if (Error Err = VisitTypes(Types, Ids, StreamTPI))
return Err;
Expected<TpiStream &> StreamIpiOrErr = Pdb.getPDBIpiStream();
if (!StreamIpiOrErr)
return StreamIpiOrErr.takeError();
TpiStream &StreamIpi = *StreamIpiOrErr;
StreamIpi.buildHashMap();
LLVM_DEBUG({
W.getOStream() << formatv("Showing {0:N} IPI records\n",
StreamIpi.getNumTypeRecords());
});
return VisitTypes(Ids, Ids, StreamIPI);
}
Error LVCodeViewReader::traverseSymbolsSubsection(StringRef Subsection,
const SectionRef &Section,
StringRef SectionContents) {
ArrayRef<uint8_t> BinaryData(Subsection.bytes_begin(),
Subsection.bytes_end());
LVSymbolVisitorDelegate VisitorDelegate(this, Section, &getObj(),
SectionContents);
CVSymbolArray Symbols;
BinaryStreamReader Reader(BinaryData, llvm::endianness::little);
if (Error E = Reader.readArray(Symbols, Reader.getLength()))
return createStringError(errorToErrorCode(std::move(E)), getFileName());
LazyRandomTypeCollection &Types = types();
LazyRandomTypeCollection &Ids = ids();
SymbolVisitorCallbackPipeline Pipeline;
SymbolDeserializer Deserializer(&VisitorDelegate,
CodeViewContainer::ObjectFile);
// As we are processing a COFF format, use TPI as IPI, so the generic code
// to process the CodeView format does not contain any additional checks.
LVSymbolVisitor Traverser(this, W, &LogicalVisitor, Types, Ids,
&VisitorDelegate, LogicalVisitor.getShared());
Pipeline.addCallbackToPipeline(Deserializer);
Pipeline.addCallbackToPipeline(Traverser);
CVSymbolVisitor Visitor(Pipeline);
return Visitor.visitSymbolStream(Symbols);
}
Error LVCodeViewReader::traverseSymbolSection(StringRef SectionName,
const SectionRef &Section) {
LLVM_DEBUG({
ListScope D(W, "CodeViewDebugInfo");
W.printNumber("Section", SectionName, getObj().getSectionID(Section));
});
Expected<StringRef> SectionOrErr = Section.getContents();
if (!SectionOrErr)
return SectionOrErr.takeError();
StringRef SectionContents = *SectionOrErr;
StringRef Data = SectionContents;
SmallVector<StringRef, 10> SymbolNames;
StringMap<StringRef> FunctionLineTables;
uint32_t Magic;
if (Error E = consume(Data, Magic))
return createStringError(errorToErrorCode(std::move(E)), getFileName());
if (Magic != COFF::DEBUG_SECTION_MAGIC)
return createStringError(object_error::parse_failed, getFileName());
BinaryStreamReader FSReader(Data, llvm::endianness::little);
if (Error Err = initializeFileAndStringTables(FSReader))
return Err;
while (!Data.empty()) {
// The section consists of a number of subsection in the following format:
// |SubSectionType|SubSectionSize|Contents...|
uint32_t SubType, SubSectionSize;
if (Error E = consume(Data, SubType))
return createStringError(errorToErrorCode(std::move(E)), getFileName());
if (Error E = consume(Data, SubSectionSize))
return createStringError(errorToErrorCode(std::move(E)), getFileName());
// Process the subsection as normal even if the ignore bit is set.
SubType &= ~SubsectionIgnoreFlag;
// Get the contents of the subsection.
if (SubSectionSize > Data.size())
return createStringError(object_error::parse_failed, getFileName());
StringRef Contents = Data.substr(0, SubSectionSize);
// Add SubSectionSize to the current offset and align that offset
// to find the next subsection.
size_t SectionOffset = Data.data() - SectionContents.data();
size_t NextOffset = SectionOffset + SubSectionSize;
NextOffset = alignTo(NextOffset, 4);
if (NextOffset > SectionContents.size())
return createStringError(object_error::parse_failed, getFileName());
Data = SectionContents.drop_front(NextOffset);
switch (DebugSubsectionKind(SubType)) {
case DebugSubsectionKind::Symbols:
if (Error Err =
traverseSymbolsSubsection(Contents, Section, SectionContents))
return Err;
break;
case DebugSubsectionKind::InlineeLines:
if (Error Err = traverseInlineeLines(Contents))
return Err;
break;
case DebugSubsectionKind::Lines:
// Holds a PC to file:line table. Some data to parse this subsection
// is stored in the other subsections, so just check sanity and store
// the pointers for deferred processing.
// Collect function and ranges only if we need to print logical lines.
if (options().getGeneralCollectRanges()) {
if (SubSectionSize < 12) {
// There should be at least three words to store two function
// relocations and size of the code.
return createStringError(object_error::parse_failed, getFileName());
}
StringRef SymbolName;
if (Error Err = resolveSymbolName(getObj().getCOFFSection(Section),
SectionOffset, SymbolName))
return createStringError(errorToErrorCode(std::move(Err)),
getFileName());
LLVM_DEBUG({ W.printString("Symbol Name", SymbolName); });
if (!FunctionLineTables.try_emplace(SymbolName, Contents).second) {
// Saw debug info for this function already?
return createStringError(object_error::parse_failed, getFileName());
}
SymbolNames.push_back(SymbolName);
}
break;
// Do nothing for unrecognized subsections.
default:
break;
}
W.flush();
}
// Traverse the line tables now that we've read all the subsections and
// know all the required information.
for (StringRef SymbolName : SymbolNames) {
LLVM_DEBUG({
ListScope S(W, "FunctionLineTable");
W.printString("Symbol Name", SymbolName);
});
BinaryStreamReader Reader(FunctionLineTables[SymbolName],
llvm::endianness::little);
DebugLinesSubsectionRef Lines;
if (Error E = Lines.initialize(Reader))
return createStringError(errorToErrorCode(std::move(E)), getFileName());
// Find the associated symbol table information.
LVSymbolTableEntry SymbolTableEntry = getSymbolTableEntry(SymbolName);
LVScope *Function = SymbolTableEntry.Scope;
if (!Function)
continue;
LVAddress Addendum = SymbolTableEntry.Address;
LVSectionIndex SectionIndex = SymbolTableEntry.SectionIndex;
// The given scope represents the function that contains the line numbers.
// Collect all generated debug lines associated with the function.
CULines.clear();
// For the given scope, collect all scopes ranges.
LVRange *ScopesWithRanges = getSectionRanges(SectionIndex);
ScopesWithRanges->clear();
Function->getRanges(*ScopesWithRanges);
ScopesWithRanges->sort();
uint16_t Segment = Lines.header()->RelocSegment;
uint32_t Begin = Lines.header()->RelocOffset;
uint32_t Size = Lines.header()->CodeSize;
for (const LineColumnEntry &Block : Lines)
if (Error Err = createLines(Block.LineNumbers, Addendum, Segment, Begin,
Size, Block.NameIndex))
return Err;
// Include lines from any inlined functions within the current function.
includeInlineeLines(SectionIndex, Function);
if (Error Err = createInstructions(Function, SectionIndex))
return Err;
processLines(&CULines, SectionIndex, Function);
}
return Error::success();
}
void LVCodeViewReader::sortScopes() { Root->sort(); }
void LVCodeViewReader::print(raw_ostream &OS) const {
LLVM_DEBUG(dbgs() << "CreateReaders\n");
}
void LVCodeViewReader::mapRangeAddress(const ObjectFile &Obj,
const SectionRef &Section,
bool IsComdat) {
if (!Obj.isCOFF())
return;
const COFFObjectFile *Object = cast<COFFObjectFile>(&Obj);
for (const SymbolRef &Sym : Object->symbols()) {
if (!Section.containsSymbol(Sym))
continue;
COFFSymbolRef Symbol = Object->getCOFFSymbol(Sym);
if (Symbol.getComplexType() != llvm::COFF::IMAGE_SYM_DTYPE_FUNCTION)
continue;
StringRef SymbolName;
Expected<StringRef> SymNameOrErr = Object->getSymbolName(Symbol);
if (!SymNameOrErr) {
W.startLine() << "Invalid symbol name: " << Symbol.getSectionNumber()
<< "\n";
consumeError(SymNameOrErr.takeError());
continue;
}
SymbolName = *SymNameOrErr;
LLVM_DEBUG({
Expected<const coff_section *> SectionOrErr =
Object->getSection(Symbol.getSectionNumber());
if (!SectionOrErr) {
W.startLine() << "Invalid section number: " << Symbol.getSectionNumber()
<< "\n";
consumeError(SectionOrErr.takeError());
return;
}
W.printNumber("Section #", Symbol.getSectionNumber());
W.printString("Name", SymbolName);
W.printHex("Value", Symbol.getValue());
});
// Record the symbol name (linkage) and its loading address.
addToSymbolTable(SymbolName, Symbol.getValue(), Symbol.getSectionNumber(),
IsComdat);
}
}
Error LVCodeViewReader::createScopes(COFFObjectFile &Obj) {
if (Error Err = loadTargetInfo(Obj))
return Err;
// Initialization required when processing a COFF file:
// Cache the symbols relocations.
// Create a mapping for virtual addresses.
// Get the functions entry points.
cacheRelocations();
mapVirtualAddress(Obj);
for (const SectionRef &Section : Obj.sections()) {
Expected<StringRef> SectionNameOrErr = Section.getName();
if (!SectionNameOrErr)
return SectionNameOrErr.takeError();
// .debug$T is a standard CodeView type section, while .debug$P is the
// same format but used for MSVC precompiled header object files.
if (*SectionNameOrErr == ".debug$T" || *SectionNameOrErr == ".debug$P")
if (Error Err = traverseTypeSection(*SectionNameOrErr, Section))
return Err;
}
// Process collected namespaces.
LogicalVisitor.processNamespaces();
for (const SectionRef &Section : Obj.sections()) {
Expected<StringRef> SectionNameOrErr = Section.getName();
if (!SectionNameOrErr)
return SectionNameOrErr.takeError();
if (*SectionNameOrErr == ".debug$S")
if (Error Err = traverseSymbolSection(*SectionNameOrErr, Section))
return Err;
}
// Check if we have to close the Compile Unit scope.
LogicalVisitor.closeScope();
// Traverse the strings recorded and transform them into filenames.
LogicalVisitor.processFiles();
// Process collected element lines.
LogicalVisitor.processLines();
// Translate composite names into a single component.
Root->transformScopedName();
return Error::success();
}
Error LVCodeViewReader::createScopes(PDBFile &Pdb) {
if (Error Err = loadTargetInfo(Pdb))
return Err;
if (!Pdb.hasPDBTpiStream() || !Pdb.hasPDBDbiStream())
return Error::success();
// Open the executable associated with the PDB file and get the section
// addresses used to calculate linear addresses for CodeView Symbols.
if (!ExePath.empty()) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
MemoryBuffer::getFileOrSTDIN(ExePath);
if (BuffOrErr.getError()) {
return createStringError(errc::bad_file_descriptor,
"File '%s' does not exist.", ExePath.c_str());
}
BinaryBuffer = std::move(BuffOrErr.get());
// Check if the buffer corresponds to a PECOFF executable.
assert(identify_magic(BinaryBuffer->getBuffer()) ==
file_magic::pecoff_executable &&
"Invalid PECOFF executable file.");
Expected<std::unique_ptr<Binary>> BinOrErr =
createBinary(BinaryBuffer->getMemBufferRef());
if (errorToErrorCode(BinOrErr.takeError())) {
return createStringError(errc::not_supported,
"Binary object format in '%s' is not supported.",
ExePath.c_str());
}
BinaryExecutable = std::move(*BinOrErr);
if (COFFObjectFile *COFFObject =
dyn_cast<COFFObjectFile>(BinaryExecutable.get()))
mapVirtualAddress(*COFFObject);
}
// In order to generate a full logical view, we have to traverse both
// streams TPI and IPI if they are present. The following table gives
// the stream where a specified type is located. If the IPI stream is
// not present, all the types are located in the TPI stream.
//
// TPI Stream:
// LF_POINTER LF_MODIFIER LF_PROCEDURE LF_MFUNCTION
// LF_LABEL LF_ARGLIST LF_FIELDLIST LF_ARRAY
// LF_CLASS LF_STRUCTURE LF_INTERFACE LF_UNION
// LF_ENUM LF_TYPESERVER2 LF_VFTABLE LF_VTSHAPE
// LF_BITFIELD LF_METHODLIST LF_PRECOMP LF_ENDPRECOMP
//
// IPI stream:
// LF_FUNC_ID LF_MFUNC_ID LF_BUILDINFO
// LF_SUBSTR_LIST LF_STRING_ID LF_UDT_SRC_LINE
// LF_UDT_MOD_SRC_LINE
LazyRandomTypeCollection &Types = types();
LazyRandomTypeCollection &Ids = ids();
if (Error Err = traverseTypes(Pdb, Types, Ids))
return Err;
// Process collected namespaces.
LogicalVisitor.processNamespaces();
LLVM_DEBUG({ W.getOStream() << "Traversing inlined lines\n"; });
auto VisitInlineeLines = [&](int32_t Modi, const SymbolGroup &SG,
DebugInlineeLinesSubsectionRef &Lines) -> Error {
return collectInlineeInfo(Lines, &SG);
};
FilterOptions Filters = {};
LinePrinter Printer(/*Indent=*/2, false, nulls(), Filters);
const PrintScope HeaderScope(Printer, /*IndentLevel=*/2);
if (Error Err = iterateModuleSubsections<DebugInlineeLinesSubsectionRef>(
Input, HeaderScope, VisitInlineeLines))
return Err;
// Traverse global symbols.
LLVM_DEBUG({ W.getOStream() << "Traversing global symbols\n"; });
if (Pdb.hasPDBGlobalsStream()) {
Expected<GlobalsStream &> GlobalsOrErr = Pdb.getPDBGlobalsStream();
if (!GlobalsOrErr)
return GlobalsOrErr.takeError();
GlobalsStream &Globals = *GlobalsOrErr;
const GSIHashTable &Table = Globals.getGlobalsTable();
Expected<SymbolStream &> ExpectedSyms = Pdb.getPDBSymbolStream();
if (ExpectedSyms) {
SymbolVisitorCallbackPipeline Pipeline;
SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
LVSymbolVisitor Traverser(this, W, &LogicalVisitor, Types, Ids, nullptr,
LogicalVisitor.getShared());
// As the global symbols do not have an associated Compile Unit, create
// one, as the container for all global symbols.
RecordPrefix Prefix(SymbolKind::S_COMPILE3);
CVSymbol Symbol(&Prefix, sizeof(Prefix));
uint32_t Offset = 0;
if (Error Err = Traverser.visitSymbolBegin(Symbol, Offset))
consumeError(std::move(Err));
else {
// The CodeView compile unit containing the global symbols does not
// have a name; generate one using its parent name (object filename)
// follow by the '_global' string.
std::string Name(CompileUnit->getParentScope()->getName());
CompileUnit->setName(Name.append("_global"));
Pipeline.addCallbackToPipeline(Deserializer);
Pipeline.addCallbackToPipeline(Traverser);
CVSymbolVisitor Visitor(Pipeline);
BinaryStreamRef SymStream =
ExpectedSyms->getSymbolArray().getUnderlyingStream();
for (uint32_t PubSymOff : Table) {
Expected<CVSymbol> Sym = readSymbolFromStream(SymStream, PubSymOff);
if (Sym) {
if (Error Err = Visitor.visitSymbolRecord(*Sym, PubSymOff))
return createStringError(errorToErrorCode(std::move(Err)),
getFileName());
} else {
consumeError(Sym.takeError());
}
}
}
LogicalVisitor.closeScope();
} else {
consumeError(ExpectedSyms.takeError());
}
}
// Traverse symbols (DBI).
LLVM_DEBUG({ W.getOStream() << "Traversing symbol groups\n"; });
auto VisitSymbolGroup = [&](uint32_t Modi, const SymbolGroup &SG) -> Error {
Expected<ModuleDebugStreamRef> ExpectedModS =
getModuleDebugStream(Pdb, Modi);
if (ExpectedModS) {
ModuleDebugStreamRef &ModS = *ExpectedModS;
LLVM_DEBUG({
W.getOStream() << formatv("Traversing Group: Mod {0:4}\n", Modi);
});
SymbolVisitorCallbackPipeline Pipeline;
SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
LVSymbolVisitor Traverser(this, W, &LogicalVisitor, Types, Ids, nullptr,
LogicalVisitor.getShared());
Pipeline.addCallbackToPipeline(Deserializer);
Pipeline.addCallbackToPipeline(Traverser);
CVSymbolVisitor Visitor(Pipeline);
BinarySubstreamRef SS = ModS.getSymbolsSubstream();
if (Error Err =
Visitor.visitSymbolStream(ModS.getSymbolArray(), SS.Offset))
return createStringError(errorToErrorCode(std::move(Err)),
getFileName());
} else {
// If the module stream does not exist, it is not an error condition.
consumeError(ExpectedModS.takeError());
}
return Error::success();
};
if (Error Err = iterateSymbolGroups(Input, HeaderScope, VisitSymbolGroup))
return Err;
// At this stage, the logical view contains all scopes, symbols and types.
// For PDBs we can use the module id, to access its specific compile unit.
// The line record addresses has been already resolved, so we can apply the
// flow as when processing DWARF.
LLVM_DEBUG({ W.getOStream() << "Traversing lines\n"; });
// Record all line records for a Compile Unit.
CULines.clear();
auto VisitDebugLines = [this](int32_t Modi, const SymbolGroup &SG,
DebugLinesSubsectionRef &Lines) -> Error {
if (!options().getPrintLines())
return Error::success();
uint16_t Segment = Lines.header()->RelocSegment;
uint32_t Begin = Lines.header()->RelocOffset;
uint32_t Size = Lines.header()->CodeSize;
LLVM_DEBUG({ W.getOStream() << formatv("Modi = {0}\n", Modi); });
// We have line information for a new module; finish processing the
// collected information for the current module. Once it is done, start
// recording the line information for the new module.
if (CurrentModule != Modi) {
if (Error Err = processModule())
return Err;
CULines.clear();
CurrentModule = Modi;
}
for (const LineColumnEntry &Block : Lines)
if (Error Err = createLines(Block.LineNumbers, /*Addendum=*/0, Segment,
Begin, Size, Block.NameIndex, &SG))
return Err;
return Error::success();
};
if (Error Err = iterateModuleSubsections<DebugLinesSubsectionRef>(
Input, HeaderScope, VisitDebugLines))
return Err;
// Check if we have to close the Compile Unit scope.
LogicalVisitor.closeScope();
// Process collected element lines.
LogicalVisitor.processLines();
// Translate composite names into a single component.
Root->transformScopedName();
return Error::success();
}
Error LVCodeViewReader::processModule() {
if (LVScope *Scope = getScopeForModule(CurrentModule)) {
CompileUnit = static_cast<LVScopeCompileUnit *>(Scope);
LLVM_DEBUG({ dbgs() << "Processing Scope: " << Scope->getName() << "\n"; });
// For the given compile unit, collect all scopes ranges.
// For a complete ranges and lines mapping, the logical view support
// needs for the compile unit to have a low and high pc values. We
// can traverse the 'Modules' section and get the information for the
// specific module. Another option, is from all the ranges collected
// to take the first and last values.
LVSectionIndex SectionIndex = DotTextSectionIndex;
LVRange *ScopesWithRanges = getSectionRanges(SectionIndex);
ScopesWithRanges->clear();
CompileUnit->getRanges(*ScopesWithRanges);
if (!ScopesWithRanges->empty())
CompileUnit->addObject(ScopesWithRanges->getLower(),
ScopesWithRanges->getUpper());
ScopesWithRanges->sort();
if (Error Err = createInstructions())
return Err;
// Include lines from any inlined functions within the current function.
includeInlineeLines(SectionIndex, Scope);
processLines(&CULines, SectionIndex, nullptr);
}
return Error::success();
}
// In order to create the scopes, the CodeView Reader will:
// = Traverse the TPI/IPI stream (Type visitor):
// Collect forward references, scoped names, type indexes that will represent
// a logical element, strings, line records, linkage names.
// = Traverse the symbols section (Symbol visitor):
// Create the scopes tree and creates the required logical elements, by
// using the collected indexes from the type visitor.
Error LVCodeViewReader::createScopes() {
LLVM_DEBUG({
W.startLine() << "\n";
W.printString("File", getFileName().str());
W.printString("Exe", ExePath);
W.printString("Format", FileFormatName);
});
if (Error Err = LVReader::createScopes())
return Err;
LogicalVisitor.setRoot(Root);
if (isObj()) {
if (Error Err = createScopes(getObj()))
return Err;
} else {
if (Error Err = createScopes(getPdb()))
return Err;
}
return Error::success();
}
Error LVCodeViewReader::loadTargetInfo(const ObjectFile &Obj) {
// Detect the architecture from the object file. We usually don't need OS
// info to lookup a target and create register info.
Triple TT;
TT.setArch(Triple::ArchType(Obj.getArch()));
TT.setVendor(Triple::UnknownVendor);
TT.setOS(Triple::UnknownOS);
// Features to be passed to target/subtarget
Expected<SubtargetFeatures> Features = Obj.getFeatures();
SubtargetFeatures FeaturesValue;
if (!Features) {
consumeError(Features.takeError());
FeaturesValue = SubtargetFeatures();
}
FeaturesValue = *Features;
return loadGenericTargetInfo(TT.str(), FeaturesValue.getString());
}
Error LVCodeViewReader::loadTargetInfo(const PDBFile &Pdb) {
Triple TT;
TT.setArch(Triple::ArchType::x86_64);
TT.setVendor(Triple::UnknownVendor);
TT.setOS(Triple::Win32);
StringRef TheFeature = "";
return loadGenericTargetInfo(TT.str(), TheFeature);
}
std::string LVCodeViewReader::getRegisterName(LVSmall Opcode,
ArrayRef<uint64_t> Operands) {
// Get Compilation Unit CPU Type.
CPUType CPU = getCompileUnitCPUType();
// For CodeView the register always is in Operands[0];
RegisterId Register = (RegisterId(Operands[0]));
return formatRegisterId(Register, CPU);
}