diff options
Diffstat (limited to 'llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp')
-rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp | 1221 |
1 files changed, 1221 insertions, 0 deletions
diff --git a/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp b/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp new file mode 100644 index 000000000000..d72fe2683f92 --- /dev/null +++ b/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp @@ -0,0 +1,1221 @@ +//===-- 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/EnumTables.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/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include "llvm/DebugInfo/PDB/GenericError.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/Demangle/Demangle.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); + + for (const RelocationRef &Relocacion : Section.relocations()) + RelocMap[CoffSection].push_back(Relocacion); + + // Sort relocations by address. + llvm::sort(RelocMap[CoffSection], [](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 StringRef::npos != Name.find(String); + }; + auto Starts = [=](const char *Pattern) -> bool { + return Name.startswith(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::support::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, support::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, support::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::support::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, support::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::support::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, support::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.count(SymbolName) != 0) { + // Saw debug info for this function already? + return createStringError(object_error::parse_failed, getFileName()); + } + + FunctionLineTables[SymbolName] = Contents; + 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], support::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); +} |