Files
clang-p2996/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp
Scott Linder 2e6bb8c9b8 [DebugInfo] Support more than 2 operands in DWARF operations
Update DWARFExpression::Operation and LVOperation to support more than
2 operands.

Take the opportunity to use a SmallVector, which will handle at least 2
operands without allocation anyway, and removes the static limit
completely.

As there is no longer the concept of an "unused operand", remove
Operation::Encoding::SizeNA. Any use of it is now replaced with explicit
checks for how many operands an operation has.

There are still places where the limit remains 2, namely in the
DWARFLinker and in DIExpressions, but these can be updated in later
patches as-needed.

There are no explicit tests as this is nearly NFC: no new operation is
added which makes use of the additional operand capacity yet. A future
patch adding a new DWARF extension point will include operations which
require the support.

Reviewed By: Orlando, CarlosAlbertoEnciso

Differential Revision: https://reviews.llvm.org/D147270
2023-06-19 19:38:26 +00:00

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