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