Index: include/llvm/DebugInfo/CodeView/CVRecord.h =================================================================== --- include/llvm/DebugInfo/CodeView/CVRecord.h +++ include/llvm/DebugInfo/CodeView/CVRecord.h @@ -11,6 +11,7 @@ #define LLVM_DEBUGINFO_CODEVIEW_RECORDITERATOR_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/iterator_range.h" #include "llvm/DebugInfo/CodeView/CodeViewError.h" #include "llvm/DebugInfo/CodeView/RecordSerialization.h" @@ -26,6 +27,7 @@ Kind Type; ArrayRef Data; ArrayRef RawData; + Optional Hash; }; } namespace msf { Index: include/llvm/DebugInfo/CodeView/TypeSerializationVisitor.h =================================================================== --- include/llvm/DebugInfo/CodeView/TypeSerializationVisitor.h +++ include/llvm/DebugInfo/CodeView/TypeSerializationVisitor.h @@ -36,27 +36,22 @@ virtual Error visitTypeEnd(CVType &Record) override { // Since this visitor's purpose is to serialize the record, fill out the // fields of `Record` with the bytes of the record. - if (Record.Type == TypeLeafKind::LF_FIELDLIST) + if (Record.Type == TypeLeafKind::LF_FIELDLIST) { TypeTableBuilder.writeFieldList(FieldListBuilder); + updateCVRecord(Record); + } - StringRef S = TypeTableBuilder.getRecords().back(); - ArrayRef Data(S.bytes_begin(), S.bytes_end()); - Record.RawData = Data; - Record.Data = Record.RawData.drop_front(sizeof(RecordPrefix)); - Record.Length = Data.size() - sizeof(ulittle16_t); return Error::success(); } #define TYPE_RECORD(EnumName, EnumVal, Name) \ - virtual Error visitKnownRecord(CVRecord &CVR, \ - Name##Record &Record) override { \ - visitKnownRecordImpl(Record); \ + virtual Error visitKnownRecord(CVType &CVR, Name##Record &Record) override { \ + visitKnownRecordImpl(CVR, Record); \ return Error::success(); \ } #define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) #define MEMBER_RECORD(EnumName, EnumVal, Name) \ - virtual Error visitKnownRecord(CVRecord &CVR, \ - Name##Record &Record) override { \ + virtual Error visitKnownRecord(CVType &CVR, Name##Record &Record) override { \ visitMemberRecordImpl(Record); \ return Error::success(); \ } @@ -64,15 +59,24 @@ #include "llvm/DebugInfo/CodeView/TypeRecords.def" private: - template void visitKnownRecordImpl(RecordKind &Record) { + void updateCVRecord(CVType &Record) { + StringRef S = TypeTableBuilder.getRecords().back(); + ArrayRef Data(S.bytes_begin(), S.bytes_end()); + Record.RawData = Data; + Record.Data = Record.RawData.drop_front(sizeof(RecordPrefix)); + Record.Length = Data.size() - sizeof(ulittle16_t); + } + template + void visitKnownRecordImpl(CVType &CVR, RecordKind &Record) { TypeTableBuilder.writeKnownType(Record); + updateCVRecord(CVR); } template void visitMemberRecordImpl(RecordKind &Record) { FieldListBuilder.writeMemberType(Record); } - void visitKnownRecordImpl(FieldListRecord &FieldList) {} + void visitKnownRecordImpl(CVType &CVR, FieldListRecord &FieldList) {} FieldListRecordBuilder &FieldListBuilder; MemoryTypeTableBuilder &TypeTableBuilder; Index: include/llvm/DebugInfo/MSF/MSFBuilder.h =================================================================== --- include/llvm/DebugInfo/MSF/MSFBuilder.h +++ include/llvm/DebugInfo/MSF/MSFBuilder.h @@ -71,13 +71,13 @@ /// particular stream to occupy the original set of blocks. If the given /// blocks are already allocated, or if the number of blocks specified is /// incorrect for the given stream size, this function will return an Error. - Error addStream(uint32_t Size, ArrayRef Blocks); + Expected addStream(uint32_t Size, ArrayRef Blocks); /// Add a stream to the MSF file with the given size, occupying any available /// blocks that the builder decides to use. This is useful when building a /// new PDB file from scratch and you don't care what blocks a stream occupies /// but you just want it to work. - Error addStream(uint32_t Size); + Expected addStream(uint32_t Size); /// Update the size of an existing stream. This will allocate or deallocate /// blocks as needed to match the requested size. This can fail if `CanGrow` @@ -113,6 +113,8 @@ /// MSF layout and can be written directly to the MSF file. Expected build(); + BumpPtrAllocator &getAllocator() { return Allocator; } + private: MSFBuilder(uint32_t BlockSize, uint32_t MinBlockCount, bool CanGrow, BumpPtrAllocator &Allocator); Index: include/llvm/DebugInfo/PDB/Raw/DbiStreamBuilder.h =================================================================== --- include/llvm/DebugInfo/PDB/Raw/DbiStreamBuilder.h +++ include/llvm/DebugInfo/PDB/Raw/DbiStreamBuilder.h @@ -21,6 +21,9 @@ #include "llvm/DebugInfo/PDB/Raw/RawConstants.h" namespace llvm { +namespace msf { +class MSFBuilder; +} namespace pdb { class DbiStream; struct DbiStreamHeader; @@ -28,7 +31,7 @@ class DbiStreamBuilder { public: - DbiStreamBuilder(BumpPtrAllocator &Allocator); + DbiStreamBuilder(msf::MSFBuilder &Msf); DbiStreamBuilder(const DbiStreamBuilder &) = delete; DbiStreamBuilder &operator=(const DbiStreamBuilder &) = delete; @@ -46,6 +49,8 @@ Error addModuleInfo(StringRef ObjFile, StringRef Module); Error addModuleSourceFile(StringRef Module, StringRef File); + Error finalizeMsfLayout(); + Expected> build(PDBFile &File, const msf::WritableStream &Buffer); Error commit(const msf::MSFLayout &Layout, @@ -66,6 +71,7 @@ StringRef Mod; }; + msf::MSFBuilder &Msf; BumpPtrAllocator &Allocator; Optional VerHeader; Index: include/llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h =================================================================== --- include/llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h +++ include/llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h @@ -20,6 +20,7 @@ namespace llvm { namespace msf { +class MSFBuilder; class StreamWriter; } namespace pdb { @@ -27,7 +28,7 @@ class InfoStreamBuilder { public: - InfoStreamBuilder(); + InfoStreamBuilder(msf::MSFBuilder &Msf); InfoStreamBuilder(const InfoStreamBuilder &) = delete; InfoStreamBuilder &operator=(const InfoStreamBuilder &) = delete; @@ -40,6 +41,8 @@ uint32_t calculateSerializedLength() const; + Error finalizeMsfLayout(); + Expected> build(PDBFile &File, const msf::WritableStream &Buffer); @@ -47,6 +50,8 @@ const msf::WritableStream &Buffer) const; private: + msf::MSFBuilder &Msf; + PdbRaw_ImplVer Ver; uint32_t Sig; uint32_t Age; Index: include/llvm/DebugInfo/PDB/Raw/RawConstants.h =================================================================== --- include/llvm/DebugInfo/PDB/Raw/RawConstants.h +++ include/llvm/DebugInfo/PDB/Raw/RawConstants.h @@ -63,6 +63,8 @@ StreamTPI = 2, StreamDBI = 3, StreamIPI = 4, + + kSpecialStreamCount }; enum class DbgHeaderType : uint16_t { Index: include/llvm/DebugInfo/PDB/Raw/TpiHashing.h =================================================================== --- /dev/null +++ include/llvm/DebugInfo/PDB/Raw/TpiHashing.h @@ -0,0 +1,90 @@ +//===- TpiHashing.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_TPIHASHING_H +#define LLVM_DEBUGINFO_PDB_TPIHASHING_H + +#include "llvm/ADT/Optional.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" +#include "llvm/DebugInfo/MSF/StreamArray.h" +#include "llvm/DebugInfo/PDB/Raw/RawError.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace pdb { +class TpiHashUpdater : public codeview::TypeVisitorCallbacks { +public: + TpiHashUpdater() {} + +#define TYPE_RECORD(EnumName, EnumVal, Name) \ + virtual Error visitKnownRecord(codeview::CVType &CVR, \ + codeview::Name##Record &Record) override { \ + visitKnownRecordImpl(CVR, Record); \ + return Error::success(); \ + } +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#define MEMBER_RECORD(EnumName, EnumVal, Name) +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/TypeRecords.def" + +private: + template + void visitKnownRecordImpl(codeview::CVType &CVR, RecordKind &Record) { + CVR.Hash = 0; + } + + void visitKnownRecordImpl(codeview::CVType &CVR, + codeview::UdtSourceLineRecord &Rec); + void visitKnownRecordImpl(codeview::CVType &CVR, + codeview::UdtModSourceLineRecord &Rec); + void visitKnownRecordImpl(codeview::CVType &CVR, codeview::ClassRecord &Rec); + void visitKnownRecordImpl(codeview::CVType &CVR, codeview::EnumRecord &Rec); + void visitKnownRecordImpl(codeview::CVType &CVR, codeview::UnionRecord &Rec); +}; + +class TpiHashVerifier : public codeview::TypeVisitorCallbacks { +public: + TpiHashVerifier(msf::FixedStreamArray &HashValues, + uint32_t NumHashBuckets) + : HashValues(HashValues), NumHashBuckets(NumHashBuckets) {} + + Error visitKnownRecord(codeview::CVType &CVR, + codeview::UdtSourceLineRecord &Rec) override; + Error visitKnownRecord(codeview::CVType &CVR, + codeview::UdtModSourceLineRecord &Rec) override; + Error visitKnownRecord(codeview::CVType &CVR, + codeview::ClassRecord &Rec) override; + Error visitKnownRecord(codeview::CVType &CVR, + codeview::EnumRecord &Rec) override; + Error visitKnownRecord(codeview::CVType &CVR, + codeview::UnionRecord &Rec) override; + Error visitTypeBegin(codeview::CVType &CVR) override; + +private: + Error verifySourceLine(codeview::TypeIndex TI); + + Error errorInvalidHash() { + return make_error( + raw_error_code::invalid_tpi_hash, + "Type index is 0x" + + utohexstr(codeview::TypeIndex::FirstNonSimpleIndex + Index)); + } + + msf::FixedStreamArray HashValues; + codeview::CVType RawRecord; + uint32_t NumHashBuckets; + uint32_t Index = -1; +}; +} +} + +#endif Index: include/llvm/DebugInfo/PDB/Raw/TpiStream.h =================================================================== --- include/llvm/DebugInfo/PDB/Raw/TpiStream.h +++ include/llvm/DebugInfo/PDB/Raw/TpiStream.h @@ -61,7 +61,7 @@ codeview::CVTypeArray TypeRecords; - std::unique_ptr HashStream; + std::unique_ptr HashStream; msf::FixedStreamArray HashValues; msf::FixedStreamArray TypeIndexOffsets; msf::FixedStreamArray HashAdjustments; Index: include/llvm/DebugInfo/PDB/Raw/TpiStreamBuilder.h =================================================================== --- include/llvm/DebugInfo/PDB/Raw/TpiStreamBuilder.h +++ include/llvm/DebugInfo/PDB/Raw/TpiStreamBuilder.h @@ -25,6 +25,8 @@ class TypeRecord; } namespace msf { +class ByteStream; +class MSFBuilder; struct MSFLayout; class ReadableStreamRef; class WritableStream; @@ -45,7 +47,7 @@ class TpiStreamBuilder { public: - explicit TpiStreamBuilder(BumpPtrAllocator &Allocator); + explicit TpiStreamBuilder(msf::MSFBuilder &Msf); ~TpiStreamBuilder(); TpiStreamBuilder(const TpiStreamBuilder &) = delete; @@ -54,6 +56,8 @@ void setVersionHeader(PdbRaw_TpiVer Version); void addTypeRecord(const codeview::CVType &Record); + Error finalizeMsfLayout(); + Expected> build(PDBFile &File, const msf::WritableStream &Buffer); @@ -62,13 +66,17 @@ uint32_t calculateSerializedLength() const; private: + uint32_t calculateHashBufferSize() const; Error finalize(); + msf::MSFBuilder &Msf; BumpPtrAllocator &Allocator; Optional VerHeader; std::vector TypeRecords; msf::SequencedItemStream TypeRecordStream; + uint32_t HashStreamIndex = kInvalidStreamIndex; + std::unique_ptr HashValueStream; const TpiStreamHeader *Header; }; Index: lib/DebugInfo/MSF/MSFBuilder.cpp =================================================================== --- lib/DebugInfo/MSF/MSFBuilder.cpp +++ lib/DebugInfo/MSF/MSFBuilder.cpp @@ -122,7 +122,8 @@ bool MSFBuilder::isBlockFree(uint32_t Idx) const { return FreeBlocks[Idx]; } -Error MSFBuilder::addStream(uint32_t Size, ArrayRef Blocks) { +Expected MSFBuilder::addStream(uint32_t Size, + ArrayRef Blocks) { // Add a new stream mapped to the specified blocks. Verify that the specified // blocks are both necessary and sufficient for holding the requested number // of bytes, and verify that all requested blocks are free. @@ -145,17 +146,17 @@ FreeBlocks.reset(Block); } StreamData.push_back(std::make_pair(Size, Blocks)); - return Error::success(); + return StreamData.size() - 1; } -Error MSFBuilder::addStream(uint32_t Size) { +Expected MSFBuilder::addStream(uint32_t Size) { uint32_t ReqBlocks = bytesToBlocks(Size, BlockSize); std::vector NewBlocks; NewBlocks.resize(ReqBlocks); if (auto EC = allocateBlocks(ReqBlocks, NewBlocks)) - return EC; + return std::move(EC); StreamData.push_back(std::make_pair(Size, NewBlocks)); - return Error::success(); + return StreamData.size() - 1; } Error MSFBuilder::setStreamSize(uint32_t Idx, uint32_t Size) { Index: lib/DebugInfo/PDB/CMakeLists.txt =================================================================== --- lib/DebugInfo/PDB/CMakeLists.txt +++ lib/DebugInfo/PDB/CMakeLists.txt @@ -45,6 +45,7 @@ Raw/RawError.cpp Raw/RawSession.cpp Raw/SymbolStream.cpp + Raw/TpiHashing.cpp Raw/TpiStream.cpp Raw/TpiStreamBuilder.cpp) Index: lib/DebugInfo/PDB/Raw/DbiStreamBuilder.cpp =================================================================== --- lib/DebugInfo/PDB/Raw/DbiStreamBuilder.cpp +++ lib/DebugInfo/PDB/Raw/DbiStreamBuilder.cpp @@ -9,6 +9,7 @@ #include "llvm/DebugInfo/PDB/Raw/DbiStreamBuilder.h" +#include "llvm/DebugInfo/MSF/MSFBuilder.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" #include "llvm/DebugInfo/MSF/StreamWriter.h" #include "llvm/DebugInfo/PDB/Raw/DbiStream.h" @@ -23,9 +24,10 @@ class ModiSubstreamBuilder {}; } -DbiStreamBuilder::DbiStreamBuilder(BumpPtrAllocator &Allocator) - : Allocator(Allocator), Age(1), BuildNumber(0), PdbDllVersion(0), - PdbDllRbld(0), Flags(0), MachineType(PDB_Machine::x86), Header(nullptr) {} +DbiStreamBuilder::DbiStreamBuilder(msf::MSFBuilder &Msf) + : Msf(Msf), Allocator(Msf.getAllocator()), Age(1), BuildNumber(0), + PdbDllVersion(0), PdbDllRbld(0), Flags(0), MachineType(PDB_Machine::x86), + Header(nullptr) {} void DbiStreamBuilder::setVersionHeader(PdbRaw_DbiVer V) { VerHeader = V; } @@ -227,6 +229,13 @@ return Error::success(); } +Error DbiStreamBuilder::finalizeMsfLayout() { + uint32_t Length = calculateSerializedLength(); + if (auto EC = Msf.setStreamSize(StreamDBI, Length)) + return std::move(EC); + return Error::success(); +} + Expected> DbiStreamBuilder::build(PDBFile &File, const msf::WritableStream &Buffer) { if (!VerHeader.hasValue()) Index: lib/DebugInfo/PDB/Raw/InfoStreamBuilder.cpp =================================================================== --- lib/DebugInfo/PDB/Raw/InfoStreamBuilder.cpp +++ lib/DebugInfo/PDB/Raw/InfoStreamBuilder.cpp @@ -9,6 +9,7 @@ #include "llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h" +#include "llvm/DebugInfo/MSF/MSFBuilder.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" #include "llvm/DebugInfo/MSF/StreamWriter.h" #include "llvm/DebugInfo/PDB/Raw/InfoStream.h" @@ -20,8 +21,8 @@ using namespace llvm::msf; using namespace llvm::pdb; -InfoStreamBuilder::InfoStreamBuilder() - : Ver(PdbRaw_ImplVer::PdbImplVC70), Sig(-1), Age(0) {} +InfoStreamBuilder::InfoStreamBuilder(msf::MSFBuilder &Msf) + : Msf(Msf), Ver(PdbRaw_ImplVer::PdbImplVC70), Sig(-1), Age(0) {} void InfoStreamBuilder::setVersion(PdbRaw_ImplVer V) { Ver = V; } @@ -39,6 +40,13 @@ return sizeof(InfoStreamHeader) + NamedStreams.calculateSerializedLength(); } +Error InfoStreamBuilder::finalizeMsfLayout() { + uint32_t Length = calculateSerializedLength(); + if (auto EC = Msf.setStreamSize(StreamPDB, Length)) + return std::move(EC); + return Error::success(); +} + Expected> InfoStreamBuilder::build(PDBFile &File, const msf::WritableStream &Buffer) { auto StreamData = MappedBlockStream::createIndexedStream(File.getMsfLayout(), Index: lib/DebugInfo/PDB/Raw/PDBFileBuilder.cpp =================================================================== --- lib/DebugInfo/PDB/Raw/PDBFileBuilder.cpp +++ lib/DebugInfo/PDB/Raw/PDBFileBuilder.cpp @@ -50,36 +50,33 @@ InfoStreamBuilder &PDBFileBuilder::getInfoBuilder() { if (!Info) - Info = llvm::make_unique(); + Info = llvm::make_unique(*Msf); return *Info; } DbiStreamBuilder &PDBFileBuilder::getDbiBuilder() { if (!Dbi) - Dbi = llvm::make_unique(Allocator); + Dbi = llvm::make_unique(*Msf); return *Dbi; } TpiStreamBuilder &PDBFileBuilder::getTpiBuilder() { if (!Tpi) - Tpi = llvm::make_unique(Allocator); + Tpi = llvm::make_unique(*Msf); return *Tpi; } Expected PDBFileBuilder::finalizeMsfLayout() const { if (Info) { - uint32_t Length = Info->calculateSerializedLength(); - if (auto EC = Msf->setStreamSize(StreamPDB, Length)) + if (auto EC = Info->finalizeMsfLayout()) return std::move(EC); } if (Dbi) { - uint32_t Length = Dbi->calculateSerializedLength(); - if (auto EC = Msf->setStreamSize(StreamDBI, Length)) + if (auto EC = Dbi->finalizeMsfLayout()) return std::move(EC); } if (Tpi) { - uint32_t Length = Tpi->calculateSerializedLength(); - if (auto EC = Msf->setStreamSize(StreamTPI, Length)) + if (auto EC = Tpi->finalizeMsfLayout()) return std::move(EC); } Index: lib/DebugInfo/PDB/Raw/TpiHashing.cpp =================================================================== --- /dev/null +++ lib/DebugInfo/PDB/Raw/TpiHashing.cpp @@ -0,0 +1,110 @@ +//===- TpiHashing.cpp -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Raw/TpiHashing.h" + +#include "llvm/DebugInfo/PDB/Raw/Hash.h" +#include "llvm/DebugInfo/PDB/Raw/RawError.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +// Corresponds to `fUDTAnon`. +template static bool isAnonymous(T &Rec) { + StringRef Name = Rec.getName(); + return Name == "" || Name == "__unnamed" || + Name.endswith("::") || Name.endswith("::__unnamed"); +} + +// Computes a hash for a given TPI record. +template +static uint32_t getTpiHash(T &Rec, ArrayRef FullRecord) { + auto Opts = static_cast(Rec.getOptions()); + + bool ForwardRef = + Opts & static_cast(ClassOptions::ForwardReference); + bool Scoped = Opts & static_cast(ClassOptions::Scoped); + bool UniqueName = Opts & static_cast(ClassOptions::HasUniqueName); + bool IsAnon = UniqueName && isAnonymous(Rec); + + if (!ForwardRef && !Scoped && !IsAnon) + return hashStringV1(Rec.getName()); + if (!ForwardRef && UniqueName && !IsAnon) + return hashStringV1(Rec.getUniqueName()); + return hashBufferV8(FullRecord); +} + +template static uint32_t getSourceLineHash(T &Rec) { + char Buf[4]; + support::endian::write32le(Buf, Rec.getUDT().getIndex()); + return hashStringV1(StringRef(Buf, 4)); +} + +void TpiHashUpdater::visitKnownRecordImpl(CVType &CVR, + UdtSourceLineRecord &Rec) { + CVR.Hash = getSourceLineHash(Rec); +} + +void TpiHashUpdater::visitKnownRecordImpl(CVType &CVR, + UdtModSourceLineRecord &Rec) { + CVR.Hash = getSourceLineHash(Rec); +} + +void TpiHashUpdater::visitKnownRecordImpl(CVType &CVR, ClassRecord &Rec) { + CVR.Hash = getTpiHash(Rec, CVR.RawData); +} + +void TpiHashUpdater::visitKnownRecordImpl(CVType &CVR, EnumRecord &Rec) { + CVR.Hash = getTpiHash(Rec, CVR.RawData); +} + +void TpiHashUpdater::visitKnownRecordImpl(CVType &CVR, UnionRecord &Rec) { + CVR.Hash = getTpiHash(Rec, CVR.RawData); +} + +Error TpiHashVerifier::visitKnownRecord(CVType &CVR, UdtSourceLineRecord &Rec) { + return verifySourceLine(Rec.getUDT()); +} + +Error TpiHashVerifier::visitKnownRecord(CVType &CVR, + UdtModSourceLineRecord &Rec) { + return verifySourceLine(Rec.getUDT()); +} + +Error TpiHashVerifier::visitKnownRecord(CVType &CVR, ClassRecord &Rec) { + if (getTpiHash(Rec, CVR.RawData) % NumHashBuckets != HashValues[Index]) + return errorInvalidHash(); + return Error::success(); +} +Error TpiHashVerifier::visitKnownRecord(CVType &CVR, EnumRecord &Rec) { + if (getTpiHash(Rec, CVR.RawData) % NumHashBuckets != HashValues[Index]) + return errorInvalidHash(); + return Error::success(); +} +Error TpiHashVerifier::visitKnownRecord(CVType &CVR, UnionRecord &Rec) { + if (getTpiHash(Rec, CVR.RawData) % NumHashBuckets != HashValues[Index]) + return errorInvalidHash(); + return Error::success(); +} + +Error TpiHashVerifier::verifySourceLine(codeview::TypeIndex TI) { + char Buf[4]; + support::endian::write32le(Buf, TI.getIndex()); + uint32_t Hash = hashStringV1(StringRef(Buf, 4)); + if (Hash % NumHashBuckets != HashValues[Index]) + return errorInvalidHash(); + return Error::success(); +} + +Error TpiHashVerifier::visitTypeBegin(CVType &Rec) { + ++Index; + RawRecord = Rec; + return Error::success(); +} Index: lib/DebugInfo/PDB/Raw/TpiStream.cpp =================================================================== --- lib/DebugInfo/PDB/Raw/TpiStream.cpp +++ lib/DebugInfo/PDB/Raw/TpiStream.cpp @@ -17,11 +17,11 @@ #include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" #include "llvm/DebugInfo/MSF/StreamReader.h" -#include "llvm/DebugInfo/PDB/Raw/Hash.h" #include "llvm/DebugInfo/PDB/Raw/PDBFile.h" #include "llvm/DebugInfo/PDB/Raw/RawConstants.h" #include "llvm/DebugInfo/PDB/Raw/RawError.h" #include "llvm/DebugInfo/PDB/Raw/RawTypes.h" +#include "llvm/DebugInfo/PDB/Raw/TpiHashing.h" #include "llvm/Support/Endian.h" @@ -37,97 +37,6 @@ TpiStream::~TpiStream() {} -// Corresponds to `fUDTAnon`. -template static bool isAnonymous(T &Rec) { - StringRef Name = Rec.getName(); - return Name == "" || Name == "__unnamed" || - Name.endswith("::") || Name.endswith("::__unnamed"); -} - -// Computes a hash for a given TPI record. -template -static uint32_t getTpiHash(T &Rec, const CVRecord &RawRec) { - auto Opts = static_cast(Rec.getOptions()); - - bool ForwardRef = - Opts & static_cast(ClassOptions::ForwardReference); - bool Scoped = Opts & static_cast(ClassOptions::Scoped); - bool UniqueName = Opts & static_cast(ClassOptions::HasUniqueName); - bool IsAnon = UniqueName && isAnonymous(Rec); - - if (!ForwardRef && !Scoped && !IsAnon) - return hashStringV1(Rec.getName()); - if (!ForwardRef && UniqueName && !IsAnon) - return hashStringV1(Rec.getUniqueName()); - return hashBufferV8(RawRec.RawData); -} - -namespace { -class TpiHashVerifier : public TypeVisitorCallbacks { -public: - TpiHashVerifier(FixedStreamArray &HashValues, - uint32_t NumHashBuckets) - : HashValues(HashValues), NumHashBuckets(NumHashBuckets) {} - - Error visitKnownRecord(CVRecord &CVR, - UdtSourceLineRecord &Rec) override { - return verifySourceLine(Rec); - } - - Error visitKnownRecord(CVRecord &CVR, - UdtModSourceLineRecord &Rec) override { - return verifySourceLine(Rec); - } - - Error visitKnownRecord(CVRecord &CVR, - ClassRecord &Rec) override { - return verify(Rec); - } - Error visitKnownRecord(CVRecord &CVR, - EnumRecord &Rec) override { - return verify(Rec); - } - Error visitKnownRecord(CVRecord &CVR, - UnionRecord &Rec) override { - return verify(Rec); - } - - Error visitTypeBegin(CVRecord &Rec) override { - ++Index; - RawRecord = Rec; - return Error::success(); - } - -private: - template Error verify(T &Rec) { - uint32_t Hash = getTpiHash(Rec, RawRecord); - if (Hash % NumHashBuckets != HashValues[Index]) - return errorInvalidHash(); - return Error::success(); - } - - template Error verifySourceLine(T &Rec) { - char Buf[4]; - support::endian::write32le(Buf, Rec.getUDT().getIndex()); - uint32_t Hash = hashStringV1(StringRef(Buf, 4)); - if (Hash % NumHashBuckets != HashValues[Index]) - return errorInvalidHash(); - return Error::success(); - } - - Error errorInvalidHash() { - return make_error( - raw_error_code::invalid_tpi_hash, - "Type index is 0x" + utohexstr(TypeIndex::FirstNonSimpleIndex + Index)); - } - - FixedStreamArray HashValues; - CVRecord RawRecord; - uint32_t NumHashBuckets; - uint32_t Index = -1; -}; -} - // Verifies that a given type record matches with a given hash value. // Currently we only verify SRC_LINE records. Error TpiStream::verifyHashValues() { @@ -193,6 +102,9 @@ HSR.setOffset(Header->HashValueBuffer.Off); if (auto EC = HSR.readArray(HashValues, NumHashValues)) return EC; + std::vector HashValueList; + for (auto I : HashValues) + HashValueList.push_back(I); HSR.setOffset(Header->IndexOffsetBuffer.Off); uint32_t NumTypeIndexOffsets = Index: lib/DebugInfo/PDB/Raw/TpiStreamBuilder.cpp =================================================================== --- lib/DebugInfo/PDB/Raw/TpiStreamBuilder.cpp +++ lib/DebugInfo/PDB/Raw/TpiStreamBuilder.cpp @@ -2,6 +2,7 @@ #include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/MSF/MSFBuilder.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" #include "llvm/DebugInfo/MSF/StreamWriter.h" #include "llvm/DebugInfo/PDB/Raw/PDBFile.h" @@ -14,8 +15,8 @@ using namespace llvm::pdb; using namespace llvm::support; -TpiStreamBuilder::TpiStreamBuilder(BumpPtrAllocator &Allocator) - : Allocator(Allocator), Header(nullptr) {} +TpiStreamBuilder::TpiStreamBuilder(MSFBuilder &Msf) + : Msf(Msf), Allocator(Msf.getAllocator()), Header(nullptr) {} TpiStreamBuilder::~TpiStreamBuilder() {} @@ -35,6 +36,7 @@ TpiStreamHeader *H = Allocator.Allocate(); uint32_t Count = TypeRecords.size(); + uint32_t HashBufferSize = calculateHashBufferSize(); H->Version = *VerHeader; H->HeaderSize = sizeof(TpiStreamHeader); @@ -42,13 +44,19 @@ H->TypeIndexEnd = H->TypeIndexBegin + Count; H->TypeRecordBytes = TypeRecordStream.getLength(); - H->HashStreamIndex = kInvalidStreamIndex; + H->HashStreamIndex = HashStreamIndex; H->HashAuxStreamIndex = kInvalidStreamIndex; H->HashKeySize = sizeof(ulittle32_t); H->NumHashBuckets = MinTpiHashBuckets; - H->HashValueBuffer.Length = 0; + // Recall that hash values go into a completely different stream identified by + // the `HashStreamIndex` field of the `TpiStreamHeader`. Therefore, the data + // begins at offset 0 of this independent stream. + H->HashValueBuffer.Off = 0; + H->HashValueBuffer.Length = HashBufferSize; + H->HashAdjBuffer.Off = H->HashValueBuffer.Off + H->HashValueBuffer.Length; H->HashAdjBuffer.Length = 0; + H->IndexOffsetBuffer.Off = H->HashAdjBuffer.Off + H->HashAdjBuffer.Length; H->IndexOffsetBuffer.Length = 0; Header = H; @@ -56,7 +64,39 @@ } uint32_t TpiStreamBuilder::calculateSerializedLength() const { - return sizeof(TpiStreamHeader) + TypeRecordStream.getLength(); + return sizeof(TpiStreamHeader) + TypeRecordStream.getLength() + + calculateHashBufferSize(); +} + +uint32_t TpiStreamBuilder::calculateHashBufferSize() const { + if (TypeRecords.empty() || !TypeRecords[0].Hash.hasValue()) + return 0; + return TypeRecords.size() * sizeof(ulittle32_t); +} + +Error TpiStreamBuilder::finalizeMsfLayout() { + uint32_t Length = calculateSerializedLength(); + if (auto EC = Msf.setStreamSize(StreamTPI, Length)) + return std::move(EC); + + uint32_t HashBufferSize = calculateHashBufferSize(); + + if (HashBufferSize == 0) + return Error::success(); + + auto ExpectedIndex = Msf.addStream(HashBufferSize); + if (!ExpectedIndex) + return ExpectedIndex.takeError(); + HashStreamIndex = *ExpectedIndex; + ulittle32_t *H = Allocator.Allocate(TypeRecords.size()); + MutableArrayRef HashBuffer(H, TypeRecords.size()); + for (uint32_t I = 0; I < TypeRecords.size(); ++I) { + HashBuffer[I] = *TypeRecords[I].Hash % MinTpiHashBuckets; + } + ArrayRef Bytes(reinterpret_cast(HashBuffer.data()), + HashBufferSize); + HashValueStream = llvm::make_unique(Bytes); + return Error::success(); } Expected> @@ -72,6 +112,12 @@ auto Tpi = llvm::make_unique(File, std::move(StreamData)); Tpi->Header = Header; Tpi->TypeRecords = VarStreamArray(TypeRecordStream); + if (HashValueStream) { + Tpi->HashStream = std::move(HashValueStream); + StreamReader HSR(*Tpi->HashStream); + if (auto EC = HSR.readArray(Tpi->HashValues, TypeRecords.size())) + return std::move(EC); + } return std::move(Tpi); } @@ -91,5 +137,13 @@ if (auto EC = Writer.writeArray(RecordArray)) return EC; + if (HashStreamIndex != kInvalidStreamIndex) { + auto HVS = WritableMappedBlockStream::createIndexedStream(Layout, Buffer, + HashStreamIndex); + StreamWriter HW(*HVS); + if (auto EC = HW.writeStreamRef(*HashValueStream)) + return EC; + } + return Error::success(); } Index: test/DebugInfo/PDB/pdbdump-readwrite.test =================================================================== --- test/DebugInfo/PDB/pdbdump-readwrite.test +++ test/DebugInfo/PDB/pdbdump-readwrite.test @@ -10,12 +10,12 @@ CHECK-NEXT: BlockSize: 4096 CHECK-NEXT: FreeBlockMap: 2 CHECK-NEXT: NumBlocks: 25 -CHECK-NEXT: NumDirectoryBytes: 136 +CHECK-NEXT: NumDirectoryBytes: CHECK-NEXT: Unknown1: 0 -CHECK-NEXT: BlockMapAddr: 24 +CHECK-NEXT: BlockMapAddr: CHECK-NEXT: NumDirectoryBlocks: 1 -CHECK-NEXT: DirectoryBlocks: [23] -CHECK-NEXT: NumStreams: 17 +CHECK-NEXT: DirectoryBlocks: +CHECK-NEXT: NumStreams: CHECK-NEXT: } CHECK: PDB Stream { CHECK-NEXT: Version: 20000404 Index: test/DebugInfo/PDB/pdbdump-write.test =================================================================== --- test/DebugInfo/PDB/pdbdump-write.test +++ test/DebugInfo/PDB/pdbdump-write.test @@ -12,6 +12,6 @@ ; ; 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 -tpi-stream %p/Inputs/empty.pdb > %t.3 -; RUN: llvm-pdbdump pdb2yaml -pdb-stream -tpi-stream %t.2 > %t.4 +; RUN: llvm-pdbdump pdb2yaml -pdb-stream -tpi-stream -no-file-headers %p/Inputs/empty.pdb > %t.3 +; RUN: llvm-pdbdump pdb2yaml -pdb-stream -tpi-stream -no-file-headers %t.2 > %t.4 ; RUN: diff %t.3 %t.4 Index: tools/llvm-pdbdump/PdbYaml.cpp =================================================================== --- tools/llvm-pdbdump/PdbYaml.cpp +++ tools/llvm-pdbdump/PdbYaml.cpp @@ -19,6 +19,7 @@ #include "llvm/DebugInfo/PDB/PDBExtras.h" #include "llvm/DebugInfo/PDB/PDBTypes.h" #include "llvm/DebugInfo/PDB/Raw/PDBFile.h" +#include "llvm/DebugInfo/PDB/Raw/TpiHashing.h" using namespace llvm; using namespace llvm::pdb; @@ -216,6 +217,7 @@ codeview::TypeDeserializer Deserializer; codeview::TypeSerializationVisitor Serializer(Context.FieldListBuilder, Context.TypeTableBuilder); + pdb::TpiHashUpdater Hasher; if (IO.outputting()) { // For PDB to Yaml, deserialize into a high level record type, then dump it. @@ -226,6 +228,7 @@ // to bytes. Pipeline.addCallbackToPipeline(Context.Dumper); Pipeline.addCallbackToPipeline(Serializer); + Pipeline.addCallbackToPipeline(Hasher); } codeview::CVTypeVisitor Visitor(Pipeline); Index: tools/llvm-pdbdump/llvm-pdbdump.cpp =================================================================== --- tools/llvm-pdbdump/llvm-pdbdump.cpp +++ tools/llvm-pdbdump/llvm-pdbdump.cpp @@ -328,37 +328,14 @@ PDBFileBuilder Builder(Allocator); ExitOnErr(Builder.initialize(YamlObj.Headers->SuperBlock)); - ExitOnErr(Builder.getMsfBuilder().setDirectoryBlocksHint( - YamlObj.Headers->DirectoryBlocks)); - if (!YamlObj.StreamSizes.hasValue()) { - ExitOnErr(make_error( - generic_error_code::unspecified, - "Cannot generate a PDB when stream sizes are not known")); - } - - if (YamlObj.StreamMap.hasValue()) { - if (YamlObj.StreamMap->size() != YamlObj.StreamSizes->size()) { - ExitOnErr(make_error(generic_error_code::unspecified, - "YAML specifies different number of " - "streams in stream sizes and stream " - "map")); - } - - auto &Sizes = *YamlObj.StreamSizes; - auto &Map = *YamlObj.StreamMap; - for (uint32_t I = 0; I < Sizes.size(); ++I) { - uint32_t Size = Sizes[I]; - std::vector Blocks; - for (auto E : Map[I].Blocks) - Blocks.push_back(E); - ExitOnErr(Builder.getMsfBuilder().addStream(Size, Blocks)); - } - } else { - auto &Sizes = *YamlObj.StreamSizes; - for (auto S : Sizes) { - ExitOnErr(Builder.getMsfBuilder().addStream(S)); - } - } + // Add each of the reserved streams. We ignore stream metadata in the + // yaml, because we will reconstruct our own view of the streams. For + // example, the YAML may say that there were 20 streams in the original + // PDB, but maybe we only dump a subset of those 20 streams, so we will + // have fewer, and the ones we do have may end up with different indices + // than the ones in the original PDB. So we just start with a clean slate. + for (uint32_t I = 0; I < kSpecialStreamCount; ++I) + ExitOnErr(Builder.getMsfBuilder().addStream(0)); if (YamlObj.PdbStream.hasValue()) { auto &InfoBuilder = Builder.getInfoBuilder();