diff options
| author | Carlos Alberto Enciso <carlos.alberto.enciso@gmail.com> | 2023-02-27 08:12:19 +0000 |
|---|---|---|
| committer | Carlos Alberto Enciso <carlos.alberto.enciso@gmail.com> | 2023-02-27 09:15:43 +0000 |
| commit | e7950fceb1e7f82370f6cff80b258e552eb410a6 (patch) | |
| tree | 5f279137f0d8de5bac094604f2f08fa7d80db335 /llvm/lib | |
| parent | b6f48341c5956a5b1d60ceb9963a2a9e5937788d (diff) | |
| download | llvm-e7950fceb1e7f82370f6cff80b258e552eb410a6.zip llvm-e7950fceb1e7f82370f6cff80b258e552eb410a6.tar.gz llvm-e7950fceb1e7f82370f6cff80b258e552eb410a6.tar.bz2 | |
[llvm-debuginfo-analyzer] (09/09) - CodeView Reader
llvm-debuginfo-analyzer is a command line tool that processes debug
info contained in a binary file and produces a debug information
format agnostic “Logical View”, which is a high-level semantic
representation of the debug info, independent of the low-level
format.
The code has been divided into the following patches:
1) Interval tree
2) Driver and documentation
3) Logical elements
4) Locations and ranges
5) Select elements
6) Warning and internal options
7) Compare elements
8) ELF Reader
9) CodeView Reader
Full details:
https://discourse.llvm.org/t/llvm-dev-rfc-llvm-dva-debug-information-visual-analyzer/62570
This patch:
This is a high level summary of the changes in this patch.
CodeView Reader
- Support for CodeView/PDB.
LVCodeViewReader, LVTypeVisitor, LVSymbolVisitor, LVLogicalVisitor
Reviewed By: psamolysov, probinson, djtodoro, zequanwu
Differential Revision: https://reviews.llvm.org/D125784
Diffstat (limited to 'llvm/lib')
| -rw-r--r-- | llvm/lib/DebugInfo/LogicalView/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp | 37 | ||||
| -rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp | 3 | ||||
| -rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp | 28 | ||||
| -rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp | 103 | ||||
| -rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp | 7 | ||||
| -rw-r--r-- | llvm/lib/DebugInfo/LogicalView/LVReaderHandler.cpp | 126 | ||||
| -rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp | 129 | ||||
| -rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp | 1221 | ||||
| -rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp | 3525 | ||||
| -rw-r--r-- | llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp | 4 |
11 files changed, 5187 insertions, 1 deletions
diff --git a/llvm/lib/DebugInfo/LogicalView/CMakeLists.txt b/llvm/lib/DebugInfo/LogicalView/CMakeLists.txt index 3388c25..38a1746 100644 --- a/llvm/lib/DebugInfo/LogicalView/CMakeLists.txt +++ b/llvm/lib/DebugInfo/LogicalView/CMakeLists.txt @@ -22,6 +22,8 @@ add_lv_impl_folder(Core add_lv_impl_folder(Readers LVReaderHandler.cpp Readers/LVBinaryReader.cpp + Readers/LVCodeViewReader.cpp + Readers/LVCodeViewVisitor.cpp Readers/LVELFReader.cpp ) @@ -39,9 +41,12 @@ add_llvm_component_library(LLVMDebugInfoLogicalView LINK_COMPONENTS BinaryFormat + Demangle Object MC Support TargetParser DebugInfoDWARF + DebugInfoCodeView + DebugInfoPDB ) diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp index a320752..cfe304e 100644 --- a/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp @@ -17,6 +17,7 @@ #include "llvm/DebugInfo/LogicalView/Core/LVType.h" using namespace llvm; +using namespace llvm::codeview; using namespace llvm::logicalview; #define DEBUG_TYPE "Element" @@ -103,6 +104,14 @@ void LVElement::setFilename(StringRef Filename) { FilenameIndex = getStringIndex(Filename); } +void LVElement::setInnerComponent(StringRef Name) { + if (Name.size()) { + StringRef InnerComponent; + std::tie(std::ignore, InnerComponent) = getInnerComponent(Name); + setName(InnerComponent); + } +} + // Return the string representation of a DIE offset. std::string LVElement::typeOffsetAsString() const { if (options().getAttributeOffset()) { @@ -126,6 +135,19 @@ StringRef LVElement::accessibilityString(uint32_t Access) const { } } +std::optional<uint32_t> LVElement::getAccessibilityCode(MemberAccess Access) { + switch (Access) { + case MemberAccess::Private: + return dwarf::DW_ACCESS_private; + case MemberAccess::Protected: + return dwarf::DW_ACCESS_protected; + case MemberAccess::Public: + return dwarf::DW_ACCESS_public; + default: + return std::nullopt; + } +} + StringRef LVElement::externalString() const { return getIsExternal() ? "extern" : StringRef(); } @@ -160,6 +182,21 @@ StringRef LVElement::virtualityString(uint32_t Virtuality) const { } } +std::optional<uint32_t> LVElement::getVirtualityCode(MethodKind Virtuality) { + switch (Virtuality) { + case MethodKind::Virtual: + return dwarf::DW_VIRTUALITY_virtual; + case MethodKind::PureVirtual: + return dwarf::DW_VIRTUALITY_pure_virtual; + case MethodKind::IntroducingVirtual: + case MethodKind::PureIntroducingVirtual: + // No direct equivalents in DWARF. Assume Virtual. + return dwarf::DW_VIRTUALITY_virtual; + default: + return std::nullopt; + } +} + void LVElement::resolve() { if (getIsResolved()) return; diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp index 88f66cf..613452c0 100644 --- a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp @@ -182,6 +182,9 @@ Error LVReader::createSplitFolder() { // Get the filename for given object. StringRef LVReader::getFilename(LVObject *Object, size_t Index) const { + // TODO: The current CodeView Reader implementation does not have support + // for multiple compile units. Until we have a proper offset calculation, + // check only in the current compile unit. if (CompileUnits.size()) { // Get Compile Unit for the given object. LVCompileUnits::const_iterator Iter = diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp index 2fc97cc..2f26025 100644 --- a/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp @@ -589,6 +589,10 @@ Error LVScope::doPrint(bool Split, bool Match, bool Print, raw_ostream &OS, // split context, then switch to the reader output stream. raw_ostream *StreamSplit = &OS; + // Ignore the CU generated by the VS toolchain, when compiling to PDB. + if (getIsSystem() && !options().getAttributeSystem()) + return Error::success(); + // If 'Split', we use the scope name (CU name) as the ouput file; the // delimiters in the pathname, must be replaced by a normal character. if (getIsCompileUnit()) { @@ -1784,6 +1788,8 @@ void LVScopeFunction::resolveReferences() { // DW_AT_external DW_FORM_flag_present // 00000070 DW_TAG_subprogram "bar" // DW_AT_specification DW_FORM_ref4 0x00000048 + // CodeView does not include any information at the class level to + // mark the member function as external. // If there is a reference linking the declaration and definition, mark // the definition as extern, to facilitate the logical view comparison. if (getHasReferenceSpecification()) { @@ -2019,6 +2025,28 @@ void LVScopeRoot::processRangeInformation() { } } +void LVScopeRoot::transformScopedName() { + // Recursively transform all names. + std::function<void(LVScope * Parent)> TraverseScope = [&](LVScope *Parent) { + auto Traverse = [&](const auto *Set) { + if (Set) + for (const auto &Entry : *Set) + Entry->setInnerComponent(); + }; + if (const LVScopes *Scopes = Parent->getScopes()) + for (LVScope *Scope : *Scopes) { + Scope->setInnerComponent(); + TraverseScope(Scope); + } + Traverse(Parent->getSymbols()); + Traverse(Parent->getTypes()); + Traverse(Parent->getLines()); + }; + + // Start traversing the scopes root and transform the element name. + TraverseScope(this); +} + bool LVScopeRoot::equals(const LVScope *Scope) const { return LVScope::equals(Scope); } diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp index 6d55b75..42fb114 100644 --- a/llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp @@ -60,3 +60,106 @@ std::string llvm::logicalview::flattenedFilePath(StringRef Path) { }; return Name; } + +using LexicalEntry = std::pair<size_t, size_t>; +using LexicalIndexes = SmallVector<LexicalEntry, 10>; + +static LexicalIndexes getAllLexicalIndexes(StringRef Name) { + if (Name.empty()) + return {}; + + size_t AngleCount = 0; + size_t ColonSeen = 0; + size_t Current = 0; + + LexicalIndexes Indexes; + +#ifndef NDEBUG + auto PrintLexicalEntry = [&]() { + LexicalEntry Entry = Indexes.back(); + llvm::dbgs() << formatv( + "'{0}:{1}', '{2}'\n", Entry.first, Entry.second, + Name.substr(Entry.first, Entry.second - Entry.first + 1)); + }; +#endif + + size_t Length = Name.size(); + for (size_t Index = 0; Index < Length; ++Index) { + LLVM_DEBUG({ + llvm::dbgs() << formatv("Index: '{0}', Char: '{1}'\n", Index, + Name[Index]); + }); + switch (Name[Index]) { + case '<': + ++AngleCount; + break; + case '>': + --AngleCount; + break; + case ':': + ++ColonSeen; + break; + } + if (ColonSeen == 2) { + if (!AngleCount) { + Indexes.push_back(LexicalEntry(Current, Index - 2)); + Current = Index + 1; + LLVM_DEBUG({ PrintLexicalEntry(); }); + } + ColonSeen = 0; + continue; + } + } + + // Store last component. + Indexes.push_back(LexicalEntry(Current, Length - 1)); + LLVM_DEBUG({ PrintLexicalEntry(); }); + return Indexes; +} + +LVLexicalComponent llvm::logicalview::getInnerComponent(StringRef Name) { + if (Name.empty()) + return {}; + + LexicalIndexes Indexes = getAllLexicalIndexes(Name); + if (Indexes.size() == 1) + return std::make_tuple(StringRef(), Name); + + LexicalEntry BeginEntry = Indexes.front(); + LexicalEntry EndEntry = Indexes[Indexes.size() - 2]; + StringRef Outer = + Name.substr(BeginEntry.first, EndEntry.second - BeginEntry.first + 1); + + LexicalEntry LastEntry = Indexes.back(); + StringRef Inner = + Name.substr(LastEntry.first, LastEntry.second - LastEntry.first + 1); + + return std::make_tuple(Outer, Inner); +} + +LVStringRefs llvm::logicalview::getAllLexicalComponents(StringRef Name) { + if (Name.empty()) + return {}; + + LexicalIndexes Indexes = getAllLexicalIndexes(Name); + LVStringRefs Components; + for (const LexicalEntry &Entry : Indexes) + Components.push_back( + Name.substr(Entry.first, Entry.second - Entry.first + 1)); + + return Components; +} + +std::string llvm::logicalview::getScopedName(const LVStringRefs &Components, + StringRef BaseName) { + if (Components.empty()) + return {}; + std::string Name(BaseName); + raw_string_ostream Stream(Name); + if (BaseName.size()) + Stream << "::"; + Stream << Components[0]; + for (LVStringRefs::size_type Index = 1; Index < Components.size(); ++Index) + Stream << "::" << Components[Index]; + return Name; +} diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp index de71e09..28bccad 100644 --- a/llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp @@ -324,6 +324,13 @@ LVElement *LVTypeDefinition::getUnderlyingType() { } void LVTypeDefinition::resolveExtra() { + // In the case of CodeView, the MSVC toolset generates a series of typedefs + // that refer to internal runtime structures, that we do not process. Those + // typedefs are marked as 'system'. They have an associated logical type, + // but the underlying type always is null. + if (getIsSystem()) + return; + // Set the reference to the typedef type. if (options().getAttributeUnderlying()) { setUnderlyingType(getUnderlyingType()); diff --git a/llvm/lib/DebugInfo/LogicalView/LVReaderHandler.cpp b/llvm/lib/DebugInfo/LogicalView/LVReaderHandler.cpp index 65633f2..4c27dfe 100644 --- a/llvm/lib/DebugInfo/LogicalView/LVReaderHandler.cpp +++ b/llvm/lib/DebugInfo/LogicalView/LVReaderHandler.cpp @@ -11,8 +11,13 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/LogicalView/LVReaderHandler.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" #include "llvm/DebugInfo/LogicalView/Core/LVCompare.h" +#include "llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h" #include "llvm/DebugInfo/LogicalView/Readers/LVELFReader.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/Object/COFF.h" using namespace llvm; using namespace llvm::object; @@ -38,9 +43,19 @@ Error LVReaderHandler::createReader(StringRef Filename, LVReaders &Readers, auto CreateOneReader = [&]() -> std::unique_ptr<LVReader> { if (Input.is<ObjectFile *>()) { ObjectFile &Obj = *Input.get<ObjectFile *>(); + if (Obj.isCOFF()) { + COFFObjectFile *COFF = cast<COFFObjectFile>(&Obj); + return std::make_unique<LVCodeViewReader>(Filename, FileFormatName, + *COFF, W, ExePath); + } if (Obj.isELF() || Obj.isMachO()) return std::make_unique<LVELFReader>(Filename, FileFormatName, Obj, W); } + if (Input.is<PDBFile *>()) { + PDBFile &Pdb = *Input.get<PDBFile *>(); + return std::make_unique<LVCodeViewReader>(Filename, FileFormatName, Pdb, + W, ExePath); + } return nullptr; }; @@ -76,8 +91,102 @@ Error LVReaderHandler::handleArchive(LVReaders &Readers, StringRef Filename, return Error::success(); } +// Search for a matching executable image for the given PDB path. +static std::string searchForExe(const StringRef Path, + const StringRef Extension) { + SmallString<128> ExePath(Path); + llvm::sys::path::replace_extension(ExePath, Extension); + + std::unique_ptr<IPDBSession> Session; + if (Error Err = loadDataForEXE(PDB_ReaderType::Native, ExePath, Session)) { + consumeError(std::move(Err)); + return {}; + } + // We have a candidate for the executable image. + Expected<std::string> PdbPathOrErr = NativeSession::searchForPdb({ExePath}); + if (!PdbPathOrErr) { + consumeError(PdbPathOrErr.takeError()); + return {}; + } + // Convert any Windows backslashes into forward slashes to get the path. + std::string ConvertedPath = sys::path::convert_to_slash( + PdbPathOrErr.get(), sys::path::Style::windows); + if (ConvertedPath == Path) + return std::string(ExePath); + + return {}; +} + +// Search for a matching object image for the given PDB path. +static std::string searchForObj(const StringRef Path, + const StringRef Extension) { + SmallString<128> ObjPath(Path); + llvm::sys::path::replace_extension(ObjPath, Extension); + if (llvm::sys::fs::exists(ObjPath)) { + ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr = + MemoryBuffer::getFileOrSTDIN(ObjPath); + if (!BuffOrErr) + return {}; + return std::string(ObjPath); + } + + return {}; +} + Error LVReaderHandler::handleBuffer(LVReaders &Readers, StringRef Filename, MemoryBufferRef Buffer, StringRef ExePath) { + // As PDB does not support the Binary interface, at this point we can check + // if the buffer corresponds to a PDB or PE file. + file_magic FileMagic = identify_magic(Buffer.getBuffer()); + if (FileMagic == file_magic::pdb) { + if (!ExePath.empty()) + return handleObject(Readers, Filename, Buffer.getBuffer(), ExePath); + + // Search in the directory derived from the given 'Filename' for a + // matching object file (.o, .obj, .lib) or a matching executable file + // (.exe/.dll) and try to create the reader based on the matched file. + // If no matching file is found then we load the original PDB file. + std::vector<StringRef> ExecutableExtensions = {"exe", "dll"}; + for (StringRef Extension : ExecutableExtensions) { + std::string ExecutableImage = searchForExe(Filename, Extension); + if (ExecutableImage.empty()) + continue; + if (Error Err = handleObject(Readers, Filename, Buffer.getBuffer(), + ExecutableImage)) { + consumeError(std::move(Err)); + continue; + } + return Error::success(); + } + + std::vector<StringRef> ObjectExtensions = {"o", "obj", "lib"}; + for (StringRef Extension : ObjectExtensions) { + std::string ObjectImage = searchForObj(Filename, Extension); + if (ObjectImage.empty()) + continue; + if (Error Err = handleFile(Readers, ObjectImage)) { + consumeError(std::move(Err)); + continue; + } + return Error::success(); + } + + // No matching executable/object image was found. Load the given PDB. + return handleObject(Readers, Filename, Buffer.getBuffer(), ExePath); + } + if (FileMagic == file_magic::pecoff_executable) { + // If we have a valid executable, try to find a matching PDB file. + Expected<std::string> PdbPath = NativeSession::searchForPdb({Filename}); + if (errorToErrorCode(PdbPath.takeError())) { + return createStringError( + errc::not_supported, + "Binary object format in '%s' does not have debug info.", + Filename.str().c_str()); + } + // Process the matching PDB file and pass the executable filename. + return handleFile(Readers, PdbPath.get(), Filename); + } + Expected<std::unique_ptr<Binary>> BinOrErr = createBinary(Buffer); if (errorToErrorCode(BinOrErr.takeError())) { return createStringError(errc::not_supported, @@ -147,6 +256,23 @@ Error LVReaderHandler::handleObject(LVReaders &Readers, StringRef Filename, Filename.str().c_str()); } +Error LVReaderHandler::handleObject(LVReaders &Readers, StringRef Filename, + StringRef Buffer, StringRef ExePath) { + std::unique_ptr<IPDBSession> Session; + if (Error Err = loadDataForPDB(PDB_ReaderType::Native, Filename, Session)) + return createStringError(errorToErrorCode(std::move(Err)), "%s", + Filename.str().c_str()); + + std::unique_ptr<NativeSession> PdbSession; + PdbSession.reset(static_cast<NativeSession *>(Session.release())); + PdbOrObj Input = &PdbSession->getPDBFile(); + StringRef FileFormatName; + size_t Pos = Buffer.find_first_of("\r\n"); + if (Pos) + FileFormatName = Buffer.substr(0, Pos - 1); + return createReader(Filename, Readers, Input, FileFormatName, ExePath); +} + Error LVReaderHandler::createReaders() { LLVM_DEBUG(dbgs() << "createReaders\n"); for (std::string &Object : Objects) { diff --git a/llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp b/llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp index 1fb5c4a..2be72a2 100644 --- a/llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp @@ -190,6 +190,61 @@ void LVBinaryReader::mapVirtualAddress(const object::ObjectFile &Obj) { }); } +void LVBinaryReader::mapVirtualAddress(const object::COFFObjectFile &COFFObj) { + ErrorOr<uint64_t> ImageBase = COFFObj.getImageBase(); + if (ImageBase) + ImageBaseAddress = ImageBase.get(); + + LLVM_DEBUG({ + dbgs() << "ImageBaseAddress: " << hexValue(ImageBaseAddress) << "\n"; + }); + + uint32_t Flags = COFF::IMAGE_SCN_CNT_CODE | COFF::IMAGE_SCN_LNK_COMDAT; + + for (const object::SectionRef &Section : COFFObj.sections()) { + if (!Section.isText() || Section.isVirtual() || !Section.getSize()) + continue; + + const object::coff_section *COFFSection = COFFObj.getCOFFSection(Section); + VirtualAddress = COFFSection->VirtualAddress; + bool IsComdat = (COFFSection->Characteristics & Flags) == Flags; + + // Record section information required for symbol resolution. + // Note: The section index returned by 'getIndex()' is zero based. + Sections.emplace(Section.getIndex() + 1, Section); + addSectionAddress(Section); + + // Additional initialization on the specific object format. + mapRangeAddress(COFFObj, Section, IsComdat); + } + + LLVM_DEBUG({ + dbgs() << "\nSections Information:\n"; + for (LVSections::reference Entry : Sections) { + LVSectionIndex SectionIndex = Entry.first; + const object::SectionRef Section = Entry.second; + const object::coff_section *COFFSection = COFFObj.getCOFFSection(Section); + Expected<StringRef> SectionNameOrErr = Section.getName(); + if (!SectionNameOrErr) + consumeError(SectionNameOrErr.takeError()); + dbgs() << "\nIndex: " << format_decimal(SectionIndex, 3) + << " Name: " << *SectionNameOrErr << "\n" + << "Size: " << hexValue(Section.getSize()) << "\n" + << "VirtualAddress: " << hexValue(VirtualAddress) << "\n" + << "SectionAddress: " << hexValue(Section.getAddress()) << "\n" + << "PointerToRawData: " << hexValue(COFFSection->PointerToRawData) + << "\n" + << "SizeOfRawData: " << hexValue(COFFSection->SizeOfRawData) + << "\n"; + } + dbgs() << "\nObject Section Information:\n"; + for (LVSectionAddresses::const_reference Entry : SectionAddresses) + dbgs() << "[" << hexValue(Entry.first) << ":" + << hexValue(Entry.first + Entry.second.getSize()) + << "] Size: " << hexValue(Entry.second.getSize()) << "\n"; + }); +} + Error LVBinaryReader::loadGenericTargetInfo(StringRef TheTriple, StringRef TheFeatures) { std::string TargetLookupError; @@ -804,6 +859,80 @@ void LVBinaryReader::processLines(LVLines *DebugLines, } } +// Traverse the scopes for the given 'Function' looking for any inlined +// scopes with inlined lines, which are found in 'CUInlineeLines'. +void LVBinaryReader::includeInlineeLines(LVSectionIndex SectionIndex, + LVScope *Function) { + SmallVector<LVInlineeLine::iterator> InlineeIters; + std::function<void(LVScope * Parent)> FindInlinedScopes = + [&](LVScope *Parent) { + if (const LVScopes *Scopes = Parent->getScopes()) + for (LVScope *Scope : *Scopes) { + LVInlineeLine::iterator Iter = CUInlineeLines.find(Scope); + if (Iter != CUInlineeLines.end()) + InlineeIters.push_back(Iter); + FindInlinedScopes(Scope); + } + }; + + // Find all inlined scopes for the given 'Function'. + FindInlinedScopes(Function); + for (LVInlineeLine::iterator InlineeIter : InlineeIters) { + LVScope *Scope = InlineeIter->first; + addToSymbolTable(Scope->getLinkageName(), Scope, SectionIndex); + + // TODO: Convert this into a reference. + LVLines *InlineeLines = InlineeIter->second.get(); + LLVM_DEBUG({ + dbgs() << "Inlined lines for: " << Scope->getName() << "\n"; + for (const LVLine *Line : *InlineeLines) + dbgs() << "[" << hexValue(Line->getAddress()) << "] " + << Line->getLineNumber() << "\n"; + dbgs() << format("Debug lines: %d\n", CULines.size()); + for (const LVLine *Line : CULines) + dbgs() << "Line address: " << hexValue(Line->getOffset()) << ", (" + << Line->getLineNumber() << ")\n"; + ; + }); + + // The inlined lines must be merged using its address, in order to keep + // the real order of the instructions. The inlined lines are mixed with + // the other non-inlined lines. + if (InlineeLines->size()) { + // First address of inlinee code. + uint64_t InlineeStart = (InlineeLines->front())->getAddress(); + LVLines::iterator Iter = std::find_if( + CULines.begin(), CULines.end(), [&](LVLine *Item) -> bool { + return Item->getAddress() == InlineeStart; + }); + if (Iter != CULines.end()) { + // 'Iter' points to the line where the inlined function is called. + // Emulate the DW_AT_call_line attribute. + Scope->setCallLineNumber((*Iter)->getLineNumber()); + // Mark the referenced line as the start of the inlined function. + // Skip the first line during the insertion, as the address and + // line number as the same. Otherwise we have to erase and insert. + (*Iter)->setLineNumber((*InlineeLines->begin())->getLineNumber()); + ++Iter; + CULines.insert(Iter, InlineeLines->begin() + 1, InlineeLines->end()); + } + } + + // Remove this set of lines from the container; each inlined function + // creates an unique set of lines. Remove only the created container. + CUInlineeLines.erase(InlineeIter); + InlineeLines->clear(); + } + LLVM_DEBUG({ + dbgs() << "Merged Inlined lines for: " << Function->getName() << "\n"; + dbgs() << format("Debug lines: %d\n", CULines.size()); + for (const LVLine *Line : CULines) + dbgs() << "Line address: " << hexValue(Line->getOffset()) << ", (" + << Line->getLineNumber() << ")\n"; + ; + }); +} + void LVBinaryReader::print(raw_ostream &OS) const { OS << "LVBinaryReader\n"; LLVM_DEBUG(dbgs() << "PrintReader\n"); diff --git a/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp b/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp new file mode 100644 index 0000000..5b099d4 --- /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, + uint64_t Operands[2]) { + // 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); +} diff --git a/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp b/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp new file mode 100644 index 0000000..2139124 --- /dev/null +++ b/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp @@ -0,0 +1,3525 @@ +//===-- LVCodeViewVisitor.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 LVCodeViewVisitor class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h" +#include "llvm/BinaryFormat/Magic.h" +#include "llvm/DebugInfo/CodeView/EnumTables.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h" +#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.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/LogicalView/Readers/LVCodeViewReader.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/InputFile.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/Demangle/Demangle.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::object; +using namespace llvm::pdb; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "CodeViewUtilities" + +namespace llvm { +namespace logicalview { + +static TypeIndex getTrueType(TypeIndex &TI) { + // Dealing with a MSVC generated PDB, we encountered a type index with the + // value of: 0x0280xxxx where xxxx=0000. + // + // There is some documentation about type indices: + // https://llvm.org/docs/PDB/TpiStream.html + // + // A type index is a 32-bit integer that uniquely identifies a type inside + // of an object file’s .debug$T section or a PDB file’s TPI or IPI stream. + // The value of the type index for the first type record from the TPI stream + // is given by the TypeIndexBegin member of the TPI Stream Header although + // in practice this value is always equal to 0x1000 (4096). + // + // Any type index with a high bit set is considered to come from the IPI + // stream, although this appears to be more of a hack, and LLVM does not + // generate type indices of this nature. They can, however, be observed in + // Microsoft PDBs occasionally, so one should be prepared to handle them. + // Note that having the high bit set is not a necessary condition to + // determine whether a type index comes from the IPI stream, it is only + // sufficient. + LLVM_DEBUG( + { dbgs() << "Index before: " << HexNumber(TI.getIndex()) << "\n"; }); + TI.setIndex(TI.getIndex() & 0x0000ffff); + LLVM_DEBUG( + { dbgs() << "Index after: " << HexNumber(TI.getIndex()) << "\n"; }); + return TI; +} + +static const EnumEntry<TypeLeafKind> LeafTypeNames[] = { +#define CV_TYPE(enum, val) {#enum, enum}, +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" +}; + +// Return the type name pointed by the type index. It uses the kind to query +// the associated name for the record type. +static StringRef getRecordName(LazyRandomTypeCollection &Types, TypeIndex TI) { + if (TI.isSimple()) + return {}; + + StringRef RecordName; + CVType CVReference = Types.getType(TI); + auto GetName = [&](auto Record) { + if (Error Err = TypeDeserializer::deserializeAs( + const_cast<CVType &>(CVReference), Record)) + consumeError(std::move(Err)); + else + RecordName = Record.getName(); + }; + + TypeRecordKind RK = static_cast<TypeRecordKind>(CVReference.kind()); + if (RK == TypeRecordKind::Class || RK == TypeRecordKind::Struct) + GetName(ClassRecord(RK)); + else if (RK == TypeRecordKind::Union) + GetName(UnionRecord(RK)); + else if (RK == TypeRecordKind::Enum) + GetName(EnumRecord(RK)); + + return RecordName; +} + +} // namespace logicalview +} // namespace llvm + +#undef DEBUG_TYPE +#define DEBUG_TYPE "CodeViewDataVisitor" + +namespace llvm { +namespace logicalview { + +// Keeps the type indexes with line information. +using LVLineRecords = std::vector<TypeIndex>; + +namespace { + +class LVTypeRecords { + LVShared *Shared = nullptr; + + // Logical elements associated to their CodeView Type Index. + using RecordEntry = std::pair<TypeLeafKind, LVElement *>; + using RecordTable = std::map<TypeIndex, RecordEntry>; + RecordTable RecordFromTypes; + RecordTable RecordFromIds; + + using NameTable = std::map<StringRef, TypeIndex>; + NameTable NameFromTypes; + NameTable NameFromIds; + +public: + LVTypeRecords(LVShared *Shared) : Shared(Shared) {} + + void add(uint32_t StreamIdx, TypeIndex TI, TypeLeafKind Kind, + LVElement *Element = nullptr); + void add(uint32_t StreamIdx, TypeIndex TI, StringRef Name); + LVElement *find(uint32_t StreamIdx, TypeIndex TI, bool Create = true); + TypeIndex find(uint32_t StreamIdx, StringRef Name); +}; + +class LVForwardReferences { + // Forward reference and its definitions (Name as key). + using ForwardEntry = std::pair<TypeIndex, TypeIndex>; + using ForwardTypeNames = std::map<StringRef, ForwardEntry>; + ForwardTypeNames ForwardTypesNames; + + // Forward reference and its definition (TypeIndex as key). + using ForwardType = std::map<TypeIndex, TypeIndex>; + ForwardType ForwardTypes; + + // Forward types and its references. + void add(TypeIndex TIForward, TypeIndex TIReference) { + ForwardTypes.emplace(TIForward, TIReference); + } + + void add(StringRef Name, TypeIndex TIForward) { + if (ForwardTypesNames.find(Name) == ForwardTypesNames.end()) { + ForwardTypesNames.emplace( + std::piecewise_construct, std::forward_as_tuple(Name), + std::forward_as_tuple(TIForward, TypeIndex::None())); + } else { + // Update a recorded definition with its reference. + ForwardTypesNames[Name].first = TIForward; + add(TIForward, ForwardTypesNames[Name].second); + } + } + + // Update a previously recorded forward reference with its definition. + void update(StringRef Name, TypeIndex TIReference) { + if (ForwardTypesNames.find(Name) != ForwardTypesNames.end()) { + // Update the recorded forward reference with its definition. + ForwardTypesNames[Name].second = TIReference; + add(ForwardTypesNames[Name].first, TIReference); + } else { + // We have not seen the forward reference. Insert the definition. + ForwardTypesNames.emplace( + std::piecewise_construct, std::forward_as_tuple(Name), + std::forward_as_tuple(TypeIndex::None(), TIReference)); + } + } + +public: + LVForwardReferences() = default; + + void record(bool IsForwardRef, StringRef Name, TypeIndex TI) { + // We are expecting for the forward references to be first. But that + // is not always the case. A name must be recorded regardless of the + // order in which the forward reference appears. + (IsForwardRef) ? add(Name, TI) : update(Name, TI); + } + + TypeIndex find(TypeIndex TIForward) { + return (ForwardTypes.find(TIForward) != ForwardTypes.end()) + ? ForwardTypes[TIForward] + : TypeIndex::None(); + } + + TypeIndex find(StringRef Name) { + return (ForwardTypesNames.find(Name) != ForwardTypesNames.end()) + ? ForwardTypesNames[Name].second + : TypeIndex::None(); + } + + // If the given TI corresponds to a reference, return the reference. + // Otherwise return the given TI. + TypeIndex remap(TypeIndex TI) { + TypeIndex Forward = find(TI); + return Forward.isNoneType() ? TI : Forward; + } +}; + +// Namespace deduction. +class LVNamespaceDeduction { + LVShared *Shared = nullptr; + + using Names = std::map<StringRef, LVScope *>; + Names NamespaceNames; + + using LookupSet = std::set<StringRef>; + LookupSet DeducedScopes; + LookupSet UnresolvedScopes; + LookupSet IdentifiedNamespaces; + + void add(StringRef Name, LVScope *Namespace) { + if (NamespaceNames.find(Name) == NamespaceNames.end()) + NamespaceNames.emplace(Name, Namespace); + } + +public: + LVNamespaceDeduction(LVShared *Shared) : Shared(Shared) {} + + void init(); + void add(StringRef String); + LVScope *get(LVStringRefs Components); + LVScope *get(StringRef Name, bool CheckScope = true); + + // Find the logical namespace for the 'Name' component. + LVScope *find(StringRef Name) { + LVScope *Namespace = (NamespaceNames.find(Name) != NamespaceNames.end()) + ? NamespaceNames[Name] + : nullptr; + return Namespace; + } + + // For the given lexical components, return a tuple with the first entry + // being the outermost namespace and the second entry being the first + // non-namespace. + LVLexicalIndex find(LVStringRefs Components) { + if (Components.empty()) + return {}; + + LVStringRefs::size_type FirstNamespace = 0; + LVStringRefs::size_type FirstNonNamespace; + for (LVStringRefs::size_type Index = 0; Index < Components.size(); + ++Index) { + FirstNonNamespace = Index; + LookupSet::iterator Iter = IdentifiedNamespaces.find(Components[Index]); + if (Iter == IdentifiedNamespaces.end()) + // The component is not a namespace name. + break; + } + return std::make_tuple(FirstNamespace, FirstNonNamespace); + } +}; + +// Strings. +class LVStringRecords { + using StringEntry = std::tuple<uint32_t, std::string, LVScopeCompileUnit *>; + using StringIds = std::map<TypeIndex, StringEntry>; + StringIds Strings; + +public: + LVStringRecords() = default; + + void add(TypeIndex TI, StringRef String) { + static uint32_t Index = 0; + if (Strings.find(TI) == Strings.end()) + Strings.emplace( + std::piecewise_construct, std::forward_as_tuple(TI), + std::forward_as_tuple(++Index, std::string(String), nullptr)); + } + + StringRef find(TypeIndex TI) { + StringIds::iterator Iter = Strings.find(TI); + return Iter != Strings.end() ? std::get<1>(Iter->second) : StringRef{}; + } + + uint32_t findIndex(TypeIndex TI) { + StringIds::iterator Iter = Strings.find(TI); + return Iter != Strings.end() ? std::get<0>(Iter->second) : 0; + } + + // Move strings representing the filenames to the compile unit. + void addFilenames(); + void addFilenames(LVScopeCompileUnit *Scope); +}; +} // namespace + +using LVTypeKinds = std::set<TypeLeafKind>; +using LVSymbolKinds = std::set<SymbolKind>; + +// The following data keeps forward information, type records, names for +// namespace deduction, strings records, line records. +// It is shared by the type visitor, symbol visitor and logical visitor and +// it is independent from the CodeViewReader. +struct LVShared { + LVCodeViewReader *Reader; + LVLogicalVisitor *Visitor; + LVForwardReferences ForwardReferences; + LVLineRecords LineRecords; + LVNamespaceDeduction NamespaceDeduction; + LVStringRecords StringRecords; + LVTypeRecords TypeRecords; + + // In order to determine which types and/or symbols records should be handled + // by the reader, we record record kinds seen by the type and symbol visitors. + // At the end of the scopes creation, the '--internal=tag' option will allow + // to print the unique record ids collected. + LVTypeKinds TypeKinds; + LVSymbolKinds SymbolKinds; + + LVShared(LVCodeViewReader *Reader, LVLogicalVisitor *Visitor) + : Reader(Reader), Visitor(Visitor), NamespaceDeduction(this), + TypeRecords(this) {} + ~LVShared() = default; +}; +} // namespace logicalview +} // namespace llvm + +void LVTypeRecords::add(uint32_t StreamIdx, TypeIndex TI, TypeLeafKind Kind, + LVElement *Element) { + RecordTable &Target = + (StreamIdx == StreamTPI) ? RecordFromTypes : RecordFromIds; + Target.emplace(std::piecewise_construct, std::forward_as_tuple(TI), + std::forward_as_tuple(Kind, Element)); +} + +void LVTypeRecords::add(uint32_t StreamIdx, TypeIndex TI, StringRef Name) { + NameTable &Target = (StreamIdx == StreamTPI) ? NameFromTypes : NameFromIds; + Target.emplace(Name, TI); +} + +LVElement *LVTypeRecords::find(uint32_t StreamIdx, TypeIndex TI, bool Create) { + RecordTable &Target = + (StreamIdx == StreamTPI) ? RecordFromTypes : RecordFromIds; + + LVElement *Element = nullptr; + RecordTable::iterator Iter = Target.find(TI); + if (Iter != Target.end()) { + Element = Iter->second.second; + if (Element || !Create) + return Element; + + // Create the logical element if not found. + Element = Shared->Visitor->createElement(Iter->second.first); + if (Element) { + Element->setOffset(TI.getIndex()); + Element->setOffsetFromTypeIndex(); + Target[TI].second = Element; + } + } + return Element; +} + +TypeIndex LVTypeRecords::find(uint32_t StreamIdx, StringRef Name) { + NameTable &Target = (StreamIdx == StreamTPI) ? NameFromTypes : NameFromIds; + NameTable::iterator Iter = Target.find(Name); + return Iter != Target.end() ? Iter->second : TypeIndex::None(); +} + +void LVStringRecords::addFilenames() { + for (StringIds::const_reference Entry : Strings) { + StringRef Name = std::get<1>(Entry.second); + LVScopeCompileUnit *Scope = std::get<2>(Entry.second); + Scope->addFilename(transformPath(Name)); + } + Strings.clear(); +} + +void LVStringRecords::addFilenames(LVScopeCompileUnit *Scope) { + for (StringIds::reference Entry : Strings) + if (!std::get<2>(Entry.second)) + std::get<2>(Entry.second) = Scope; +} + +void LVNamespaceDeduction::add(StringRef String) { + StringRef InnerComponent; + StringRef OuterComponent; + std::tie(OuterComponent, InnerComponent) = getInnerComponent(String); + DeducedScopes.insert(InnerComponent); + if (OuterComponent.size()) + UnresolvedScopes.insert(OuterComponent); +} + +void LVNamespaceDeduction::init() { + // We have 2 sets of names: + // - deduced scopes (class, structure, union and enum) and + // - unresolved scopes, that can represent namespaces or any deduced. + // Before creating the namespaces, we have to traverse the unresolved + // and remove any references to already deduced scopes. + LVStringRefs Components; + for (const StringRef &Unresolved : UnresolvedScopes) { + Components = getAllLexicalComponents(Unresolved); + for (const StringRef &Component : Components) { + LookupSet::iterator Iter = DeducedScopes.find(Component); + if (Iter == DeducedScopes.end()) + IdentifiedNamespaces.insert(Component); + } + } + + LLVM_DEBUG({ + auto Print = [&](LookupSet &Container, const char *Title) { + auto Header = [&]() { + dbgs() << formatv("\n{0}\n", fmt_repeat('=', 72)); + dbgs() << formatv("{0}\n", Title); + dbgs() << formatv("{0}\n", fmt_repeat('=', 72)); + }; + Header(); + for (const StringRef &Item : Container) + dbgs() << formatv("'{0}'\n", Item.str().c_str()); + }; + + Print(DeducedScopes, "Deducted Scopes"); + Print(UnresolvedScopes, "Unresolved Scopes"); + Print(IdentifiedNamespaces, "Namespaces"); + }); +} + +LVScope *LVNamespaceDeduction::get(LVStringRefs Components) { + LLVM_DEBUG({ + for (const StringRef &Component : Components) + dbgs() << formatv("'{0}'\n", Component.str().c_str()); + }); + + if (Components.empty()) + return nullptr; + + // Update the namespaces relationship. + LVScope *Namespace = nullptr; + LVScope *Parent = Shared->Reader->getCompileUnit(); + for (const StringRef &Component : Components) { + // Check if we have seen the namespace. + Namespace = find(Component); + if (!Namespace) { + // We have identified namespaces that are generated by MSVC. Mark them + // as 'system' so they will be excluded from the logical view. + Namespace = Shared->Reader->createScopeNamespace(); + Namespace->setTag(dwarf::DW_TAG_namespace); + Namespace->setName(Component); + Parent->addElement(Namespace); + getReader().isSystemEntry(Namespace); + add(Component, Namespace); + } + Parent = Namespace; + } + return Parent; +} + +LVScope *LVNamespaceDeduction::get(StringRef ScopedName, bool CheckScope) { + LVStringRefs Components = getAllLexicalComponents(ScopedName); + if (CheckScope) + Components.erase(std::remove_if(Components.begin(), Components.end(), + [&](StringRef Component) { + LookupSet::iterator Iter = + IdentifiedNamespaces.find(Component); + return Iter == IdentifiedNamespaces.end(); + }), + Components.end()); + + LLVM_DEBUG( + { dbgs() << formatv("ScopedName: '{0}'\n", ScopedName.str().c_str()); }); + + return get(Components); +} + +#undef DEBUG_TYPE +#define DEBUG_TYPE "CodeViewTypeVisitor" + +//===----------------------------------------------------------------------===// +// TypeRecord traversal. +//===----------------------------------------------------------------------===// +void LVTypeVisitor::printTypeIndex(StringRef FieldName, TypeIndex TI, + uint32_t StreamIdx) const { + codeview::printTypeIndex(W, FieldName, TI, + StreamIdx == StreamTPI ? Types : Ids); +} + +Error LVTypeVisitor::visitTypeBegin(CVType &Record) { + return visitTypeBegin(Record, TypeIndex::fromArrayIndex(Types.size())); +} + +Error LVTypeVisitor::visitTypeBegin(CVType &Record, TypeIndex TI) { + LLVM_DEBUG({ + W.getOStream() << formatTypeLeafKind(Record.kind()); + W.getOStream() << " (" << HexNumber(TI.getIndex()) << ")\n"; + }); + + if (options().getInternalTag()) + Shared->TypeKinds.insert(Record.kind()); + + // The collected type records, will be use to create the logical elements + // during the symbols traversal when a type is referenced. + CurrentTypeIndex = TI; + Shared->TypeRecords.add(StreamIdx, TI, Record.kind()); + return Error::success(); +} + +Error LVTypeVisitor::visitUnknownType(CVType &Record) { + LLVM_DEBUG({ W.printNumber("Length", uint32_t(Record.content().size())); }); + return Error::success(); +} + +Error LVTypeVisitor::visitMemberBegin(CVMemberRecord &Record) { + LLVM_DEBUG({ + W.startLine() << formatTypeLeafKind(Record.Kind); + W.getOStream() << " {\n"; + W.indent(); + }); + return Error::success(); +} + +Error LVTypeVisitor::visitMemberEnd(CVMemberRecord &Record) { + LLVM_DEBUG({ + W.unindent(); + W.startLine() << "}\n"; + }); + return Error::success(); +} + +Error LVTypeVisitor::visitUnknownMember(CVMemberRecord &Record) { + LLVM_DEBUG({ W.printHex("UnknownMember", unsigned(Record.Kind)); }); + return Error::success(); +} + +// LF_BUILDINFO (TPI)/(IPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, BuildInfoRecord &Args) { + // All the args are references into the TPI/IPI stream. + LLVM_DEBUG({ + W.printNumber("NumArgs", static_cast<uint32_t>(Args.getArgs().size())); + ListScope Arguments(W, "Arguments"); + for (TypeIndex Arg : Args.getArgs()) + printTypeIndex("ArgType", Arg, StreamIPI); + }); + + // Only add the strings that hold information about filenames. They will be + // used to complete the line/file information for the logical elements. + // There are other strings holding information about namespaces. + TypeIndex TI; + StringRef String; + + // Absolute CWD path + TI = Args.getArgs()[BuildInfoRecord::BuildInfoArg::CurrentDirectory]; + String = Ids.getTypeName(TI); + if (!String.empty()) + Shared->StringRecords.add(TI, String); + + // Get the compile unit name. + TI = Args.getArgs()[BuildInfoRecord::BuildInfoArg::SourceFile]; + String = Ids.getTypeName(TI); + if (!String.empty()) + Shared->StringRecords.add(TI, String); + LogicalVisitor->setCompileUnitName(std::string(String)); + + return Error::success(); +} + +// LF_CLASS, LF_STRUCTURE, LF_INTERFACE (TPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, ClassRecord &Class) { + LLVM_DEBUG({ + printTypeIndex("TypeIndex", CurrentTypeIndex, StreamTPI); + printTypeIndex("FieldListType", Class.getFieldList(), StreamTPI); + W.printString("Name", Class.getName()); + }); + + // Collect class name for scope deduction. + Shared->NamespaceDeduction.add(Class.getName()); + Shared->ForwardReferences.record(Class.isForwardRef(), Class.getName(), + CurrentTypeIndex); + + // Collect class name for contained scopes deduction. + Shared->TypeRecords.add(StreamIdx, CurrentTypeIndex, Class.getName()); + return Error::success(); +} + +// LF_ENUM (TPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, EnumRecord &Enum) { + LLVM_DEBUG({ + printTypeIndex("TypeIndex", CurrentTypeIndex, StreamTPI); + printTypeIndex("FieldListType", Enum.getFieldList(), StreamTPI); + W.printString("Name", Enum.getName()); + }); + + // Collect enum name for scope deduction. + Shared->NamespaceDeduction.add(Enum.getName()); + return Error::success(); +} + +// LF_FUNC_ID (TPI)/(IPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, FuncIdRecord &Func) { + LLVM_DEBUG({ + printTypeIndex("TypeIndex", CurrentTypeIndex, StreamTPI); + printTypeIndex("Type", Func.getFunctionType(), StreamTPI); + printTypeIndex("Parent", Func.getParentScope(), StreamTPI); + W.printString("Name", Func.getName()); + }); + + // Collect function name for scope deduction. + Shared->NamespaceDeduction.add(Func.getName()); + return Error::success(); +} + +// LF_PROCEDURE (TPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, ProcedureRecord &Proc) { + LLVM_DEBUG({ + printTypeIndex("TypeIndex", CurrentTypeIndex, StreamTPI); + printTypeIndex("ReturnType", Proc.getReturnType(), StreamTPI); + W.printNumber("NumParameters", Proc.getParameterCount()); + printTypeIndex("ArgListType", Proc.getArgumentList(), StreamTPI); + }); + + // Collect procedure information as they can be referenced by typedefs. + Shared->TypeRecords.add(StreamTPI, CurrentTypeIndex, {}); + return Error::success(); +} + +// LF_STRING_ID (TPI)/(IPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, StringIdRecord &String) { + // No additional references are needed. + LLVM_DEBUG({ + printTypeIndex("Id", String.getId(), StreamIPI); + W.printString("StringData", String.getString()); + }); + return Error::success(); +} + +// LF_UDT_SRC_LINE (TPI)/(IPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, + UdtSourceLineRecord &Line) { + // UDT and SourceFile are references into the TPI/IPI stream. + LLVM_DEBUG({ + printTypeIndex("UDT", Line.getUDT(), StreamIPI); + printTypeIndex("SourceFile", Line.getSourceFile(), StreamIPI); + W.printNumber("LineNumber", Line.getLineNumber()); + }); + + Shared->LineRecords.push_back(CurrentTypeIndex); + return Error::success(); +} + +// LF_UNION (TPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, UnionRecord &Union) { + LLVM_DEBUG({ + W.printNumber("MemberCount", Union.getMemberCount()); + printTypeIndex("FieldList", Union.getFieldList(), StreamTPI); + W.printNumber("SizeOf", Union.getSize()); + W.printString("Name", Union.getName()); + if (Union.hasUniqueName()) + W.printString("UniqueName", Union.getUniqueName()); + }); + + // Collect union name for scope deduction. + Shared->NamespaceDeduction.add(Union.getName()); + Shared->ForwardReferences.record(Union.isForwardRef(), Union.getName(), + CurrentTypeIndex); + + // Collect class name for contained scopes deduction. + Shared->TypeRecords.add(StreamIdx, CurrentTypeIndex, Union.getName()); + return Error::success(); +} + +#undef DEBUG_TYPE +#define DEBUG_TYPE "CodeViewSymbolVisitor" + +//===----------------------------------------------------------------------===// +// SymbolRecord traversal. +//===----------------------------------------------------------------------===// +void LVSymbolVisitorDelegate::printRelocatedField(StringRef Label, + uint32_t RelocOffset, + uint32_t Offset, + StringRef *RelocSym) { + Reader->printRelocatedField(Label, CoffSection, RelocOffset, Offset, + RelocSym); +} + +void LVSymbolVisitorDelegate::getLinkageName(uint32_t RelocOffset, + uint32_t Offset, + StringRef *RelocSym) { + Reader->getLinkageName(CoffSection, RelocOffset, Offset, RelocSym); +} + +StringRef +LVSymbolVisitorDelegate::getFileNameForFileOffset(uint32_t FileOffset) { + Expected<StringRef> Name = Reader->getFileNameForFileOffset(FileOffset); + if (!Name) { + consumeError(Name.takeError()); + return {}; + } + return *Name; +} + +DebugStringTableSubsectionRef LVSymbolVisitorDelegate::getStringTable() { + return Reader->CVStringTable; +} + +void LVSymbolVisitor::printLocalVariableAddrRange( + const LocalVariableAddrRange &Range, uint32_t RelocationOffset) { + DictScope S(W, "LocalVariableAddrRange"); + if (ObjDelegate) + ObjDelegate->printRelocatedField("OffsetStart", RelocationOffset, + Range.OffsetStart); + W.printHex("ISectStart", Range.ISectStart); + W.printHex("Range", Range.Range); +} + +void LVSymbolVisitor::printLocalVariableAddrGap( + ArrayRef<LocalVariableAddrGap> Gaps) { + for (const LocalVariableAddrGap &Gap : Gaps) { + ListScope S(W, "LocalVariableAddrGap"); + W.printHex("GapStartOffset", Gap.GapStartOffset); + W.printHex("Range", Gap.Range); + } +} + +void LVSymbolVisitor::printTypeIndex(StringRef FieldName, TypeIndex TI) const { + codeview::printTypeIndex(W, FieldName, TI, Types); +} + +Error LVSymbolVisitor::visitSymbolBegin(CVSymbol &Record) { + return visitSymbolBegin(Record, 0); +} + +Error LVSymbolVisitor::visitSymbolBegin(CVSymbol &Record, uint32_t Offset) { + SymbolKind Kind = Record.kind(); + LLVM_DEBUG({ + W.printNumber("Offset", Offset); + W.printEnum("Begin Kind", unsigned(Kind), getSymbolTypeNames()); + }); + + if (options().getInternalTag()) + Shared->SymbolKinds.insert(Kind); + + LogicalVisitor->CurrentElement = LogicalVisitor->createElement(Kind); + if (!LogicalVisitor->CurrentElement) { + LLVM_DEBUG({ + // We have an unsupported Symbol or Type Record. + // W.printEnum("Kind ignored", unsigned(Kind), getSymbolTypeNames()); + }); + return Error::success(); + } + + // Offset carried by the traversal routines when dealing with streams. + CurrentOffset = Offset; + IsCompileUnit = false; + if (!LogicalVisitor->CurrentElement->getOffsetFromTypeIndex()) + LogicalVisitor->CurrentElement->setOffset(Offset); + if (symbolOpensScope(Kind) || (IsCompileUnit = symbolIsCompileUnit(Kind))) { + assert(LogicalVisitor->CurrentScope && "Invalid scope!"); + LogicalVisitor->addElement(LogicalVisitor->CurrentScope, IsCompileUnit); + } else { + if (LogicalVisitor->CurrentSymbol) + LogicalVisitor->addElement(LogicalVisitor->CurrentSymbol); + if (LogicalVisitor->CurrentType) + LogicalVisitor->addElement(LogicalVisitor->CurrentType); + } + + return Error::success(); +} + +Error LVSymbolVisitor::visitSymbolEnd(CVSymbol &Record) { + SymbolKind Kind = Record.kind(); + LLVM_DEBUG( + { W.printEnum("End Kind", unsigned(Kind), getSymbolTypeNames()); }); + + if (symbolEndsScope(Kind)) { + LogicalVisitor->popScope(); + } + + return Error::success(); +} + +Error LVSymbolVisitor::visitUnknownSymbol(CVSymbol &Record) { + LLVM_DEBUG({ W.printNumber("Length", Record.length()); }); + return Error::success(); +} + +// S_BLOCK32 +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, BlockSym &Block) { + LLVM_DEBUG({ + W.printHex("CodeSize", Block.CodeSize); + W.printHex("Segment", Block.Segment); + W.printString("BlockName", Block.Name); + }); + + if (LVScope *Scope = LogicalVisitor->CurrentScope) { + StringRef LinkageName; + if (ObjDelegate) + ObjDelegate->getLinkageName(Block.getRelocationOffset(), Block.CodeOffset, + &LinkageName); + Scope->setLinkageName(LinkageName); + + if (options().getGeneralCollectRanges()) { + // Record converted segment::offset addressing for this scope. + LVAddress Addendum = Reader->getSymbolTableAddress(LinkageName); + LVAddress LowPC = + Reader->linearAddress(Block.Segment, Block.CodeOffset, Addendum); + LVAddress HighPC = LowPC + Block.CodeSize - 1; + Scope->addObject(LowPC, HighPC); + } + } + + return Error::success(); +} + +// S_BPREL32 +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + BPRelativeSym &Local) { + LLVM_DEBUG({ + printTypeIndex("Type", Local.Type); + W.printNumber("Offset", Local.Offset); + W.printString("VarName", Local.Name); + }); + + if (LVSymbol *Symbol = LogicalVisitor->CurrentSymbol) { + Symbol->setName(Local.Name); + // From the MS_Symbol_Type.pdf documentation (S_BPREL32): + // This symbol specifies symbols that are allocated on the stack for a + // procedure. For C and C++, these include the actual function parameters + // and the local non-static variables of functions. + // However, the offset for 'this' comes as a negative value. + + // Symbol was created as 'variable'; determine its real kind. + Symbol->resetIsVariable(); + + if (Local.Name.equals("this")) { + Symbol->setIsParameter(); + Symbol->setIsArtificial(); + } else { + // Determine symbol kind. + bool(Local.Offset > 0) ? Symbol->setIsParameter() + : Symbol->setIsVariable(); + } + + // Update correct debug information tag. + if (Symbol->getIsParameter()) + Symbol->setTag(dwarf::DW_TAG_formal_parameter); + + LVElement *Element = LogicalVisitor->getElement(StreamTPI, Local.Type); + if (Element && Element->getIsScoped()) { + // We have a local type. Find its parent function. + LVScope *Parent = Symbol->getFunctionParent(); + // The element representing the type has been already finalized. If + // the type is an aggregate type, its members have been already added. + // As the type is local, its level will be changed. + + // FIXME: Currently the algorithm used to scope lambda functions is + // incorrect. Before we allocate the type at this scope, check if is + // already allocated in other scope. + if (!Element->getParentScope()) { + Parent->addElement(Element); + Element->updateLevel(Parent); + } + } + Symbol->setType(Element); + } + + return Error::success(); +} + +// S_REGREL32 +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + RegRelativeSym &Local) { + LLVM_DEBUG({ + printTypeIndex("Type", Local.Type); + W.printNumber("Offset", Local.Offset); + W.printString("VarName", Local.Name); + }); + + if (LVSymbol *Symbol = LogicalVisitor->CurrentSymbol) { + Symbol->setName(Local.Name); + + // Symbol was created as 'variable'; determine its real kind. + Symbol->resetIsVariable(); + + // Check for the 'this' symbol. + if (Local.Name.equals("this")) { + Symbol->setIsArtificial(); + Symbol->setIsParameter(); + } else { + // Determine symbol kind. + determineSymbolKind(Symbol, Local.Register); + } + + // Update correct debug information tag. + if (Symbol->getIsParameter()) + Symbol->setTag(dwarf::DW_TAG_formal_parameter); + + LVElement *Element = LogicalVisitor->getElement(StreamTPI, Local.Type); + if (Element && Element->getIsScoped()) { + // We have a local type. Find its parent function. + LVScope *Parent = Symbol->getFunctionParent(); + // The element representing the type has been already finalized. If + // the type is an aggregate type, its members have been already added. + // As the type is local, its level will be changed. + + // FIXME: Currently the algorithm used to scope lambda functions is + // incorrect. Before we allocate the type at this scope, check if is + // already allocated in other scope. + if (!Element->getParentScope()) { + Parent->addElement(Element); + Element->updateLevel(Parent); + } + } + Symbol->setType(Element); + } + + return Error::success(); +} + +// S_BUILDINFO +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &CVR, + BuildInfoSym &BuildInfo) { + LLVM_DEBUG({ printTypeIndex("BuildId", BuildInfo.BuildId); }); + + CVType CVBuildType = Ids.getType(BuildInfo.BuildId); + if (Error Err = LogicalVisitor->finishVisitation( + CVBuildType, BuildInfo.BuildId, Reader->getCompileUnit())) + return Err; + + return Error::success(); +} + +// S_COMPILE2 +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + Compile2Sym &Compile2) { + LLVM_DEBUG({ + W.printEnum("Language", uint8_t(Compile2.getLanguage()), + getSourceLanguageNames()); + W.printFlags("Flags", uint32_t(Compile2.getFlags()), + getCompileSym3FlagNames()); + W.printEnum("Machine", unsigned(Compile2.Machine), getCPUTypeNames()); + W.printString("VersionName", Compile2.Version); + }); + + // MSVC generates the following sequence for a CodeView module: + // S_OBJNAME --> Set 'CurrentObjectName'. + // S_COMPILE2 --> Set the compile unit name using 'CurrentObjectName'. + // ... + // S_BUILDINFO --> Extract the source name. + // + // Clang generates the following sequence for a CodeView module: + // S_COMPILE2 --> Set the compile unit name to empty string. + // ... + // S_BUILDINFO --> Extract the source name. + // + // For both toolchains, update the compile unit name from S_BUILDINFO. + if (LVScope *Scope = LogicalVisitor->CurrentScope) { + // The name of the CU, was extracted from the 'BuildInfo' subsection. + Reader->setCompileUnitCPUType(Compile2.Machine); + Scope->setName(CurrentObjectName); + if (options().getAttributeProducer()) + Scope->setProducer(Compile2.Version); + getReader().isSystemEntry(Scope, CurrentObjectName); + + // The line records in CodeView are recorded per Module ID. Update + // the relationship between the current CU and the Module ID. + Reader->addModule(Scope); + + // Updated the collected strings with their associated compile unit. + Shared->StringRecords.addFilenames(Reader->getCompileUnit()); + } + + // Clear any previous ObjectName. + CurrentObjectName = ""; + return Error::success(); +} + +// S_COMPILE3 +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + Compile3Sym &Compile3) { + LLVM_DEBUG({ + W.printEnum("Language", uint8_t(Compile3.getLanguage()), + getSourceLanguageNames()); + W.printFlags("Flags", uint32_t(Compile3.getFlags()), + getCompileSym3FlagNames()); + W.printEnum("Machine", unsigned(Compile3.Machine), getCPUTypeNames()); + W.printString("VersionName", Compile3.Version); + }); + + // MSVC generates the following sequence for a CodeView module: + // S_OBJNAME --> Set 'CurrentObjectName'. + // S_COMPILE3 --> Set the compile unit name using 'CurrentObjectName'. + // ... + // S_BUILDINFO --> Extract the source name. + // + // Clang generates the following sequence for a CodeView module: + // S_COMPILE3 --> Set the compile unit name to empty string. + // ... + // S_BUILDINFO --> Extract the source name. + // + // For both toolchains, update the compile unit name from S_BUILDINFO. + if (LVScope *Scope = LogicalVisitor->CurrentScope) { + // The name of the CU, was extracted from the 'BuildInfo' subsection. + Reader->setCompileUnitCPUType(Compile3.Machine); + Scope->setName(CurrentObjectName); + if (options().getAttributeProducer()) + Scope->setProducer(Compile3.Version); + getReader().isSystemEntry(Scope, CurrentObjectName); + + // The line records in CodeView are recorded per Module ID. Update + // the relationship between the current CU and the Module ID. + Reader->addModule(Scope); + + // Updated the collected strings with their associated compile unit. + Shared->StringRecords.addFilenames(Reader->getCompileUnit()); + } + + // Clear any previous ObjectName. + CurrentObjectName = ""; + return Error::success(); +} + +// S_CONSTANT, S_MANCONSTANT +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + ConstantSym &Constant) { + LLVM_DEBUG({ + printTypeIndex("Type", Constant.Type); + W.printNumber("Value", Constant.Value); + W.printString("Name", Constant.Name); + }); + + if (LVSymbol *Symbol = LogicalVisitor->CurrentSymbol) { + Symbol->setName(Constant.Name); + Symbol->setType(LogicalVisitor->getElement(StreamTPI, Constant.Type)); + Symbol->resetIncludeInPrint(); + } + + return Error::success(); +} + +// S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE +Error LVSymbolVisitor::visitKnownRecord( + CVSymbol &Record, + DefRangeFramePointerRelFullScopeSym &DefRangeFramePointerRelFullScope) { + // DefRanges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + W.printNumber("Offset", DefRangeFramePointerRelFullScope.Offset); + }); + + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Offset, 0]. + dwarf::Attribute Attr = + dwarf::Attribute(SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE); + + uint64_t Operand1 = DefRangeFramePointerRelFullScope.Offset; + Symbol->addLocation(Attr, 0, 0, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0); + } + + return Error::success(); +} + +// S_DEFRANGE_FRAMEPOINTER_REL +Error LVSymbolVisitor::visitKnownRecord( + CVSymbol &Record, DefRangeFramePointerRelSym &DefRangeFramePointerRel) { + // DefRanges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + W.printNumber("Offset", DefRangeFramePointerRel.Hdr.Offset); + printLocalVariableAddrRange(DefRangeFramePointerRel.Range, + DefRangeFramePointerRel.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeFramePointerRel.Gaps); + }); + + // We are expecting the following sequence: + // 128 | S_LOCAL [size = 20] `ParamBar` + // ... + // 148 | S_DEFRANGE_FRAMEPOINTER_REL [size = 16] + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Offset, 0]. + dwarf::Attribute Attr = + dwarf::Attribute(SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL); + uint64_t Operand1 = DefRangeFramePointerRel.Hdr.Offset; + + LocalVariableAddrRange Range = DefRangeFramePointerRel.Range; + LVAddress Address = + Reader->linearAddress(Range.ISectStart, Range.OffsetStart); + + Symbol->addLocation(Attr, Address, Address + Range.Range, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0); + } + + return Error::success(); +} + +// S_DEFRANGE_REGISTER_REL +Error LVSymbolVisitor::visitKnownRecord( + CVSymbol &Record, DefRangeRegisterRelSym &DefRangeRegisterRel) { + // DefRanges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + W.printBoolean("HasSpilledUDTMember", + DefRangeRegisterRel.hasSpilledUDTMember()); + W.printNumber("OffsetInParent", DefRangeRegisterRel.offsetInParent()); + W.printNumber("BasePointerOffset", + DefRangeRegisterRel.Hdr.BasePointerOffset); + printLocalVariableAddrRange(DefRangeRegisterRel.Range, + DefRangeRegisterRel.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeRegisterRel.Gaps); + }); + + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Register, Offset]. + dwarf::Attribute Attr = + dwarf::Attribute(SymbolKind::S_DEFRANGE_REGISTER_REL); + uint64_t Operand1 = DefRangeRegisterRel.Hdr.Register; + uint64_t Operand2 = DefRangeRegisterRel.Hdr.BasePointerOffset; + + LocalVariableAddrRange Range = DefRangeRegisterRel.Range; + LVAddress Address = + Reader->linearAddress(Range.ISectStart, Range.OffsetStart); + + Symbol->addLocation(Attr, Address, Address + Range.Range, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, Operand2); + } + + return Error::success(); +} + +// S_DEFRANGE_REGISTER +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + DefRangeRegisterSym &DefRangeRegister) { + // DefRanges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + W.printEnum("Register", uint16_t(DefRangeRegister.Hdr.Register), + getRegisterNames(Reader->getCompileUnitCPUType())); + W.printNumber("MayHaveNoName", DefRangeRegister.Hdr.MayHaveNoName); + printLocalVariableAddrRange(DefRangeRegister.Range, + DefRangeRegister.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeRegister.Gaps); + }); + + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Register, 0]. + dwarf::Attribute Attr = dwarf::Attribute(SymbolKind::S_DEFRANGE_REGISTER); + uint64_t Operand1 = DefRangeRegister.Hdr.Register; + + LocalVariableAddrRange Range = DefRangeRegister.Range; + LVAddress Address = + Reader->linearAddress(Range.ISectStart, Range.OffsetStart); + + Symbol->addLocation(Attr, Address, Address + Range.Range, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0); + } + + return Error::success(); +} + +// S_DEFRANGE_SUBFIELD_REGISTER +Error LVSymbolVisitor::visitKnownRecord( + CVSymbol &Record, DefRangeSubfieldRegisterSym &DefRangeSubfieldRegister) { + // DefRanges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + W.printEnum("Register", uint16_t(DefRangeSubfieldRegister.Hdr.Register), + getRegisterNames(Reader->getCompileUnitCPUType())); + W.printNumber("MayHaveNoName", DefRangeSubfieldRegister.Hdr.MayHaveNoName); + W.printNumber("OffsetInParent", + DefRangeSubfieldRegister.Hdr.OffsetInParent); + printLocalVariableAddrRange(DefRangeSubfieldRegister.Range, + DefRangeSubfieldRegister.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeSubfieldRegister.Gaps); + }); + + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Register, 0]. + dwarf::Attribute Attr = + dwarf::Attribute(SymbolKind::S_DEFRANGE_SUBFIELD_REGISTER); + uint64_t Operand1 = DefRangeSubfieldRegister.Hdr.Register; + + LocalVariableAddrRange Range = DefRangeSubfieldRegister.Range; + LVAddress Address = + Reader->linearAddress(Range.ISectStart, Range.OffsetStart); + + Symbol->addLocation(Attr, Address, Address + Range.Range, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0); + } + + return Error::success(); +} + +// S_DEFRANGE_SUBFIELD +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + DefRangeSubfieldSym &DefRangeSubfield) { + // DefRanges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + if (ObjDelegate) { + DebugStringTableSubsectionRef Strings = ObjDelegate->getStringTable(); + auto ExpectedProgram = Strings.getString(DefRangeSubfield.Program); + if (!ExpectedProgram) { + consumeError(ExpectedProgram.takeError()); + return llvm::make_error<CodeViewError>( + "String table offset outside of bounds of String Table!"); + } + W.printString("Program", *ExpectedProgram); + } + W.printNumber("OffsetInParent", DefRangeSubfield.OffsetInParent); + printLocalVariableAddrRange(DefRangeSubfield.Range, + DefRangeSubfield.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeSubfield.Gaps); + }); + + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Program, 0]. + dwarf::Attribute Attr = dwarf::Attribute(SymbolKind::S_DEFRANGE_SUBFIELD); + uint64_t Operand1 = DefRangeSubfield.Program; + + LocalVariableAddrRange Range = DefRangeSubfield.Range; + LVAddress Address = + Reader->linearAddress(Range.ISectStart, Range.OffsetStart); + + Symbol->addLocation(Attr, Address, Address + Range.Range, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0); + } + + return Error::success(); +} + +// S_DEFRANGE +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + DefRangeSym &DefRange) { + // DefRanges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + if (ObjDelegate) { + DebugStringTableSubsectionRef Strings = ObjDelegate->getStringTable(); + auto ExpectedProgram = Strings.getString(DefRange.Program); + if (!ExpectedProgram) { + consumeError(ExpectedProgram.takeError()); + return llvm::make_error<CodeViewError>( + "String table offset outside of bounds of String Table!"); + } + W.printString("Program", *ExpectedProgram); + } + printLocalVariableAddrRange(DefRange.Range, DefRange.getRelocationOffset()); + printLocalVariableAddrGap(DefRange.Gaps); + }); + + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Program, 0]. + dwarf::Attribute Attr = dwarf::Attribute(SymbolKind::S_DEFRANGE); + uint64_t Operand1 = DefRange.Program; + + LocalVariableAddrRange Range = DefRange.Range; + LVAddress Address = + Reader->linearAddress(Range.ISectStart, Range.OffsetStart); + + Symbol->addLocation(Attr, Address, Address + Range.Range, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0); + } + + return Error::success(); +} + +// S_FRAMEPROC +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + FrameProcSym &FrameProc) { + if (LVScope *Function = LogicalVisitor->getReaderScope()) { + // S_FRAMEPROC contains extra information for the function described + // by any of the previous generated records: + // S_GPROC32, S_LPROC32, S_LPROC32_ID, S_GPROC32_ID. + + // The generated sequence is: + // S_GPROC32_ID ... + // S_FRAMEPROC ... + + // Collect additional inline flags for the current scope function. + FrameProcedureOptions Flags = FrameProc.Flags; + if (FrameProcedureOptions::MarkedInline == + (Flags & FrameProcedureOptions::MarkedInline)) + Function->setInlineCode(dwarf::DW_INL_declared_inlined); + if (FrameProcedureOptions::Inlined == + (Flags & FrameProcedureOptions::Inlined)) + Function->setInlineCode(dwarf::DW_INL_inlined); + + // To determine the symbol kind for any symbol declared in that function, + // we can access the S_FRAMEPROC for the parent scope function. It contains + // information about the local fp and param fp registers and compare with + // the register in the S_REGREL32 to get a match. + codeview::CPUType CPU = Reader->getCompileUnitCPUType(); + LocalFrameRegister = FrameProc.getLocalFramePtrReg(CPU); + ParamFrameRegister = FrameProc.getParamFramePtrReg(CPU); + } + + return Error::success(); +} + +// S_GDATA32, S_LDATA32, S_LMANDATA, S_GMANDATA +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, DataSym &Data) { + LLVM_DEBUG({ + printTypeIndex("Type", Data.Type); + W.printString("DisplayName", Data.Name); + }); + + if (LVSymbol *Symbol = LogicalVisitor->CurrentSymbol) { + StringRef LinkageName; + if (ObjDelegate) + ObjDelegate->getLinkageName(Data.getRelocationOffset(), Data.DataOffset, + &LinkageName); + + Symbol->setName(Data.Name); + Symbol->setLinkageName(LinkageName); + + // The MSVC generates local data as initialization for aggregates. It + // contains the address for an initialization function. + // The symbols contains the '$initializer$' pattern. Allow them only if + // the '--internal=system' option is given. + // 0 | S_LDATA32 `Struct$initializer$` + // type = 0x1040 (void ()*) + if (getReader().isSystemEntry(Symbol) && !options().getAttributeSystem()) { + Symbol->resetIncludeInPrint(); + return Error::success(); + } + + if (LVScope *Namespace = Shared->NamespaceDeduction.get(Data.Name)) { + // The variable is already at different scope. In order to reflect + // the correct parent, move it to the namespace. + if (Symbol->getParentScope()->removeElement(Symbol)) + Namespace->addElement(Symbol); + } + + Symbol->setType(LogicalVisitor->getElement(StreamTPI, Data.Type)); + if (Record.kind() == SymbolKind::S_GDATA32) + Symbol->setIsExternal(); + } + + return Error::success(); +} + +// S_INLINESITE +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + InlineSiteSym &InlineSite) { + LLVM_DEBUG({ printTypeIndex("Inlinee", InlineSite.Inlinee); }); + + if (LVScope *InlinedFunction = LogicalVisitor->CurrentScope) { + LVScope *AbstractFunction = Reader->createScopeFunction(); + AbstractFunction->setIsSubprogram(); + AbstractFunction->setTag(dwarf::DW_TAG_subprogram); + AbstractFunction->setInlineCode(dwarf::DW_INL_inlined); + AbstractFunction->setIsInlinedAbstract(); + InlinedFunction->setReference(AbstractFunction); + + LogicalVisitor->startProcessArgumentList(); + // 'Inlinee' is a Type ID. + CVType CVFunctionType = Ids.getType(InlineSite.Inlinee); + if (Error Err = LogicalVisitor->finishVisitation( + CVFunctionType, InlineSite.Inlinee, AbstractFunction)) + return Err; + LogicalVisitor->stopProcessArgumentList(); + + // For inlined functions set the linkage name to be the same as + // the name. It used to find their lines and ranges. + StringRef Name = AbstractFunction->getName(); + InlinedFunction->setName(Name); + InlinedFunction->setLinkageName(Name); + + // Process annotation bytes to calculate code and line offsets. + if (Error Err = LogicalVisitor->inlineSiteAnnotation( + AbstractFunction, InlinedFunction, InlineSite)) + return Err; + } + + return Error::success(); +} + +// S_LOCAL +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, LocalSym &Local) { + LLVM_DEBUG({ + printTypeIndex("Type", Local.Type); + W.printFlags("Flags", uint16_t(Local.Flags), getLocalFlagNames()); + W.printString("VarName", Local.Name); + }); + + if (LVSymbol *Symbol = LogicalVisitor->CurrentSymbol) { + Symbol->setName(Local.Name); + + // Symbol was created as 'variable'; determine its real kind. + Symbol->resetIsVariable(); + + // Be sure the 'this' symbol is marked as 'compiler generated'. + if (bool(Local.Flags & LocalSymFlags::IsCompilerGenerated) || + Local.Name.equals("this")) { + Symbol->setIsArtificial(); + Symbol->setIsParameter(); + } else { + bool(Local.Flags & LocalSymFlags::IsParameter) ? Symbol->setIsParameter() + : Symbol->setIsVariable(); + } + + // Update correct debug information tag. + if (Symbol->getIsParameter()) + Symbol->setTag(dwarf::DW_TAG_formal_parameter); + + LVElement *Element = LogicalVisitor->getElement(StreamTPI, Local.Type); + if (Element && Element->getIsScoped()) { + // We have a local type. Find its parent function. + LVScope *Parent = Symbol->getFunctionParent(); + // The element representing the type has been already finalized. If + // the type is an aggregate type, its members have been already added. + // As the type is local, its level will be changed. + Parent->addElement(Element); + Element->updateLevel(Parent); + } + Symbol->setType(Element); + + // The CodeView records (S_DEFFRAME_*) describing debug location for + // this symbol, do not have any direct reference to it. Those records + // are emitted after this symbol. Record the current symbol. + LocalSymbol = Symbol; + } + + return Error::success(); +} + +// S_OBJNAME +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, ObjNameSym &ObjName) { + LLVM_DEBUG({ + W.printHex("Signature", ObjName.Signature); + W.printString("ObjectName", ObjName.Name); + }); + + CurrentObjectName = ObjName.Name; + return Error::success(); +} + +// S_GPROC32, S_LPROC32, S_LPROC32_ID, S_GPROC32_ID +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, ProcSym &Proc) { + if (InFunctionScope) + return llvm::make_error<CodeViewError>("Visiting a ProcSym while inside " + "function scope!"); + + InFunctionScope = true; + + LLVM_DEBUG({ + printTypeIndex("FunctionType", Proc.FunctionType); + W.printHex("Segment", Proc.Segment); + W.printFlags("Flags", static_cast<uint8_t>(Proc.Flags), + getProcSymFlagNames()); + W.printString("DisplayName", Proc.Name); + }); + + // Clang and Microsoft generated different debug information records: + // For functions definitions: + // Clang: S_GPROC32 -> LF_FUNC_ID -> LF_PROCEDURE + // Microsoft: S_GPROC32 -> LF_PROCEDURE + + // For member function definition: + // Clang: S_GPROC32 -> LF_MFUNC_ID -> LF_MFUNCTION + // Microsoft: S_GPROC32 -> LF_MFUNCTION + // In order to support both sequences, if we found LF_FUNCTION_ID, just + // get the TypeIndex for LF_PROCEDURE. + + // For the given test case, we have the sequence: + // namespace NSP_local { + // void foo_local() { + // } + // } + // + // 0x1000 | LF_STRING_ID String: NSP_local + // 0x1002 | LF_PROCEDURE + // return type = 0x0003 (void), # args = 0, param list = 0x1001 + // calling conv = cdecl, options = None + // 0x1003 | LF_FUNC_ID + // name = foo_local, type = 0x1002, parent scope = 0x1000 + // 0 | S_GPROC32_ID `NSP_local::foo_local` + // type = `0x1003 (foo_local)` + // 0x1004 | LF_STRING_ID String: suite + // 0x1005 | LF_STRING_ID String: suite_local.cpp + // + // The LF_STRING_ID can hold different information: + // 0x1000 - The enclosing namespace. + // 0x1004 - The compile unit directory name. + // 0x1005 - The compile unit name. + // + // Before deducting its scope, we need to evaluate its type and create any + // associated namespaces. + if (LVScope *Function = LogicalVisitor->CurrentScope) { + StringRef LinkageName; + if (ObjDelegate) + ObjDelegate->getLinkageName(Proc.getRelocationOffset(), Proc.CodeOffset, + &LinkageName); + + // The line table can be accessed using the linkage name. + Reader->addToSymbolTable(LinkageName, Function); + Function->setName(Proc.Name); + Function->setLinkageName(LinkageName); + + if (options().getGeneralCollectRanges()) { + // Record converted segment::offset addressing for this scope. + LVAddress Addendum = Reader->getSymbolTableAddress(LinkageName); + LVAddress LowPC = + Reader->linearAddress(Proc.Segment, Proc.CodeOffset, Addendum); + LVAddress HighPC = LowPC + Proc.CodeSize - 1; + Function->addObject(LowPC, HighPC); + + // If the scope is a function, add it to the public names. + if ((options().getAttributePublics() || options().getPrintAnyLine()) && + !Function->getIsInlinedFunction()) + Reader->getCompileUnit()->addPublicName(Function, LowPC, HighPC); + } + + if (Function->getIsSystem() && !options().getAttributeSystem()) { + Function->resetIncludeInPrint(); + return Error::success(); + } + + TypeIndex TIFunctionType = Proc.FunctionType; + if (TIFunctionType.isSimple()) + Function->setType(LogicalVisitor->getElement(StreamTPI, TIFunctionType)); + else { + // We have to detect the correct stream, using the lexical parent + // name, as there is not other obvious way to get the stream. + // Normal function: LF_FUNC_ID (TPI)/(IPI) + // LF_PROCEDURE (TPI) + // Lambda function: LF_MFUNCTION (TPI) + // Member function: LF_MFUNC_ID (TPI)/(IPI) + + StringRef OuterComponent; + std::tie(OuterComponent, std::ignore) = getInnerComponent(Proc.Name); + TypeIndex TI = Shared->ForwardReferences.find(OuterComponent); + + std::optional<CVType> CVFunctionType; + auto GetRecordType = [&]() -> bool { + CVFunctionType = Ids.tryGetType(TIFunctionType); + if (!CVFunctionType) + return false; + + if (TI.isNoneType()) + // Normal function. + if (CVFunctionType->kind() == LF_FUNC_ID) + return true; + + // Member function. + return (CVFunctionType->kind() == LF_MFUNC_ID); + }; + + // We can have a LF_FUNC_ID, LF_PROCEDURE or LF_MFUNCTION. + if (!GetRecordType()) { + CVFunctionType = Types.tryGetType(TIFunctionType); + if (!CVFunctionType) + return llvm::make_error<CodeViewError>("Invalid type index"); + } + + if (Error Err = LogicalVisitor->finishVisitation( + *CVFunctionType, TIFunctionType, Function)) + return Err; + } + + if (Record.kind() == SymbolKind::S_GPROC32 || + Record.kind() == SymbolKind::S_GPROC32_ID) + Function->setIsExternal(); + + // We don't have a way to see if the symbol is compiler generated. Use + // the linkage name, to detect `scalar deleting destructor' functions. + std::string DemangledSymbol = demangle(std::string(LinkageName)); + if (DemangledSymbol.find("scalar deleting dtor") != std::string::npos) { + Function->setIsArtificial(); + } else { + // Clang generates global ctor and dtor names containing the substrings: + // 'dynamic initializer for' and 'dynamic atexit destructor for'. + if (DemangledSymbol.find("dynamic atexit destructor for") != + std::string::npos) + Function->setIsArtificial(); + } + } + + return Error::success(); +} + +// S_END +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + ScopeEndSym &ScopeEnd) { + InFunctionScope = false; + return Error::success(); +} + +// S_THUNK32 +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, Thunk32Sym &Thunk) { + if (InFunctionScope) + return llvm::make_error<CodeViewError>("Visiting a Thunk32Sym while inside " + "function scope!"); + + InFunctionScope = true; + + LLVM_DEBUG({ + W.printHex("Segment", Thunk.Segment); + W.printString("Name", Thunk.Name); + }); + + if (LVScope *Function = LogicalVisitor->CurrentScope) + Function->setName(Thunk.Name); + + return Error::success(); +} + +// S_UDT, S_COBOLUDT +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, UDTSym &UDT) { + LLVM_DEBUG({ + printTypeIndex("Type", UDT.Type); + W.printString("UDTName", UDT.Name); + }); + + if (LVType *Type = LogicalVisitor->CurrentType) { + if (LVScope *Namespace = Shared->NamespaceDeduction.get(UDT.Name)) { + if (Type->getParentScope()->removeElement(Type)) + Namespace->addElement(Type); + } + + Type->setName(UDT.Name); + + // We have to determine if the typedef is a real C/C++ definition or is + // the S_UDT record that describe all the user defined types. + // 0 | S_UDT `Name` original type = 0x1009 + // 0x1009 | LF_STRUCTURE `Name` + // Ignore type definitions for RTTI types: + // _s__RTTIBaseClassArray, _s__RTTIBaseClassDescriptor, + // _s__RTTICompleteObjectLocator, _s__RTTIClassHierarchyDescriptor. + if (getReader().isSystemEntry(Type)) + Type->resetIncludeInPrint(); + else { + StringRef RecordName = getRecordName(Types, UDT.Type); + if (UDT.Name.equals(RecordName)) + Type->resetIncludeInPrint(); + Type->setType(LogicalVisitor->getElement(StreamTPI, UDT.Type)); + } + } + + return Error::success(); +} + +// S_UNAMESPACE +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + UsingNamespaceSym &UN) { + LLVM_DEBUG({ W.printString("Namespace", UN.Name); }); + return Error::success(); +} + +#undef DEBUG_TYPE +#define DEBUG_TYPE "CodeViewLogicalVisitor" + +//===----------------------------------------------------------------------===// +// Logical visitor. +//===----------------------------------------------------------------------===// +LVLogicalVisitor::LVLogicalVisitor(LVCodeViewReader *Reader, ScopedPrinter &W, + InputFile &Input) + : Reader(Reader), W(W), Input(Input) { + // The LogicalVisitor connects the CodeViewReader with the visitors that + // traverse the types, symbols, etc. Do any initialization that is needed. + Shared = std::make_shared<LVShared>(Reader, this); +} + +void LVLogicalVisitor::printTypeIndex(StringRef FieldName, TypeIndex TI, + uint32_t StreamIdx) { + codeview::printTypeIndex(W, FieldName, TI, + StreamIdx == StreamTPI ? types() : ids()); +} + +void LVLogicalVisitor::printTypeBegin(CVType &Record, TypeIndex TI, + LVElement *Element, uint32_t StreamIdx) { + W.getOStream() << "\n"; + W.startLine() << formatTypeLeafKind(Record.kind()); + W.getOStream() << " (" << HexNumber(TI.getIndex()) << ")"; + W.getOStream() << " {\n"; + W.indent(); + W.printEnum("TypeLeafKind", unsigned(Record.kind()), ArrayRef(LeafTypeNames)); + printTypeIndex("TI", TI, StreamIdx); + W.startLine() << "Element: " << HexNumber(Element->getOffset()) << " " + << Element->getName() << "\n"; +} + +void LVLogicalVisitor::printTypeEnd(CVType &Record) { + W.unindent(); + W.startLine() << "}\n"; +} + +void LVLogicalVisitor::printMemberBegin(CVMemberRecord &Record, TypeIndex TI, + LVElement *Element, + uint32_t StreamIdx) { + W.getOStream() << "\n"; + W.startLine() << formatTypeLeafKind(Record.Kind); + W.getOStream() << " (" << HexNumber(TI.getIndex()) << ")"; + W.getOStream() << " {\n"; + W.indent(); + W.printEnum("TypeLeafKind", unsigned(Record.Kind), ArrayRef(LeafTypeNames)); + printTypeIndex("TI", TI, StreamIdx); + W.startLine() << "Element: " << HexNumber(Element->getOffset()) << " " + << Element->getName() << "\n"; +} + +void LVLogicalVisitor::printMemberEnd(CVMemberRecord &Record) { + W.unindent(); + W.startLine() << "}\n"; +} + +Error LVLogicalVisitor::visitUnknownType(CVType &Record, TypeIndex TI) { + LLVM_DEBUG({ + printTypeIndex("\nTI", TI, StreamTPI); + W.printNumber("Length", uint32_t(Record.content().size())); + }); + return Error::success(); +} + +// LF_ARGLIST (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, ArgListRecord &Args, + TypeIndex TI, LVElement *Element) { + ArrayRef<TypeIndex> Indices = Args.getIndices(); + uint32_t Size = Indices.size(); + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printNumber("NumArgs", Size); + ListScope Arguments(W, "Arguments"); + for (uint32_t I = 0; I < Size; ++I) + printTypeIndex("ArgType", Indices[I], StreamTPI); + printTypeEnd(Record); + }); + + LVScope *Function = static_cast<LVScope *>(Element); + for (uint32_t Index = 0; Index < Size; ++Index) { + TypeIndex ParameterType = Indices[Index]; + createParameter(ParameterType, StringRef(), Function); + } + + return Error::success(); +} + +// LF_ARRAY (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, ArrayRecord &AT, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("ElementType", AT.getElementType(), StreamTPI); + printTypeIndex("IndexType", AT.getIndexType(), StreamTPI); + W.printNumber("SizeOf", AT.getSize()); + W.printString("Name", AT.getName()); + printTypeEnd(Record); + }); + + if (Element->getIsFinalized()) + return Error::success(); + Element->setIsFinalized(); + + LVScopeArray *Array = static_cast<LVScopeArray *>(Element); + if (!Array) + return Error::success(); + + Reader->getCompileUnit()->addElement(Array); + TypeIndex TIElementType = AT.getElementType(); + + LVType *PrevSubrange = nullptr; + LazyRandomTypeCollection &Types = types(); + + // As the logical view is modeled on DWARF, for each dimension we have to + // create a DW_TAG_subrange_type, with dimension size. + // The subrange type can be: unsigned __int32 or unsigned __int64. + auto AddSubrangeType = [&](ArrayRecord &AR) { + LVType *Subrange = Reader->createTypeSubrange(); + Subrange->setTag(dwarf::DW_TAG_subrange_type); + Subrange->setType(getElement(StreamTPI, AR.getIndexType())); + Subrange->setCount(AR.getSize()); + Subrange->setOffset( + TIElementType.isSimple() + ? (uint32_t)(TypeLeafKind)TIElementType.getSimpleKind() + : TIElementType.getIndex()); + Array->addElement(Subrange); + + if (PrevSubrange) + if (int64_t Count = Subrange->getCount()) + PrevSubrange->setCount(PrevSubrange->getCount() / Count); + PrevSubrange = Subrange; + }; + + // Preserve the original TypeIndex; it would be updated in the case of: + // - The array type contains qualifiers. + // - In multidimensional arrays, the last LF_ARRAY entry contains the type. + TypeIndex TIArrayType; + + // For each dimension in the array, there is a LF_ARRAY entry. The last + // entry contains the array type, which can be a LF_MODIFIER in the case + // of the type being modified by a qualifier (const, etc). + ArrayRecord AR(AT); + CVType CVEntry = Record; + while (CVEntry.kind() == LF_ARRAY) { + // Create the subrange information, required by the logical view. Once + // the array has been processed, the dimension sizes will updated, as + // the sizes are a progression. For instance: + // sizeof(int) = 4 + // int Array[2]; Sizes: 8 Dim: 8 / 4 -> [2] + // int Array[2][3]; Sizes: 24, 12 Dim: 24 / 12 -> [2] + // Dim: 12 / 4 -> [3] + // int Array[2][3][4]; sizes: 96, 48, 16 Dim: 96 / 48 -> [2] + // Dim: 48 / 16 -> [3] + // Dim: 16 / 4 -> [4] + AddSubrangeType(AR); + TIArrayType = TIElementType; + + // The current ElementType can be a modifier, in which case we need to + // get the type being modified. + // If TypeIndex is not a simple type, check if we have a qualified type. + if (!TIElementType.isSimple()) { + CVType CVElementType = Types.getType(TIElementType); + if (CVElementType.kind() == LF_MODIFIER) { + LVElement *QualifiedType = + Shared->TypeRecords.find(StreamTPI, TIElementType); + if (Error Err = + finishVisitation(CVElementType, TIElementType, QualifiedType)) + return Err; + // Get the TypeIndex of the type that the LF_MODIFIER modifies. + TIElementType = getModifiedType(CVElementType); + } + } + // Ends the traversal, as we have reached a simple type (int, char, etc). + if (TIElementType.isSimple()) + break; + + // Read next dimension linked entry, if any. + CVEntry = Types.getType(TIElementType); + if (Error Err = TypeDeserializer::deserializeAs( + const_cast<CVType &>(CVEntry), AR)) { + consumeError(std::move(Err)); + break; + } + TIElementType = AR.getElementType(); + // NOTE: The typeindex has a value of: 0x0280.0000 + getTrueType(TIElementType); + } + + Array->setName(AT.getName()); + TIArrayType = Shared->ForwardReferences.remap(TIArrayType); + Array->setType(getElement(StreamTPI, TIArrayType)); + + if (PrevSubrange) + // In the case of an aggregate type (class, struct, union, interface), + // get the aggregate size. As the original record is pointing to its + // reference, we have to update it. + if (uint64_t Size = + isAggregate(CVEntry) + ? getSizeInBytesForTypeRecord(Types.getType(TIArrayType)) + : getSizeInBytesForTypeIndex(TIElementType)) + PrevSubrange->setCount(PrevSubrange->getCount() / Size); + + return Error::success(); +} + +// LF_BITFIELD (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, BitFieldRecord &BF, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("Type", TI, StreamTPI); + W.printNumber("BitSize", BF.getBitSize()); + W.printNumber("BitOffset", BF.getBitOffset()); + printTypeEnd(Record); + }); + + Element->setType(getElement(StreamTPI, BF.getType())); + Element->setBitSize(BF.getBitSize()); + return Error::success(); +} + +// LF_BUILDINFO (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, BuildInfoRecord &BI, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamIPI); + W.printNumber("NumArgs", static_cast<uint32_t>(BI.getArgs().size())); + ListScope Arguments(W, "Arguments"); + for (TypeIndex Arg : BI.getArgs()) + printTypeIndex("ArgType", Arg, StreamIPI); + printTypeEnd(Record); + }); + + // The given 'Element' refers to the current compilation unit. + // All the args are references into the TPI/IPI stream. + TypeIndex TIName = BI.getArgs()[BuildInfoRecord::BuildInfoArg::SourceFile]; + std::string Name = std::string(ids().getTypeName(TIName)); + + // There are cases where LF_BUILDINFO fields are empty. + if (!Name.empty()) + Element->setName(Name); + + return Error::success(); +} + +// LF_CLASS, LF_STRUCTURE, LF_INTERFACE (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, ClassRecord &Class, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printNumber("MemberCount", Class.getMemberCount()); + printTypeIndex("FieldList", Class.getFieldList(), StreamTPI); + printTypeIndex("DerivedFrom", Class.getDerivationList(), StreamTPI); + printTypeIndex("VShape", Class.getVTableShape(), StreamTPI); + W.printNumber("SizeOf", Class.getSize()); + W.printString("Name", Class.getName()); + if (Class.hasUniqueName()) + W.printString("UniqueName", Class.getUniqueName()); + printTypeEnd(Record); + }); + + if (Element->getIsFinalized()) + return Error::success(); + Element->setIsFinalized(); + + LVScopeAggregate *Scope = static_cast<LVScopeAggregate *>(Element); + if (!Scope) + return Error::success(); + + Scope->setName(Class.getName()); + if (Class.hasUniqueName()) + Scope->setLinkageName(Class.getUniqueName()); + + if (Class.isNested()) { + Scope->setIsNested(); + createParents(Class.getName(), Scope); + } + + if (Class.isScoped()) + Scope->setIsScoped(); + + // Nested types will be added to their parents at creation. The forward + // references are only processed to finish the referenced element creation. + if (!(Class.isNested() || Class.isScoped())) { + if (LVScope *Namespace = Shared->NamespaceDeduction.get(Class.getName())) + Namespace->addElement(Scope); + else + Reader->getCompileUnit()->addElement(Scope); + } + + LazyRandomTypeCollection &Types = types(); + TypeIndex TIFieldList = Class.getFieldList(); + if (TIFieldList.isNoneType()) { + TypeIndex ForwardType = Shared->ForwardReferences.find(Class.getName()); + if (!ForwardType.isNoneType()) { + CVType CVReference = Types.getType(ForwardType); + TypeRecordKind RK = static_cast<TypeRecordKind>(CVReference.kind()); + ClassRecord ReferenceRecord(RK); + if (Error Err = TypeDeserializer::deserializeAs( + const_cast<CVType &>(CVReference), ReferenceRecord)) + return Err; + TIFieldList = ReferenceRecord.getFieldList(); + } + } + + if (!TIFieldList.isNoneType()) { + // Pass down the TypeIndex 'TI' for the aggregate containing the field list. + CVType CVFieldList = Types.getType(TIFieldList); + if (Error Err = finishVisitation(CVFieldList, TI, Scope)) + return Err; + } + + return Error::success(); +} + +// LF_ENUM (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, EnumRecord &Enum, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printNumber("NumEnumerators", Enum.getMemberCount()); + printTypeIndex("UnderlyingType", Enum.getUnderlyingType(), StreamTPI); + printTypeIndex("FieldListType", Enum.getFieldList(), StreamTPI); + W.printString("Name", Enum.getName()); + printTypeEnd(Record); + }); + + LVScopeEnumeration *Scope = static_cast<LVScopeEnumeration *>(Element); + if (!Scope) + return Error::success(); + + if (Scope->getIsFinalized()) + return Error::success(); + Scope->setIsFinalized(); + + // Set the name, as in the case of nested, it would determine the relation + // to any potential parent, via the LF_NESTTYPE record. + Scope->setName(Enum.getName()); + if (Enum.hasUniqueName()) + Scope->setLinkageName(Enum.getUniqueName()); + + Scope->setType(getElement(StreamTPI, Enum.getUnderlyingType())); + + if (Enum.isNested()) { + Scope->setIsNested(); + createParents(Enum.getName(), Scope); + } + + if (Enum.isScoped()) { + Scope->setIsScoped(); + Scope->setIsEnumClass(); + } + + // Nested types will be added to their parents at creation. + if (!(Enum.isNested() || Enum.isScoped())) { + if (LVScope *Namespace = Shared->NamespaceDeduction.get(Enum.getName())) + Namespace->addElement(Scope); + else + Reader->getCompileUnit()->addElement(Scope); + } + + TypeIndex TIFieldList = Enum.getFieldList(); + if (!TIFieldList.isNoneType()) { + LazyRandomTypeCollection &Types = types(); + CVType CVFieldList = Types.getType(TIFieldList); + if (Error Err = finishVisitation(CVFieldList, TIFieldList, Scope)) + return Err; + } + + return Error::success(); +} + +// LF_FIELDLIST (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + FieldListRecord &FieldList, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeEnd(Record); + }); + + if (Error Err = visitFieldListMemberStream(TI, Element, FieldList.Data)) + return Err; + + return Error::success(); +} + +// LF_FUNC_ID (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, FuncIdRecord &Func, + TypeIndex TI, LVElement *Element) { + // ParentScope and FunctionType are references into the TPI stream. + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamIPI); + printTypeIndex("ParentScope", Func.getParentScope(), StreamTPI); + printTypeIndex("FunctionType", Func.getFunctionType(), StreamTPI); + W.printString("Name", Func.getName()); + printTypeEnd(Record); + }); + + // The TypeIndex (LF_PROCEDURE) returned by 'getFunctionType' is the + // function propotype, we need to use the function definition. + if (LVScope *FunctionDcl = static_cast<LVScope *>(Element)) { + // For inlined functions, the inlined instance has been already processed + // (all its information is contained in the Symbols section). + // 'Element' points to the created 'abstract' (out-of-line) function. + // Use the parent scope information to allocate it to the correct scope. + LazyRandomTypeCollection &Types = types(); + TypeIndex TIParent = Func.getParentScope(); + if (FunctionDcl->getIsInlinedAbstract()) { + FunctionDcl->setName(Func.getName()); + if (TIParent.isNoneType()) + Reader->getCompileUnit()->addElement(FunctionDcl); + } + + if (!TIParent.isNoneType()) { + CVType CVParentScope = ids().getType(TIParent); + if (Error Err = finishVisitation(CVParentScope, TIParent, FunctionDcl)) + return Err; + } + + TypeIndex TIFunctionType = Func.getFunctionType(); + CVType CVFunctionType = Types.getType(TIFunctionType); + if (Error Err = + finishVisitation(CVFunctionType, TIFunctionType, FunctionDcl)) + return Err; + + FunctionDcl->setIsFinalized(); + } + + return Error::success(); +} + +// LF_LABEL (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, LabelRecord &LR, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_MFUNC_ID (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, MemberFuncIdRecord &Id, + TypeIndex TI, LVElement *Element) { + // ClassType and FunctionType are references into the TPI stream. + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamIPI); + printTypeIndex("ClassType", Id.getClassType(), StreamTPI); + printTypeIndex("FunctionType", Id.getFunctionType(), StreamTPI); + W.printString("Name", Id.getName()); + printTypeEnd(Record); + }); + + LVScope *FunctionDcl = static_cast<LVScope *>(Element); + if (FunctionDcl->getIsInlinedAbstract()) { + // For inlined functions, the inlined instance has been already processed + // (all its information is contained in the Symbols section). + // 'Element' points to the created 'abstract' (out-of-line) function. + // Use the parent scope information to allocate it to the correct scope. + if (LVScope *Class = static_cast<LVScope *>( + Shared->TypeRecords.find(StreamTPI, Id.getClassType()))) + Class->addElement(FunctionDcl); + } + + TypeIndex TIFunctionType = Id.getFunctionType(); + CVType CVFunction = types().getType(TIFunctionType); + if (Error Err = finishVisitation(CVFunction, TIFunctionType, Element)) + return Err; + + return Error::success(); +} + +// LF_MFUNCTION (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + MemberFunctionRecord &MF, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("ReturnType", MF.getReturnType(), StreamTPI); + printTypeIndex("ClassType", MF.getClassType(), StreamTPI); + printTypeIndex("ThisType", MF.getThisType(), StreamTPI); + W.printNumber("NumParameters", MF.getParameterCount()); + printTypeIndex("ArgListType", MF.getArgumentList(), StreamTPI); + W.printNumber("ThisAdjustment", MF.getThisPointerAdjustment()); + printTypeEnd(Record); + }); + + if (LVScope *MemberFunction = static_cast<LVScope *>(Element)) { + LVElement *Class = getElement(StreamTPI, MF.getClassType()); + + MemberFunction->setIsFinalized(); + MemberFunction->setType(getElement(StreamTPI, MF.getReturnType())); + MemberFunction->setOffset(TI.getIndex()); + MemberFunction->setOffsetFromTypeIndex(); + + if (ProcessArgumentList) { + ProcessArgumentList = false; + + if (!MemberFunction->getIsStatic()) { + LVElement *ThisPointer = getElement(StreamTPI, MF.getThisType()); + // When creating the 'this' pointer, check if it points to a reference. + ThisPointer->setType(Class); + LVSymbol *This = + createParameter(ThisPointer, StringRef(), MemberFunction); + This->setIsArtificial(); + } + + // Create formal parameters. + LazyRandomTypeCollection &Types = types(); + CVType CVArguments = Types.getType(MF.getArgumentList()); + if (Error Err = finishVisitation(CVArguments, MF.getArgumentList(), + MemberFunction)) + return Err; + } + } + + return Error::success(); +} + +// LF_METHODLIST (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + MethodOverloadListRecord &Overloads, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeEnd(Record); + }); + + for (OneMethodRecord &Method : Overloads.Methods) { + CVMemberRecord Record; + Record.Kind = LF_METHOD; + Method.Name = OverloadedMethodName; + if (Error Err = visitKnownMember(Record, Method, TI, Element)) + return Err; + } + + return Error::success(); +} + +// LF_MODIFIER (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, ModifierRecord &Mod, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("ModifiedType", Mod.getModifiedType(), StreamTPI); + printTypeEnd(Record); + }); + + // Create the modified type, which will be attached to the type(s) that + // contains the modifiers. + LVElement *ModifiedType = getElement(StreamTPI, Mod.getModifiedType()); + + // At this point the types recording the qualifiers do not have a + // scope parent. They must be assigned to the current compile unit. + LVScopeCompileUnit *CompileUnit = Reader->getCompileUnit(); + + // The incoming element does not have a defined kind. Use the given + // modifiers to complete its type. A type can have more than one modifier; + // in that case, we have to create an extra type to have the other modifier. + LVType *LastLink = static_cast<LVType *>(Element); + if (!LastLink->getParentScope()) + CompileUnit->addElement(LastLink); + + bool SeenModifier = false; + uint16_t Mods = static_cast<uint16_t>(Mod.getModifiers()); + if (Mods & uint16_t(ModifierOptions::Const)) { + SeenModifier = true; + LastLink->setTag(dwarf::DW_TAG_const_type); + LastLink->setIsConst(); + LastLink->setName("const"); + } + if (Mods & uint16_t(ModifierOptions::Volatile)) { + if (SeenModifier) { + LVType *Volatile = Reader->createType(); + Volatile->setIsModifier(); + LastLink->setType(Volatile); + LastLink = Volatile; + CompileUnit->addElement(LastLink); + } + LastLink->setTag(dwarf::DW_TAG_volatile_type); + LastLink->setIsVolatile(); + LastLink->setName("volatile"); + } + if (Mods & uint16_t(ModifierOptions::Unaligned)) { + if (SeenModifier) { + LVType *Unaligned = Reader->createType(); + Unaligned->setIsModifier(); + LastLink->setType(Unaligned); + LastLink = Unaligned; + CompileUnit->addElement(LastLink); + } + LastLink->setTag(dwarf::DW_TAG_unaligned); + LastLink->setIsUnaligned(); + LastLink->setName("unaligned"); + } + + LastLink->setType(ModifiedType); + return Error::success(); +} + +// LF_POINTER (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, PointerRecord &Ptr, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("PointeeType", Ptr.getReferentType(), StreamTPI); + W.printNumber("IsFlat", Ptr.isFlat()); + W.printNumber("IsConst", Ptr.isConst()); + W.printNumber("IsVolatile", Ptr.isVolatile()); + W.printNumber("IsUnaligned", Ptr.isUnaligned()); + W.printNumber("IsRestrict", Ptr.isRestrict()); + W.printNumber("IsThisPtr&", Ptr.isLValueReferenceThisPtr()); + W.printNumber("IsThisPtr&&", Ptr.isRValueReferenceThisPtr()); + W.printNumber("SizeOf", Ptr.getSize()); + + if (Ptr.isPointerToMember()) { + const MemberPointerInfo &MI = Ptr.getMemberInfo(); + printTypeIndex("ClassType", MI.getContainingType(), StreamTPI); + } + printTypeEnd(Record); + }); + + // Find the pointed-to type. + LVType *Pointer = static_cast<LVType *>(Element); + LVElement *Pointee = nullptr; + + PointerMode Mode = Ptr.getMode(); + Pointee = Ptr.isPointerToMember() + ? Shared->TypeRecords.find(StreamTPI, Ptr.getReferentType()) + : getElement(StreamTPI, Ptr.getReferentType()); + + // At this point the types recording the qualifiers do not have a + // scope parent. They must be assigned to the current compile unit. + LVScopeCompileUnit *CompileUnit = Reader->getCompileUnit(); + + // Order for the different modifiers: + // <restrict> <pointer, Reference, ValueReference> <const, volatile> + // Const and volatile already processed. + bool SeenModifier = false; + LVType *LastLink = Pointer; + if (!LastLink->getParentScope()) + CompileUnit->addElement(LastLink); + + if (Ptr.isRestrict()) { + SeenModifier = true; + LVType *Restrict = Reader->createType(); + Restrict->setTag(dwarf::DW_TAG_restrict_type); + Restrict->setIsRestrict(); + Restrict->setName("restrict"); + LastLink->setType(Restrict); + LastLink = Restrict; + CompileUnit->addElement(LastLink); + } + if (Mode == PointerMode::LValueReference) { + if (SeenModifier) { + LVType *LReference = Reader->createType(); + LReference->setIsModifier(); + LastLink->setType(LReference); + LastLink = LReference; + CompileUnit->addElement(LastLink); + } + LastLink->setTag(dwarf::DW_TAG_reference_type); + LastLink->setIsReference(); + LastLink->setName("&"); + } + if (Mode == PointerMode::RValueReference) { + if (SeenModifier) { + LVType *RReference = Reader->createType(); + RReference->setIsModifier(); + LastLink->setType(RReference); + LastLink = RReference; + CompileUnit->addElement(LastLink); + } + LastLink->setTag(dwarf::DW_TAG_rvalue_reference_type); + LastLink->setIsRvalueReference(); + LastLink->setName("&&"); + } + + // When creating the pointer, check if it points to a reference. + LastLink->setType(Pointee); + return Error::success(); +} + +// LF_PROCEDURE (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, ProcedureRecord &Proc, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("ReturnType", Proc.getReturnType(), StreamTPI); + W.printNumber("NumParameters", Proc.getParameterCount()); + printTypeIndex("ArgListType", Proc.getArgumentList(), StreamTPI); + printTypeEnd(Record); + }); + + // There is no need to traverse the argument list, as the CodeView format + // declares the parameters as a 'S_LOCAL' symbol tagged as parameter. + // Only process parameters when dealing with inline functions. + if (LVScope *FunctionDcl = static_cast<LVScope *>(Element)) { + FunctionDcl->setType(getElement(StreamTPI, Proc.getReturnType())); + + if (ProcessArgumentList) { + ProcessArgumentList = false; + // Create formal parameters. + LazyRandomTypeCollection &Types = types(); + CVType CVArguments = Types.getType(Proc.getArgumentList()); + if (Error Err = finishVisitation(CVArguments, Proc.getArgumentList(), + FunctionDcl)) + return Err; + } + } + + return Error::success(); +} + +// LF_UNION (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, UnionRecord &Union, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printNumber("MemberCount", Union.getMemberCount()); + printTypeIndex("FieldList", Union.getFieldList(), StreamTPI); + W.printNumber("SizeOf", Union.getSize()); + W.printString("Name", Union.getName()); + if (Union.hasUniqueName()) + W.printString("UniqueName", Union.getUniqueName()); + printTypeEnd(Record); + }); + + LVScopeAggregate *Scope = static_cast<LVScopeAggregate *>(Element); + if (!Scope) + return Error::success(); + + if (Scope->getIsFinalized()) + return Error::success(); + Scope->setIsFinalized(); + + Scope->setName(Union.getName()); + if (Union.hasUniqueName()) + Scope->setLinkageName(Union.getUniqueName()); + + if (Union.isNested()) { + Scope->setIsNested(); + createParents(Union.getName(), Scope); + } else { + if (LVScope *Namespace = Shared->NamespaceDeduction.get(Union.getName())) + Namespace->addElement(Scope); + else + Reader->getCompileUnit()->addElement(Scope); + } + + if (!Union.getFieldList().isNoneType()) { + LazyRandomTypeCollection &Types = types(); + // Pass down the TypeIndex 'TI' for the aggregate containing the field list. + CVType CVFieldList = Types.getType(Union.getFieldList()); + if (Error Err = finishVisitation(CVFieldList, TI, Scope)) + return Err; + } + + return Error::success(); +} + +// LF_TYPESERVER2 (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, TypeServer2Record &TS, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printString("Guid", formatv("{0}", TS.getGuid()).str()); + W.printNumber("Age", TS.getAge()); + W.printString("Name", TS.getName()); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_VFTABLE (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, VFTableRecord &VFT, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("CompleteClass", VFT.getCompleteClass(), StreamTPI); + printTypeIndex("OverriddenVFTable", VFT.getOverriddenVTable(), StreamTPI); + W.printHex("VFPtrOffset", VFT.getVFPtrOffset()); + W.printString("VFTableName", VFT.getName()); + for (const StringRef &N : VFT.getMethodNames()) + W.printString("MethodName", N); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_VTSHAPE (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + VFTableShapeRecord &Shape, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printNumber("VFEntryCount", Shape.getEntryCount()); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_SUBSTR_LIST (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + StringListRecord &Strings, + TypeIndex TI, LVElement *Element) { + // All the indices are references into the TPI/IPI stream. + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamIPI); + ArrayRef<TypeIndex> Indices = Strings.getIndices(); + uint32_t Size = Indices.size(); + W.printNumber("NumStrings", Size); + ListScope Arguments(W, "Strings"); + for (uint32_t I = 0; I < Size; ++I) + printTypeIndex("String", Indices[I], StreamIPI); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_STRING_ID (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, StringIdRecord &String, + TypeIndex TI, LVElement *Element) { + // All args are references into the TPI/IPI stream. + LLVM_DEBUG({ + printTypeIndex("\nTI", TI, StreamIPI); + printTypeIndex("Id", String.getId(), StreamIPI); + W.printString("StringData", String.getString()); + }); + + if (LVScope *Namespace = Shared->NamespaceDeduction.get( + String.getString(), /*CheckScope=*/false)) { + // The function is already at different scope. In order to reflect + // the correct parent, move it to the namespace. + if (LVScope *Scope = Element->getParentScope()) + Scope->removeElement(Element); + Namespace->addElement(Element); + } + + return Error::success(); +} + +// LF_UDT_SRC_LINE (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + UdtSourceLineRecord &SourceLine, + TypeIndex TI, LVElement *Element) { + // All args are references into the TPI/IPI stream. + LLVM_DEBUG({ + printTypeIndex("\nTI", TI, StreamIPI); + printTypeIndex("UDT", SourceLine.getUDT(), StreamIPI); + printTypeIndex("SourceFile", SourceLine.getSourceFile(), StreamIPI); + W.printNumber("LineNumber", SourceLine.getLineNumber()); + }); + return Error::success(); +} + +// LF_UDT_MOD_SRC_LINE (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + UdtModSourceLineRecord &ModSourceLine, + TypeIndex TI, LVElement *Element) { + // All args are references into the TPI/IPI stream. + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamIPI); + printTypeIndex("\nTI", TI, StreamIPI); + printTypeIndex("UDT", ModSourceLine.getUDT(), StreamIPI); + printTypeIndex("SourceFile", ModSourceLine.getSourceFile(), StreamIPI); + W.printNumber("LineNumber", ModSourceLine.getLineNumber()); + W.printNumber("Module", ModSourceLine.getModule()); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_PRECOMP (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, PrecompRecord &Precomp, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printHex("StartIndex", Precomp.getStartTypeIndex()); + W.printHex("Count", Precomp.getTypesCount()); + W.printHex("Signature", Precomp.getSignature()); + W.printString("PrecompFile", Precomp.getPrecompFilePath()); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_ENDPRECOMP (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + EndPrecompRecord &EndPrecomp, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printHex("Signature", EndPrecomp.getSignature()); + printTypeEnd(Record); + }); + return Error::success(); +} + +Error LVLogicalVisitor::visitUnknownMember(CVMemberRecord &Record, + TypeIndex TI) { + LLVM_DEBUG({ W.printHex("UnknownMember", unsigned(Record.Kind)); }); + return Error::success(); +} + +// LF_BCLASS, LF_BINTERFACE +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + BaseClassRecord &Base, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("BaseType", Base.getBaseType(), StreamTPI); + W.printHex("BaseOffset", Base.getBaseOffset()); + printMemberEnd(Record); + }); + + createElement(Record.Kind); + if (LVSymbol *Symbol = CurrentSymbol) { + LVElement *BaseClass = getElement(StreamTPI, Base.getBaseType()); + Symbol->setName(BaseClass->getName()); + Symbol->setType(BaseClass); + Symbol->setAccessibilityCode(Base.getAccess()); + static_cast<LVScope *>(Element)->addElement(Symbol); + } + + return Error::success(); +} + +// LF_MEMBER +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + DataMemberRecord &Field, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("Type", Field.getType(), StreamTPI); + W.printHex("FieldOffset", Field.getFieldOffset()); + W.printString("Name", Field.getName()); + printMemberEnd(Record); + }); + + // Create the data member. + createDataMember(Record, static_cast<LVScope *>(Element), Field.getName(), + Field.getType(), Field.getAccess()); + return Error::success(); +} + +// LF_ENUMERATE +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + EnumeratorRecord &Enum, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + W.printNumber("EnumValue", Enum.getValue()); + W.printString("Name", Enum.getName()); + printMemberEnd(Record); + }); + + createElement(Record.Kind); + if (LVType *Type = CurrentType) { + Type->setName(Enum.getName()); + SmallString<16> Value; + Enum.getValue().toString(Value, 16, true, true); + Type->setValue(Value); + static_cast<LVScope *>(Element)->addElement(CurrentType); + } + + return Error::success(); +} + +// LF_INDEX +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + ListContinuationRecord &Cont, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("ContinuationIndex", Cont.getContinuationIndex(), StreamTPI); + printMemberEnd(Record); + }); + return Error::success(); +} + +// LF_NESTTYPE +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + NestedTypeRecord &Nested, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("Type", Nested.getNestedType(), StreamTPI); + W.printString("Name", Nested.getName()); + printMemberEnd(Record); + }); + + if (LVElement *Typedef = createElement(SymbolKind::S_UDT)) { + Typedef->setName(Nested.getName()); + LVElement *NestedType = getElement(StreamTPI, Nested.getNestedType()); + Typedef->setType(NestedType); + LVScope *Scope = static_cast<LVScope *>(Element); + Scope->addElement(Typedef); + + if (NestedType && NestedType->getIsNested()) { + // 'Element' is an aggregate type that may contains this nested type + // definition. Used their scoped names, to decide on their relationship. + StringRef RecordName = getRecordName(types(), TI); + + StringRef NestedTypeName = NestedType->getName(); + if (NestedTypeName.size() && RecordName.size()) { + StringRef OuterComponent; + std::tie(OuterComponent, std::ignore) = + getInnerComponent(NestedTypeName); + // We have an already created nested type. Add it to the current scope + // and update all its children if any. + if (OuterComponent.size() && OuterComponent.equals(RecordName)) { + if (!NestedType->getIsScopedAlready()) { + Scope->addElement(NestedType); + NestedType->setIsScopedAlready(); + NestedType->updateLevel(Scope); + } + Typedef->resetIncludeInPrint(); + } + } + } + } + + return Error::success(); +} + +// LF_ONEMETHOD +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + OneMethodRecord &Method, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("Type", Method.getType(), StreamTPI); + // If virtual, then read the vftable offset. + if (Method.isIntroducingVirtual()) + W.printHex("VFTableOffset", Method.getVFTableOffset()); + W.printString("Name", Method.getName()); + printMemberEnd(Record); + }); + + // All the LF_ONEMETHOD objects share the same type description. + // We have to create a scope object for each one and get the required + // information from the LF_MFUNCTION object. + ProcessArgumentList = true; + if (LVElement *MemberFunction = createElement(TypeLeafKind::LF_ONEMETHOD)) { + MemberFunction->setIsFinalized(); + static_cast<LVScope *>(Element)->addElement(MemberFunction); + + MemberFunction->setName(Method.getName()); + MemberFunction->setAccessibilityCode(Method.getAccess()); + + MethodKind Kind = Method.getMethodKind(); + if (Kind == MethodKind::Static) + MemberFunction->setIsStatic(); + MemberFunction->setVirtualityCode(Kind); + + MethodOptions Flags = Method.Attrs.getFlags(); + if (MethodOptions::CompilerGenerated == + (Flags & MethodOptions::CompilerGenerated)) + MemberFunction->setIsArtificial(); + + LazyRandomTypeCollection &Types = types(); + CVType CVMethodType = Types.getType(Method.getType()); + if (Error Err = + finishVisitation(CVMethodType, Method.getType(), MemberFunction)) + return Err; + } + ProcessArgumentList = false; + + return Error::success(); +} + +// LF_METHOD +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + OverloadedMethodRecord &Method, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + W.printHex("MethodCount", Method.getNumOverloads()); + printTypeIndex("MethodListIndex", Method.getMethodList(), StreamTPI); + W.printString("Name", Method.getName()); + printMemberEnd(Record); + }); + + // Record the overloaded method name, which will be used during the + // traversal of the method list. + LazyRandomTypeCollection &Types = types(); + OverloadedMethodName = Method.getName(); + CVType CVMethods = Types.getType(Method.getMethodList()); + if (Error Err = finishVisitation(CVMethods, Method.getMethodList(), Element)) + return Err; + + return Error::success(); +} + +// LF_STMEMBER +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + StaticDataMemberRecord &Field, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("Type", Field.getType(), StreamTPI); + W.printString("Name", Field.getName()); + printMemberEnd(Record); + }); + + // Create the data member. + createDataMember(Record, static_cast<LVScope *>(Element), Field.getName(), + Field.getType(), Field.getAccess()); + return Error::success(); +} + +// LF_VFUNCTAB +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + VFPtrRecord &VFTable, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("Type", VFTable.getType(), StreamTPI); + printMemberEnd(Record); + }); + return Error::success(); +} + +// LF_VBCLASS, LF_IVBCLASS +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + VirtualBaseClassRecord &Base, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("BaseType", Base.getBaseType(), StreamTPI); + printTypeIndex("VBPtrType", Base.getVBPtrType(), StreamTPI); + W.printHex("VBPtrOffset", Base.getVBPtrOffset()); + W.printHex("VBTableIndex", Base.getVTableIndex()); + printMemberEnd(Record); + }); + + createElement(Record.Kind); + if (LVSymbol *Symbol = CurrentSymbol) { + LVElement *BaseClass = getElement(StreamTPI, Base.getBaseType()); + Symbol->setName(BaseClass->getName()); + Symbol->setType(BaseClass); + Symbol->setAccessibilityCode(Base.getAccess()); + Symbol->setVirtualityCode(MethodKind::Virtual); + static_cast<LVScope *>(Element)->addElement(Symbol); + } + + return Error::success(); +} + +Error LVLogicalVisitor::visitMemberRecord(CVMemberRecord &Record, + TypeVisitorCallbacks &Callbacks, + TypeIndex TI, LVElement *Element) { + if (Error Err = Callbacks.visitMemberBegin(Record)) + return Err; + + switch (Record.Kind) { + default: + if (Error Err = Callbacks.visitUnknownMember(Record)) + return Err; + break; +#define MEMBER_RECORD(EnumName, EnumVal, Name) \ + case EnumName: { \ + if (Error Err = \ + visitKnownMember<Name##Record>(Record, Callbacks, TI, Element)) \ + return Err; \ + break; \ + } +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \ + MEMBER_RECORD(EnumVal, EnumVal, AliasName) +#define TYPE_RECORD(EnumName, EnumVal, Name) +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" + } + + if (Error Err = Callbacks.visitMemberEnd(Record)) + return Err; + + return Error::success(); +} + +Error LVLogicalVisitor::finishVisitation(CVType &Record, TypeIndex TI, + LVElement *Element) { + switch (Record.kind()) { + default: + if (Error Err = visitUnknownType(Record, TI)) + return Err; + break; +#define TYPE_RECORD(EnumName, EnumVal, Name) \ + case EnumName: { \ + if (Error Err = visitKnownRecord<Name##Record>(Record, TI, Element)) \ + return Err; \ + break; \ + } +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \ + TYPE_RECORD(EnumVal, EnumVal, AliasName) +#define MEMBER_RECORD(EnumName, EnumVal, Name) +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" + } + + return Error::success(); +} + +// Customized version of 'FieldListVisitHelper'. +Error LVLogicalVisitor::visitFieldListMemberStream( + TypeIndex TI, LVElement *Element, ArrayRef<uint8_t> FieldList) { + BinaryByteStream Stream(FieldList, llvm::support::little); + BinaryStreamReader Reader(Stream); + FieldListDeserializer Deserializer(Reader); + TypeVisitorCallbackPipeline Pipeline; + Pipeline.addCallbackToPipeline(Deserializer); + + TypeLeafKind Leaf; + while (!Reader.empty()) { + if (Error Err = Reader.readEnum(Leaf)) + return Err; + + CVMemberRecord Record; + Record.Kind = Leaf; + if (Error Err = visitMemberRecord(Record, Pipeline, TI, Element)) + return Err; + } + + return Error::success(); +} + +void LVLogicalVisitor::addElement(LVScope *Scope, bool IsCompileUnit) { + // The CodeView specifications does not treat S_COMPILE2 and S_COMPILE3 + // as symbols that open a scope. The CodeView reader, treat them in a + // similar way as DWARF. As there is no a symbole S_END to close the + // compile unit, we need to check for the next compile unit. + if (IsCompileUnit) { + if (!ScopeStack.empty()) + popScope(); + InCompileUnitScope = true; + } + + pushScope(Scope); + ReaderParent->addElement(Scope); +} + +void LVLogicalVisitor::addElement(LVSymbol *Symbol) { + ReaderScope->addElement(Symbol); +} + +void LVLogicalVisitor::addElement(LVType *Type) { + ReaderScope->addElement(Type); +} + +LVElement *LVLogicalVisitor::createElement(TypeLeafKind Kind) { + CurrentScope = nullptr; + CurrentSymbol = nullptr; + CurrentType = nullptr; + + if (Kind < TypeIndex::FirstNonSimpleIndex) { + CurrentType = Reader->createType(); + CurrentType->setIsBase(); + CurrentType->setTag(dwarf::DW_TAG_base_type); + if (options().getAttributeBase()) + CurrentType->setIncludeInPrint(); + return CurrentType; + } + + switch (Kind) { + // Types. + case TypeLeafKind::LF_ENUMERATE: + CurrentType = Reader->createTypeEnumerator(); + CurrentType->setTag(dwarf::DW_TAG_enumerator); + return CurrentType; + case TypeLeafKind::LF_MODIFIER: + CurrentType = Reader->createType(); + CurrentType->setIsModifier(); + return CurrentType; + case TypeLeafKind::LF_POINTER: + CurrentType = Reader->createType(); + CurrentType->setIsPointer(); + CurrentType->setName("*"); + CurrentType->setTag(dwarf::DW_TAG_pointer_type); + return CurrentType; + + // Symbols. + case TypeLeafKind::LF_BCLASS: + case TypeLeafKind::LF_IVBCLASS: + case TypeLeafKind::LF_VBCLASS: + CurrentSymbol = Reader->createSymbol(); + CurrentSymbol->setTag(dwarf::DW_TAG_inheritance); + CurrentSymbol->setIsInheritance(); + return CurrentSymbol; + case TypeLeafKind::LF_MEMBER: + case TypeLeafKind::LF_STMEMBER: + CurrentSymbol = Reader->createSymbol(); + CurrentSymbol->setIsMember(); + CurrentSymbol->setTag(dwarf::DW_TAG_member); + return CurrentSymbol; + + // Scopes. + case TypeLeafKind::LF_ARRAY: + CurrentScope = Reader->createScopeArray(); + CurrentScope->setTag(dwarf::DW_TAG_array_type); + return CurrentScope; + case TypeLeafKind::LF_CLASS: + CurrentScope = Reader->createScopeAggregate(); + CurrentScope->setTag(dwarf::DW_TAG_class_type); + CurrentScope->setIsClass(); + return CurrentScope; + case TypeLeafKind::LF_ENUM: + CurrentScope = Reader->createScopeEnumeration(); + CurrentScope->setTag(dwarf::DW_TAG_enumeration_type); + return CurrentScope; + case TypeLeafKind::LF_METHOD: + case TypeLeafKind::LF_ONEMETHOD: + case TypeLeafKind::LF_PROCEDURE: + CurrentScope = Reader->createScopeFunction(); + CurrentScope->setIsSubprogram(); + CurrentScope->setTag(dwarf::DW_TAG_subprogram); + return CurrentScope; + case TypeLeafKind::LF_STRUCTURE: + CurrentScope = Reader->createScopeAggregate(); + CurrentScope->setIsStructure(); + CurrentScope->setTag(dwarf::DW_TAG_structure_type); + return CurrentScope; + case TypeLeafKind::LF_UNION: + CurrentScope = Reader->createScopeAggregate(); + CurrentScope->setIsUnion(); + CurrentScope->setTag(dwarf::DW_TAG_union_type); + return CurrentScope; + default: + // If '--internal=tag' and '--print=warning' are specified in the command + // line, we record and print each seen 'TypeLeafKind'. + break; + } + return nullptr; +} + +LVElement *LVLogicalVisitor::createElement(SymbolKind Kind) { + CurrentScope = nullptr; + CurrentSymbol = nullptr; + CurrentType = nullptr; + switch (Kind) { + // Types. + case SymbolKind::S_UDT: + CurrentType = Reader->createTypeDefinition(); + CurrentType->setTag(dwarf::DW_TAG_typedef); + return CurrentType; + + // Symbols. + case SymbolKind::S_CONSTANT: + CurrentSymbol = Reader->createSymbol(); + CurrentSymbol->setIsConstant(); + CurrentSymbol->setTag(dwarf::DW_TAG_constant); + return CurrentSymbol; + + case SymbolKind::S_BPREL32: + case SymbolKind::S_REGREL32: + case SymbolKind::S_GDATA32: + case SymbolKind::S_LDATA32: + case SymbolKind::S_LOCAL: + // During the symbol traversal more information is available to + // determine if the symbol is a parameter or a variable. At this + // stage mark it as variable. + CurrentSymbol = Reader->createSymbol(); + CurrentSymbol->setIsVariable(); + CurrentSymbol->setTag(dwarf::DW_TAG_variable); + return CurrentSymbol; + + // Scopes. + case SymbolKind::S_BLOCK32: + CurrentScope = Reader->createScope(); + CurrentScope->setIsLexicalBlock(); + CurrentScope->setTag(dwarf::DW_TAG_lexical_block); + return CurrentScope; + case SymbolKind::S_COMPILE2: + case SymbolKind::S_COMPILE3: + CurrentScope = Reader->createScopeCompileUnit(); + CurrentScope->setTag(dwarf::DW_TAG_compile_unit); + Reader->setCompileUnit(static_cast<LVScopeCompileUnit *>(CurrentScope)); + return CurrentScope; + case SymbolKind::S_INLINESITE: + case SymbolKind::S_INLINESITE2: + CurrentScope = Reader->createScopeFunctionInlined(); + CurrentScope->setIsInlinedFunction(); + CurrentScope->setTag(dwarf::DW_TAG_inlined_subroutine); + return CurrentScope; + case SymbolKind::S_LPROC32: + case SymbolKind::S_GPROC32: + case SymbolKind::S_LPROC32_ID: + case SymbolKind::S_GPROC32_ID: + case SymbolKind::S_SEPCODE: + case SymbolKind::S_THUNK32: + CurrentScope = Reader->createScopeFunction(); + CurrentScope->setIsSubprogram(); + CurrentScope->setTag(dwarf::DW_TAG_subprogram); + return CurrentScope; + default: + // If '--internal=tag' and '--print=warning' are specified in the command + // line, we record and print each seen 'SymbolKind'. + break; + } + return nullptr; +} + +LVElement *LVLogicalVisitor::createElement(TypeIndex TI, TypeLeafKind Kind) { + LVElement *Element = Shared->TypeRecords.find(StreamTPI, TI); + if (!Element) { + // We are dealing with a base type or pointer to a base type, which are + // not included explicitly in the CodeView format. + if (Kind < TypeIndex::FirstNonSimpleIndex) { + Element = createElement(Kind); + Element->setIsFinalized(); + Shared->TypeRecords.add(StreamTPI, (TypeIndex)Kind, Kind, Element); + Element->setOffset(Kind); + return Element; + } + // We are dealing with a pointer to a base type. + if (TI.getIndex() < TypeIndex::FirstNonSimpleIndex) { + Element = createElement(Kind); + Shared->TypeRecords.add(StreamTPI, TI, Kind, Element); + Element->setOffset(TI.getIndex()); + Element->setOffsetFromTypeIndex(); + return Element; + } + + W.printString("** Not implemented. **"); + printTypeIndex("TypeIndex", TI, StreamTPI); + W.printString("TypeLeafKind", formatTypeLeafKind(Kind)); + return nullptr; + } + + Element->setOffset(TI.getIndex()); + Element->setOffsetFromTypeIndex(); + return Element; +} + +void LVLogicalVisitor::createDataMember(CVMemberRecord &Record, LVScope *Parent, + StringRef Name, TypeIndex TI, + MemberAccess Access) { + LLVM_DEBUG({ + printTypeIndex("TypeIndex", TI, StreamTPI); + W.printString("TypeName", Name); + }); + + createElement(Record.Kind); + if (LVSymbol *Symbol = CurrentSymbol) { + Symbol->setName(Name); + if (TI.isNoneType() || TI.isSimple()) + Symbol->setType(getElement(StreamTPI, TI)); + else { + LazyRandomTypeCollection &Types = types(); + CVType CVMemberType = Types.getType(TI); + if (CVMemberType.kind() == LF_BITFIELD) { + if (Error Err = finishVisitation(CVMemberType, TI, Symbol)) { + consumeError(std::move(Err)); + return; + } + } else + Symbol->setType(getElement(StreamTPI, TI)); + } + Symbol->setAccessibilityCode(Access); + Parent->addElement(Symbol); + } +} + +LVSymbol *LVLogicalVisitor::createParameter(LVElement *Element, StringRef Name, + LVScope *Parent) { + LVSymbol *Parameter = Reader->createSymbol(); + Parent->addElement(Parameter); + Parameter->setIsParameter(); + Parameter->setTag(dwarf::DW_TAG_formal_parameter); + Parameter->setName(Name); + Parameter->setType(Element); + return Parameter; +} + +LVSymbol *LVLogicalVisitor::createParameter(TypeIndex TI, StringRef Name, + LVScope *Parent) { + return createParameter(getElement(StreamTPI, TI), Name, Parent); +} + +LVType *LVLogicalVisitor::createBaseType(TypeIndex TI, StringRef TypeName) { + TypeLeafKind SimpleKind = (TypeLeafKind)TI.getSimpleKind(); + TypeIndex TIR = (TypeIndex)SimpleKind; + LLVM_DEBUG({ + printTypeIndex("TypeIndex", TIR, StreamTPI); + W.printString("TypeName", TypeName); + }); + + if (LVElement *Element = Shared->TypeRecords.find(StreamTPI, TIR)) + return static_cast<LVType *>(Element); + + if (createElement(TIR, SimpleKind)) { + CurrentType->setName(TypeName); + Reader->getCompileUnit()->addElement(CurrentType); + } + return CurrentType; +} + +LVType *LVLogicalVisitor::createPointerType(TypeIndex TI, StringRef TypeName) { + LLVM_DEBUG({ + printTypeIndex("TypeIndex", TI, StreamTPI); + W.printString("TypeName", TypeName); + }); + + if (LVElement *Element = Shared->TypeRecords.find(StreamTPI, TI)) + return static_cast<LVType *>(Element); + + LVType *Pointee = createBaseType(TI, TypeName.drop_back(1)); + if (createElement(TI, TypeLeafKind::LF_POINTER)) { + CurrentType->setIsFinalized(); + CurrentType->setType(Pointee); + Reader->getCompileUnit()->addElement(CurrentType); + } + return CurrentType; +} + +void LVLogicalVisitor::createParents(StringRef ScopedName, LVElement *Element) { + // For the given test case: + // + // struct S { enum E { ... }; }; + // S::E V; + // + // 0 | S_LOCAL `V` + // type=0x1004 (S::E), flags = none + // 0x1004 | LF_ENUM `S::E` + // options: has unique name | is nested + // 0x1009 | LF_STRUCTURE `S` + // options: contains nested class + // + // When the local 'V' is processed, its type 'E' is created. But There is + // no direct reference to its parent 'S'. We use the scoped name for 'E', + // to create its parents. + + // The input scoped name must have at least parent and nested names. + // Drop the last element name, as it corresponds to the nested type. + LVStringRefs Components = getAllLexicalComponents(ScopedName); + if (Components.size() < 2) + return; + Components.pop_back(); + + LVStringRefs::size_type FirstNamespace; + LVStringRefs::size_type FirstAggregate; + std::tie(FirstNamespace, FirstAggregate) = + Shared->NamespaceDeduction.find(Components); + + LLVM_DEBUG({ + W.printString("First Namespace", Components[FirstNamespace]); + W.printString("First NonNamespace", Components[FirstAggregate]); + }); + + // Create any referenced namespaces. + if (FirstNamespace < FirstAggregate) { + Shared->NamespaceDeduction.get( + LVStringRefs(Components.begin() + FirstNamespace, + Components.begin() + FirstAggregate)); + } + + // Traverse the enclosing scopes (aggregates) and create them. In the + // case of nested empty aggregates, MSVC does not emit a full record + // description. It emits only the reference record. + LVScope *Aggregate = nullptr; + TypeIndex TIAggregate; + std::string AggregateName = getScopedName( + LVStringRefs(Components.begin(), Components.begin() + FirstAggregate)); + + // This traversal is executed at least once. + for (LVStringRefs::size_type Index = FirstAggregate; + Index < Components.size(); ++Index) { + AggregateName = getScopedName(LVStringRefs(Components.begin() + Index, + Components.begin() + Index + 1), + AggregateName); + TIAggregate = Shared->ForwardReferences.remap( + Shared->TypeRecords.find(StreamTPI, AggregateName)); + Aggregate = + TIAggregate.isNoneType() + ? nullptr + : static_cast<LVScope *>(getElement(StreamTPI, TIAggregate)); + } + + // Workaround for cases where LF_NESTTYPE is missing for nested templates. + // If we manage to get parent information from the scoped name, we can add + // the nested type without relying on the LF_NESTTYPE. + if (Aggregate && !Element->getIsScopedAlready()) { + Aggregate->addElement(Element); + Element->setIsScopedAlready(); + } +} + +LVElement *LVLogicalVisitor::getElement(uint32_t StreamIdx, TypeIndex TI, + LVScope *Parent) { + LLVM_DEBUG({ printTypeIndex("TypeIndex", TI, StreamTPI); }); + TI = Shared->ForwardReferences.remap(TI); + LLVM_DEBUG({ printTypeIndex("TypeIndex Remap", TI, StreamTPI); }); + + LVElement *Element = Shared->TypeRecords.find(StreamIdx, TI); + if (!Element) { + if (TI.isNoneType() || TI.isSimple()) { + StringRef TypeName = TypeIndex::simpleTypeName(TI); + // If the name ends with "*", create 2 logical types: a pointer and a + // pointee type. TypeIndex is composed of a SympleTypeMode byte followed + // by a SimpleTypeKind byte. The logical pointer will be identified by + // the full TypeIndex value and the pointee by the SimpleTypeKind. + return (TypeName.back() == '*') ? createPointerType(TI, TypeName) + : createBaseType(TI, TypeName); + } + + LLVM_DEBUG({ W.printHex("TypeIndex not implemented: ", TI.getIndex()); }); + return nullptr; + } + + // The element has been finalized. + if (Element->getIsFinalized()) + return Element; + + // Add the element in case of a given parent. + if (Parent) + Parent->addElement(Element); + + // Check for a composite type. + LazyRandomTypeCollection &Types = types(); + CVType CVRecord = Types.getType(TI); + if (Error Err = finishVisitation(CVRecord, TI, Element)) { + consumeError(std::move(Err)); + return nullptr; + } + Element->setIsFinalized(); + return Element; +} + +void LVLogicalVisitor::processLines() { + // Traverse the collected LF_UDT_SRC_LINE records and add the source line + // information to the logical elements. + for (const TypeIndex &Entry : Shared->LineRecords) { + CVType CVRecord = ids().getType(Entry); + UdtSourceLineRecord Line; + if (Error Err = TypeDeserializer::deserializeAs( + const_cast<CVType &>(CVRecord), Line)) + consumeError(std::move(Err)); + else { + LLVM_DEBUG({ + printTypeIndex("UDT", Line.getUDT(), StreamIPI); + printTypeIndex("SourceFile", Line.getSourceFile(), StreamIPI); + W.printNumber("LineNumber", Line.getLineNumber()); + }); + + // The TypeIndex returned by 'getUDT()' must point to an already + // created logical element. If no logical element is found, it means + // the LF_UDT_SRC_LINE is associated with a system TypeIndex. + if (LVElement *Element = Shared->TypeRecords.find( + StreamTPI, Line.getUDT(), /*Create=*/false)) { + Element->setLineNumber(Line.getLineNumber()); + Element->setFilenameIndex( + Shared->StringRecords.findIndex(Line.getSourceFile())); + } + } + } +} + +void LVLogicalVisitor::processNamespaces() { + // Create namespaces. + Shared->NamespaceDeduction.init(); +} + +void LVLogicalVisitor::processFiles() { Shared->StringRecords.addFilenames(); } + +void LVLogicalVisitor::printRecords(raw_ostream &OS) const { + if (!options().getInternalTag()) + return; + + unsigned Count = 0; + auto PrintItem = [&](StringRef Name) { + auto NewLine = [&]() { + if (++Count == 4) { + Count = 0; + OS << "\n"; + } + }; + OS << format("%20s", Name.str().c_str()); + NewLine(); + }; + + OS << "\nTypes:\n"; + for (const TypeLeafKind &Kind : Shared->TypeKinds) + PrintItem(formatTypeLeafKind(Kind)); + Shared->TypeKinds.clear(); + + Count = 0; + OS << "\nSymbols:\n"; + for (const SymbolKind &Kind : Shared->SymbolKinds) + PrintItem(LVCodeViewReader::getSymbolKindName(Kind)); + Shared->SymbolKinds.clear(); + + OS << "\n"; +} + +Error LVLogicalVisitor::inlineSiteAnnotation(LVScope *AbstractFunction, + LVScope *InlinedFunction, + InlineSiteSym &InlineSite) { + // Get the parent scope to update the address ranges of the nested + // scope representing the inlined function. + LVAddress ParentLowPC = 0; + LVScope *Parent = InlinedFunction->getParentScope(); + if (const LVLocations *Locations = Parent->getRanges()) { + if (!Locations->empty()) + ParentLowPC = (*Locations->begin())->getLowerAddress(); + } + + // For the given inlinesite, get the initial line number and its + // source filename. Update the logical scope representing it. + uint32_t LineNumber = 0; + StringRef Filename; + LVInlineeInfo::iterator Iter = InlineeInfo.find(InlineSite.Inlinee); + if (Iter != InlineeInfo.end()) { + LineNumber = Iter->second.first; + Filename = Iter->second.second; + AbstractFunction->setLineNumber(LineNumber); + // TODO: This part needs additional work in order to set properly the + // correct filename in order to detect changes between filenames. + // AbstractFunction->setFilename(Filename); + } + + LLVM_DEBUG({ + dbgs() << "inlineSiteAnnotation\n" + << "Abstract: " << AbstractFunction->getName() << "\n" + << "Inlined: " << InlinedFunction->getName() << "\n" + << "Parent: " << Parent->getName() << "\n" + << "Low PC: " << hexValue(ParentLowPC) << "\n"; + }); + + // Get the source lines if requested by command line option. + if (!options().getPrintLines()) + return Error::success(); + + // Limitation: Currently we don't track changes in the FileOffset. The + // side effects are the caller that it is unable to differentiate the + // source filename for the inlined code. + uint64_t CodeOffset = ParentLowPC; + int32_t LineOffset = LineNumber; + uint32_t FileOffset = 0; + + auto UpdateClose = [&]() { LLVM_DEBUG({ dbgs() << ("\n"); }); }; + auto UpdateCodeOffset = [&](uint32_t Delta) { + CodeOffset += Delta; + LLVM_DEBUG({ + dbgs() << formatv(" code 0x{0} (+0x{1})", utohexstr(CodeOffset), + utohexstr(Delta)); + }); + }; + auto UpdateLineOffset = [&](int32_t Delta) { + LineOffset += Delta; + LLVM_DEBUG({ + char Sign = Delta > 0 ? '+' : '-'; + dbgs() << formatv(" line {0} ({1}{2})", LineOffset, Sign, + std::abs(Delta)); + }); + }; + auto UpdateFileOffset = [&](int32_t Offset) { + FileOffset = Offset; + LLVM_DEBUG({ dbgs() << formatv(" file {0}", FileOffset); }); + }; + + LVLines InlineeLines; + auto CreateLine = [&]() { + // Create the logical line record. + LVLineDebug *Line = Reader->createLineDebug(); + Line->setAddress(CodeOffset); + Line->setLineNumber(LineOffset); + // TODO: This part needs additional work in order to set properly the + // correct filename in order to detect changes between filenames. + // Line->setFilename(Filename); + InlineeLines.push_back(Line); + }; + + bool SeenLowAddress = false; + bool SeenHighAddress = false; + uint64_t LowPC = 0; + uint64_t HighPC = 0; + + for (auto &Annot : InlineSite.annotations()) { + LLVM_DEBUG({ + dbgs() << formatv(" {0}", + fmt_align(toHex(Annot.Bytes), AlignStyle::Left, 9)); + }); + + // Use the opcode to interpret the integer values. + switch (Annot.OpCode) { + case BinaryAnnotationsOpCode::ChangeCodeOffset: + case BinaryAnnotationsOpCode::CodeOffset: + case BinaryAnnotationsOpCode::ChangeCodeLength: + UpdateCodeOffset(Annot.U1); + UpdateClose(); + if (Annot.OpCode == BinaryAnnotationsOpCode::ChangeCodeOffset) { + CreateLine(); + LowPC = CodeOffset; + SeenLowAddress = true; + break; + } + if (Annot.OpCode == BinaryAnnotationsOpCode::ChangeCodeLength) { + HighPC = CodeOffset - 1; + SeenHighAddress = true; + } + break; + case BinaryAnnotationsOpCode::ChangeCodeLengthAndCodeOffset: + UpdateCodeOffset(Annot.U2); + UpdateClose(); + break; + case BinaryAnnotationsOpCode::ChangeLineOffset: + case BinaryAnnotationsOpCode::ChangeCodeOffsetAndLineOffset: + UpdateCodeOffset(Annot.U1); + UpdateLineOffset(Annot.S1); + UpdateClose(); + if (Annot.OpCode == + BinaryAnnotationsOpCode::ChangeCodeOffsetAndLineOffset) + CreateLine(); + break; + case BinaryAnnotationsOpCode::ChangeFile: + UpdateFileOffset(Annot.U1); + UpdateClose(); + break; + default: + break; + } + if (SeenLowAddress && SeenHighAddress) { + SeenLowAddress = false; + SeenHighAddress = false; + InlinedFunction->addObject(LowPC, HighPC); + } + } + + Reader->addInlineeLines(InlinedFunction, InlineeLines); + UpdateClose(); + + return Error::success(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp b/llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp index 009cd11..081cede 100644 --- a/llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp @@ -240,7 +240,9 @@ Error DbiModuleList::initializeFileInfo(BinaryStreamRef FileInfo) { } uint32_t DbiModuleList::getModuleCount() const { - return FileInfoHeader->NumModules; + // Workaround to avoid the crash until upstream issue is fixed: + // https://github.com/llvm/llvm-project/issues/55214 + return FileInfoHeader ? FileInfoHeader->NumModules : 0; } uint32_t DbiModuleList::getSourceFileCount() const { |
