summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lld/COFF/PDB.cpp12
-rw-r--r--llvm/include/llvm/DebugInfo/CodeView/CVTypeDumper.h7
-rw-r--r--llvm/include/llvm/DebugInfo/CodeView/CVTypeVisitor.h8
-rw-r--r--llvm/include/llvm/DebugInfo/CodeView/CodeViewError.h1
-rw-r--r--llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h1
-rw-r--r--llvm/include/llvm/DebugInfo/CodeView/TypeServerHandler.h36
-rw-r--r--llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h5
-rw-r--r--llvm/include/llvm/DebugInfo/PDB/Native/PDBFile.h6
-rw-r--r--llvm/include/llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.h48
-rw-r--r--llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h2
-rw-r--r--llvm/lib/DebugInfo/CodeView/CVTypeDumper.cpp4
-rw-r--r--llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp69
-rw-r--r--llvm/lib/DebugInfo/CodeView/CodeViewError.cpp2
-rw-r--r--llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp11
-rw-r--r--llvm/lib/DebugInfo/PDB/CMakeLists.txt1
-rw-r--r--llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp2
-rw-r--r--llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp11
-rw-r--r--llvm/lib/DebugInfo/PDB/Native/PDBTypeServerHandler.cpp119
-rw-r--r--llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp3
-rw-r--r--llvm/tools/llvm-readobj/CMakeLists.txt1
-rw-r--r--llvm/tools/llvm-readobj/COFFDumper.cpp3
-rw-r--r--llvm/tools/llvm-readobj/LLVMBuild.txt2
-rw-r--r--llvm/unittests/DebugInfo/PDB/CMakeLists.txt1
-rw-r--r--llvm/unittests/DebugInfo/PDB/TypeServerHandlerTest.cpp175
24 files changed, 513 insertions, 17 deletions
diff --git a/lld/COFF/PDB.cpp b/lld/COFF/PDB.cpp
index 091a887e0cc..f2d41308c1b 100644
--- a/lld/COFF/PDB.cpp
+++ b/lld/COFF/PDB.cpp
@@ -29,12 +29,14 @@
#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.h"
#include "llvm/DebugInfo/PDB/Native/StringTableBuilder.h"
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/ScopedPrinter.h"
#include <memory>
@@ -94,9 +96,15 @@ static std::vector<uint8_t> mergeDebugT(SymbolTable *Symtab) {
msf::ByteStream Stream(Data);
codeview::CVTypeArray Types;
msf::StreamReader Reader(Stream);
+ // Follow type servers. If the same type server is encountered more than
+ // once for this instance of `PDBTypeServerHandler` (for example if many
+ // object files reference the same TypeServer), the types from the
+ // TypeServer will only be visited once.
+ pdb::PDBTypeServerHandler Handler;
+ Handler.addSearchPath(llvm::sys::path::parent_path(File->getName()));
if (auto EC = Reader.readArray(Types, Reader.getLength()))
fatal(EC, "Reader::readArray failed");
- if (auto Err = codeview::mergeTypeStreams(Builder, Types))
+ if (auto Err = codeview::mergeTypeStreams(Builder, &Handler, Types))
fatal(Err, "codeview::mergeTypeStreams failed");
}
@@ -116,6 +124,8 @@ static void dumpDebugT(ScopedPrinter &W, ObjectFile *File) {
TypeDatabase TDB;
TypeDumpVisitor TDV(TDB, &W, false);
+ // Use a default implementation that does not follow type servers and instead
+ // just dumps the contents of the TypeServer2 record.
CVTypeDumper TypeDumper(TDB);
if (auto EC = TypeDumper.dump(Data, TDV))
fatal(EC, "CVTypeDumper::dump failed");
diff --git a/llvm/include/llvm/DebugInfo/CodeView/CVTypeDumper.h b/llvm/include/llvm/DebugInfo/CodeView/CVTypeDumper.h
index e1dd6a10b5a..02f14ea2107 100644
--- a/llvm/include/llvm/DebugInfo/CodeView/CVTypeDumper.h
+++ b/llvm/include/llvm/DebugInfo/CodeView/CVTypeDumper.h
@@ -22,10 +22,14 @@ namespace llvm {
namespace codeview {
+class TypeServerHandler;
+
/// Dumper for CodeView type streams found in COFF object files and PDB files.
class CVTypeDumper {
public:
- explicit CVTypeDumper(TypeDatabase &TypeDB) : TypeDB(TypeDB) {}
+ explicit CVTypeDumper(TypeDatabase &TypeDB,
+ TypeServerHandler *Handler = nullptr)
+ : TypeDB(TypeDB), Handler(Handler) {}
/// Dumps one type record. Returns false if there was a type parsing error,
/// and true otherwise. This should be called in order, since the dumper
@@ -48,6 +52,7 @@ public:
private:
TypeDatabase &TypeDB;
+ TypeServerHandler *Handler;
};
} // end namespace codeview
diff --git a/llvm/include/llvm/DebugInfo/CodeView/CVTypeVisitor.h b/llvm/include/llvm/DebugInfo/CodeView/CVTypeVisitor.h
index d1b0363a413..ab4b0cbaf96 100644
--- a/llvm/include/llvm/DebugInfo/CodeView/CVTypeVisitor.h
+++ b/llvm/include/llvm/DebugInfo/CodeView/CVTypeVisitor.h
@@ -10,9 +10,10 @@
#ifndef LLVM_DEBUGINFO_CODEVIEW_CVTYPEVISITOR_H
#define LLVM_DEBUGINFO_CODEVIEW_CVTYPEVISITOR_H
-#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/DebugInfo/CodeView/CVRecord.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeServerHandler.h"
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
#include "llvm/Support/Error.h"
@@ -23,11 +24,14 @@ class CVTypeVisitor {
public:
explicit CVTypeVisitor(TypeVisitorCallbacks &Callbacks);
+ void addTypeServerHandler(TypeServerHandler &Handler);
+
Error visitTypeRecord(CVType &Record);
Error visitMemberRecord(CVMemberRecord &Record);
/// Visits the type records in Data. Sets the error flag on parse failures.
Error visitTypeStream(const CVTypeArray &Types);
+ Error visitTypeStream(CVTypeRange Types);
Error visitFieldListMemberStream(ArrayRef<uint8_t> FieldList);
Error visitFieldListMemberStream(msf::StreamReader Reader);
@@ -35,6 +39,8 @@ public:
private:
/// The interface to the class that gets notified of each visitation.
TypeVisitorCallbacks &Callbacks;
+
+ TinyPtrVector<TypeServerHandler *> Handlers;
};
} // end namespace codeview
diff --git a/llvm/include/llvm/DebugInfo/CodeView/CodeViewError.h b/llvm/include/llvm/DebugInfo/CodeView/CodeViewError.h
index 0556fd0e19f..586a720ce6e 100644
--- a/llvm/include/llvm/DebugInfo/CodeView/CodeViewError.h
+++ b/llvm/include/llvm/DebugInfo/CodeView/CodeViewError.h
@@ -21,6 +21,7 @@ enum class cv_error_code {
insufficient_buffer,
operation_unsupported,
corrupt_record,
+ no_records,
unknown_member_record,
};
diff --git a/llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h b/llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h
index 4f1c047815d..9af70fd1bf2 100644
--- a/llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h
+++ b/llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h
@@ -43,6 +43,7 @@ struct CVMemberRecord {
ArrayRef<uint8_t> Data;
};
typedef msf::VarStreamArray<CVType> CVTypeArray;
+typedef iterator_range<CVTypeArray::Iterator> CVTypeRange;
/// Equvalent to CV_fldattr_t in cvinfo.h.
struct MemberAttributes {
diff --git a/llvm/include/llvm/DebugInfo/CodeView/TypeServerHandler.h b/llvm/include/llvm/DebugInfo/CodeView/TypeServerHandler.h
new file mode 100644
index 00000000000..35f06eaf6eb
--- /dev/null
+++ b/llvm/include/llvm/DebugInfo/CodeView/TypeServerHandler.h
@@ -0,0 +1,36 @@
+//===- TypeServerHandler.h --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_DEBUGINFO_CODEVIEW_TYPESERVERHANDLER_H
+#define LLVM_DEBUGINFO_CODEVIEW_TYPESERVERHANDLER_H
+
+#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+namespace codeview {
+class TypeVisitorCallbacks;
+
+class TypeServerHandler {
+public:
+ virtual ~TypeServerHandler() {}
+
+ /// Handle a TypeServer record. If the implementation returns true
+ /// the record will not be processed by the top-level visitor. If
+ /// it returns false, it will be processed. If it returns an Error,
+ /// then the top-level visitor will fail.
+ virtual Expected<bool> handle(TypeServer2Record &TS,
+ TypeVisitorCallbacks &Callbacks) {
+ return false;
+ }
+};
+}
+}
+
+#endif
diff --git a/llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h b/llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h
index 3c915af1657..a06b5b7ceed 100644
--- a/llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h
+++ b/llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h
@@ -18,8 +18,11 @@
namespace llvm {
namespace codeview {
+class TypeServerHandler;
+
/// Merges one type stream into another. Returns true on success.
-Error mergeTypeStreams(TypeTableBuilder &DestStream, const CVTypeArray &Types);
+Error mergeTypeStreams(TypeTableBuilder &DestStream, TypeServerHandler *Handler,
+ const CVTypeArray &Types);
} // end namespace codeview
} // end namespace llvm
diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/PDBFile.h b/llvm/include/llvm/DebugInfo/PDB/Native/PDBFile.h
index 9fb0ccc87f1..ba73424c485 100644
--- a/llvm/include/llvm/DebugInfo/PDB/Native/PDBFile.h
+++ b/llvm/include/llvm/DebugInfo/PDB/Native/PDBFile.h
@@ -42,10 +42,13 @@ class PDBFile : public msf::IMSFFile {
friend PDBFileBuilder;
public:
- PDBFile(std::unique_ptr<msf::ReadableStream> PdbFileBuffer,
+ PDBFile(StringRef Path, std::unique_ptr<msf::ReadableStream> PdbFileBuffer,
BumpPtrAllocator &Allocator);
~PDBFile() override;
+ StringRef getFileDirectory() const;
+ StringRef getFilePath() const;
+
uint32_t getFreeBlockMapBlock() const;
uint32_t getUnknown1() const;
@@ -110,6 +113,7 @@ private:
const msf::ReadableStream &MsfData,
uint32_t StreamIndex) const;
+ std::string FilePath;
BumpPtrAllocator &Allocator;
std::unique_ptr<msf::ReadableStream> Buffer;
diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.h b/llvm/include/llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.h
new file mode 100644
index 00000000000..d965e1008e9
--- /dev/null
+++ b/llvm/include/llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.h
@@ -0,0 +1,48 @@
+//===- PDBTypeServerHandler.h -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_DEBUGINFO_PDB_PDBTYPESERVERHANDLER_H
+#define LLVM_DEBUGINFO_PDB_PDBTYPESERVERHANDLER_H
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
+#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeServerHandler.h"
+#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
+#include "llvm/DebugInfo/PDB/PDBTypes.h"
+
+#include <memory>
+#include <string>
+
+namespace llvm {
+namespace pdb {
+class NativeSession;
+
+class PDBTypeServerHandler : public codeview::TypeServerHandler {
+public:
+ PDBTypeServerHandler(bool RevisitAlways = false);
+
+ void addSearchPath(StringRef Path);
+ Expected<bool> handle(codeview::TypeServer2Record &TS,
+ codeview::TypeVisitorCallbacks &Callbacks) override;
+
+private:
+ Expected<bool> handleInternal(PDBFile &File,
+ codeview::TypeVisitorCallbacks &Callbacks);
+
+ bool RevisitAlways;
+ std::unique_ptr<NativeSession> Session;
+ SmallVector<SmallString<64>, 4> SearchPaths;
+};
+}
+}
+
+#endif
diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h b/llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h
index b9368a7ef25..ca678015072 100644
--- a/llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h
+++ b/llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h
@@ -50,7 +50,7 @@ public:
msf::FixedStreamArray<TypeIndexOffset> getTypeIndexOffsets() const;
HashTable &getHashAdjusters();
- iterator_range<codeview::CVTypeArray::Iterator> types(bool *HadError) const;
+ codeview::CVTypeRange types(bool *HadError) const;
Error commit();
diff --git a/llvm/lib/DebugInfo/CodeView/CVTypeDumper.cpp b/llvm/lib/DebugInfo/CodeView/CVTypeDumper.cpp
index fcd239cce0d..c7cd58a10e6 100644
--- a/llvm/lib/DebugInfo/CodeView/CVTypeDumper.cpp
+++ b/llvm/lib/DebugInfo/CodeView/CVTypeDumper.cpp
@@ -28,6 +28,8 @@ Error CVTypeDumper::dump(const CVType &Record, TypeVisitorCallbacks &Dumper) {
Pipeline.addCallbackToPipeline(Dumper);
CVTypeVisitor Visitor(Pipeline);
+ if (Handler)
+ Visitor.addTypeServerHandler(*Handler);
CVType RecordCopy = Record;
if (auto EC = Visitor.visitTypeRecord(RecordCopy))
@@ -45,6 +47,8 @@ Error CVTypeDumper::dump(const CVTypeArray &Types,
Pipeline.addCallbackToPipeline(Dumper);
CVTypeVisitor Visitor(Pipeline);
+ if (Handler)
+ Visitor.addTypeServerHandler(*Handler);
if (auto EC = Visitor.visitTypeStream(Types))
return EC;
diff --git a/llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp b/llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp
index 5171e24f3aa..48160252c6d 100644
--- a/llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp
+++ b/llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp
@@ -10,9 +10,14 @@
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
#include "llvm/DebugInfo/CodeView/CodeViewError.h"
+#include "llvm/DebugInfo/CodeView/TypeDatabase.h"
+#include "llvm/DebugInfo/CodeView/TypeDatabaseVisitor.h"
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h"
+#include "llvm/DebugInfo/CodeView/TypeServerHandler.h"
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
#include "llvm/DebugInfo/MSF/ByteStream.h"
+#include "llvm/DebugInfo/MSF/StreamReader.h"
using namespace llvm;
using namespace llvm::codeview;
@@ -21,7 +26,8 @@ CVTypeVisitor::CVTypeVisitor(TypeVisitorCallbacks &Callbacks)
: Callbacks(Callbacks) {}
template <typename T>
-static Error visitKnownRecord(CVType &Record, TypeVisitorCallbacks &Callbacks) {
+static Error visitKnownRecord(CVTypeVisitor &Visitor, CVType &Record,
+ TypeVisitorCallbacks &Callbacks) {
TypeRecordKind RK = static_cast<TypeRecordKind>(Record.Type);
T KnownRecord(RK);
if (auto EC = Callbacks.visitKnownRecord(Record, KnownRecord))
@@ -39,7 +45,58 @@ static Error visitKnownMember(CVMemberRecord &Record,
return Error::success();
}
+static Expected<TypeServer2Record> deserializeTypeServerRecord(CVType &Record) {
+ class StealTypeServerVisitor : public TypeVisitorCallbacks {
+ public:
+ explicit StealTypeServerVisitor(TypeServer2Record &TR) : TR(TR) {}
+
+ Error visitKnownRecord(CVType &CVR, TypeServer2Record &Record) override {
+ TR = Record;
+ return Error::success();
+ }
+
+ private:
+ TypeServer2Record &TR;
+ };
+
+ TypeServer2Record R(TypeRecordKind::TypeServer2);
+ TypeDeserializer Deserializer;
+ StealTypeServerVisitor Thief(R);
+ TypeVisitorCallbackPipeline Pipeline;
+ Pipeline.addCallbackToPipeline(Deserializer);
+ Pipeline.addCallbackToPipeline(Thief);
+ CVTypeVisitor Visitor(Pipeline);
+ if (auto EC = Visitor.visitTypeRecord(Record))
+ return std::move(EC);
+
+ return R;
+}
+
+void CVTypeVisitor::addTypeServerHandler(TypeServerHandler &Handler) {
+ Handlers.push_back(&Handler);
+}
+
Error CVTypeVisitor::visitTypeRecord(CVType &Record) {
+ if (Record.Type == TypeLeafKind::LF_TYPESERVER2 && !Handlers.empty()) {
+ auto TS = deserializeTypeServerRecord(Record);
+ if (!TS)
+ return TS.takeError();
+
+ for (auto Handler : Handlers) {
+ auto ExpectedResult = Handler->handle(*TS, Callbacks);
+ // If there was an error, return the error.
+ if (!ExpectedResult)
+ return ExpectedResult.takeError();
+
+ // If the handler processed the record, return success.
+ if (*ExpectedResult)
+ return Error::success();
+
+ // Otherwise keep searching for a handler, eventually falling out and
+ // using the default record handler.
+ }
+ }
+
if (auto EC = Callbacks.visitTypeBegin(Record))
return EC;
@@ -50,7 +107,7 @@ Error CVTypeVisitor::visitTypeRecord(CVType &Record) {
break;
#define TYPE_RECORD(EnumName, EnumVal, Name) \
case EnumName: { \
- if (auto EC = visitKnownRecord<Name##Record>(Record, Callbacks)) \
+ if (auto EC = visitKnownRecord<Name##Record>(*this, Record, Callbacks)) \
return EC; \
break; \
}
@@ -109,6 +166,14 @@ Error CVTypeVisitor::visitTypeStream(const CVTypeArray &Types) {
return Error::success();
}
+Error CVTypeVisitor::visitTypeStream(CVTypeRange Types) {
+ for (auto I : Types) {
+ if (auto EC = visitTypeRecord(I))
+ return EC;
+ }
+ return Error::success();
+}
+
Error CVTypeVisitor::visitFieldListMemberStream(msf::StreamReader Reader) {
FieldListDeserializer Deserializer(Reader);
TypeVisitorCallbackPipeline Pipeline;
diff --git a/llvm/lib/DebugInfo/CodeView/CodeViewError.cpp b/llvm/lib/DebugInfo/CodeView/CodeViewError.cpp
index 55c10c076ee..8de266b836b 100644
--- a/llvm/lib/DebugInfo/CodeView/CodeViewError.cpp
+++ b/llvm/lib/DebugInfo/CodeView/CodeViewError.cpp
@@ -31,6 +31,8 @@ public:
"bytes.";
case cv_error_code::corrupt_record:
return "The CodeView record is corrupted.";
+ case cv_error_code::no_records:
+ return "There are no records";
case cv_error_code::operation_unsupported:
return "The requested operation is not supported.";
case cv_error_code::unknown_member_record:
diff --git a/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp b/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp
index 07984ad2fe3..85104d25cb4 100644
--- a/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp
+++ b/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp
@@ -54,8 +54,9 @@ namespace {
/// existing destination type index.
class TypeStreamMerger : public TypeVisitorCallbacks {
public:
- TypeStreamMerger(TypeTableBuilder &DestStream)
- : DestStream(DestStream), FieldListBuilder(DestStream) {}
+ TypeStreamMerger(TypeTableBuilder &DestStream, TypeServerHandler *Handler)
+ : DestStream(DestStream), FieldListBuilder(DestStream), Handler(Handler) {
+ }
/// TypeVisitorCallbacks overrides.
#define TYPE_RECORD(EnumName, EnumVal, Name) \
@@ -109,6 +110,7 @@ private:
TypeTableBuilder &DestStream;
FieldListRecordBuilder FieldListBuilder;
+ TypeServerHandler *Handler;
bool IsInFieldList{false};
size_t BeginIndexMapSize = 0;
@@ -175,6 +177,8 @@ Error TypeStreamMerger::mergeStream(const CVTypeArray &Types) {
Pipeline.addCallbackToPipeline(*this);
CVTypeVisitor Visitor(Pipeline);
+ if (Handler)
+ Visitor.addTypeServerHandler(*Handler);
if (auto EC = Visitor.visitTypeStream(Types))
return EC;
@@ -186,6 +190,7 @@ Error TypeStreamMerger::mergeStream(const CVTypeArray &Types) {
}
Error llvm::codeview::mergeTypeStreams(TypeTableBuilder &DestStream,
+ TypeServerHandler *Handler,
const CVTypeArray &Types) {
- return TypeStreamMerger(DestStream).mergeStream(Types);
+ return TypeStreamMerger(DestStream, Handler).mergeStream(Types);
}
diff --git a/llvm/lib/DebugInfo/PDB/CMakeLists.txt b/llvm/lib/DebugInfo/PDB/CMakeLists.txt
index 774edcd4011..4d4eb6ceaf1 100644
--- a/llvm/lib/DebugInfo/PDB/CMakeLists.txt
+++ b/llvm/lib/DebugInfo/PDB/CMakeLists.txt
@@ -44,6 +44,7 @@ add_pdb_impl_folder(Native
Native/NativeSession.cpp
Native/PDBFile.cpp
Native/PDBFileBuilder.cpp
+ Native/PDBTypeServerHandler.cpp
Native/PublicsStream.cpp
Native/RawError.cpp
Native/StringTable.cpp
diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp
index e2c23317511..cafaa9bffa3 100644
--- a/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp
+++ b/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp
@@ -47,7 +47,7 @@ Error NativeSession::createFromPdb(StringRef Path,
auto Stream = llvm::make_unique<MemoryBufferByteStream>(std::move(Buffer));
auto Allocator = llvm::make_unique<BumpPtrAllocator>();
- auto File = llvm::make_unique<PDBFile>(std::move(Stream), *Allocator);
+ auto File = llvm::make_unique<PDBFile>(Path, std::move(Stream), *Allocator);
if (auto EC = File->parseFileHeaders())
return EC;
if (auto EC = File->parseStreamData())
diff --git a/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp b/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp
index 02e883b8418..3a3692dcac5 100644
--- a/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp
+++ b/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp
@@ -25,6 +25,7 @@
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/Path.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
@@ -38,12 +39,18 @@ namespace {
typedef FixedStreamArray<support::ulittle32_t> ulittle_array;
} // end anonymous namespace
-PDBFile::PDBFile(std::unique_ptr<ReadableStream> PdbFileBuffer,
+PDBFile::PDBFile(StringRef Path, std::unique_ptr<ReadableStream> PdbFileBuffer,
BumpPtrAllocator &Allocator)
- : Allocator(Allocator), Buffer(std::move(PdbFileBuffer)) {}
+ : FilePath(Path), Allocator(Allocator), Buffer(std::move(PdbFileBuffer)) {}
PDBFile::~PDBFile() = default;
+StringRef PDBFile::getFilePath() const { return FilePath; }
+
+StringRef PDBFile::getFileDirectory() const {
+ return sys::path::parent_path(FilePath);
+}
+
uint32_t PDBFile::getBlockSize() const { return ContainerLayout.SB->BlockSize; }
uint32_t PDBFile::getFreeBlockMapBlock() const {
diff --git a/llvm/lib/DebugInfo/PDB/Native/PDBTypeServerHandler.cpp b/llvm/lib/DebugInfo/PDB/Native/PDBTypeServerHandler.cpp
new file mode 100644
index 00000000000..629f3e80b0e
--- /dev/null
+++ b/llvm/lib/DebugInfo/PDB/Native/PDBTypeServerHandler.cpp
@@ -0,0 +1,119 @@
+//===- PDBTypeServerHandler.cpp ---------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Handles CodeView LF_TYPESERVER2 records by attempting to locate a matching
+// PDB file, then loading the PDB file and visiting all types from the
+// referenced PDB using the original supplied visitor.
+//
+// The net effect of this is that when visiting a PDB containing a TypeServer
+// record, the TypeServer record is "replaced" with all of the records in
+// the referenced PDB file. If a single instance of PDBTypeServerHandler
+// encounters the same TypeServer multiple times (for example reusing one
+// PDBTypeServerHandler across multiple visitations of distinct object files or
+// PDB files), PDBTypeServerHandler will optionally revisit all the records
+// again, or simply consume the record and do nothing.
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.h"
+
+#include "llvm/DebugInfo/CodeView/CodeViewError.h"
+#include "llvm/DebugInfo/PDB/GenericError.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/DebugInfo/PDB/PDB.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+static void ignoreErrors(Error EC) {
+ llvm::handleAllErrors(std::move(EC), [&](ErrorInfoBase &EIB) {});
+}
+
+PDBTypeServerHandler::PDBTypeServerHandler(bool RevisitAlways)
+ : RevisitAlways(RevisitAlways) {}
+
+void PDBTypeServerHandler::addSearchPath(StringRef Path) {
+ if (Path.empty() || !sys::fs::is_directory(Path))
+ return;
+
+ SearchPaths.push_back(Path);
+}
+
+Expected<bool>
+PDBTypeServerHandler::handleInternal(PDBFile &File,
+ TypeVisitorCallbacks &Callbacks) {
+ auto ExpectedTpi = File.getPDBTpiStream();
+ if (!ExpectedTpi)
+ return ExpectedTpi.takeError();
+ CVTypeVisitor Visitor(Callbacks);
+
+ if (auto EC = Visitor.visitTypeStream(ExpectedTpi->types(nullptr)))
+ return std::move(EC);
+
+ return true;
+}
+
+Expected<bool> PDBTypeServerHandler::handle(TypeServer2Record &TS,
+ TypeVisitorCallbacks &Callbacks) {
+ if (Session) {
+ // If we've already handled this TypeServer and we only want to handle each
+ // TypeServer once, consume the record without doing anything.
+ if (!RevisitAlways)
+ return true;
+
+ return handleInternal(Session->getPDBFile(), Callbacks);
+ }
+
+ StringRef File = sys::path::filename(TS.Name);
+ if (File.empty())
+ return make_error<CodeViewError>(
+ cv_error_code::corrupt_record,
+ "TypeServer2Record does not contain filename!");
+
+ for (auto Path : SearchPaths) {
+ sys::path::append(Path, File);
+ if (!sys::fs::exists(Path))
+ continue;
+
+ std::unique_ptr<IPDBSession> ThisSession;
+ if (auto EC = loadDataForPDB(PDB_ReaderType::Native, Path, ThisSession)) {
+ // It is not an error if this PDB fails to load, it just means that it
+ // doesn't match and we should continue searching.
+ ignoreErrors(std::move(EC));
+ continue;
+ }
+
+ std::unique_ptr<NativeSession> NS(
+ static_cast<NativeSession *>(ThisSession.release()));
+ PDBFile &File = NS->getPDBFile();
+ auto ExpectedInfo = File.getPDBInfoStream();
+ // All PDB Files should have an Info stream.
+ if (!ExpectedInfo)
+ return ExpectedInfo.takeError();
+
+ // 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.
+ ArrayRef<uint8_t> GuidBytes(ExpectedInfo->getGuid().Guid);
+ StringRef GuidStr(reinterpret_cast<const char *>(GuidBytes.begin()),
+ GuidBytes.size());
+ if (GuidStr != TS.Guid)
+ continue;
+
+ Session = std::move(NS);
+ return handleInternal(File, Callbacks);
+ }
+
+ // We couldn't find a matching PDB, so let it be handled by someone else.
+ return false;
+}
diff --git a/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp b/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp
index 603a2c5e833..a24291f5cd4 100644
--- a/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp
+++ b/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp
@@ -16,6 +16,7 @@
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
#include "llvm/DebugInfo/MSF/StreamReader.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.h"
#include "llvm/DebugInfo/PDB/Native/RawConstants.h"
#include "llvm/DebugInfo/PDB/Native/RawError.h"
#include "llvm/DebugInfo/PDB/Native/RawTypes.h"
@@ -164,7 +165,7 @@ FixedStreamArray<TypeIndexOffset> TpiStream::getTypeIndexOffsets() const {
HashTable &TpiStream::getHashAdjusters() { return HashAdjusters; }
-iterator_range<CVTypeArray::Iterator> TpiStream::types(bool *HadError) const {
+CVTypeRange TpiStream::types(bool *HadError) const {
return make_range(TypeRecords.begin(HadError), TypeRecords.end());
}
diff --git a/llvm/tools/llvm-readobj/CMakeLists.txt b/llvm/tools/llvm-readobj/CMakeLists.txt
index 5fd45a8cff6..0ad149538f6 100644
--- a/llvm/tools/llvm-readobj/CMakeLists.txt
+++ b/llvm/tools/llvm-readobj/CMakeLists.txt
@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
Support
DebugInfoCodeView
DebugInfoMSF
+ DebugInfoPDB
)
add_llvm_tool(llvm-readobj
diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp
index 3d4ebc8a735..a0274c0ad95 100644
--- a/llvm/tools/llvm-readobj/COFFDumper.cpp
+++ b/llvm/tools/llvm-readobj/COFFDumper.cpp
@@ -43,6 +43,7 @@
#include "llvm/Support/Compiler.h"
#include "llvm/Support/DataExtractor.h"
#include "llvm/Support/Format.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/Win64EH.h"
@@ -1086,7 +1087,7 @@ void COFFDumper::mergeCodeViewTypes(TypeTableBuilder &CVTypes) {
error(object_error::parse_failed);
}
- if (auto EC = mergeTypeStreams(CVTypes, Types)) {
+ if (auto EC = mergeTypeStreams(CVTypes, nullptr, Types)) {
consumeError(std::move(EC));
return error(object_error::parse_failed);
}
diff --git a/llvm/tools/llvm-readobj/LLVMBuild.txt b/llvm/tools/llvm-readobj/LLVMBuild.txt
index 76dd436e21d..c0ed38e18d0 100644
--- a/llvm/tools/llvm-readobj/LLVMBuild.txt
+++ b/llvm/tools/llvm-readobj/LLVMBuild.txt
@@ -19,4 +19,4 @@
type = Tool
name = llvm-readobj
parent = Tools
-required_libraries = all-targets BitReader Object DebugInfoCodeView DebugInfoMSF
+required_libraries = all-targets BitReader Object DebugInfoCodeView DebugInfoPDB DebugInfoMSF
diff --git a/llvm/unittests/DebugInfo/PDB/CMakeLists.txt b/llvm/unittests/DebugInfo/PDB/CMakeLists.txt
index 3ba3196ed1c..cbbbd817748 100644
--- a/llvm/unittests/DebugInfo/PDB/CMakeLists.txt
+++ b/llvm/unittests/DebugInfo/PDB/CMakeLists.txt
@@ -10,6 +10,7 @@ set(DebugInfoPDBSources
StringTableBuilderTest.cpp
MSFBuilderTest.cpp
PDBApiTest.cpp
+ TypeServerHandlerTest.cpp
)
add_llvm_unittest(DebugInfoPDBTests
diff --git a/llvm/unittests/DebugInfo/PDB/TypeServerHandlerTest.cpp b/llvm/unittests/DebugInfo/PDB/TypeServerHandlerTest.cpp
new file mode 100644
index 00000000000..6995e8f9dde
--- /dev/null
+++ b/llvm/unittests/DebugInfo/PDB/TypeServerHandlerTest.cpp
@@ -0,0 +1,175 @@
+//===- llvm/unittest/DebugInfo/PDB/TypeServerHandlerTest.cpp --------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ErrorChecking.h"
+
+#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
+#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h"
+#include "llvm/DebugInfo/CodeView/TypeSerializer.h"
+#include "llvm/DebugInfo/CodeView/TypeServerHandler.h"
+#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
+#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
+#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
+#include "llvm/DebugInfo/PDB/Native/RawTypes.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Error.h"
+
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+namespace {
+
+constexpr uint8_t Guid[] = {0x2a, 0x2c, 0x1c, 0x2a, 0xcb, 0x9e, 0x48, 0x18,
+ 0x82, 0x82, 0x7a, 0x87, 0xc3, 0xfe, 0x16, 0xe8};
+StringRef GuidStr(reinterpret_cast<const char *>(Guid),
+ llvm::array_lengthof(Guid));
+
+constexpr const char *Name = "Test Name";
+constexpr int Age = 1;
+
+class MockTypeServerHandler : public TypeServerHandler {
+public:
+ explicit MockTypeServerHandler(bool HandleAlways)
+ : HandleAlways(HandleAlways) {}
+
+ Expected<bool> handle(TypeServer2Record &TS,
+ TypeVisitorCallbacks &Callbacks) override {
+ if (TS.Age != Age || TS.Guid != GuidStr || TS.Name != Name)
+ return make_error<CodeViewError>(cv_error_code::corrupt_record,
+ "Invalid TypeServer record!");
+
+ if (Handled && !HandleAlways)
+ return false;
+
+ Handled = true;
+ return true;
+ }
+
+ bool Handled = false;
+ bool HandleAlways;
+};
+
+class MockTypeVisitorCallbacks : public TypeVisitorCallbacks {
+public:
+ enum class State {
+ Ready,
+ VisitTypeBegin,
+ VisitKnownRecord,
+ VisitTypeEnd,
+ };
+ Error visitTypeBegin(CVType &CVT) override {
+ if (S != State::Ready)
+ return make_error<CodeViewError>(cv_error_code::unspecified,
+ "Invalid visitor state!");
+
+ S = State::VisitTypeBegin;
+ return Error::success();
+ }
+
+ Error visitKnownRecord(CVType &CVT, TypeServer2Record &TS) override {
+ if (S != State::VisitTypeBegin)
+ return make_error<CodeViewError>(cv_error_code::unspecified,
+ "Invalid visitor state!");
+
+ S = State::VisitKnownRecord;
+ return Error::success();
+ }
+
+ Error visitTypeEnd(CVType &CVT) override {
+ if (S != State::VisitKnownRecord)
+ return make_error<CodeViewError>(cv_error_code::unspecified,
+ "Invalid visitor state!");
+
+ S = State::VisitTypeEnd;
+ return Error::success();
+ }
+
+ State S = State::Ready;
+};
+
+class TypeServerHandlerTest : public testing::Test {
+public:
+ void SetUp() override {
+ TypeServer2Record R(TypeRecordKind::TypeServer2);
+ R.Age = Age;
+ R.Guid = GuidStr;
+ R.Name = Name;
+
+ TypeTableBuilder Builder(Allocator);
+ Builder.writeKnownType(R);
+ TypeServerRecord.RecordData = Builder.records().front();
+ TypeServerRecord.Type = TypeLeafKind::LF_TYPESERVER2;
+ }
+
+protected:
+ BumpPtrAllocator Allocator;
+ CVType TypeServerRecord;
+};
+
+// Test that when no type server handler is registered, it gets handled by the
+// normal
+// visitor callbacks.
+TEST_F(TypeServerHandlerTest, VisitRecordNoTypeServer) {
+ MockTypeVisitorCallbacks C2;
+ MockTypeVisitorCallbacks C1;
+ TypeVisitorCallbackPipeline Pipeline;
+
+ Pipeline.addCallbackToPipeline(C1);
+ Pipeline.addCallbackToPipeline(C2);
+ CVTypeVisitor Visitor(Pipeline);
+ EXPECT_NO_ERROR(Visitor.visitTypeRecord(TypeServerRecord));
+
+ EXPECT_EQ(MockTypeVisitorCallbacks::State::VisitTypeEnd, C1.S);
+ EXPECT_EQ(MockTypeVisitorCallbacks::State::VisitTypeEnd, C2.S);
+}
+
+// Test that when a TypeServerHandler is registered, it gets consumed by the
+// handler if and only if the handler returns true.
+TEST_F(TypeServerHandlerTest, VisitRecordWithTypeServerOnce) {
+ MockTypeServerHandler Handler(false);
+
+ MockTypeVisitorCallbacks C1;
+ CVTypeVisitor Visitor(C1);
+ Visitor.addTypeServerHandler(Handler);
+
+ // Our mock server returns true the first time.
+ EXPECT_NO_ERROR(Visitor.visitTypeRecord(TypeServerRecord));
+ EXPECT_TRUE(Handler.Handled);
+ EXPECT_EQ(MockTypeVisitorCallbacks::State::Ready, C1.S);
+
+ // And false the second time.
+ EXPECT_NO_ERROR(Visitor.visitTypeRecord(TypeServerRecord));
+ EXPECT_TRUE(Handler.Handled);
+ EXPECT_EQ(MockTypeVisitorCallbacks::State::VisitTypeEnd, C1.S);
+}
+
+// Test that when a type server handler is registered, if the handler keeps
+// returning true, it will keep getting consumed by the handler and not go
+// to the default processor.
+TEST_F(TypeServerHandlerTest, VisitRecordWithTypeServerAlways) {
+ MockTypeServerHandler Handler(true);
+
+ MockTypeVisitorCallbacks C1;
+ CVTypeVisitor Visitor(C1);
+ Visitor.addTypeServerHandler(Handler);
+
+ EXPECT_NO_ERROR(Visitor.visitTypeRecord(TypeServerRecord));
+ EXPECT_TRUE(Handler.Handled);
+ EXPECT_EQ(MockTypeVisitorCallbacks::State::Ready, C1.S);
+
+ EXPECT_NO_ERROR(Visitor.visitTypeRecord(TypeServerRecord));
+ EXPECT_TRUE(Handler.Handled);
+ EXPECT_EQ(MockTypeVisitorCallbacks::State::Ready, C1.S);
+}
+
+} // end anonymous namespace
OpenPOWER on IntegriCloud