mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-26 12:43:36 +01:00
[pdb] Write PDB TPI Stream from Yaml.
This writes the full sequence of type records described in Yaml to the TPI stream of the PDB file. Reviewed By: rnk Differential Revision: https://reviews.llvm.org/D24316 llvm-svn: 281063
This commit is contained in:
parent
7259fc9dc4
commit
c2876ae1eb
@ -20,7 +20,8 @@ namespace codeview {
|
||||
|
||||
class MemoryTypeTableBuilder : public TypeTableBuilder {
|
||||
public:
|
||||
MemoryTypeTableBuilder() {}
|
||||
explicit MemoryTypeTableBuilder(BumpPtrAllocator &Allocator)
|
||||
: RecordStorage(Allocator) {}
|
||||
|
||||
bool empty() const { return Records.empty(); }
|
||||
|
||||
@ -33,12 +34,13 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
TypeIndex writeRecord(llvm::StringRef Data) override;
|
||||
|
||||
ArrayRef<StringRef> getRecords() const { return Records; }
|
||||
|
||||
private:
|
||||
std::vector<StringRef> Records;
|
||||
BumpPtrAllocator RecordStorage;
|
||||
BumpPtrAllocator &RecordStorage;
|
||||
DenseMap<StringRef, TypeIndex> HashedRecords;
|
||||
};
|
||||
|
||||
|
@ -47,6 +47,7 @@ public:
|
||||
llvm::StringRef str();
|
||||
|
||||
uint64_t size() const { return Stream.tell(); }
|
||||
TypeRecordKind kind() const { return Kind; }
|
||||
|
||||
void truncate(uint64_t Size) {
|
||||
// This works because raw_svector_ostream is not buffered.
|
||||
@ -56,10 +57,12 @@ public:
|
||||
|
||||
void reset(TypeRecordKind K) {
|
||||
Buffer.clear();
|
||||
Kind = K;
|
||||
writeTypeRecordKind(K);
|
||||
}
|
||||
|
||||
private:
|
||||
TypeRecordKind Kind;
|
||||
llvm::SmallVector<char, 256> Buffer;
|
||||
llvm::raw_svector_ostream Stream;
|
||||
llvm::support::endian::Writer<llvm::support::endianness::little> Writer;
|
||||
|
74
include/llvm/DebugInfo/CodeView/TypeSerializationVisitor.h
Normal file
74
include/llvm/DebugInfo/CodeView/TypeSerializationVisitor.h
Normal file
@ -0,0 +1,74 @@
|
||||
//===- TypeSerializationVisitor.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_TYPESERIALIZATIONVISITOR_H
|
||||
#define LLVM_DEBUGINFO_CODEVIEW_TYPESERIALIZATIONVISITOR_H
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/FieldListRecordBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace codeview {
|
||||
|
||||
class TypeSerializationVisitor : public TypeVisitorCallbacks {
|
||||
public:
|
||||
TypeSerializationVisitor(FieldListRecordBuilder &FieldListBuilder,
|
||||
MemoryTypeTableBuilder &TypeTableBuilder)
|
||||
: FieldListBuilder(FieldListBuilder), TypeTableBuilder(TypeTableBuilder) {
|
||||
}
|
||||
|
||||
virtual Expected<TypeLeafKind> visitTypeBegin(const CVType &Record) override {
|
||||
if (Record.Type == TypeLeafKind::LF_FIELDLIST)
|
||||
FieldListBuilder.reset();
|
||||
return Record.Type;
|
||||
}
|
||||
|
||||
virtual Error visitTypeEnd(const CVRecord<TypeLeafKind> &Record) override {
|
||||
if (Record.Type == TypeLeafKind::LF_FIELDLIST)
|
||||
TypeTableBuilder.writeFieldList(FieldListBuilder);
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
#define TYPE_RECORD(EnumName, EnumVal, Name) \
|
||||
virtual Error visitKnownRecord(const CVRecord<TypeLeafKind> &CVR, \
|
||||
Name##Record &Record) override { \
|
||||
visitKnownRecordImpl(Record); \
|
||||
return Error::success(); \
|
||||
}
|
||||
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
|
||||
virtual Error visitKnownRecord(const CVRecord<TypeLeafKind> &CVR, \
|
||||
Name##Record &Record) override { \
|
||||
visitMemberRecordImpl(Record); \
|
||||
return Error::success(); \
|
||||
}
|
||||
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
|
||||
|
||||
private:
|
||||
template <typename RecordKind> void visitKnownRecordImpl(RecordKind &Record) {
|
||||
TypeTableBuilder.writeKnownType(Record);
|
||||
}
|
||||
template <typename RecordKind>
|
||||
void visitMemberRecordImpl(RecordKind &Record) {
|
||||
FieldListBuilder.writeMemberType(Record);
|
||||
}
|
||||
|
||||
void visitKnownRecordImpl(FieldListRecord &FieldList) {}
|
||||
|
||||
FieldListRecordBuilder &FieldListBuilder;
|
||||
MemoryTypeTableBuilder &TypeTableBuilder;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -63,6 +63,11 @@ public:
|
||||
TypeIndex writeRecord(TypeRecordBuilder &builder);
|
||||
|
||||
virtual TypeIndex writeRecord(llvm::StringRef record) = 0;
|
||||
|
||||
ArrayRef<TypeRecordKind> getRecordKinds() const { return RecordKinds; }
|
||||
|
||||
private:
|
||||
std::vector<TypeRecordKind> RecordKinds;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
91
include/llvm/DebugInfo/MSF/SequencedItemStream.h
Normal file
91
include/llvm/DebugInfo/MSF/SequencedItemStream.h
Normal file
@ -0,0 +1,91 @@
|
||||
//===- SequencedItemStream.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_MSF_SEQUENCEDITEMSTREAM_H
|
||||
#define LLVM_DEBUGINFO_MSF_SEQUENCEDITEMSTREAM_H
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/DebugInfo/MSF/MSFError.h"
|
||||
#include "llvm/DebugInfo/MSF/StreamInterface.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace llvm {
|
||||
namespace msf {
|
||||
template <typename T> struct SequencedItemTraits {
|
||||
static size_t length(const T &Item) = delete;
|
||||
static ArrayRef<uint8_t> bytes(const T &Item) = delete;
|
||||
};
|
||||
|
||||
/// SequencedItemStream represents a sequence of objects stored in a
|
||||
/// standard container but for which it is useful to view as a stream of
|
||||
/// contiguous bytes. An example of this might be if you have a std::vector
|
||||
/// of TPI records, where each record contains a byte sequence that
|
||||
/// represents that one record serialized, but where each consecutive item
|
||||
/// might not be allocated immediately after the previous item. Using a
|
||||
/// SequencedItemStream, we can adapt the VarStreamArray class to trivially
|
||||
/// extract one item at a time, allowing the data to be used anywhere a
|
||||
/// VarStreamArray could be used.
|
||||
template <typename T, typename Traits = SequencedItemTraits<T>>
|
||||
class SequencedItemStream : public ReadableStream {
|
||||
public:
|
||||
SequencedItemStream() {}
|
||||
|
||||
Error readBytes(uint32_t Offset, uint32_t Size,
|
||||
ArrayRef<uint8_t> &Buffer) const override {
|
||||
auto ExpectedIndex = translateOffsetIndex(Offset);
|
||||
if (!ExpectedIndex)
|
||||
return ExpectedIndex.takeError();
|
||||
const auto &Item = Items[*ExpectedIndex];
|
||||
if (Size > Traits::length(Item))
|
||||
return make_error<MSFError>(msf_error_code::insufficient_buffer);
|
||||
Buffer = Traits::bytes(Item).take_front(Size);
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error readLongestContiguousChunk(uint32_t Offset,
|
||||
ArrayRef<uint8_t> &Buffer) const override {
|
||||
auto ExpectedIndex = translateOffsetIndex(Offset);
|
||||
if (!ExpectedIndex)
|
||||
return ExpectedIndex.takeError();
|
||||
Buffer = Traits::bytes(Items[*ExpectedIndex]);
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
void setItems(ArrayRef<T> ItemArray) { Items = ItemArray; }
|
||||
|
||||
uint32_t getLength() const override {
|
||||
uint32_t Size = 0;
|
||||
for (const auto &Item : Items)
|
||||
Size += Traits::length(Item);
|
||||
return Size;
|
||||
}
|
||||
|
||||
private:
|
||||
Expected<uint32_t> translateOffsetIndex(uint32_t Offset) const {
|
||||
uint32_t CurrentOffset = 0;
|
||||
uint32_t CurrentIndex = 0;
|
||||
for (const auto &Item : Items) {
|
||||
if (CurrentOffset >= Offset)
|
||||
break;
|
||||
CurrentOffset += Traits::length(Item);
|
||||
++CurrentIndex;
|
||||
}
|
||||
if (CurrentOffset != Offset)
|
||||
return make_error<MSFError>(msf_error_code::insufficient_buffer);
|
||||
return CurrentIndex;
|
||||
}
|
||||
ArrayRef<T> Items;
|
||||
};
|
||||
} // end namespace msf
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_DEBUGINFO_MSF_SEQUENCEDITEMSTREAM_H
|
@ -10,6 +10,7 @@
|
||||
#ifndef LLVM_DEBUGINFO_MSF_STREAMARRAY_H
|
||||
#define LLVM_DEBUGINFO_MSF_STREAMARRAY_H
|
||||
|
||||
#include "llvm/DebugInfo/MSF/SequencedItemStream.h"
|
||||
#include "llvm/DebugInfo/MSF/StreamRef.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
|
@ -92,6 +92,8 @@ public:
|
||||
Expected<SymbolStream &> getPDBSymbolStream();
|
||||
Expected<NameHashTable &> getStringTable();
|
||||
|
||||
BumpPtrAllocator &getAllocator() { return Allocator; }
|
||||
|
||||
private:
|
||||
BumpPtrAllocator &Allocator;
|
||||
|
||||
|
@ -28,6 +28,7 @@ class MSFBuilder;
|
||||
namespace pdb {
|
||||
class DbiStreamBuilder;
|
||||
class InfoStreamBuilder;
|
||||
class TpiStreamBuilder;
|
||||
|
||||
class PDBFileBuilder {
|
||||
public:
|
||||
@ -40,6 +41,7 @@ public:
|
||||
msf::MSFBuilder &getMsfBuilder();
|
||||
InfoStreamBuilder &getInfoBuilder();
|
||||
DbiStreamBuilder &getDbiBuilder();
|
||||
TpiStreamBuilder &getTpiBuilder();
|
||||
|
||||
Expected<std::unique_ptr<PDBFile>>
|
||||
build(std::unique_ptr<msf::WritableStream> PdbFileBuffer);
|
||||
@ -54,6 +56,7 @@ private:
|
||||
std::unique_ptr<msf::MSFBuilder> Msf;
|
||||
std::unique_ptr<InfoStreamBuilder> Info;
|
||||
std::unique_ptr<DbiStreamBuilder> Dbi;
|
||||
std::unique_ptr<TpiStreamBuilder> Tpi;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -266,6 +266,34 @@ struct PDB_UniqueId {
|
||||
char Guid[16];
|
||||
};
|
||||
|
||||
// The header preceeding the global TPI stream.
|
||||
// This corresponds to `HDR` in PDB/dbi/tpi.h.
|
||||
struct TpiStreamHeader {
|
||||
struct EmbeddedBuf {
|
||||
support::little32_t Off;
|
||||
support::ulittle32_t Length;
|
||||
};
|
||||
|
||||
support::ulittle32_t Version;
|
||||
support::ulittle32_t HeaderSize;
|
||||
support::ulittle32_t TypeIndexBegin;
|
||||
support::ulittle32_t TypeIndexEnd;
|
||||
support::ulittle32_t TypeRecordBytes;
|
||||
|
||||
// The following members correspond to `TpiHash` in PDB/dbi/tpi.h.
|
||||
support::ulittle16_t HashStreamIndex;
|
||||
support::ulittle16_t HashAuxStreamIndex;
|
||||
support::ulittle32_t HashKeySize;
|
||||
support::ulittle32_t NumHashBuckets;
|
||||
|
||||
EmbeddedBuf HashValueBuffer;
|
||||
EmbeddedBuf IndexOffsetBuffer;
|
||||
EmbeddedBuf HashAdjBuffer;
|
||||
};
|
||||
|
||||
const uint32_t MinTpiHashBuckets = 0x1000;
|
||||
const uint32_t MaxTpiHashBuckets = 0x40000;
|
||||
|
||||
/// The header preceeding the global PDB Stream (Stream 1)
|
||||
struct InfoStreamHeader {
|
||||
support::ulittle32_t Version;
|
||||
|
@ -27,7 +27,7 @@ namespace pdb {
|
||||
class PDBFile;
|
||||
|
||||
class TpiStream {
|
||||
struct HeaderInfo;
|
||||
friend class TpiStreamBuilder;
|
||||
|
||||
public:
|
||||
TpiStream(const PDBFile &File,
|
||||
@ -66,7 +66,7 @@ private:
|
||||
msf::FixedStreamArray<TypeIndexOffset> TypeIndexOffsets;
|
||||
msf::FixedStreamArray<TypeIndexOffset> HashAdjustments;
|
||||
|
||||
const HeaderInfo *Header;
|
||||
const TpiStreamHeader *Header;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
76
include/llvm/DebugInfo/PDB/Raw/TpiStreamBuilder.h
Normal file
76
include/llvm/DebugInfo/PDB/Raw/TpiStreamBuilder.h
Normal file
@ -0,0 +1,76 @@
|
||||
//===- TpiStreamBuilder.h - PDB Tpi Stream Creation -------------*- 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_RAW_PDBTPISTREAMBUILDER_H
|
||||
#define LLVM_DEBUGINFO_PDB_RAW_PDBTPISTREAMBUILDER_H
|
||||
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/MSF/ByteStream.h"
|
||||
#include "llvm/DebugInfo/MSF/SequencedItemStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/RawConstants.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
namespace codeview {
|
||||
class TypeRecord;
|
||||
}
|
||||
namespace msf {
|
||||
struct MSFLayout;
|
||||
class ReadableStreamRef;
|
||||
class WritableStream;
|
||||
|
||||
template <> struct SequencedItemTraits<llvm::codeview::CVType> {
|
||||
static size_t length(const codeview::CVType &Item) { return Item.Length; }
|
||||
static ArrayRef<uint8_t> bytes(const codeview::CVType &Item) {
|
||||
return Item.RawData;
|
||||
}
|
||||
};
|
||||
}
|
||||
namespace pdb {
|
||||
class PDBFile;
|
||||
class TpiStream;
|
||||
struct TpiStreamHeader;
|
||||
|
||||
class TpiStreamBuilder {
|
||||
public:
|
||||
explicit TpiStreamBuilder(BumpPtrAllocator &Allocator);
|
||||
~TpiStreamBuilder();
|
||||
|
||||
TpiStreamBuilder(const TpiStreamBuilder &) = delete;
|
||||
TpiStreamBuilder &operator=(const TpiStreamBuilder &) = delete;
|
||||
|
||||
void setVersionHeader(PdbRaw_TpiVer Version);
|
||||
void addTypeRecord(const codeview::CVType &Record);
|
||||
|
||||
Expected<std::unique_ptr<TpiStream>> build(PDBFile &File,
|
||||
const msf::WritableStream &Buffer);
|
||||
|
||||
Error commit(const msf::MSFLayout &Layout, const msf::WritableStream &Buffer);
|
||||
|
||||
uint32_t calculateSerializedLength() const;
|
||||
|
||||
private:
|
||||
Error finalize();
|
||||
|
||||
BumpPtrAllocator &Allocator;
|
||||
|
||||
Optional<PdbRaw_TpiVer> VerHeader;
|
||||
std::vector<codeview::CVType> TypeRecords;
|
||||
msf::SequencedItemStream<codeview::CVType> TypeRecordStream;
|
||||
|
||||
const TpiStreamHeader *Header;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -40,7 +40,8 @@ using namespace llvm::codeview;
|
||||
using namespace llvm::msf;
|
||||
|
||||
CodeViewDebug::CodeViewDebug(AsmPrinter *AP)
|
||||
: DebugHandlerBase(AP), OS(*Asm->OutStreamer), CurFn(nullptr) {
|
||||
: DebugHandlerBase(AP), OS(*Asm->OutStreamer), Allocator(),
|
||||
TypeTable(Allocator), CurFn(nullptr) {
|
||||
// If module doesn't have named metadata anchors or COFF debug section
|
||||
// is not available, skip any debug info related stuff.
|
||||
if (!MMI->getModule()->getNamedMetadata("llvm.dbg.cu") ||
|
||||
|
@ -36,6 +36,7 @@ struct ClassInfo;
|
||||
/// \brief Collects and handles line tables information in a CodeView format.
|
||||
class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
|
||||
MCStreamer &OS;
|
||||
llvm::BumpPtrAllocator Allocator;
|
||||
codeview::MemoryTypeTableBuilder TypeTable;
|
||||
|
||||
/// Represents the most general definition range.
|
||||
|
@ -81,14 +81,17 @@ Error CVTypeVisitor::visitTypeRecord(const CVRecord<TypeLeafKind> &Record) {
|
||||
else
|
||||
return ExpectedKind.takeError();
|
||||
|
||||
CVType RecordCopy = Record;
|
||||
RecordCopy.Type = Kind;
|
||||
|
||||
switch (Kind) {
|
||||
default:
|
||||
if (auto EC = Callbacks.visitUnknownType(Record))
|
||||
if (auto EC = Callbacks.visitUnknownType(RecordCopy))
|
||||
return EC;
|
||||
break;
|
||||
#define TYPE_RECORD(EnumName, EnumVal, Name) \
|
||||
case EnumName: { \
|
||||
if (auto EC = visitKnownRecord<Name##Record>(Record, Callbacks)) \
|
||||
if (auto EC = visitKnownRecord<Name##Record>(RecordCopy, Callbacks)) \
|
||||
return EC; \
|
||||
break; \
|
||||
}
|
||||
@ -101,7 +104,7 @@ Error CVTypeVisitor::visitTypeRecord(const CVRecord<TypeLeafKind> &Record) {
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
|
||||
}
|
||||
|
||||
if (auto EC = Callbacks.visitTypeEnd(Record))
|
||||
if (auto EC = Callbacks.visitTypeEnd(RecordCopy))
|
||||
return EC;
|
||||
|
||||
return Error::success();
|
||||
|
@ -13,7 +13,7 @@ using namespace llvm;
|
||||
using namespace codeview;
|
||||
|
||||
TypeRecordBuilder::TypeRecordBuilder(TypeRecordKind Kind)
|
||||
: Stream(Buffer), Writer(Stream) {
|
||||
: Kind(Kind), Stream(Buffer), Writer(Stream) {
|
||||
writeTypeRecordKind(Kind);
|
||||
}
|
||||
|
||||
|
@ -257,7 +257,9 @@ TypeIndex TypeTableBuilder::writeKnownType(const BuildInfoRecord &Record) {
|
||||
}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeRecord(TypeRecordBuilder &Builder) {
|
||||
return writeRecord(Builder.str());
|
||||
TypeIndex I = writeRecord(Builder.str());
|
||||
RecordKinds.push_back(Builder.kind());
|
||||
return I;
|
||||
}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeFieldList(FieldListRecordBuilder &FieldList) {
|
||||
|
@ -45,7 +45,8 @@ add_pdb_impl_folder(Raw
|
||||
Raw/RawError.cpp
|
||||
Raw/RawSession.cpp
|
||||
Raw/SymbolStream.cpp
|
||||
Raw/TpiStream.cpp)
|
||||
Raw/TpiStream.cpp
|
||||
Raw/TpiStreamBuilder.cpp)
|
||||
|
||||
list(APPEND LIBPDB_ADDITIONAL_HEADER_DIRS "${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/PDB/Raw")
|
||||
list(APPEND LIBPDB_ADDITIONAL_HEADER_DIRS "${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/PDB")
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include "llvm/DebugInfo/PDB/Raw/InfoStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/RawError.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/TpiStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/TpiStreamBuilder.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
@ -58,6 +60,12 @@ DbiStreamBuilder &PDBFileBuilder::getDbiBuilder() {
|
||||
return *Dbi;
|
||||
}
|
||||
|
||||
TpiStreamBuilder &PDBFileBuilder::getTpiBuilder() {
|
||||
if (!Tpi)
|
||||
Tpi = llvm::make_unique<TpiStreamBuilder>(Allocator);
|
||||
return *Tpi;
|
||||
}
|
||||
|
||||
Expected<msf::MSFLayout> PDBFileBuilder::finalizeMsfLayout() const {
|
||||
if (Info) {
|
||||
uint32_t Length = Info->calculateSerializedLength();
|
||||
@ -69,6 +77,11 @@ Expected<msf::MSFLayout> PDBFileBuilder::finalizeMsfLayout() const {
|
||||
if (auto EC = Msf->setStreamSize(StreamDBI, Length))
|
||||
return std::move(EC);
|
||||
}
|
||||
if (Tpi) {
|
||||
uint32_t Length = Tpi->calculateSerializedLength();
|
||||
if (auto EC = Msf->setStreamSize(StreamTPI, Length))
|
||||
return std::move(EC);
|
||||
}
|
||||
|
||||
return Msf->build();
|
||||
}
|
||||
@ -96,6 +109,13 @@ PDBFileBuilder::build(std::unique_ptr<msf::WritableStream> PdbFileBuffer) {
|
||||
File->Dbi = std::move(*ExpectedDbi);
|
||||
}
|
||||
|
||||
if (Tpi) {
|
||||
auto ExpectedTpi = Tpi->build(*File, *PdbFileBuffer);
|
||||
if (!ExpectedTpi)
|
||||
return ExpectedTpi.takeError();
|
||||
File->Tpi = std::move(*ExpectedTpi);
|
||||
}
|
||||
|
||||
if (File->Info && File->Dbi && File->Info->getAge() != File->Dbi->getAge())
|
||||
return llvm::make_error<RawError>(
|
||||
raw_error_code::corrupt_file,
|
||||
@ -144,5 +164,10 @@ Error PDBFileBuilder::commit(const msf::WritableStream &Buffer) {
|
||||
return EC;
|
||||
}
|
||||
|
||||
if (Tpi) {
|
||||
if (auto EC = Tpi->commit(Layout, Buffer))
|
||||
return EC;
|
||||
}
|
||||
|
||||
return Buffer.commit();
|
||||
}
|
@ -31,35 +31,6 @@ using namespace llvm::support;
|
||||
using namespace llvm::msf;
|
||||
using namespace llvm::pdb;
|
||||
|
||||
namespace {
|
||||
const uint32_t MinHashBuckets = 0x1000;
|
||||
const uint32_t MaxHashBuckets = 0x40000;
|
||||
}
|
||||
|
||||
// This corresponds to `HDR` in PDB/dbi/tpi.h.
|
||||
struct TpiStream::HeaderInfo {
|
||||
struct EmbeddedBuf {
|
||||
little32_t Off;
|
||||
ulittle32_t Length;
|
||||
};
|
||||
|
||||
ulittle32_t Version;
|
||||
ulittle32_t HeaderSize;
|
||||
ulittle32_t TypeIndexBegin;
|
||||
ulittle32_t TypeIndexEnd;
|
||||
ulittle32_t TypeRecordBytes;
|
||||
|
||||
// The following members correspond to `TpiHash` in PDB/dbi/tpi.h.
|
||||
ulittle16_t HashStreamIndex;
|
||||
ulittle16_t HashAuxStreamIndex;
|
||||
ulittle32_t HashKeySize;
|
||||
ulittle32_t NumHashBuckets;
|
||||
|
||||
EmbeddedBuf HashValueBuffer;
|
||||
EmbeddedBuf IndexOffsetBuffer;
|
||||
EmbeddedBuf HashAdjBuffer;
|
||||
};
|
||||
|
||||
TpiStream::TpiStream(const PDBFile &File,
|
||||
std::unique_ptr<MappedBlockStream> Stream)
|
||||
: Pdb(File), Stream(std::move(Stream)) {}
|
||||
@ -175,7 +146,7 @@ Error TpiStream::verifyHashValues() {
|
||||
Error TpiStream::reload() {
|
||||
StreamReader Reader(*Stream);
|
||||
|
||||
if (Reader.bytesRemaining() < sizeof(HeaderInfo))
|
||||
if (Reader.bytesRemaining() < sizeof(TpiStreamHeader))
|
||||
return make_error<RawError>(raw_error_code::corrupt_file,
|
||||
"TPI Stream does not contain a header.");
|
||||
|
||||
@ -187,7 +158,7 @@ Error TpiStream::reload() {
|
||||
return make_error<RawError>(raw_error_code::corrupt_file,
|
||||
"Unsupported TPI Version.");
|
||||
|
||||
if (Header->HeaderSize != sizeof(HeaderInfo))
|
||||
if (Header->HeaderSize != sizeof(TpiStreamHeader))
|
||||
return make_error<RawError>(raw_error_code::corrupt_file,
|
||||
"Corrupt TPI Header size.");
|
||||
|
||||
@ -195,8 +166,8 @@ Error TpiStream::reload() {
|
||||
return make_error<RawError>(raw_error_code::corrupt_file,
|
||||
"TPI Stream expected 4 byte hash key size.");
|
||||
|
||||
if (Header->NumHashBuckets < MinHashBuckets ||
|
||||
Header->NumHashBuckets > MaxHashBuckets)
|
||||
if (Header->NumHashBuckets < MinTpiHashBuckets ||
|
||||
Header->NumHashBuckets > MaxTpiHashBuckets)
|
||||
return make_error<RawError>(raw_error_code::corrupt_file,
|
||||
"TPI Stream Invalid number of hash buckets.");
|
||||
|
||||
@ -205,40 +176,44 @@ Error TpiStream::reload() {
|
||||
return EC;
|
||||
|
||||
// Hash indices, hash values, etc come from the hash stream.
|
||||
if (Header->HashStreamIndex >= Pdb.getNumStreams())
|
||||
return make_error<RawError>(raw_error_code::corrupt_file,
|
||||
"Invalid TPI hash stream index.");
|
||||
auto HS = MappedBlockStream::createIndexedStream(
|
||||
Pdb.getMsfLayout(), Pdb.getMsfBuffer(), Header->HashStreamIndex);
|
||||
StreamReader HSR(*HS);
|
||||
if (Header->HashStreamIndex != kInvalidStreamIndex) {
|
||||
if (Header->HashStreamIndex >= Pdb.getNumStreams())
|
||||
return make_error<RawError>(raw_error_code::corrupt_file,
|
||||
"Invalid TPI hash stream index.");
|
||||
|
||||
uint32_t NumHashValues = Header->HashValueBuffer.Length / sizeof(ulittle32_t);
|
||||
if (NumHashValues != NumTypeRecords())
|
||||
return make_error<RawError>(
|
||||
raw_error_code::corrupt_file,
|
||||
"TPI hash count does not match with the number of type records.");
|
||||
HSR.setOffset(Header->HashValueBuffer.Off);
|
||||
if (auto EC = HSR.readArray(HashValues, NumHashValues))
|
||||
return EC;
|
||||
auto HS = MappedBlockStream::createIndexedStream(
|
||||
Pdb.getMsfLayout(), Pdb.getMsfBuffer(), Header->HashStreamIndex);
|
||||
StreamReader HSR(*HS);
|
||||
|
||||
HSR.setOffset(Header->IndexOffsetBuffer.Off);
|
||||
uint32_t NumTypeIndexOffsets =
|
||||
Header->IndexOffsetBuffer.Length / sizeof(TypeIndexOffset);
|
||||
if (auto EC = HSR.readArray(TypeIndexOffsets, NumTypeIndexOffsets))
|
||||
return EC;
|
||||
uint32_t NumHashValues =
|
||||
Header->HashValueBuffer.Length / sizeof(ulittle32_t);
|
||||
if (NumHashValues != NumTypeRecords())
|
||||
return make_error<RawError>(
|
||||
raw_error_code::corrupt_file,
|
||||
"TPI hash count does not match with the number of type records.");
|
||||
HSR.setOffset(Header->HashValueBuffer.Off);
|
||||
if (auto EC = HSR.readArray(HashValues, NumHashValues))
|
||||
return EC;
|
||||
|
||||
HSR.setOffset(Header->HashAdjBuffer.Off);
|
||||
uint32_t NumHashAdjustments =
|
||||
Header->HashAdjBuffer.Length / sizeof(TypeIndexOffset);
|
||||
if (auto EC = HSR.readArray(HashAdjustments, NumHashAdjustments))
|
||||
return EC;
|
||||
HSR.setOffset(Header->IndexOffsetBuffer.Off);
|
||||
uint32_t NumTypeIndexOffsets =
|
||||
Header->IndexOffsetBuffer.Length / sizeof(TypeIndexOffset);
|
||||
if (auto EC = HSR.readArray(TypeIndexOffsets, NumTypeIndexOffsets))
|
||||
return EC;
|
||||
|
||||
HashStream = std::move(HS);
|
||||
HSR.setOffset(Header->HashAdjBuffer.Off);
|
||||
uint32_t NumHashAdjustments =
|
||||
Header->HashAdjBuffer.Length / sizeof(TypeIndexOffset);
|
||||
if (auto EC = HSR.readArray(HashAdjustments, NumHashAdjustments))
|
||||
return EC;
|
||||
|
||||
// TPI hash table is a parallel array for the type records.
|
||||
// Verify that the hash values match with type records.
|
||||
if (auto EC = verifyHashValues())
|
||||
return EC;
|
||||
HashStream = std::move(HS);
|
||||
|
||||
// TPI hash table is a parallel array for the type records.
|
||||
// Verify that the hash values match with type records.
|
||||
if (auto EC = verifyHashValues())
|
||||
return EC;
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
95
lib/DebugInfo/PDB/Raw/TpiStreamBuilder.cpp
Normal file
95
lib/DebugInfo/PDB/Raw/TpiStreamBuilder.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
#include "llvm/DebugInfo/PDB/Raw/TpiStreamBuilder.h"
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
|
||||
#include "llvm/DebugInfo/MSF/StreamWriter.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/RawError.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/TpiStream.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::msf;
|
||||
using namespace llvm::pdb;
|
||||
using namespace llvm::support;
|
||||
|
||||
TpiStreamBuilder::TpiStreamBuilder(BumpPtrAllocator &Allocator)
|
||||
: Allocator(Allocator), Header(nullptr) {}
|
||||
|
||||
TpiStreamBuilder::~TpiStreamBuilder() {}
|
||||
|
||||
void TpiStreamBuilder::setVersionHeader(PdbRaw_TpiVer Version) {
|
||||
VerHeader = Version;
|
||||
}
|
||||
|
||||
void TpiStreamBuilder::addTypeRecord(const codeview::CVType &Record) {
|
||||
TypeRecords.push_back(Record);
|
||||
TypeRecordStream.setItems(TypeRecords);
|
||||
}
|
||||
|
||||
Error TpiStreamBuilder::finalize() {
|
||||
if (Header)
|
||||
return Error::success();
|
||||
|
||||
TpiStreamHeader *H = Allocator.Allocate<TpiStreamHeader>();
|
||||
|
||||
uint32_t Count = TypeRecords.size();
|
||||
|
||||
H->Version = *VerHeader;
|
||||
H->HeaderSize = sizeof(TpiStreamHeader);
|
||||
H->TypeIndexBegin = codeview::TypeIndex::FirstNonSimpleIndex;
|
||||
H->TypeIndexEnd = H->TypeIndexBegin + Count;
|
||||
H->TypeRecordBytes = TypeRecordStream.getLength();
|
||||
|
||||
H->HashStreamIndex = kInvalidStreamIndex;
|
||||
H->HashAuxStreamIndex = kInvalidStreamIndex;
|
||||
H->HashKeySize = sizeof(ulittle32_t);
|
||||
H->NumHashBuckets = MinTpiHashBuckets;
|
||||
|
||||
H->HashValueBuffer.Length = 0;
|
||||
H->HashAdjBuffer.Length = 0;
|
||||
H->IndexOffsetBuffer.Length = 0;
|
||||
|
||||
Header = H;
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
uint32_t TpiStreamBuilder::calculateSerializedLength() const {
|
||||
return sizeof(TpiStreamHeader) + TypeRecordStream.getLength();
|
||||
}
|
||||
|
||||
Expected<std::unique_ptr<TpiStream>>
|
||||
TpiStreamBuilder::build(PDBFile &File, const msf::WritableStream &Buffer) {
|
||||
if (!VerHeader.hasValue())
|
||||
return make_error<RawError>(raw_error_code::unspecified,
|
||||
"Missing TPI Stream Version");
|
||||
if (auto EC = finalize())
|
||||
return std::move(EC);
|
||||
|
||||
auto StreamData = MappedBlockStream::createIndexedStream(File.getMsfLayout(),
|
||||
Buffer, StreamTPI);
|
||||
auto Tpi = llvm::make_unique<TpiStream>(File, std::move(StreamData));
|
||||
Tpi->Header = Header;
|
||||
Tpi->TypeRecords = VarStreamArray<codeview::CVType>(TypeRecordStream);
|
||||
return std::move(Tpi);
|
||||
}
|
||||
|
||||
Error TpiStreamBuilder::commit(const msf::MSFLayout &Layout,
|
||||
const msf::WritableStream &Buffer) {
|
||||
if (auto EC = finalize())
|
||||
return EC;
|
||||
|
||||
auto InfoS =
|
||||
WritableMappedBlockStream::createIndexedStream(Layout, Buffer, StreamTPI);
|
||||
|
||||
StreamWriter Writer(*InfoS);
|
||||
if (auto EC = Writer.writeObject(*Header))
|
||||
return EC;
|
||||
|
||||
auto RecordArray = VarStreamArray<codeview::CVType>(TypeRecordStream);
|
||||
if (auto EC = Writer.writeArray(RecordArray))
|
||||
return EC;
|
||||
|
||||
return Error::success();
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
RUN: llvm-pdbdump pdb2yaml -dbi-module-info -dbi-module-source-info \
|
||||
RUN: -dbi-stream -pdb-stream -stream-directory -stream-metadata \
|
||||
RUN: %p/Inputs/empty.pdb > %t.1
|
||||
RUN: -dbi-stream -pdb-stream -tpi-stream -stream-directory \
|
||||
RUN: -stream-metadata %p/Inputs/empty.pdb > %t.1
|
||||
RUN: llvm-pdbdump yaml2pdb -pdb=%t.2 %t.1
|
||||
|
||||
RUN: llvm-pdbdump raw -headers %p/Inputs/empty.pdb | FileCheck %s
|
||||
RUN: llvm-pdbdump raw -headers %t.2 | FileCheck %s
|
||||
RUN: llvm-pdbdump raw -headers -tpi-records %p/Inputs/empty.pdb | FileCheck %s
|
||||
RUN: llvm-pdbdump raw -headers -tpi-records %t.2 | FileCheck %s
|
||||
|
||||
CHECK: FileHeaders {
|
||||
CHECK-NEXT: BlockSize: 4096
|
||||
@ -17,13 +17,16 @@ CHECK-NEXT: NumDirectoryBlocks: 1
|
||||
CHECK-NEXT: DirectoryBlocks: [23]
|
||||
CHECK-NEXT: NumStreams: 17
|
||||
CHECK-NEXT: }
|
||||
CHECK-NEXT: PDB Stream {
|
||||
CHECK: PDB Stream {
|
||||
CHECK-NEXT: Version: 20000404
|
||||
CHECK-NEXT: Signature: 0x54E507E2
|
||||
CHECK-NEXT: Age: 1
|
||||
CHECK-NEXT: Guid: {0B355641-86A0-A249-896F-9988FAE52FF0}
|
||||
CHECK-NEXT: }
|
||||
CHECK-NEXT: DBI Stream {
|
||||
CHECK: Type Info Stream (TPI) {
|
||||
CHECK-NEXT: TPI Version: 20040203
|
||||
CHECK-NEXT: Record count: 75
|
||||
CHECK: DBI Stream {
|
||||
CHECK-NEXT: Dbi Version: 19990903
|
||||
CHECK-NEXT: Age: 1
|
||||
CHECK-NEXT: Incremental Linking: Yes
|
||||
|
@ -10,8 +10,8 @@
|
||||
; stream metadata, since the layout of the MSF file might be different
|
||||
; (for example if we don't write the entire stream)
|
||||
;
|
||||
; RUN: llvm-pdbdump pdb2yaml -stream-metadata -stream-directory -pdb-stream %p/Inputs/empty.pdb > %t.1
|
||||
; RUN: llvm-pdbdump pdb2yaml -stream-metadata -stream-directory -pdb-stream -tpi-stream %p/Inputs/empty.pdb > %t.1
|
||||
; RUN: llvm-pdbdump yaml2pdb -pdb=%t.2 %t.1
|
||||
; RUN: llvm-pdbdump pdb2yaml -pdb-stream %p/Inputs/empty.pdb > %t.3
|
||||
; RUN: llvm-pdbdump pdb2yaml -pdb-stream %t.2 > %t.4
|
||||
; RUN: llvm-pdbdump pdb2yaml -pdb-stream -tpi-stream %p/Inputs/empty.pdb > %t.3
|
||||
; RUN: llvm-pdbdump pdb2yaml -pdb-stream -tpi-stream %t.2 > %t.4
|
||||
; RUN: diff %t.3 %t.4
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "CodeViewYaml.h"
|
||||
#include "PdbYaml.h"
|
||||
#include "YamlSerializationContext.h"
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/EnumTables.h"
|
||||
@ -269,34 +270,20 @@ template <> struct ScalarTraits<APSInt> {
|
||||
static bool mustQuote(StringRef Scalar) { return false; }
|
||||
};
|
||||
|
||||
void MappingContextTraits<CVType, YamlTypeDumperCallbacks>::mapping(
|
||||
IO &IO, CVType &Record, YamlTypeDumperCallbacks &Dumper) {
|
||||
void MappingContextTraits<CVType, pdb::yaml::SerializationContext>::mapping(
|
||||
IO &IO, CVType &Record, pdb::yaml::SerializationContext &Context) {
|
||||
if (IO.outputting()) {
|
||||
codeview::TypeDeserializer Deserializer;
|
||||
|
||||
codeview::TypeVisitorCallbackPipeline Pipeline;
|
||||
Pipeline.addCallbackToPipeline(Deserializer);
|
||||
Pipeline.addCallbackToPipeline(Dumper);
|
||||
Pipeline.addCallbackToPipeline(Context.Dumper);
|
||||
|
||||
codeview::CVTypeVisitor Visitor(Pipeline);
|
||||
consumeError(Visitor.visitTypeRecord(Record));
|
||||
}
|
||||
}
|
||||
|
||||
void MappingContextTraits<FieldListRecord, YamlTypeDumperCallbacks>::mapping(
|
||||
IO &IO, FieldListRecord &FieldList, YamlTypeDumperCallbacks &Dumper) {
|
||||
if (IO.outputting()) {
|
||||
codeview::TypeDeserializer Deserializer;
|
||||
|
||||
codeview::TypeVisitorCallbackPipeline Pipeline;
|
||||
Pipeline.addCallbackToPipeline(Deserializer);
|
||||
Pipeline.addCallbackToPipeline(Dumper);
|
||||
|
||||
codeview::CVTypeVisitor Visitor(Pipeline);
|
||||
consumeError(Visitor.visitFieldListMemberStream(FieldList.Data));
|
||||
}
|
||||
}
|
||||
|
||||
void MappingTraits<StringIdRecord>::mapping(IO &IO, StringIdRecord &String) {
|
||||
IO.mapRequired("Id", String.Id);
|
||||
IO.mapRequired("String", String.String);
|
||||
@ -549,13 +536,23 @@ llvm::codeview::yaml::YamlTypeDumperCallbacks::visitTypeBegin(
|
||||
}
|
||||
|
||||
void llvm::codeview::yaml::YamlTypeDumperCallbacks::visitKnownRecordImpl(
|
||||
const char *Name, const CVType &Type, FieldListRecord &FieldList) {
|
||||
|
||||
std::vector<llvm::pdb::yaml::PdbTpiRecord> Records;
|
||||
const char *Name, const CVType &CVR, FieldListRecord &FieldList) {
|
||||
std::vector<llvm::pdb::yaml::PdbTpiRecord> FieldListRecords;
|
||||
if (YamlIO.outputting()) {
|
||||
FieldListRecordSplitter Splitter(Records);
|
||||
// If we are outputting, then `FieldList.Data` contains a huge chunk of data
|
||||
// representing the serialized list of members. We need to split it up into
|
||||
// individual CVType records where each record represents an individual
|
||||
// member. This way, we can simply map the entire thing as a Yaml sequence,
|
||||
// which will recurse back to the standard handler for top-level fields
|
||||
// (top-level and member fields all have the exact same Yaml syntax so use
|
||||
// the same parser).
|
||||
//
|
||||
// If we are not outputting, then the array contains no data starting out,
|
||||
// and is instead populated from the sequence represented by the yaml --
|
||||
// again, using the same logic that we use for top-level records.
|
||||
FieldListRecordSplitter Splitter(FieldListRecords);
|
||||
CVTypeVisitor V(Splitter);
|
||||
consumeError(V.visitFieldListMemberStream(FieldList.Data));
|
||||
}
|
||||
YamlIO.mapRequired(Name, Records);
|
||||
YamlIO.mapRequired("FieldList", FieldListRecords, Context);
|
||||
}
|
||||
|
@ -11,15 +11,23 @@
|
||||
#define LLVM_TOOLS_LLVMPDBDUMP_CODEVIEWYAML_H
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/CodeView.h"
|
||||
#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||
#include "llvm/Support/YAMLTraits.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace pdb {
|
||||
namespace yaml {
|
||||
struct SerializationContext;
|
||||
}
|
||||
}
|
||||
namespace codeview {
|
||||
namespace yaml {
|
||||
class YamlTypeDumperCallbacks : public TypeVisitorCallbacks {
|
||||
public:
|
||||
YamlTypeDumperCallbacks(llvm::yaml::IO &IO) : YamlIO(IO) {}
|
||||
YamlTypeDumperCallbacks(llvm::yaml::IO &IO,
|
||||
llvm::pdb::yaml::SerializationContext &Context)
|
||||
: YamlIO(IO), Context(Context) {}
|
||||
|
||||
virtual Expected<TypeLeafKind>
|
||||
visitTypeBegin(const CVRecord<TypeLeafKind> &Record) override;
|
||||
@ -42,13 +50,19 @@ private:
|
||||
YamlIO.mapRequired(Name, Record);
|
||||
}
|
||||
|
||||
void visitKnownRecordImpl(const char *Name, const CVType &Type,
|
||||
void visitKnownRecordImpl(const char *Name, const CVType &CVR,
|
||||
FieldListRecord &FieldList);
|
||||
|
||||
llvm::yaml::IO &YamlIO;
|
||||
llvm::pdb::yaml::SerializationContext &Context;
|
||||
};
|
||||
}
|
||||
}
|
||||
namespace pdb {
|
||||
namespace yaml {
|
||||
struct SerializationContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace llvm {
|
||||
@ -58,10 +72,9 @@ template <> struct MappingTraits<codeview::MemberPointerInfo> {
|
||||
};
|
||||
|
||||
template <>
|
||||
struct MappingContextTraits<codeview::CVType,
|
||||
codeview::yaml::YamlTypeDumperCallbacks> {
|
||||
struct MappingContextTraits<codeview::CVType, pdb::yaml::SerializationContext> {
|
||||
static void mapping(IO &IO, codeview::CVType &Obj,
|
||||
codeview::yaml::YamlTypeDumperCallbacks &Context);
|
||||
pdb::yaml::SerializationContext &Context);
|
||||
};
|
||||
|
||||
template <> struct ScalarEnumerationTraits<codeview::TypeLeafKind> {
|
||||
@ -77,13 +90,6 @@ template <> struct ScalarEnumerationTraits<codeview::TypeLeafKind> {
|
||||
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
|
||||
|
||||
template <>
|
||||
struct MappingContextTraits<codeview::FieldListRecord,
|
||||
codeview::yaml::YamlTypeDumperCallbacks> {
|
||||
static void mapping(IO &IO, codeview::FieldListRecord &Record,
|
||||
codeview::yaml::YamlTypeDumperCallbacks &Context);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,9 +10,11 @@
|
||||
#include "PdbYaml.h"
|
||||
|
||||
#include "CodeViewYaml.h"
|
||||
#include "YamlSerializationContext.h"
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeSerializationVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBExtras.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBTypes.h"
|
||||
@ -134,7 +136,7 @@ void MappingTraits<PdbObject>::mapping(IO &IO, PdbObject &Obj) {
|
||||
IO.mapOptional("StreamMap", Obj.StreamMap);
|
||||
IO.mapOptional("PdbStream", Obj.PdbStream);
|
||||
IO.mapOptional("DbiStream", Obj.DbiStream);
|
||||
IO.mapOptional("TpiStream", Obj.TpiStream);
|
||||
IO.mapOptionalWithContext("TpiStream", Obj.TpiStream, Obj.Allocator);
|
||||
}
|
||||
|
||||
void MappingTraits<MSFHeaders>::mapping(IO &IO, MSFHeaders &Obj) {
|
||||
@ -181,10 +183,18 @@ void MappingTraits<PdbDbiStream>::mapping(IO &IO, PdbDbiStream &Obj) {
|
||||
IO.mapOptional("Modules", Obj.ModInfos);
|
||||
}
|
||||
|
||||
void MappingTraits<PdbTpiStream>::mapping(IO &IO,
|
||||
pdb::yaml::PdbTpiStream &Obj) {
|
||||
void MappingContextTraits<PdbTpiStream, BumpPtrAllocator>::mapping(
|
||||
IO &IO, pdb::yaml::PdbTpiStream &Obj, BumpPtrAllocator &Allocator) {
|
||||
// Create a single serialization context that will be passed through the
|
||||
// entire process of serializing / deserializing a Tpi Stream. This is
|
||||
// especially important when we are going from Pdb -> Yaml because we need
|
||||
// to maintain state in a TypeTableBuilder across mappings, and at the end of
|
||||
// the entire process, we need to have one TypeTableBuilder that has every
|
||||
// record.
|
||||
pdb::yaml::SerializationContext Context(IO, Allocator);
|
||||
|
||||
IO.mapRequired("Version", Obj.Version);
|
||||
IO.mapRequired("Records", Obj.Records);
|
||||
IO.mapRequired("Records", Obj.Records, Context);
|
||||
}
|
||||
|
||||
void MappingTraits<NamedStreamMapping>::mapping(IO &IO,
|
||||
@ -199,21 +209,40 @@ void MappingTraits<PdbDbiModuleInfo>::mapping(IO &IO, PdbDbiModuleInfo &Obj) {
|
||||
IO.mapOptional("SourceFiles", Obj.SourceFiles);
|
||||
}
|
||||
|
||||
void MappingTraits<PdbTpiRecord>::mapping(IO &IO,
|
||||
pdb::yaml::PdbTpiRecord &Obj) {
|
||||
void MappingContextTraits<PdbTpiRecord, pdb::yaml::SerializationContext>::
|
||||
mapping(IO &IO, pdb::yaml::PdbTpiRecord &Obj,
|
||||
pdb::yaml::SerializationContext &Context) {
|
||||
codeview::TypeVisitorCallbackPipeline Pipeline;
|
||||
codeview::TypeDeserializer Deserializer;
|
||||
codeview::TypeSerializationVisitor Serializer(Context.FieldListBuilder,
|
||||
Context.TypeTableBuilder);
|
||||
|
||||
if (IO.outputting()) {
|
||||
codeview::TypeDeserializer Deserializer;
|
||||
codeview::yaml::YamlTypeDumperCallbacks Callbacks(IO);
|
||||
|
||||
codeview::TypeVisitorCallbackPipeline Pipeline;
|
||||
// For PDB to Yaml, deserialize into a high level record type, then dump it.
|
||||
Pipeline.addCallbackToPipeline(Deserializer);
|
||||
Pipeline.addCallbackToPipeline(Callbacks);
|
||||
|
||||
codeview::CVTypeVisitor Visitor(Pipeline);
|
||||
consumeError(Visitor.visitTypeRecord(Obj.Record));
|
||||
Pipeline.addCallbackToPipeline(Context.Dumper);
|
||||
} else {
|
||||
codeview::yaml::YamlTypeDumperCallbacks Callbacks(IO);
|
||||
codeview::CVTypeVisitor Visitor(Callbacks);
|
||||
consumeError(Visitor.visitTypeRecord(Obj.Record));
|
||||
// For Yaml to PDB, extract from the high level record type, then write it
|
||||
// to bytes.
|
||||
Pipeline.addCallbackToPipeline(Context.Dumper);
|
||||
Pipeline.addCallbackToPipeline(Serializer);
|
||||
}
|
||||
|
||||
codeview::CVTypeVisitor Visitor(Pipeline);
|
||||
consumeError(Visitor.visitTypeRecord(Obj.Record));
|
||||
|
||||
if (!IO.outputting()) {
|
||||
// For Yaml to PDB, we need to update the input Object with the bytes for
|
||||
// this record.
|
||||
ArrayRef<StringRef> Records = Context.TypeTableBuilder.getRecords();
|
||||
ArrayRef<codeview::TypeRecordKind> Kinds =
|
||||
Context.TypeTableBuilder.getRecordKinds();
|
||||
|
||||
StringRef ThisRecord = Records.back();
|
||||
Obj.Record.Type = static_cast<codeview::TypeLeafKind>(Kinds.back());
|
||||
Obj.Record.Data =
|
||||
ArrayRef<uint8_t>(ThisRecord.bytes_begin(), ThisRecord.bytes_end());
|
||||
Obj.Record.RawData = Obj.Record.Data;
|
||||
Obj.Record.Length = ThisRecord.size();
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ namespace llvm {
|
||||
namespace pdb {
|
||||
|
||||
namespace yaml {
|
||||
struct SerializationContext;
|
||||
|
||||
struct MSFHeaders {
|
||||
msf::SuperBlock SuperBlock;
|
||||
uint32_t NumDirectoryBlocks;
|
||||
@ -79,12 +81,16 @@ struct PdbTpiStream {
|
||||
};
|
||||
|
||||
struct PdbObject {
|
||||
explicit PdbObject(BumpPtrAllocator &Allocator) : Allocator(Allocator) {}
|
||||
|
||||
Optional<MSFHeaders> Headers;
|
||||
Optional<std::vector<uint32_t>> StreamSizes;
|
||||
Optional<std::vector<StreamBlockList>> StreamMap;
|
||||
Optional<PdbInfoStream> PdbStream;
|
||||
Optional<PdbDbiStream> DbiStream;
|
||||
Optional<PdbTpiStream> TpiStream;
|
||||
|
||||
BumpPtrAllocator &Allocator;
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -117,8 +123,10 @@ template <> struct MappingTraits<pdb::yaml::PdbDbiStream> {
|
||||
static void mapping(IO &IO, pdb::yaml::PdbDbiStream &Obj);
|
||||
};
|
||||
|
||||
template <> struct MappingTraits<pdb::yaml::PdbTpiStream> {
|
||||
static void mapping(IO &IO, pdb::yaml::PdbTpiStream &Obj);
|
||||
template <>
|
||||
struct MappingContextTraits<pdb::yaml::PdbTpiStream, llvm::BumpPtrAllocator> {
|
||||
static void mapping(IO &IO, pdb::yaml::PdbTpiStream &Obj,
|
||||
llvm::BumpPtrAllocator &Allocator);
|
||||
};
|
||||
|
||||
template <> struct MappingTraits<pdb::yaml::NamedStreamMapping> {
|
||||
@ -129,8 +137,11 @@ template <> struct MappingTraits<pdb::yaml::PdbDbiModuleInfo> {
|
||||
static void mapping(IO &IO, pdb::yaml::PdbDbiModuleInfo &Obj);
|
||||
};
|
||||
|
||||
template <> struct MappingTraits<pdb::yaml::PdbTpiRecord> {
|
||||
static void mapping(IO &IO, pdb::yaml::PdbTpiRecord &Obj);
|
||||
template <>
|
||||
struct MappingContextTraits<pdb::yaml::PdbTpiRecord,
|
||||
pdb::yaml::SerializationContext> {
|
||||
static void mapping(IO &IO, pdb::yaml::PdbTpiRecord &Obj,
|
||||
pdb::yaml::SerializationContext &Context);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,8 @@
|
||||
using namespace llvm;
|
||||
using namespace llvm::pdb;
|
||||
|
||||
YAMLOutputStyle::YAMLOutputStyle(PDBFile &File) : File(File), Out(outs()) {}
|
||||
YAMLOutputStyle::YAMLOutputStyle(PDBFile &File)
|
||||
: File(File), Out(outs()), Obj(File.getAllocator()) {}
|
||||
|
||||
Error YAMLOutputStyle::dump() {
|
||||
if (opts::pdb2yaml::StreamDirectory)
|
||||
|
36
tools/llvm-pdbdump/YamlSerializationContext.h
Normal file
36
tools/llvm-pdbdump/YamlSerializationContext.h
Normal file
@ -0,0 +1,36 @@
|
||||
//===- YamlSerializationContext.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_TOOLS_LLVMPDBDUMP_YAMLSERIALIZATIONCONTEXT_H
|
||||
#define LLVM_TOOLS_LLVMPDBDUMP_YAMLSERIALIZATIONCONTEXT_H
|
||||
|
||||
#include "CodeViewYaml.h"
|
||||
#include "PdbYaml.h"
|
||||
#include "llvm/DebugInfo/CodeView/FieldListRecordBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace yaml {
|
||||
class IO;
|
||||
}
|
||||
|
||||
namespace pdb {
|
||||
namespace yaml {
|
||||
struct SerializationContext {
|
||||
explicit SerializationContext(llvm::yaml::IO &IO, BumpPtrAllocator &Allocator)
|
||||
: Dumper(IO, *this), TypeTableBuilder(Allocator) {}
|
||||
codeview::yaml::YamlTypeDumperCallbacks Dumper;
|
||||
codeview::MemoryTypeTableBuilder TypeTableBuilder;
|
||||
codeview::FieldListRecordBuilder FieldListBuilder;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -50,6 +50,8 @@
|
||||
#include "llvm/DebugInfo/PDB/Raw/RawConstants.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/RawError.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/RawSession.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/TpiStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/TpiStreamBuilder.h"
|
||||
#include "llvm/Support/COM.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/ConvertUTF.h"
|
||||
@ -306,7 +308,7 @@ static void yamlToPdb(StringRef Path) {
|
||||
std::unique_ptr<MemoryBuffer> &Buffer = ErrorOrBuffer.get();
|
||||
|
||||
llvm::yaml::Input In(Buffer->getBuffer());
|
||||
pdb::yaml::PdbObject YamlObj;
|
||||
pdb::yaml::PdbObject YamlObj(Allocator);
|
||||
In >> YamlObj;
|
||||
if (!YamlObj.Headers.hasValue())
|
||||
ExitOnErr(make_error<GenericError>(generic_error_code::unspecified,
|
||||
@ -382,6 +384,13 @@ static void yamlToPdb(StringRef Path) {
|
||||
}
|
||||
}
|
||||
|
||||
if (YamlObj.TpiStream.hasValue()) {
|
||||
auto &TpiBuilder = Builder.getTpiBuilder();
|
||||
TpiBuilder.setVersionHeader(YamlObj.TpiStream->Version);
|
||||
for (const auto &R : YamlObj.TpiStream->Records)
|
||||
TpiBuilder.addTypeRecord(R.Record);
|
||||
}
|
||||
|
||||
ExitOnErr(Builder.commit(*FileByteStream));
|
||||
}
|
||||
|
||||
|
@ -331,8 +331,15 @@ static bool isMipsArch(unsigned Arch) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
namespace {
|
||||
struct TypeTableBuilder {
|
||||
TypeTableBuilder() : Allocator(), Builder(Allocator) {}
|
||||
|
||||
static llvm::codeview::MemoryTypeTableBuilder CVTypes;
|
||||
llvm::BumpPtrAllocator Allocator;
|
||||
llvm::codeview::MemoryTypeTableBuilder Builder;
|
||||
};
|
||||
}
|
||||
static TypeTableBuilder CVTypes;
|
||||
|
||||
/// @brief Creates an format-specific object file dumper.
|
||||
static std::error_code createDumper(const ObjectFile *Obj,
|
||||
@ -429,7 +436,7 @@ static void dumpObject(const ObjectFile *Obj) {
|
||||
if (opts::CodeView)
|
||||
Dumper->printCodeViewDebugInfo();
|
||||
if (opts::CodeViewMergedTypes)
|
||||
Dumper->mergeCodeViewTypes(CVTypes);
|
||||
Dumper->mergeCodeViewTypes(CVTypes.Builder);
|
||||
}
|
||||
if (Obj->isMachO()) {
|
||||
if (opts::MachODataInCode)
|
||||
@ -534,7 +541,7 @@ int main(int argc, const char *argv[]) {
|
||||
|
||||
if (opts::CodeViewMergedTypes) {
|
||||
ScopedPrinter W(outs());
|
||||
dumpCodeViewMergedTypes(W, CVTypes);
|
||||
dumpCodeViewMergedTypes(W, CVTypes.Builder);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
Loading…
Reference in New Issue
Block a user