| //===-- 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]; | |
| llvm::append_range(RM, Section.relocations()); | |
| // 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 | |
| SubtargetFeatures FeaturesValue; | |
| if (Expected<SubtargetFeatures> Features = Obj.getFeatures()) | |
| FeaturesValue = std::move(*Features); | |
| else | |
| consumeError(Features.takeError()); | |
| StringRef CPU; | |
| if (auto OptCPU = Obj.tryGetCPUName()) | |
| CPU = *OptCPU; | |
| return loadGenericTargetInfo(TT.str(), FeaturesValue.getString(), CPU); | |
| } | |
| Error LVCodeViewReader::loadTargetInfo(const PDBFile &Pdb) { | |
| Triple TT; | |
| TT.setArch(Triple::ArchType::x86_64); | |
| TT.setVendor(Triple::UnknownVendor); | |
| TT.setOS(Triple::Win32); | |
| StringRef TheFeature = ""; | |
| StringRef TheCPU = ""; | |
| return loadGenericTargetInfo(TT.str(), TheFeature, TheCPU); | |
| } | |
| 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); | |
| } |