Index: lld/trunk/COFF/PDB.cpp =================================================================== --- lld/trunk/COFF/PDB.cpp +++ lld/trunk/COFF/PDB.cpp @@ -23,15 +23,15 @@ #include "llvm/DebugInfo/MSF/ByteStream.h" #include "llvm/DebugInfo/MSF/MSFBuilder.h" #include "llvm/DebugInfo/MSF/MSFCommon.h" -#include "llvm/DebugInfo/PDB/Raw/DbiStream.h" -#include "llvm/DebugInfo/PDB/Raw/DbiStreamBuilder.h" -#include "llvm/DebugInfo/PDB/Raw/InfoStream.h" -#include "llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h" -#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" -#include "llvm/DebugInfo/PDB/Raw/PDBFileBuilder.h" -#include "llvm/DebugInfo/PDB/Raw/StringTableBuilder.h" -#include "llvm/DebugInfo/PDB/Raw/TpiStream.h" -#include "llvm/DebugInfo/PDB/Raw/TpiStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#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/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" Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/DbiStream.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/DbiStream.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/DbiStream.h @@ -0,0 +1,122 @@ +//===- DbiStream.h - PDB Dbi Stream (Stream 3) Access -----------*- 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_PDBDBISTREAM_H +#define LLVM_DEBUGINFO_PDB_RAW_PDBDBISTREAM_H + +#include "llvm/DebugInfo/CodeView/ModuleSubstream.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/MSF/StreamArray.h" +#include "llvm/DebugInfo/MSF/StreamRef.h" +#include "llvm/DebugInfo/PDB/Native/ModInfo.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/DebugInfo/PDB/Native/StringTable.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace object { +struct FpoData; +struct coff_section; +} + +namespace pdb { +class DbiStreamBuilder; +class PDBFile; +class ISectionContribVisitor; + +class DbiStream { + friend class DbiStreamBuilder; + +public: + DbiStream(PDBFile &File, std::unique_ptr Stream); + ~DbiStream(); + Error reload(); + + PdbRaw_DbiVer getDbiVersion() const; + uint32_t getAge() const; + uint16_t getPublicSymbolStreamIndex() const; + uint16_t getGlobalSymbolStreamIndex() const; + + uint16_t getFlags() const; + bool isIncrementallyLinked() const; + bool hasCTypes() const; + bool isStripped() const; + + uint16_t getBuildNumber() const; + uint16_t getBuildMajorVersion() const; + uint16_t getBuildMinorVersion() const; + + uint16_t getPdbDllRbld() const; + uint32_t getPdbDllVersion() const; + + uint32_t getSymRecordStreamIndex() const; + + PDB_Machine getMachineType() const; + + /// If the given stream type is present, returns its stream index. If it is + /// not present, returns InvalidStreamIndex. + uint32_t getDebugStreamIndex(DbgHeaderType Type) const; + + ArrayRef modules() const; + + Expected getFileNameForIndex(uint32_t Index) const; + + msf::FixedStreamArray getSectionHeaders(); + + msf::FixedStreamArray getFpoRecords(); + + msf::FixedStreamArray getSectionMap() const; + void visitSectionContributions(ISectionContribVisitor &Visitor) const; + +private: + Error initializeModInfoArray(); + Error initializeSectionContributionData(); + Error initializeSectionHeadersData(); + Error initializeSectionMapData(); + Error initializeFileInfo(); + Error initializeFpoRecords(); + + PDBFile &Pdb; + std::unique_ptr Stream; + + std::vector ModuleInfos; + StringTable ECNames; + + msf::ReadableStreamRef ModInfoSubstream; + msf::ReadableStreamRef SecContrSubstream; + msf::ReadableStreamRef SecMapSubstream; + msf::ReadableStreamRef FileInfoSubstream; + msf::ReadableStreamRef TypeServerMapSubstream; + msf::ReadableStreamRef ECSubstream; + + msf::ReadableStreamRef NamesBuffer; + + msf::FixedStreamArray DbgStreams; + + PdbRaw_DbiSecContribVer SectionContribVersion; + msf::FixedStreamArray SectionContribs; + msf::FixedStreamArray SectionContribs2; + msf::FixedStreamArray SectionMap; + msf::FixedStreamArray FileNameOffsets; + + std::unique_ptr SectionHeaderStream; + msf::FixedStreamArray SectionHeaders; + + std::unique_ptr FpoStream; + msf::FixedStreamArray FpoRecords; + + const DbiStreamHeader *Header; +}; +} +} + +#endif Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h @@ -0,0 +1,125 @@ +//===- DbiStreamBuilder.h - PDB Dbi 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_PDBDBISTREAMBUILDER_H +#define LLVM_DEBUGINFO_PDB_RAW_PDBDBISTREAMBUILDER_H + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/Error.h" + +#include "llvm/DebugInfo/MSF/ByteStream.h" +#include "llvm/DebugInfo/MSF/StreamReader.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" +#include "llvm/Support/Endian.h" + +namespace llvm { +namespace msf { +class MSFBuilder; +} +namespace object { +struct coff_section; +} +namespace pdb { +class DbiStream; +struct DbiStreamHeader; +class PDBFile; + +class DbiStreamBuilder { +public: + DbiStreamBuilder(msf::MSFBuilder &Msf); + + DbiStreamBuilder(const DbiStreamBuilder &) = delete; + DbiStreamBuilder &operator=(const DbiStreamBuilder &) = delete; + + void setVersionHeader(PdbRaw_DbiVer V); + void setAge(uint32_t A); + void setBuildNumber(uint16_t B); + void setPdbDllVersion(uint16_t V); + void setPdbDllRbld(uint16_t R); + void setFlags(uint16_t F); + void setMachineType(PDB_Machine M); + void setSectionContribs(ArrayRef SecMap); + void setSectionMap(ArrayRef SecMap); + + // Add given bytes as a new stream. + Error addDbgStream(pdb::DbgHeaderType Type, ArrayRef Data); + + uint32_t calculateSerializedLength() const; + + Error addModuleInfo(StringRef ObjFile, StringRef Module); + Error addModuleSourceFile(StringRef Module, StringRef File); + + Error finalizeMsfLayout(); + + Error commit(const msf::MSFLayout &Layout, const msf::WritableStream &Buffer); + + // A helper function to create Section Contributions from COFF input + // section headers. + static std::vector + createSectionContribs(ArrayRef SecHdrs); + + // A helper function to create a Section Map from a COFF section header. + static std::vector + createSectionMap(ArrayRef SecHdrs); + +private: + struct DebugStream { + ArrayRef Data; + uint16_t StreamNumber = 0; + }; + + Error finalize(); + uint32_t calculateModiSubstreamSize() const; + uint32_t calculateSectionContribsStreamSize() const; + uint32_t calculateSectionMapStreamSize() const; + uint32_t calculateFileInfoSubstreamSize() const; + uint32_t calculateNamesBufferSize() const; + uint32_t calculateDbgStreamsSize() const; + + Error generateModiSubstream(); + Error generateFileInfoSubstream(); + + struct ModuleInfo { + std::vector SourceFiles; + StringRef Obj; + StringRef Mod; + }; + + msf::MSFBuilder &Msf; + BumpPtrAllocator &Allocator; + + Optional VerHeader; + uint32_t Age; + uint16_t BuildNumber; + uint16_t PdbDllVersion; + uint16_t PdbDllRbld; + uint16_t Flags; + PDB_Machine MachineType; + + const DbiStreamHeader *Header; + + StringMap> ModuleInfos; + std::vector ModuleInfoList; + + StringMap SourceFileNames; + + msf::WritableStreamRef NamesBuffer; + msf::MutableByteStream ModInfoBuffer; + msf::MutableByteStream FileInfoBuffer; + ArrayRef SectionContribs; + ArrayRef SectionMap; + llvm::SmallVector DbgStreams; +}; +} +} + +#endif Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/EnumTables.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/EnumTables.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/EnumTables.h @@ -0,0 +1,22 @@ +//===- EnumTables.h - Enum to string conversion tables ----------*- 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_ENUMTABLES_H +#define LLVM_DEBUGINFO_PDB_RAW_ENUMTABLES_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/ScopedPrinter.h" + +namespace llvm { +namespace pdb { +ArrayRef> getOMFSegMapDescFlagNames(); +} +} + +#endif // LLVM_DEBUGINFO_PDB_RAW_ENUMTABLES_H Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/GlobalsStream.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/GlobalsStream.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/GlobalsStream.h @@ -0,0 +1,45 @@ +//===- GlobalsStream.h - PDB Index of Symbols by Name ------ ----*- 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_GLOBALS_STREAM_H +#define LLVM_DEBUGINFO_PDB_RAW_GLOBALS_STREAM_H + +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/MSF/StreamArray.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace pdb { +class DbiStream; +class PDBFile; + +class GlobalsStream { +public: + explicit GlobalsStream(std::unique_ptr Stream); + ~GlobalsStream(); + Error commit(); + msf::FixedStreamArray getHashBuckets() const { + return HashBuckets; + } + uint32_t getNumBuckets() const { return NumBuckets; } + Error reload(); + +private: + msf::FixedStreamArray HashBuckets; + msf::FixedStreamArray HashRecords; + uint32_t NumBuckets; + std::unique_ptr Stream; +}; +} +} + +#endif Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/Hash.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/Hash.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/Hash.h @@ -0,0 +1,25 @@ +//===- Hash.h - PDB hash functions ------------------------------*- 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_HASH_H +#define LLVM_DEBUGINFO_PDB_RAW_HASH_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include + +namespace llvm { +namespace pdb { +uint32_t hashStringV1(StringRef Str); +uint32_t hashStringV2(StringRef Str); +uint32_t hashBufferV8(ArrayRef Data); +} +} + +#endif Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/HashTable.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/HashTable.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/HashTable.h @@ -0,0 +1,106 @@ +//===- HashTable.h - PDB Hash Table -----------------------------*- 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_HASHTABLE_H +#define LLVM_DEBUGINFO_PDB_RAW_HASHTABLE_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SparseBitVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator.h" +#include "llvm/DebugInfo/MSF/StreamArray.h" +#include "llvm/DebugInfo/MSF/StreamReader.h" +#include "llvm/DebugInfo/MSF/StreamWriter.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MathExtras.h" + +#include +#include + +namespace llvm { +namespace pdb { + +class HashTableIterator; + +class HashTable { + friend class HashTableIterator; + struct Header { + support::ulittle32_t Size; + support::ulittle32_t Capacity; + }; + + typedef std::vector> BucketList; + +public: + HashTable(); + explicit HashTable(uint32_t Capacity); + + Error load(msf::StreamReader &Stream); + + uint32_t calculateSerializedLength() const; + Error commit(msf::StreamWriter &Writer) const; + + void clear(); + + uint32_t capacity() const; + uint32_t size() const; + + HashTableIterator begin() const; + HashTableIterator end() const; + HashTableIterator find(uint32_t K); + + void set(uint32_t K, uint32_t V); + void remove(uint32_t K); + uint32_t get(uint32_t K); + +protected: + bool isPresent(uint32_t K) const { return Present.test(K); } + bool isDeleted(uint32_t K) const { return Deleted.test(K); } + BucketList Buckets; + mutable SparseBitVector<> Present; + mutable SparseBitVector<> Deleted; + +private: + static uint32_t maxLoad(uint32_t capacity); + void grow(); + + static Error readSparseBitVector(msf::StreamReader &Stream, + SparseBitVector<> &V); + static Error writeSparseBitVector(msf::StreamWriter &Writer, + SparseBitVector<> &Vec); +}; + +class HashTableIterator + : public iterator_facade_base> { + friend class HashTable; + HashTableIterator(const HashTable &Map, uint32_t Index, bool IsEnd); + +public: + HashTableIterator(const HashTable &Map); + + HashTableIterator &operator=(const HashTableIterator &R); + bool operator==(const HashTableIterator &R) const; + const std::pair &operator*() const; + HashTableIterator &operator++(); + +private: + bool isEnd() const { return IsEnd; } + uint32_t index() const { return Index; } + + const HashTable *Map; + uint32_t Index; + bool IsEnd; +}; + +} // end namespace pdb +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_PDB_RAW_HASHTABLE_H Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h @@ -0,0 +1,30 @@ +//===- ISectionContribVisitor.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_RAW_ISECTIONCONTRIBVISITOR_H +#define LLVM_DEBUGINFO_PDB_RAW_ISECTIONCONTRIBVISITOR_H + +namespace llvm { +namespace pdb { + +struct SectionContrib; +struct SectionContrib2; + +class ISectionContribVisitor { +public: + virtual ~ISectionContribVisitor() = default; + + virtual void visit(const SectionContrib &C) = 0; + virtual void visit(const SectionContrib2 &C) = 0; +}; + +} // end namespace pdb +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_PDB_RAW_ISECTIONCONTRIBVISITOR_H Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/InfoStream.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/InfoStream.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/InfoStream.h @@ -0,0 +1,71 @@ +//===- InfoStream.h - PDB Info Stream (Stream 1) Access ---------*- 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_PDBINFOSTREAM_H +#define LLVM_DEBUGINFO_PDB_RAW_PDBINFOSTREAM_H + +#include "llvm/ADT/StringMap.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" + +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace pdb { +class InfoStreamBuilder; +class PDBFile; + +class InfoStream { + friend class InfoStreamBuilder; + +public: + InfoStream(std::unique_ptr Stream); + + Error reload(); + + PdbRaw_ImplVer getVersion() const; + uint32_t getSignature() const; + uint32_t getAge() const; + PDB_UniqueId getGuid() const; + + const NamedStreamMap &getNamedStreams() const; + + uint32_t getNamedStreamIndex(llvm::StringRef Name) const; + iterator_range> named_streams() const; + +private: + std::unique_ptr Stream; + + // PDB file format version. We only support VC70. See the enumeration + // `PdbRaw_ImplVer` for the other possible values. + uint32_t Version; + + // A 32-bit signature unique across all PDBs. This is generated with + // a call to time() when the PDB is written, but obviously this is not + // universally unique. + uint32_t Signature; + + // The number of times the PDB has been written. Might also be used to + // ensure that the PDB matches the executable. + uint32_t Age; + + // Due to the aforementioned limitations with `Signature`, this is a new + // signature present on VC70 and higher PDBs which is guaranteed to be + // universally unique. + PDB_UniqueId Guid; + + NamedStreamMap NamedStreams; +}; +} +} + +#endif Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h @@ -0,0 +1,61 @@ +//===- InfoStreamBuilder.h - PDB Info 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_PDBINFOSTREAMBUILDER_H +#define LLVM_DEBUGINFO_PDB_RAW_PDBINFOSTREAMBUILDER_H + +#include "llvm/ADT/Optional.h" +#include "llvm/Support/Error.h" + +#include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" + +namespace llvm { +namespace msf { +class MSFBuilder; +class StreamWriter; +} +namespace pdb { +class PDBFile; +class NamedStreamMap; + +class InfoStreamBuilder { +public: + InfoStreamBuilder(msf::MSFBuilder &Msf, NamedStreamMap &NamedStreams); + InfoStreamBuilder(const InfoStreamBuilder &) = delete; + InfoStreamBuilder &operator=(const InfoStreamBuilder &) = delete; + + void setVersion(PdbRaw_ImplVer V); + void setSignature(uint32_t S); + void setAge(uint32_t A); + void setGuid(PDB_UniqueId G); + + uint32_t finalize(); + + Error finalizeMsfLayout(); + + Error commit(const msf::MSFLayout &Layout, + const msf::WritableStream &Buffer) const; + +private: + msf::MSFBuilder &Msf; + + PdbRaw_ImplVer Ver; + uint32_t Sig; + uint32_t Age; + PDB_UniqueId Guid; + + NamedStreamMap &NamedStreams; +}; +} +} + +#endif Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/ModInfo.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/ModInfo.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/ModInfo.h @@ -0,0 +1,82 @@ +//===- ModInfo.h - PDB module information -----------------------*- 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_MODINFO_H +#define LLVM_DEBUGINFO_PDB_RAW_MODINFO_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/MSF/StreamArray.h" +#include "llvm/DebugInfo/MSF/StreamRef.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/Error.h" +#include +#include + +namespace llvm { + +namespace pdb { + +class ModInfo { + friend class DbiStreamBuilder; + +public: + ModInfo(); + ModInfo(const ModInfo &Info); + ~ModInfo(); + + static Error initialize(msf::ReadableStreamRef Stream, ModInfo &Info); + + bool hasECInfo() const; + uint16_t getTypeServerIndex() const; + uint16_t getModuleStreamIndex() const; + uint32_t getSymbolDebugInfoByteSize() const; + uint32_t getLineInfoByteSize() const; + uint32_t getC13LineInfoByteSize() const; + uint32_t getNumberOfFiles() const; + uint32_t getSourceFileNameIndex() const; + uint32_t getPdbFilePathNameIndex() const; + + StringRef getModuleName() const; + StringRef getObjFileName() const; + + uint32_t getRecordLength() const; + +private: + StringRef ModuleName; + StringRef ObjFileName; + const ModuleInfoHeader *Layout = nullptr; +}; + +struct ModuleInfoEx { + ModuleInfoEx(const ModInfo &Info) : Info(Info) {} + ModuleInfoEx(const ModuleInfoEx &Ex) = default; + + ModInfo Info; + std::vector SourceFiles; +}; + +} // end namespace pdb + +namespace msf { + +template <> struct VarStreamArrayExtractor { + Error operator()(ReadableStreamRef Stream, uint32_t &Length, + pdb::ModInfo &Info) const { + if (auto EC = pdb::ModInfo::initialize(Stream, Info)) + return EC; + Length = Info.getRecordLength(); + return Error::success(); + } +}; + +} // end namespace msf + +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_PDB_RAW_MODINFO_H Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/ModStream.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/ModStream.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/ModStream.h @@ -0,0 +1,62 @@ +//===- ModStream.h - PDB Module Info Stream Access ------------------------===// +// +// 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_MODSTREAM_H +#define LLVM_DEBUGINFO_PDB_RAW_MODSTREAM_H + +#include "llvm/ADT/iterator_range.h" +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/ModuleSubstream.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/MSF/StreamArray.h" +#include "llvm/DebugInfo/MSF/StreamRef.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace pdb { +class PDBFile; +class ModInfo; + +class ModStream { +public: + ModStream(const ModInfo &Module, + std::unique_ptr Stream); + ~ModStream(); + + Error reload(); + + uint32_t signature() const { return Signature; } + + iterator_range + symbols(bool *HadError) const; + + iterator_range + lines(bool *HadError) const; + + Error commit(); + +private: + const ModInfo &Mod; + + uint32_t Signature; + + std::unique_ptr Stream; + + codeview::CVSymbolArray SymbolsSubstream; + msf::ReadableStreamRef LinesSubstream; + msf::ReadableStreamRef C13LinesSubstream; + msf::ReadableStreamRef GlobalRefsSubstream; + + codeview::ModuleSubstreamArray LineInfo; +}; +} +} + +#endif Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/NamedStreamMap.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/NamedStreamMap.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/NamedStreamMap.h @@ -0,0 +1,55 @@ +//===- NamedStreamMap.h - PDB Named Stream Map ------------------*- 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_PDBNAMEDSTREAMMAP_H +#define LLVM_DEBUGINFO_PDB_RAW_PDBNAMEDSTREAMMAP_H + +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/PDB/Native/HashTable.h" +#include "llvm/Support/Error.h" +#include + +namespace llvm { +namespace msf { +class StreamReader; +class StreamWriter; +} +namespace pdb { +class NamedStreamMapBuilder; +class NamedStreamMap { + struct FinalizationInfo { + uint32_t StringDataBytes = 0; + uint32_t SerializedLength = 0; + }; + friend NamedStreamMapBuilder; + +public: + NamedStreamMap(); + + Error load(msf::StreamReader &Stream); + Error commit(msf::StreamWriter &Writer) const; + uint32_t finalize(); + + bool get(StringRef Stream, uint32_t &StreamNo) const; + void set(StringRef Stream, uint32_t StreamNo); + void remove(StringRef Stream); + + iterator_range> entries() const; + +private: + Optional FinalizedInfo; + HashTable FinalizedHashTable; + StringMap Mapping; +}; + +} // end namespace pdb +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_PDB_RAW_PDBNAMEDSTREAMMAP_H Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/NativeSession.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/NativeSession.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/NativeSession.h @@ -0,0 +1,78 @@ +//===- NativeSession.h - Native implementation of IPDBSession ---*- 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_RAWSESSION_H +#define LLVM_DEBUGINFO_PDB_RAW_RAWSESSION_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace pdb { +class PDBFile; + +class NativeSession : public IPDBSession { +public: + NativeSession(std::unique_ptr PdbFile, + std::unique_ptr Allocator); + ~NativeSession() override; + + static Error createFromPdb(StringRef Path, + std::unique_ptr &Session); + static Error createFromExe(StringRef Path, + std::unique_ptr &Session); + + uint64_t getLoadAddress() const override; + void setLoadAddress(uint64_t Address) override; + std::unique_ptr getGlobalScope() const override; + std::unique_ptr getSymbolById(uint32_t SymbolId) const override; + + std::unique_ptr + findSymbolByAddress(uint64_t Address, PDB_SymType Type) const override; + + std::unique_ptr + findLineNumbers(const PDBSymbolCompiland &Compiland, + const IPDBSourceFile &File) const override; + std::unique_ptr + findLineNumbersByAddress(uint64_t Address, uint32_t Length) const override; + + std::unique_ptr + findSourceFiles(const PDBSymbolCompiland *Compiland, llvm::StringRef Pattern, + PDB_NameSearchFlags Flags) const override; + std::unique_ptr + findOneSourceFile(const PDBSymbolCompiland *Compiland, + llvm::StringRef Pattern, + PDB_NameSearchFlags Flags) const override; + std::unique_ptr> + findCompilandsForSourceFile(llvm::StringRef Pattern, + PDB_NameSearchFlags Flags) const override; + std::unique_ptr + findOneCompilandForSourceFile(llvm::StringRef Pattern, + PDB_NameSearchFlags Flags) const override; + std::unique_ptr getAllSourceFiles() const override; + std::unique_ptr getSourceFilesForCompiland( + const PDBSymbolCompiland &Compiland) const override; + std::unique_ptr + getSourceFileById(uint32_t FileId) const override; + + std::unique_ptr getDebugStreams() const override; + + PDBFile &getPDBFile() { return *Pdb; } + const PDBFile &getPDBFile() const { return *Pdb; } + +private: + std::unique_ptr Pdb; + std::unique_ptr Allocator; +}; +} +} + +#endif Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/PDBFile.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/PDBFile.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/PDBFile.h @@ -0,0 +1,134 @@ +//===- PDBFile.h - Low level interface to a PDB file ------------*- 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_PDBFILE_H +#define LLVM_DEBUGINFO_PDB_RAW_PDBFILE_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/DebugInfo/MSF/IMSFFile.h" +#include "llvm/DebugInfo/MSF/MSFCommon.h" +#include "llvm/DebugInfo/MSF/StreamArray.h" +#include "llvm/DebugInfo/MSF/StreamInterface.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MathExtras.h" + +#include + +namespace llvm { + +namespace msf { +class MappedBlockStream; +} + +namespace pdb { +class DbiStream; +class GlobalsStream; +class InfoStream; +class StringTable; +class PDBFileBuilder; +class PublicsStream; +class SymbolStream; +class TpiStream; + +class PDBFile : public msf::IMSFFile { + friend PDBFileBuilder; + +public: + PDBFile(std::unique_ptr PdbFileBuffer, + BumpPtrAllocator &Allocator); + ~PDBFile() override; + + uint32_t getFreeBlockMapBlock() const; + uint32_t getUnknown1() const; + + uint32_t getBlockSize() const override; + uint32_t getBlockCount() const override; + uint32_t getNumDirectoryBytes() const; + uint32_t getBlockMapIndex() const; + uint32_t getNumDirectoryBlocks() const; + uint64_t getBlockMapOffset() const; + + uint32_t getNumStreams() const override; + uint32_t getStreamByteSize(uint32_t StreamIndex) const override; + ArrayRef + getStreamBlockList(uint32_t StreamIndex) const override; + uint32_t getFileSize() const; + + Expected> getBlockData(uint32_t BlockIndex, + uint32_t NumBytes) const override; + Error setBlockData(uint32_t BlockIndex, uint32_t Offset, + ArrayRef Data) const override; + + ArrayRef getFpmPages() const { return FpmPages; } + + ArrayRef getStreamSizes() const { + return ContainerLayout.StreamSizes; + } + ArrayRef> getStreamMap() const { + return ContainerLayout.StreamMap; + } + + const msf::MSFLayout &getMsfLayout() const { return ContainerLayout; } + const msf::ReadableStream &getMsfBuffer() const { return *Buffer; } + + ArrayRef getDirectoryBlockArray() const; + + Error parseFileHeaders(); + Error parseStreamData(); + + Expected getPDBInfoStream(); + Expected getPDBDbiStream(); + Expected getPDBGlobalsStream(); + Expected getPDBTpiStream(); + Expected getPDBIpiStream(); + Expected getPDBPublicsStream(); + Expected getPDBSymbolStream(); + Expected getStringTable(); + + BumpPtrAllocator &getAllocator() { return Allocator; } + + bool hasPDBDbiStream() const; + bool hasPDBGlobalsStream(); + bool hasPDBInfoStream(); + bool hasPDBIpiStream() const; + bool hasPDBPublicsStream(); + bool hasPDBSymbolStream(); + bool hasPDBTpiStream() const; + bool hasStringTable(); + +private: + Expected> + safelyCreateIndexedStream(const msf::MSFLayout &Layout, + const msf::ReadableStream &MsfData, + uint32_t StreamIndex) const; + + BumpPtrAllocator &Allocator; + + std::unique_ptr Buffer; + + std::vector FpmPages; + msf::MSFLayout ContainerLayout; + + std::unique_ptr Globals; + std::unique_ptr Info; + std::unique_ptr Dbi; + std::unique_ptr Tpi; + std::unique_ptr Ipi; + std::unique_ptr Publics; + std::unique_ptr Symbols; + std::unique_ptr DirectoryStream; + std::unique_ptr StringTableStream; + std::unique_ptr Strings; +}; +} +} + +#endif Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/PDBFileBuilder.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/PDBFileBuilder.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/PDBFileBuilder.h @@ -0,0 +1,71 @@ +//===- PDBFileBuilder.h - PDB File 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_PDBFILEBUILDER_H +#define LLVM_DEBUGINFO_PDB_RAW_PDBFILEBUILDER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/Optional.h" +#include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/StringTableBuilder.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" + +#include +#include + +namespace llvm { +namespace msf { +class MSFBuilder; +} +namespace pdb { +class DbiStreamBuilder; +class InfoStreamBuilder; +class TpiStreamBuilder; + +class PDBFileBuilder { +public: + explicit PDBFileBuilder(BumpPtrAllocator &Allocator); + PDBFileBuilder(const PDBFileBuilder &) = delete; + PDBFileBuilder &operator=(const PDBFileBuilder &) = delete; + + Error initialize(uint32_t BlockSize); + + msf::MSFBuilder &getMsfBuilder(); + InfoStreamBuilder &getInfoBuilder(); + DbiStreamBuilder &getDbiBuilder(); + TpiStreamBuilder &getTpiBuilder(); + TpiStreamBuilder &getIpiBuilder(); + StringTableBuilder &getStringTableBuilder(); + + Error commit(StringRef Filename); + +private: + Error addNamedStream(StringRef Name, uint32_t Size); + Expected finalizeMsfLayout(); + + BumpPtrAllocator &Allocator; + + std::unique_ptr Msf; + std::unique_ptr Info; + std::unique_ptr Dbi; + std::unique_ptr Tpi; + std::unique_ptr Ipi; + + StringTableBuilder Strings; + NamedStreamMap NamedStreams; +}; +} +} + +#endif Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/PublicsStream.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/PublicsStream.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/PublicsStream.h @@ -0,0 +1,74 @@ +//===- PublicsStream.h - PDB Public Symbol Stream -------- ------*- 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_PUBLICSSTREAM_H +#define LLVM_DEBUGINFO_PDB_RAW_PUBLICSSTREAM_H + +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/MSF/StreamArray.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" + +#include "llvm/Support/Error.h" + +namespace llvm { +namespace pdb { +class DbiStream; +struct GSIHashHeader; +class PDBFile; + +class PublicsStream { + struct HeaderInfo; + +public: + PublicsStream(PDBFile &File, std::unique_ptr Stream); + ~PublicsStream(); + Error reload(); + + uint32_t getSymHash() const; + uint32_t getAddrMap() const; + uint32_t getNumBuckets() const { return NumBuckets; } + iterator_range + getSymbols(bool *HadError) const; + msf::FixedStreamArray getHashBuckets() const { + return HashBuckets; + } + msf::FixedStreamArray getAddressMap() const { + return AddressMap; + } + msf::FixedStreamArray getThunkMap() const { + return ThunkMap; + } + msf::FixedStreamArray getSectionOffsets() const { + return SectionOffsets; + } + + Error commit(); + +private: + PDBFile &Pdb; + + std::unique_ptr Stream; + uint32_t NumBuckets = 0; + ArrayRef Bitmap; + msf::FixedStreamArray HashRecords; + msf::FixedStreamArray HashBuckets; + msf::FixedStreamArray AddressMap; + msf::FixedStreamArray ThunkMap; + msf::FixedStreamArray SectionOffsets; + + const HeaderInfo *Header; + const GSIHashHeader *HashHdr; +}; +} +} + +#endif Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/RawConstants.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/RawConstants.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/RawConstants.h @@ -0,0 +1,98 @@ +//===- RawConstants.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_RAW_PDBRAWCONSTANTS_H +#define LLVM_DEBUGINFO_PDB_RAW_PDBRAWCONSTANTS_H + +#include "llvm/DebugInfo/CodeView/CodeView.h" + +#include + +namespace llvm { +namespace pdb { + +const uint16_t kInvalidStreamIndex = 0xFFFF; + +enum PdbRaw_ImplVer : uint32_t { + PdbImplVC2 = 19941610, + PdbImplVC4 = 19950623, + PdbImplVC41 = 19950814, + PdbImplVC50 = 19960307, + PdbImplVC98 = 19970604, + PdbImplVC70Dep = 19990604, // deprecated + PdbImplVC70 = 20000404, + PdbImplVC80 = 20030901, + PdbImplVC110 = 20091201, + PdbImplVC140 = 20140508, +}; + +enum PdbRaw_DbiVer : uint32_t { + PdbDbiVC41 = 930803, + PdbDbiV50 = 19960307, + PdbDbiV60 = 19970606, + PdbDbiV70 = 19990903, + PdbDbiV110 = 20091201 +}; + +enum PdbRaw_TpiVer : uint32_t { + PdbTpiV40 = 19950410, + PdbTpiV41 = 19951122, + PdbTpiV50 = 19961031, + PdbTpiV70 = 19990903, + PdbTpiV80 = 20040203, +}; + +enum PdbRaw_DbiSecContribVer : uint32_t { + DbiSecContribVer60 = 0xeffe0000 + 19970605, + DbiSecContribV2 = 0xeffe0000 + 20140516 +}; + +enum SpecialStream : uint32_t { + // Stream 0 contains the copy of previous version of the MSF directory. + // We are not currently using it, but technically if we find the main + // MSF is corrupted, we could fallback to it. + OldMSFDirectory = 0, + + StreamPDB = 1, + StreamTPI = 2, + StreamDBI = 3, + StreamIPI = 4, + + kSpecialStreamCount +}; + +enum class DbgHeaderType : uint16_t { + FPO, + Exception, + Fixup, + OmapToSrc, + OmapFromSrc, + SectionHdr, + TokenRidMap, + Xdata, + Pdata, + NewFPO, + SectionHdrOrig, + Max +}; + +enum class OMFSegDescFlags : uint16_t { + Read = 1 << 0, // Segment is readable. + Write = 1 << 1, // Segment is writable. + Execute = 1 << 2, // Segment is executable. + AddressIs32Bit = 1 << 3, // Descriptor describes a 32-bit linear address. + IsSelector = 1 << 8, // Frame represents a selector. + IsAbsoluteAddress = 1 << 9, // Frame represents an absolute address. + IsGroup = 1 << 10 // If set, descriptor represents a group. +}; + +} // end namespace pdb +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_PDB_RAW_PDBRAWCONSTANTS_H Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/RawError.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/RawError.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/RawError.h @@ -0,0 +1,52 @@ +//===- RawError.h - Error extensions for raw PDB implementation -*- 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_RAWERROR_H +#define LLVM_DEBUGINFO_PDB_RAW_RAWERROR_H + +#include "llvm/Support/Error.h" + +#include + +namespace llvm { +namespace pdb { +enum class raw_error_code { + unspecified = 1, + feature_unsupported, + invalid_format, + corrupt_file, + insufficient_buffer, + no_stream, + index_out_of_bounds, + invalid_block_address, + duplicate_entry, + no_entry, + not_writable, + invalid_tpi_hash, +}; + +/// Base class for errors originating when parsing raw PDB files +class RawError : public ErrorInfo { +public: + static char ID; + RawError(raw_error_code C); + RawError(const std::string &Context); + RawError(raw_error_code C, const std::string &Context); + + void log(raw_ostream &OS) const override; + const std::string &getErrorMessage() const; + std::error_code convertToErrorCode() const override; + +private: + std::string ErrMsg; + raw_error_code Code; +}; +} +} +#endif Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/RawTypes.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/RawTypes.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/RawTypes.h @@ -0,0 +1,317 @@ +//===- RawTypes.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_RAW_RAWTYPES_H +#define LLVM_DEBUGINFO_PDB_RAW_RAWTYPES_H + +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/Support/Endian.h" + +namespace llvm { +namespace pdb { +// This struct is defined as "SO" in langapi/include/pdb.h. +struct SectionOffset { + support::ulittle32_t Off; + support::ulittle16_t Isect; + char Padding[2]; +}; + +// This is HRFile. +struct PSHashRecord { + support::ulittle32_t Off; // Offset in the symbol record stream + support::ulittle32_t CRef; +}; + +// This struct is defined as `SC` in include/dbicommon.h +struct SectionContrib { + support::ulittle16_t ISect; + char Padding[2]; + support::little32_t Off; + support::little32_t Size; + support::ulittle32_t Characteristics; + support::ulittle16_t Imod; + char Padding2[2]; + support::ulittle32_t DataCrc; + support::ulittle32_t RelocCrc; +}; + +// This struct is defined as `SC2` in include/dbicommon.h +struct SectionContrib2 { + // To guarantee SectionContrib2 is standard layout, we cannot use inheritance. + SectionContrib Base; + support::ulittle32_t ISectCoff; +}; + +// This corresponds to the `OMFSegMap` structure. +struct SecMapHeader { + support::ulittle16_t SecCount; // Number of segment descriptors in table + support::ulittle16_t SecCountLog; // Number of logical segment descriptors +}; + +// This corresponds to the `OMFSegMapDesc` structure. The definition is not +// present in the reference implementation, but the layout is derived from +// code that accesses the fields. +struct SecMapEntry { + support::ulittle16_t Flags; // Descriptor flags. See OMFSegDescFlags + support::ulittle16_t Ovl; // Logical overlay number. + support::ulittle16_t Group; // Group index into descriptor array. + support::ulittle16_t Frame; + support::ulittle16_t SecName; // Byte index of the segment or group name + // in the sstSegName table, or 0xFFFF. + support::ulittle16_t ClassName; // Byte index of the class name in the + // sstSegName table, or 0xFFFF. + support::ulittle32_t Offset; // Byte offset of the logical segment + // within the specified physical segment. + // If group is set in flags, offset is the + // offset of the group. + support::ulittle32_t SecByteLength; // Byte count of the segment or group. +}; + +// Used for serialized hash table in TPI stream. +// In the reference, it is an array of TI and cbOff pair. +struct TypeIndexOffset { + codeview::TypeIndex Type; + support::ulittle32_t Offset; +}; + +/// Some of the values are stored in bitfields. Since this needs to be portable +/// across compilers and architectures (big / little endian in particular) we +/// can't use the actual structures below, but must instead do the shifting +/// and masking ourselves. The struct definitions are provided for reference. +struct DbiFlags { + /// uint16_t IncrementalLinking : 1; // True if linked incrementally + /// uint16_t IsStripped : 1; // True if private symbols were + /// stripped. + /// uint16_t HasCTypes : 1; // True if linked with /debug:ctypes. + /// uint16_t Reserved : 13; + static const uint16_t FlagIncrementalMask = 0x0001; + static const uint16_t FlagStrippedMask = 0x0002; + static const uint16_t FlagHasCTypesMask = 0x0004; +}; + +struct DbiBuildNo { + /// uint16_t MinorVersion : 8; + /// uint16_t MajorVersion : 7; + /// uint16_t NewVersionFormat : 1; + static const uint16_t BuildMinorMask = 0x00FF; + static const uint16_t BuildMinorShift = 0; + + static const uint16_t BuildMajorMask = 0x7F00; + static const uint16_t BuildMajorShift = 8; +}; + +/// The fixed size header that appears at the beginning of the DBI Stream. +struct DbiStreamHeader { + support::little32_t VersionSignature; + support::ulittle32_t VersionHeader; + + /// How "old" is this DBI Stream. Should match the age of the PDB InfoStream. + support::ulittle32_t Age; + + /// Global symbol stream # + support::ulittle16_t GlobalSymbolStreamIndex; + + /// See DbiBuildNo structure. + support::ulittle16_t BuildNumber; + + /// Public symbols stream # + support::ulittle16_t PublicSymbolStreamIndex; + + /// version of mspdbNNN.dll + support::ulittle16_t PdbDllVersion; + + /// Symbol records stream # + support::ulittle16_t SymRecordStreamIndex; + + /// rbld number of mspdbNNN.dll + support::ulittle16_t PdbDllRbld; + + /// Size of module info stream + support::little32_t ModiSubstreamSize; + + /// Size of sec. contrib stream + support::little32_t SecContrSubstreamSize; + + /// Size of sec. map substream + support::little32_t SectionMapSize; + + /// Size of file info substream + support::little32_t FileInfoSize; + + /// Size of type server map + support::little32_t TypeServerSize; + + /// Index of MFC Type Server + support::ulittle32_t MFCTypeServerIndex; + + /// Size of DbgHeader info + support::little32_t OptionalDbgHdrSize; + + /// Size of EC stream (what is EC?) + support::little32_t ECSubstreamSize; + + /// See DbiFlags enum. + support::ulittle16_t Flags; + + /// See PDB_MachineType enum. + support::ulittle16_t MachineType; + + /// Pad to 64 bytes + support::ulittle32_t Reserved; +}; +static_assert(sizeof(DbiStreamHeader) == 64, "Invalid DbiStreamHeader size!"); + +struct SectionContribEntry { + support::ulittle16_t Section; + char Padding1[2]; + support::little32_t Offset; + support::little32_t Size; + support::ulittle32_t Characteristics; + support::ulittle16_t ModuleIndex; + char Padding2[2]; + support::ulittle32_t DataCrc; + support::ulittle32_t RelocCrc; +}; + +/// The header preceeding the File Info Substream of the DBI stream. +struct FileInfoSubstreamHeader { + /// Total # of modules, should match number of records in the ModuleInfo + /// substream. + support::ulittle16_t NumModules; + + /// Total # of source files. This value is not accurate because PDB actually + /// supports more than 64k source files, so we ignore it and compute the value + /// from other stream fields. + support::ulittle16_t NumSourceFiles; + + /// Following this header the File Info Substream is laid out as follows: + /// ulittle16_t ModIndices[NumModules]; + /// ulittle16_t ModFileCounts[NumModules]; + /// ulittle32_t FileNameOffsets[NumSourceFiles]; + /// char Names[][NumSourceFiles]; + /// with the caveat that `NumSourceFiles` cannot be trusted, so + /// it is computed by summing the `ModFileCounts` array. +}; + +struct ModInfoFlags { + /// uint16_t fWritten : 1; // True if ModInfo is dirty + /// uint16_t fECEnabled : 1; // Is EC symbolic info present? (What is EC?) + /// uint16_t unused : 6; // Reserved + /// uint16_t iTSM : 8; // Type Server Index for this module + static const uint16_t HasECFlagMask = 0x2; + + static const uint16_t TypeServerIndexMask = 0xFF00; + static const uint16_t TypeServerIndexShift = 8; +}; + +/// The header preceeding each entry in the Module Info substream of the DBI +/// stream. +struct ModuleInfoHeader { + /// Currently opened module. This field is a pointer in the reference + /// implementation, but that won't work on 64-bit systems, and anyway it + /// doesn't make sense to read a pointer from a file. For now it is unused, + /// so just ignore it. + support::ulittle32_t Mod; + + /// First section contribution of this module. + SectionContribEntry SC; + + /// See ModInfoFlags definition. + support::ulittle16_t Flags; + + /// Stream Number of module debug info + support::ulittle16_t ModDiStream; + + /// Size of local symbol debug info in above stream + support::ulittle32_t SymBytes; + + /// Size of line number debug info in above stream + support::ulittle32_t LineBytes; + + /// Size of C13 line number info in above stream + support::ulittle32_t C13Bytes; + + /// Number of files contributing to this module + support::ulittle16_t NumFiles; + + /// Padding so the next field is 4-byte aligned. + char Padding1[2]; + + /// Array of [0..NumFiles) DBI name buffer offsets. This field is a pointer + /// in the reference implementation, but as with `Mod`, we ignore it for now + /// since it is unused. + support::ulittle32_t FileNameOffs; + + /// Name Index for src file name + support::ulittle32_t SrcFileNameNI; + + /// Name Index for path to compiler PDB + support::ulittle32_t PdbFilePathNI; + + /// Following this header are two zero terminated strings. + /// char ModuleName[]; + /// char ObjFileName[]; +}; + +/// Defines a 128-bit unique identifier. This maps to a GUID on Windows, but +/// is abstracted here for the purposes of non-Windows platforms that don't have +/// the GUID structure defined. +struct PDB_UniqueId { + uint8_t 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; + support::ulittle32_t Signature; + support::ulittle32_t Age; + PDB_UniqueId Guid; +}; + +/// The header preceeding the /names stream. +struct StringTableHeader { + support::ulittle32_t Signature; + support::ulittle32_t HashVersion; + support::ulittle32_t ByteSize; +}; + +const uint32_t StringTableSignature = 0xEFFEEFFE; + +} // namespace pdb +} // namespace llvm + +#endif Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/StringTable.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/StringTable.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/StringTable.h @@ -0,0 +1,54 @@ +//===- StringTable.h - PDB String Table -------------------------*- 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_STRINGTABLE_H +#define LLVM_DEBUGINFO_PDB_RAW_STRINGTABLE_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/MSF/StreamArray.h" +#include "llvm/DebugInfo/MSF/StreamRef.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include +#include + +namespace llvm { +namespace msf { +class StreamReader; +} +namespace pdb { + +class StringTable { +public: + StringTable(); + + Error load(msf::StreamReader &Stream); + + uint32_t getNameCount() const { return NameCount; } + uint32_t getHashVersion() const { return HashVersion; } + uint32_t getSignature() const { return Signature; } + + StringRef getStringForID(uint32_t ID) const; + uint32_t getIDForString(StringRef Str) const; + + msf::FixedStreamArray name_ids() const; + +private: + msf::ReadableStreamRef NamesBuffer; + msf::FixedStreamArray IDs; + uint32_t Signature; + uint32_t HashVersion; + uint32_t NameCount; +}; + +} // end namespace pdb +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_PDB_RAW_STRINGTABLE_H Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/StringTableBuilder.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/StringTableBuilder.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/StringTableBuilder.h @@ -0,0 +1,45 @@ +//===- StringTableBuilder.h - PDB String Table Builder ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file creates the "/names" stream. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_PDB_RAW_STRINGTABLEBUILDER_H +#define LLVM_DEBUGINFO_PDB_RAW_STRINGTABLEBUILDER_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include + +namespace llvm { +namespace msf { +class StreamWriter; +} +namespace pdb { + +class StringTableBuilder { +public: + // If string S does not exist in the string table, insert it. + // Returns the ID for S. + uint32_t insert(StringRef S); + + uint32_t finalize(); + Error commit(msf::StreamWriter &Writer) const; + +private: + DenseMap Strings; + uint32_t StringSize = 1; +}; + +} // end namespace pdb +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_PDB_RAW_STRINGTABLEBUILDER_H Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/SymbolStream.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/SymbolStream.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/SymbolStream.h @@ -0,0 +1,42 @@ +//===- SymbolStream.cpp - PDB Symbol Stream Access --------------*- 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_PDBSYMBOLSTREAM_H +#define LLVM_DEBUGINFO_PDB_RAW_PDBSYMBOLSTREAM_H + +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" + +#include "llvm/Support/Error.h" + +namespace llvm { +namespace msf { +class MappedBlockStream; +} +namespace pdb { +class PDBFile; + +class SymbolStream { +public: + SymbolStream(std::unique_ptr Stream); + ~SymbolStream(); + Error reload(); + + iterator_range + getSymbols(bool *HadError) const; + + Error commit(); + +private: + codeview::CVSymbolArray SymbolRecords; + std::unique_ptr Stream; +}; +} +} + +#endif Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/TpiHashing.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/TpiHashing.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/TpiHashing.h @@ -0,0 +1,95 @@ +//===- 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/ADT/StringExtras.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/Native/RawError.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include +#include + +namespace llvm { +namespace pdb { + +class TpiHashUpdater : public codeview::TypeVisitorCallbacks { +public: + TpiHashUpdater() = default; + +#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; +}; + +} // end namespace pdb +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_PDB_TPIHASHING_H Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/TpiStream.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/TpiStream.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/TpiStream.h @@ -0,0 +1,75 @@ +//===- TpiStream.cpp - PDB Type Info (TPI) Stream 2 Access ------*- 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_PDBTPISTREAM_H +#define LLVM_DEBUGINFO_PDB_RAW_PDBTPISTREAM_H + +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/MSF/StreamArray.h" +#include "llvm/DebugInfo/PDB/Native/HashTable.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" +#include "llvm/Support/raw_ostream.h" + +#include "llvm/Support/Error.h" + +namespace llvm { +namespace msf { +class MappedBlockStream; +} +namespace pdb { +class PDBFile; + +class TpiStream { + friend class TpiStreamBuilder; + +public: + TpiStream(const PDBFile &File, + std::unique_ptr Stream); + ~TpiStream(); + Error reload(); + + PdbRaw_TpiVer getTpiVersion() const; + + uint32_t TypeIndexBegin() const; + uint32_t TypeIndexEnd() const; + uint32_t NumTypeRecords() const; + uint16_t getTypeHashStreamIndex() const; + uint16_t getTypeHashStreamAuxIndex() const; + + uint32_t getHashKeySize() const; + uint32_t NumHashBuckets() const; + msf::FixedStreamArray getHashValues() const; + msf::FixedStreamArray getTypeIndexOffsets() const; + HashTable &getHashAdjusters(); + + iterator_range types(bool *HadError) const; + + Error commit(); + +private: + Error verifyHashValues(); + + const PDBFile &Pdb; + std::unique_ptr Stream; + + codeview::CVTypeArray TypeRecords; + + std::unique_ptr HashStream; + msf::FixedStreamArray HashValues; + msf::FixedStreamArray TypeIndexOffsets; + HashTable HashAdjusters; + + const TpiStreamHeader *Header; +}; +} +} + +#endif Index: llvm/trunk/include/llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h @@ -0,0 +1,82 @@ +//===- 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/Native/RawConstants.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Error.h" + +#include + +namespace llvm { +namespace codeview { +class TypeRecord; +} +namespace msf { +class ByteStream; +class MSFBuilder; +struct MSFLayout; +class ReadableStreamRef; +class WritableStream; + +template <> struct SequencedItemTraits { + static size_t length(const codeview::CVType &Item) { return Item.length(); } + static ArrayRef bytes(const codeview::CVType &Item) { + return Item.data(); + } +}; +} +namespace pdb { +class PDBFile; +class TpiStream; +struct TpiStreamHeader; + +class TpiStreamBuilder { +public: + explicit TpiStreamBuilder(msf::MSFBuilder &Msf, uint32_t StreamIdx); + ~TpiStreamBuilder(); + + TpiStreamBuilder(const TpiStreamBuilder &) = delete; + TpiStreamBuilder &operator=(const TpiStreamBuilder &) = delete; + + void setVersionHeader(PdbRaw_TpiVer Version); + void addTypeRecord(const codeview::CVType &Record); + + Error finalizeMsfLayout(); + + Error commit(const msf::MSFLayout &Layout, const msf::WritableStream &Buffer); + + 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; + uint32_t Idx; +}; +} +} + +#endif Index: llvm/trunk/include/llvm/DebugInfo/PDB/PDBTypes.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/PDBTypes.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/PDBTypes.h @@ -12,7 +12,7 @@ #include "llvm/Config/llvm-config.h" #include "llvm/DebugInfo/CodeView/CodeView.h" -#include "llvm/DebugInfo/PDB/Raw/RawTypes.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" #include #include #include Index: llvm/trunk/lib/DebugInfo/PDB/CMakeLists.txt =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/CMakeLists.txt +++ llvm/trunk/lib/DebugInfo/PDB/CMakeLists.txt @@ -27,32 +27,32 @@ set(LIBPDB_ADDITIONAL_HEADER_DIRS "${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/PDB/DIA") endif() -add_pdb_impl_folder(Raw - Raw/DbiStream.cpp - Raw/DbiStreamBuilder.cpp - Raw/EnumTables.cpp - Raw/GlobalsStream.cpp - Raw/GSI.cpp - Raw/Hash.cpp - Raw/HashTable.cpp - Raw/InfoStream.cpp - Raw/InfoStreamBuilder.cpp - Raw/ModInfo.cpp - Raw/ModStream.cpp - Raw/NamedStreamMap.cpp - Raw/PDBFile.cpp - Raw/PDBFileBuilder.cpp - Raw/PublicsStream.cpp - Raw/RawError.cpp - Raw/RawSession.cpp - Raw/StringTable.cpp - Raw/StringTableBuilder.cpp - Raw/SymbolStream.cpp - Raw/TpiHashing.cpp - Raw/TpiStream.cpp - Raw/TpiStreamBuilder.cpp) +add_pdb_impl_folder(Native + Native/DbiStream.cpp + Native/DbiStreamBuilder.cpp + Native/EnumTables.cpp + Native/GlobalsStream.cpp + Native/GSI.cpp + Native/Hash.cpp + Native/HashTable.cpp + Native/InfoStream.cpp + Native/InfoStreamBuilder.cpp + Native/ModInfo.cpp + Native/ModStream.cpp + Native/NamedStreamMap.cpp + Native/NativeSession.cpp + Native/PDBFile.cpp + Native/PDBFileBuilder.cpp + Native/PublicsStream.cpp + Native/RawError.cpp + Native/StringTable.cpp + Native/StringTableBuilder.cpp + Native/SymbolStream.cpp + Native/TpiHashing.cpp + Native/TpiStream.cpp + Native/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/Native") list(APPEND LIBPDB_ADDITIONAL_HEADER_DIRS "${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/PDB") add_llvm_library(LLVMDebugInfoPDB Index: llvm/trunk/lib/DebugInfo/PDB/Native/DbiStream.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/DbiStream.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/DbiStream.cpp @@ -0,0 +1,424 @@ +//===- DbiStream.cpp - PDB Dbi Stream (Stream 3) Access -------------------===// +// +// 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/Native/DbiStream.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/MSF/StreamArray.h" +#include "llvm/DebugInfo/MSF/StreamReader.h" +#include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/ModInfo.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Error.h" +#include +#include +#include + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; +using namespace llvm::support; + +template +static Error loadSectionContribs(FixedStreamArray &Output, + StreamReader &Reader) { + if (Reader.bytesRemaining() % sizeof(ContribType) != 0) + return make_error( + raw_error_code::corrupt_file, + "Invalid number of bytes of section contributions"); + + uint32_t Count = Reader.bytesRemaining() / sizeof(ContribType); + if (auto EC = Reader.readArray(Output, Count)) + return EC; + return Error::success(); +} + +DbiStream::DbiStream(PDBFile &File, std::unique_ptr Stream) + : Pdb(File), Stream(std::move(Stream)), Header(nullptr) {} + +DbiStream::~DbiStream() = default; + +Error DbiStream::reload() { + StreamReader Reader(*Stream); + + if (Stream->getLength() < sizeof(DbiStreamHeader)) + return make_error(raw_error_code::corrupt_file, + "DBI Stream does not contain a header."); + if (auto EC = Reader.readObject(Header)) + return make_error(raw_error_code::corrupt_file, + "DBI Stream does not contain a header."); + + if (Header->VersionSignature != -1) + return make_error(raw_error_code::corrupt_file, + "Invalid DBI version signature."); + + // Require at least version 7, which should be present in all PDBs + // produced in the last decade and allows us to avoid having to + // special case all kinds of complicated arcane formats. + if (Header->VersionHeader < PdbDbiV70) + return make_error(raw_error_code::feature_unsupported, + "Unsupported DBI version."); + + auto IS = Pdb.getPDBInfoStream(); + if (!IS) + return IS.takeError(); + + if (Header->Age != IS->getAge()) + return make_error(raw_error_code::corrupt_file, + "DBI Age does not match PDB Age."); + + if (Stream->getLength() != + sizeof(DbiStreamHeader) + Header->ModiSubstreamSize + + Header->SecContrSubstreamSize + Header->SectionMapSize + + Header->FileInfoSize + Header->TypeServerSize + + Header->OptionalDbgHdrSize + Header->ECSubstreamSize) + return make_error(raw_error_code::corrupt_file, + "DBI Length does not equal sum of substreams."); + + // Only certain substreams are guaranteed to be aligned. Validate + // them here. + if (Header->ModiSubstreamSize % sizeof(uint32_t) != 0) + return make_error(raw_error_code::corrupt_file, + "DBI MODI substream not aligned."); + if (Header->SecContrSubstreamSize % sizeof(uint32_t) != 0) + return make_error( + raw_error_code::corrupt_file, + "DBI section contribution substream not aligned."); + if (Header->SectionMapSize % sizeof(uint32_t) != 0) + return make_error(raw_error_code::corrupt_file, + "DBI section map substream not aligned."); + if (Header->FileInfoSize % sizeof(uint32_t) != 0) + return make_error(raw_error_code::corrupt_file, + "DBI file info substream not aligned."); + if (Header->TypeServerSize % sizeof(uint32_t) != 0) + return make_error(raw_error_code::corrupt_file, + "DBI type server substream not aligned."); + + if (auto EC = + Reader.readStreamRef(ModInfoSubstream, Header->ModiSubstreamSize)) + return EC; + if (auto EC = initializeModInfoArray()) + return EC; + + if (auto EC = Reader.readStreamRef(SecContrSubstream, + Header->SecContrSubstreamSize)) + return EC; + if (auto EC = Reader.readStreamRef(SecMapSubstream, Header->SectionMapSize)) + return EC; + if (auto EC = Reader.readStreamRef(FileInfoSubstream, Header->FileInfoSize)) + return EC; + if (auto EC = + Reader.readStreamRef(TypeServerMapSubstream, Header->TypeServerSize)) + return EC; + if (auto EC = Reader.readStreamRef(ECSubstream, Header->ECSubstreamSize)) + return EC; + if (auto EC = Reader.readArray( + DbgStreams, Header->OptionalDbgHdrSize / sizeof(ulittle16_t))) + return EC; + + if (auto EC = initializeSectionContributionData()) + return EC; + if (auto EC = initializeSectionHeadersData()) + return EC; + if (auto EC = initializeSectionMapData()) + return EC; + if (auto EC = initializeFileInfo()) + return EC; + if (auto EC = initializeFpoRecords()) + return EC; + + if (Reader.bytesRemaining() > 0) + return make_error(raw_error_code::corrupt_file, + "Found unexpected bytes in DBI Stream."); + + if (ECSubstream.getLength() > 0) { + StreamReader ECReader(ECSubstream); + if (auto EC = ECNames.load(ECReader)) + return EC; + } + + return Error::success(); +} + +PdbRaw_DbiVer DbiStream::getDbiVersion() const { + uint32_t Value = Header->VersionHeader; + return static_cast(Value); +} + +uint32_t DbiStream::getAge() const { return Header->Age; } + +uint16_t DbiStream::getPublicSymbolStreamIndex() const { + return Header->PublicSymbolStreamIndex; +} + +uint16_t DbiStream::getGlobalSymbolStreamIndex() const { + return Header->GlobalSymbolStreamIndex; +} + +uint16_t DbiStream::getFlags() const { return Header->Flags; } + +bool DbiStream::isIncrementallyLinked() const { + return (Header->Flags & DbiFlags::FlagIncrementalMask) != 0; +} + +bool DbiStream::hasCTypes() const { + return (Header->Flags & DbiFlags::FlagHasCTypesMask) != 0; +} + +bool DbiStream::isStripped() const { + return (Header->Flags & DbiFlags::FlagStrippedMask) != 0; +} + +uint16_t DbiStream::getBuildNumber() const { return Header->BuildNumber; } + +uint16_t DbiStream::getBuildMajorVersion() const { + return (Header->BuildNumber & DbiBuildNo::BuildMajorMask) >> + DbiBuildNo::BuildMajorShift; +} + +uint16_t DbiStream::getBuildMinorVersion() const { + return (Header->BuildNumber & DbiBuildNo::BuildMinorMask) >> + DbiBuildNo::BuildMinorShift; +} + +uint16_t DbiStream::getPdbDllRbld() const { return Header->PdbDllRbld; } + +uint32_t DbiStream::getPdbDllVersion() const { return Header->PdbDllVersion; } + +uint32_t DbiStream::getSymRecordStreamIndex() const { + return Header->SymRecordStreamIndex; +} + +PDB_Machine DbiStream::getMachineType() const { + uint16_t Machine = Header->MachineType; + return static_cast(Machine); +} + +msf::FixedStreamArray DbiStream::getSectionHeaders() { + return SectionHeaders; +} + +msf::FixedStreamArray DbiStream::getFpoRecords() { + return FpoRecords; +} + +ArrayRef DbiStream::modules() const { return ModuleInfos; } +msf::FixedStreamArray DbiStream::getSectionMap() const { + return SectionMap; +} + +void DbiStream::visitSectionContributions( + ISectionContribVisitor &Visitor) const { + if (SectionContribVersion == DbiSecContribVer60) { + for (auto &SC : SectionContribs) + Visitor.visit(SC); + } else if (SectionContribVersion == DbiSecContribV2) { + for (auto &SC : SectionContribs2) + Visitor.visit(SC); + } +} + +Error DbiStream::initializeSectionContributionData() { + if (SecContrSubstream.getLength() == 0) + return Error::success(); + + StreamReader SCReader(SecContrSubstream); + if (auto EC = SCReader.readEnum(SectionContribVersion)) + return EC; + + if (SectionContribVersion == DbiSecContribVer60) + return loadSectionContribs(SectionContribs, SCReader); + if (SectionContribVersion == DbiSecContribV2) + return loadSectionContribs(SectionContribs2, SCReader); + + return make_error(raw_error_code::feature_unsupported, + "Unsupported DBI Section Contribution version"); +} + +Error DbiStream::initializeModInfoArray() { + if (ModInfoSubstream.getLength() == 0) + return Error::success(); + + // Since each ModInfo in the stream is a variable length, we have to iterate + // them to know how many there actually are. + StreamReader Reader(ModInfoSubstream); + + VarStreamArray ModInfoArray; + if (auto EC = Reader.readArray(ModInfoArray, ModInfoSubstream.getLength())) + return EC; + for (auto &Info : ModInfoArray) { + ModuleInfos.emplace_back(Info); + } + + return Error::success(); +} + +// Initializes this->SectionHeaders. +Error DbiStream::initializeSectionHeadersData() { + if (DbgStreams.size() == 0) + return Error::success(); + + uint32_t StreamNum = getDebugStreamIndex(DbgHeaderType::SectionHdr); + if (StreamNum >= Pdb.getNumStreams()) + return make_error(raw_error_code::no_stream); + + auto SHS = MappedBlockStream::createIndexedStream( + Pdb.getMsfLayout(), Pdb.getMsfBuffer(), StreamNum); + + size_t StreamLen = SHS->getLength(); + if (StreamLen % sizeof(object::coff_section)) + return make_error(raw_error_code::corrupt_file, + "Corrupted section header stream."); + + size_t NumSections = StreamLen / sizeof(object::coff_section); + msf::StreamReader Reader(*SHS); + if (auto EC = Reader.readArray(SectionHeaders, NumSections)) + return make_error(raw_error_code::corrupt_file, + "Could not read a bitmap."); + + SectionHeaderStream = std::move(SHS); + return Error::success(); +} + +// Initializes this->Fpos. +Error DbiStream::initializeFpoRecords() { + if (DbgStreams.size() == 0) + return Error::success(); + + uint32_t StreamNum = getDebugStreamIndex(DbgHeaderType::NewFPO); + + // This means there is no FPO data. + if (StreamNum == kInvalidStreamIndex) + return Error::success(); + + if (StreamNum >= Pdb.getNumStreams()) + return make_error(raw_error_code::no_stream); + + auto FS = MappedBlockStream::createIndexedStream( + Pdb.getMsfLayout(), Pdb.getMsfBuffer(), StreamNum); + + size_t StreamLen = FS->getLength(); + if (StreamLen % sizeof(object::FpoData)) + return make_error(raw_error_code::corrupt_file, + "Corrupted New FPO stream."); + + size_t NumRecords = StreamLen / sizeof(object::FpoData); + msf::StreamReader Reader(*FS); + if (auto EC = Reader.readArray(FpoRecords, NumRecords)) + return make_error(raw_error_code::corrupt_file, + "Corrupted New FPO stream."); + FpoStream = std::move(FS); + return Error::success(); +} + +Error DbiStream::initializeSectionMapData() { + if (SecMapSubstream.getLength() == 0) + return Error::success(); + + StreamReader SMReader(SecMapSubstream); + const SecMapHeader *Header; + if (auto EC = SMReader.readObject(Header)) + return EC; + if (auto EC = SMReader.readArray(SectionMap, Header->SecCount)) + return EC; + return Error::success(); +} + +Error DbiStream::initializeFileInfo() { + if (FileInfoSubstream.getLength() == 0) + return Error::success(); + + const FileInfoSubstreamHeader *FH; + StreamReader FISR(FileInfoSubstream); + if (auto EC = FISR.readObject(FH)) + return EC; + + // The number of modules in the stream should be the same as reported by + // the FileInfoSubstreamHeader. + if (FH->NumModules != ModuleInfos.size()) + return make_error(raw_error_code::corrupt_file, + "FileInfo substream count doesn't match DBI."); + + FixedStreamArray ModIndexArray; + FixedStreamArray ModFileCountArray; + + // First is an array of `NumModules` module indices. This is not used for the + // same reason that `NumSourceFiles` is not used. It's an array of uint16's, + // but it's possible there are more than 64k source files, which would imply + // more than 64k modules (e.g. object files) as well. So we ignore this + // field. + if (auto EC = FISR.readArray(ModIndexArray, ModuleInfos.size())) + return EC; + if (auto EC = FISR.readArray(ModFileCountArray, ModuleInfos.size())) + return EC; + + // Compute the real number of source files. + uint32_t NumSourceFiles = 0; + for (auto Count : ModFileCountArray) + NumSourceFiles += Count; + + // This is the array that in the reference implementation corresponds to + // `ModInfo::FileLayout::FileNameOffs`, which is commented there as being a + // pointer. Due to the mentioned problems of pointers causing difficulty + // when reading from the file on 64-bit systems, we continue to ignore that + // field in `ModInfo`, and instead build a vector of StringRefs and stores + // them in `ModuleInfoEx`. The value written to and read from the file is + // not used anyway, it is only there as a way to store the offsets for the + // purposes of later accessing the names at runtime. + if (auto EC = FISR.readArray(FileNameOffsets, NumSourceFiles)) + return EC; + + if (auto EC = FISR.readStreamRef(NamesBuffer)) + return EC; + + // We go through each ModuleInfo, determine the number N of source files for + // that module, and then get the next N offsets from the Offsets array, using + // them to get the corresponding N names from the Names buffer and associating + // each one with the corresponding module. + uint32_t NextFileIndex = 0; + for (size_t I = 0; I < ModuleInfos.size(); ++I) { + uint32_t NumFiles = ModFileCountArray[I]; + ModuleInfos[I].SourceFiles.resize(NumFiles); + for (size_t J = 0; J < NumFiles; ++J, ++NextFileIndex) { + auto ThisName = getFileNameForIndex(NextFileIndex); + if (!ThisName) + return ThisName.takeError(); + ModuleInfos[I].SourceFiles[J] = *ThisName; + } + } + + return Error::success(); +} + +uint32_t DbiStream::getDebugStreamIndex(DbgHeaderType Type) const { + uint16_t T = static_cast(Type); + if (T >= DbgStreams.size()) + return kInvalidStreamIndex; + return DbgStreams[T]; +} + +Expected DbiStream::getFileNameForIndex(uint32_t Index) const { + StreamReader Names(NamesBuffer); + if (Index >= FileNameOffsets.size()) + return make_error(raw_error_code::index_out_of_bounds); + + uint32_t FileOffset = FileNameOffsets[Index]; + Names.setOffset(FileOffset); + StringRef Name; + if (auto EC = Names.readZeroString(Name)) + return std::move(EC); + return Name; +} Index: llvm/trunk/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp @@ -0,0 +1,412 @@ +//===- DbiStreamBuilder.cpp - PDB Dbi Stream Creation -----------*- C++ -*-===// +// +// 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/Native/DbiStreamBuilder.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/MSF/MSFBuilder.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/MSF/StreamWriter.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/COFF.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +namespace { +class ModiSubstreamBuilder {}; +} + +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), DbgStreams((int)DbgHeaderType::Max) {} + +void DbiStreamBuilder::setVersionHeader(PdbRaw_DbiVer V) { VerHeader = V; } + +void DbiStreamBuilder::setAge(uint32_t A) { Age = A; } + +void DbiStreamBuilder::setBuildNumber(uint16_t B) { BuildNumber = B; } + +void DbiStreamBuilder::setPdbDllVersion(uint16_t V) { PdbDllVersion = V; } + +void DbiStreamBuilder::setPdbDllRbld(uint16_t R) { PdbDllRbld = R; } + +void DbiStreamBuilder::setFlags(uint16_t F) { Flags = F; } + +void DbiStreamBuilder::setMachineType(PDB_Machine M) { MachineType = M; } + +void DbiStreamBuilder::setSectionContribs(ArrayRef Arr) { + SectionContribs = Arr; +} + +void DbiStreamBuilder::setSectionMap(ArrayRef SecMap) { + SectionMap = SecMap; +} + +Error DbiStreamBuilder::addDbgStream(pdb::DbgHeaderType Type, + ArrayRef Data) { + if (DbgStreams[(int)Type].StreamNumber) + return make_error(raw_error_code::duplicate_entry, + "The specified stream type already exists"); + auto ExpectedIndex = Msf.addStream(Data.size()); + if (!ExpectedIndex) + return ExpectedIndex.takeError(); + uint32_t Index = std::move(*ExpectedIndex); + DbgStreams[(int)Type].Data = Data; + DbgStreams[(int)Type].StreamNumber = Index; + return Error::success(); +} + +uint32_t DbiStreamBuilder::calculateSerializedLength() const { + // For now we only support serializing the header. + return sizeof(DbiStreamHeader) + calculateFileInfoSubstreamSize() + + calculateModiSubstreamSize() + calculateSectionContribsStreamSize() + + calculateSectionMapStreamSize() + calculateDbgStreamsSize(); +} + +Error DbiStreamBuilder::addModuleInfo(StringRef ObjFile, StringRef Module) { + auto Entry = llvm::make_unique(); + ModuleInfo *M = Entry.get(); + Entry->Mod = Module; + Entry->Obj = ObjFile; + auto Result = ModuleInfos.insert(std::make_pair(Module, std::move(Entry))); + if (!Result.second) + return make_error(raw_error_code::duplicate_entry, + "The specified module already exists"); + ModuleInfoList.push_back(M); + return Error::success(); +} + +Error DbiStreamBuilder::addModuleSourceFile(StringRef Module, StringRef File) { + auto ModIter = ModuleInfos.find(Module); + if (ModIter == ModuleInfos.end()) + return make_error(raw_error_code::no_entry, + "The specified module was not found"); + uint32_t Index = SourceFileNames.size(); + SourceFileNames.insert(std::make_pair(File, Index)); + auto &ModEntry = *ModIter; + ModEntry.second->SourceFiles.push_back(File); + return Error::success(); +} + +uint32_t DbiStreamBuilder::calculateModiSubstreamSize() const { + uint32_t Size = 0; + for (const auto &M : ModuleInfoList) { + Size += sizeof(ModuleInfoHeader); + Size += M->Mod.size() + 1; + Size += M->Obj.size() + 1; + } + return alignTo(Size, sizeof(uint32_t)); +} + +uint32_t DbiStreamBuilder::calculateSectionContribsStreamSize() const { + if (SectionContribs.empty()) + return 0; + return sizeof(enum PdbRaw_DbiSecContribVer) + + sizeof(SectionContribs[0]) * SectionContribs.size(); +} + +uint32_t DbiStreamBuilder::calculateSectionMapStreamSize() const { + if (SectionMap.empty()) + return 0; + return sizeof(SecMapHeader) + sizeof(SecMapEntry) * SectionMap.size(); +} + +uint32_t DbiStreamBuilder::calculateFileInfoSubstreamSize() const { + uint32_t Size = 0; + Size += sizeof(ulittle16_t); // NumModules + Size += sizeof(ulittle16_t); // NumSourceFiles + Size += ModuleInfoList.size() * sizeof(ulittle16_t); // ModIndices + Size += ModuleInfoList.size() * sizeof(ulittle16_t); // ModFileCounts + uint32_t NumFileInfos = 0; + for (const auto &M : ModuleInfoList) + NumFileInfos += M->SourceFiles.size(); + Size += NumFileInfos * sizeof(ulittle32_t); // FileNameOffsets + Size += calculateNamesBufferSize(); + return alignTo(Size, sizeof(uint32_t)); +} + +uint32_t DbiStreamBuilder::calculateNamesBufferSize() const { + uint32_t Size = 0; + for (const auto &F : SourceFileNames) { + Size += F.getKeyLength() + 1; // Names[I]; + } + return Size; +} + +uint32_t DbiStreamBuilder::calculateDbgStreamsSize() const { + return DbgStreams.size() * sizeof(uint16_t); +} + +Error DbiStreamBuilder::generateModiSubstream() { + uint32_t Size = calculateModiSubstreamSize(); + auto Data = Allocator.Allocate(Size); + + ModInfoBuffer = MutableByteStream(MutableArrayRef(Data, Size)); + + StreamWriter ModiWriter(ModInfoBuffer); + for (const auto &M : ModuleInfoList) { + ModuleInfoHeader Layout = {}; + Layout.ModDiStream = kInvalidStreamIndex; + Layout.NumFiles = M->SourceFiles.size(); + if (auto EC = ModiWriter.writeObject(Layout)) + return EC; + if (auto EC = ModiWriter.writeZeroString(M->Mod)) + return EC; + if (auto EC = ModiWriter.writeZeroString(M->Obj)) + return EC; + } + if (ModiWriter.bytesRemaining() > sizeof(uint32_t)) + return make_error(raw_error_code::invalid_format, + "Unexpected bytes in Modi Stream Data"); + return Error::success(); +} + +Error DbiStreamBuilder::generateFileInfoSubstream() { + uint32_t Size = calculateFileInfoSubstreamSize(); + uint32_t NameSize = calculateNamesBufferSize(); + auto Data = Allocator.Allocate(Size); + uint32_t NamesOffset = Size - NameSize; + + FileInfoBuffer = MutableByteStream(MutableArrayRef(Data, Size)); + + WritableStreamRef MetadataBuffer = + WritableStreamRef(FileInfoBuffer).keep_front(NamesOffset); + StreamWriter MetadataWriter(MetadataBuffer); + + uint16_t ModiCount = std::min(UINT16_MAX, ModuleInfos.size()); + uint16_t FileCount = std::min(UINT16_MAX, SourceFileNames.size()); + if (auto EC = MetadataWriter.writeInteger(ModiCount)) // NumModules + return EC; + if (auto EC = MetadataWriter.writeInteger(FileCount)) // NumSourceFiles + return EC; + for (uint16_t I = 0; I < ModiCount; ++I) { + if (auto EC = MetadataWriter.writeInteger(I)) // Mod Indices + return EC; + } + for (const auto MI : ModuleInfoList) { + FileCount = static_cast(MI->SourceFiles.size()); + if (auto EC = MetadataWriter.writeInteger(FileCount)) // Mod File Counts + return EC; + } + + // Before writing the FileNameOffsets array, write the NamesBuffer array. + // A side effect of this is that this will actually compute the various + // file name offsets, so we can then go back and write the FileNameOffsets + // array to the other substream. + NamesBuffer = WritableStreamRef(FileInfoBuffer).drop_front(NamesOffset); + StreamWriter NameBufferWriter(NamesBuffer); + for (auto &Name : SourceFileNames) { + Name.second = NameBufferWriter.getOffset(); + if (auto EC = NameBufferWriter.writeZeroString(Name.getKey())) + return EC; + } + + for (const auto MI : ModuleInfoList) { + for (StringRef Name : MI->SourceFiles) { + auto Result = SourceFileNames.find(Name); + if (Result == SourceFileNames.end()) + return make_error(raw_error_code::no_entry, + "The source file was not found."); + if (auto EC = MetadataWriter.writeInteger(Result->second)) + return EC; + } + } + + if (NameBufferWriter.bytesRemaining() > 0) + return make_error(raw_error_code::invalid_format, + "The names buffer contained unexpected data."); + + if (MetadataWriter.bytesRemaining() > sizeof(uint32_t)) + return make_error( + raw_error_code::invalid_format, + "The metadata buffer contained unexpected data."); + + return Error::success(); +} + +Error DbiStreamBuilder::finalize() { + if (Header) + return Error::success(); + + DbiStreamHeader *H = Allocator.Allocate(); + + if (auto EC = generateModiSubstream()) + return EC; + if (auto EC = generateFileInfoSubstream()) + return EC; + + H->VersionHeader = *VerHeader; + H->VersionSignature = -1; + H->Age = Age; + H->BuildNumber = BuildNumber; + H->Flags = Flags; + H->PdbDllRbld = PdbDllRbld; + H->PdbDllVersion = PdbDllVersion; + H->MachineType = static_cast(MachineType); + + H->ECSubstreamSize = 0; + H->FileInfoSize = FileInfoBuffer.getLength(); + H->ModiSubstreamSize = ModInfoBuffer.getLength(); + H->OptionalDbgHdrSize = DbgStreams.size() * sizeof(uint16_t); + H->SecContrSubstreamSize = calculateSectionContribsStreamSize(); + H->SectionMapSize = calculateSectionMapStreamSize(); + H->TypeServerSize = 0; + H->SymRecordStreamIndex = kInvalidStreamIndex; + H->PublicSymbolStreamIndex = kInvalidStreamIndex; + H->MFCTypeServerIndex = kInvalidStreamIndex; + H->GlobalSymbolStreamIndex = kInvalidStreamIndex; + + Header = H; + return Error::success(); +} + +Error DbiStreamBuilder::finalizeMsfLayout() { + uint32_t Length = calculateSerializedLength(); + if (auto EC = Msf.setStreamSize(StreamDBI, Length)) + return EC; + return Error::success(); +} + +static uint16_t toSecMapFlags(uint32_t Flags) { + uint16_t Ret = 0; + if (Flags & COFF::IMAGE_SCN_MEM_READ) + Ret |= static_cast(OMFSegDescFlags::Read); + if (Flags & COFF::IMAGE_SCN_MEM_WRITE) + Ret |= static_cast(OMFSegDescFlags::Write); + if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE) + Ret |= static_cast(OMFSegDescFlags::Execute); + if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE) + Ret |= static_cast(OMFSegDescFlags::Execute); + if (!(Flags & COFF::IMAGE_SCN_MEM_16BIT)) + Ret |= static_cast(OMFSegDescFlags::AddressIs32Bit); + + // This seems always 1. + Ret |= static_cast(OMFSegDescFlags::IsSelector); + + return Ret; +} + +// A utility function to create Section Contributions +// for a given input sections. +std::vector DbiStreamBuilder::createSectionContribs( + ArrayRef SecHdrs) { + std::vector Ret; + + // Create a SectionContrib for each input section. + for (auto &Sec : SecHdrs) { + Ret.emplace_back(); + auto &Entry = Ret.back(); + memset(&Entry, 0, sizeof(Entry)); + + Entry.Off = Sec.PointerToRawData; + Entry.Size = Sec.SizeOfRawData; + Entry.Characteristics = Sec.Characteristics; + } + return Ret; +} + +// A utility function to create a Section Map for a given list of COFF sections. +// +// A Section Map seem to be a copy of a COFF section list in other format. +// I don't know why a PDB file contains both a COFF section header and +// a Section Map, but it seems it must be present in a PDB. +std::vector DbiStreamBuilder::createSectionMap( + ArrayRef SecHdrs) { + std::vector Ret; + int Idx = 0; + + auto Add = [&]() -> SecMapEntry & { + Ret.emplace_back(); + auto &Entry = Ret.back(); + memset(&Entry, 0, sizeof(Entry)); + + Entry.Frame = Idx + 1; + + // We don't know the meaning of these fields yet. + Entry.SecName = UINT16_MAX; + Entry.ClassName = UINT16_MAX; + + return Entry; + }; + + for (auto &Hdr : SecHdrs) { + auto &Entry = Add(); + Entry.Flags = toSecMapFlags(Hdr.Characteristics); + Entry.SecByteLength = Hdr.VirtualSize; + ++Idx; + } + + // The last entry is for absolute symbols. + auto &Entry = Add(); + Entry.Flags = static_cast(OMFSegDescFlags::AddressIs32Bit) | + static_cast(OMFSegDescFlags::IsAbsoluteAddress); + Entry.SecByteLength = UINT32_MAX; + + return Ret; +} + +Error DbiStreamBuilder::commit(const msf::MSFLayout &Layout, + const msf::WritableStream &Buffer) { + if (auto EC = finalize()) + return EC; + + auto InfoS = + WritableMappedBlockStream::createIndexedStream(Layout, Buffer, StreamDBI); + + StreamWriter Writer(*InfoS); + if (auto EC = Writer.writeObject(*Header)) + return EC; + + if (auto EC = Writer.writeStreamRef(ModInfoBuffer)) + return EC; + + if (!SectionContribs.empty()) { + if (auto EC = Writer.writeEnum(DbiSecContribVer60)) + return EC; + if (auto EC = Writer.writeArray(SectionContribs)) + return EC; + } + + if (!SectionMap.empty()) { + ulittle16_t Size = static_cast(SectionMap.size()); + SecMapHeader SMHeader = {Size, Size}; + if (auto EC = Writer.writeObject(SMHeader)) + return EC; + if (auto EC = Writer.writeArray(SectionMap)) + return EC; + } + + if (auto EC = Writer.writeStreamRef(FileInfoBuffer)) + return EC; + + for (auto &Stream : DbgStreams) + if (auto EC = Writer.writeInteger(Stream.StreamNumber)) + return EC; + + for (auto &Stream : DbgStreams) { + if (Stream.StreamNumber == kInvalidStreamIndex) + continue; + auto WritableStream = WritableMappedBlockStream::createIndexedStream( + Layout, Buffer, Stream.StreamNumber); + StreamWriter DbgStreamWriter(*WritableStream); + if (auto EC = DbgStreamWriter.writeArray(Stream.Data)) + return EC; + } + + if (Writer.bytesRemaining() > 0) + return make_error(raw_error_code::invalid_format, + "Unexpected bytes found in DBI Stream"); + return Error::success(); +} Index: llvm/trunk/lib/DebugInfo/PDB/Native/EnumTables.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/EnumTables.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/EnumTables.cpp @@ -0,0 +1,38 @@ +//===- EnumTables.cpp - Enum to string conversion tables --------*- C++ -*-===// +// +// 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/Native/EnumTables.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" + +using namespace llvm; +using namespace llvm::pdb; + +#define PDB_ENUM_CLASS_ENT(enum_class, enum) \ + { #enum, std::underlying_type < enum_class > ::type(enum_class::enum) } + +#define PDB_ENUM_ENT(ns, enum) \ + { #enum, ns::enum } + +static const EnumEntry OMFSegMapDescFlagNames[] = { + PDB_ENUM_CLASS_ENT(OMFSegDescFlags, Read), + PDB_ENUM_CLASS_ENT(OMFSegDescFlags, Write), + PDB_ENUM_CLASS_ENT(OMFSegDescFlags, Execute), + PDB_ENUM_CLASS_ENT(OMFSegDescFlags, AddressIs32Bit), + PDB_ENUM_CLASS_ENT(OMFSegDescFlags, IsSelector), + PDB_ENUM_CLASS_ENT(OMFSegDescFlags, IsAbsoluteAddress), + PDB_ENUM_CLASS_ENT(OMFSegDescFlags, IsGroup), +}; + +namespace llvm { +namespace pdb { +ArrayRef> getOMFSegMapDescFlagNames() { + return makeArrayRef(OMFSegMapDescFlagNames); +} +} +} \ No newline at end of file Index: llvm/trunk/lib/DebugInfo/PDB/Native/GSI.h =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/GSI.h +++ llvm/trunk/lib/DebugInfo/PDB/Native/GSI.h @@ -0,0 +1,70 @@ +//===- GSI.h - Common Declarations for GlobalsStream and PublicsStream ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// The data structures defined in this file are based on the reference +// implementation which is available at +// https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h +// +// When you are reading the reference source code, you'd find the +// information below useful. +// +// - ppdb1->m_fMinimalDbgInfo seems to be always true. +// - SMALLBUCKETS macro is defined. +// +// The reference doesn't compile, so I learned just by reading code. +// It's not guaranteed to be correct. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_DEBUGINFO_PDB_RAW_GSI_H +#define LLVM_LIB_DEBUGINFO_PDB_RAW_GSI_H + +#include "llvm/DebugInfo/MSF/StreamArray.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" + +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" + +namespace llvm { + +namespace msf { +class StreamReader; +} + +namespace pdb { + +/// From https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.cpp +static const unsigned IPHR_HASH = 4096; + +/// Header of the hash tables found in the globals and publics sections. +/// Based on GSIHashHeader in +/// https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h +struct GSIHashHeader { + enum : unsigned { + HdrSignature = ~0U, + HdrVersion = 0xeffe0000 + 19990810, + }; + support::ulittle32_t VerSignature; + support::ulittle32_t VerHdr; + support::ulittle32_t HrSize; + support::ulittle32_t NumBuckets; +}; + +Error readGSIHashBuckets( + msf::FixedStreamArray &HashBuckets, + const GSIHashHeader *HashHdr, msf::StreamReader &Reader); +Error readGSIHashHeader(const GSIHashHeader *&HashHdr, + msf::StreamReader &Reader); +Error readGSIHashRecords(msf::FixedStreamArray &HashRecords, + const GSIHashHeader *HashHdr, + msf::StreamReader &Reader); +} +} + +#endif Index: llvm/trunk/lib/DebugInfo/PDB/Native/GSI.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/GSI.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/GSI.cpp @@ -0,0 +1,93 @@ +//===- GSI.cpp - Common Functions for GlobalsStream and PublicsStream ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "GSI.h" + +#include "llvm/DebugInfo/MSF/StreamArray.h" +#include "llvm/DebugInfo/MSF/StreamReader.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" + +#include "llvm/Support/Error.h" + +namespace llvm { +namespace pdb { + +static Error checkHashHdrVersion(const GSIHashHeader *HashHdr) { + if (HashHdr->VerHdr != GSIHashHeader::HdrVersion) + return make_error( + raw_error_code::feature_unsupported, + "Encountered unsupported globals stream version."); + + return Error::success(); +} + +Error readGSIHashBuckets( + msf::FixedStreamArray &HashBuckets, + const GSIHashHeader *HashHdr, msf::StreamReader &Reader) { + if (auto EC = checkHashHdrVersion(HashHdr)) + return EC; + + // Before the actual hash buckets, there is a bitmap of length determined by + // IPHR_HASH. + ArrayRef Bitmap; + size_t BitmapSizeInBits = alignTo(IPHR_HASH + 1, 32); + uint32_t NumBitmapEntries = BitmapSizeInBits / 8; + if (auto EC = Reader.readBytes(Bitmap, NumBitmapEntries)) + return joinErrors(std::move(EC), + make_error(raw_error_code::corrupt_file, + "Could not read a bitmap.")); + uint32_t NumBuckets = 0; + for (uint8_t B : Bitmap) + NumBuckets += countPopulation(B); + + // Hash buckets follow. + if (auto EC = Reader.readArray(HashBuckets, NumBuckets)) + return joinErrors(std::move(EC), + make_error(raw_error_code::corrupt_file, + "Hash buckets corrupted.")); + + return Error::success(); +} + +Error readGSIHashHeader(const GSIHashHeader *&HashHdr, + msf::StreamReader &Reader) { + if (Reader.readObject(HashHdr)) + return make_error(raw_error_code::corrupt_file, + "Stream does not contain a GSIHashHeader."); + + if (HashHdr->VerSignature != GSIHashHeader::HdrSignature) + return make_error( + raw_error_code::feature_unsupported, + "GSIHashHeader signature (0xffffffff) not found."); + + return Error::success(); +} + +Error readGSIHashRecords(msf::FixedStreamArray &HashRecords, + const GSIHashHeader *HashHdr, + msf::StreamReader &Reader) { + if (auto EC = checkHashHdrVersion(HashHdr)) + return EC; + + // HashHdr->HrSize specifies the number of bytes of PSHashRecords we have. + // Verify that we can read them all. + if (HashHdr->HrSize % sizeof(PSHashRecord)) + return make_error(raw_error_code::corrupt_file, + "Invalid HR array size."); + uint32_t NumHashRecords = HashHdr->HrSize / sizeof(PSHashRecord); + if (auto EC = Reader.readArray(HashRecords, NumHashRecords)) + return joinErrors(std::move(EC), + make_error(raw_error_code::corrupt_file, + "Error reading hash records.")); + + return Error::success(); +} +} +} Index: llvm/trunk/lib/DebugInfo/PDB/Native/GlobalsStream.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/GlobalsStream.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/GlobalsStream.cpp @@ -0,0 +1,42 @@ +//===- GlobalsStream.cpp - PDB Index of Symbols by Name ---- ----*- C++ -*-===// +// +// 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/Native/GlobalsStream.h" +#include "GSI.h" +#include "llvm/DebugInfo/MSF/StreamReader.h" +#include "llvm/Support/Error.h" +#include + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::pdb; + +GlobalsStream::GlobalsStream(std::unique_ptr Stream) + : Stream(std::move(Stream)) {} + +GlobalsStream::~GlobalsStream() = default; + +Error GlobalsStream::reload() { + StreamReader Reader(*Stream); + + const GSIHashHeader *HashHdr; + if (auto EC = readGSIHashHeader(HashHdr, Reader)) + return EC; + + if (auto EC = readGSIHashRecords(HashRecords, HashHdr, Reader)) + return EC; + + if (auto EC = readGSIHashBuckets(HashBuckets, HashHdr, Reader)) + return EC; + NumBuckets = HashBuckets.size(); + + return Error::success(); +} + +Error GlobalsStream::commit() { return Error::success(); } Index: llvm/trunk/lib/DebugInfo/PDB/Native/Hash.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/Hash.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/Hash.cpp @@ -0,0 +1,86 @@ +//===- Hash.cpp - PDB Hash Functions --------------------------------------===// +// +// 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/Native/Hash.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/JamCRC.h" + +using namespace llvm; +using namespace llvm::support; + +// Corresponds to `Hasher::lhashPbCb` in PDB/include/misc.h. +// Used for name hash table and TPI/IPI hashes. +uint32_t pdb::hashStringV1(StringRef Str) { + uint32_t Result = 0; + uint32_t Size = Str.size(); + + ArrayRef Longs(reinterpret_cast(Str.data()), + Size / 4); + + for (auto Value : Longs) + Result ^= Value; + + const uint8_t *Remainder = reinterpret_cast(Longs.end()); + uint32_t RemainderSize = Size % 4; + + // Maximum of 3 bytes left. Hash a 2 byte word if possible, then hash the + // possibly remaining 1 byte. + if (RemainderSize >= 2) { + uint16_t Value = *reinterpret_cast(Remainder); + Result ^= static_cast(Value); + Remainder += 2; + RemainderSize -= 2; + } + + // hash possible odd byte + if (RemainderSize == 1) { + Result ^= *(Remainder++); + } + + const uint32_t toLowerMask = 0x20202020; + Result |= toLowerMask; + Result ^= (Result >> 11); + + return Result ^ (Result >> 16); +} + +// Corresponds to `HasherV2::HashULONG` in PDB/include/misc.h. +// Used for name hash table. +uint32_t pdb::hashStringV2(StringRef Str) { + uint32_t Hash = 0xb170a1bf; + + ArrayRef Buffer(Str.begin(), Str.end()); + + ArrayRef Items( + reinterpret_cast(Buffer.data()), + Buffer.size() / sizeof(ulittle32_t)); + for (ulittle32_t Item : Items) { + Hash += Item; + Hash += (Hash << 10); + Hash ^= (Hash >> 6); + } + Buffer = Buffer.slice(Items.size() * sizeof(ulittle32_t)); + for (uint8_t Item : Buffer) { + Hash += Item; + Hash += (Hash << 10); + Hash ^= (Hash >> 6); + } + + return Hash * 1664525U + 1013904223U; +} + +// Corresponds to `SigForPbCb` in langapi/shared/crc32.h. +uint32_t pdb::hashBufferV8(ArrayRef Buf) { + JamCRC JC(/*Init=*/0U); + JC.update(makeArrayRef(reinterpret_cast(Buf.data()), + Buf.size())); + return JC.getCRC(); +} Index: llvm/trunk/lib/DebugInfo/PDB/Native/HashTable.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/HashTable.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/HashTable.cpp @@ -0,0 +1,302 @@ +//===- HashTable.cpp - PDB Hash Table ---------------------------*- C++ -*-===// +// +// 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/Native/HashTable.h" + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SparseBitVector.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" + +#include + +using namespace llvm; +using namespace llvm::pdb; + +HashTable::HashTable() : HashTable(8) {} + +HashTable::HashTable(uint32_t Capacity) { Buckets.resize(Capacity); } + +Error HashTable::load(msf::StreamReader &Stream) { + const Header *H; + if (auto EC = Stream.readObject(H)) + return EC; + if (H->Capacity == 0) + return make_error(raw_error_code::corrupt_file, + "Invalid Hash Table Capacity"); + if (H->Size > maxLoad(H->Capacity)) + return make_error(raw_error_code::corrupt_file, + "Invalid Hash Table Size"); + + Buckets.resize(H->Capacity); + + if (auto EC = readSparseBitVector(Stream, Present)) + return EC; + if (Present.count() != H->Size) + return make_error(raw_error_code::corrupt_file, + "Present bit vector does not match size!"); + + if (auto EC = readSparseBitVector(Stream, Deleted)) + return EC; + if (Present.intersects(Deleted)) + return make_error(raw_error_code::corrupt_file, + "Present bit vector interesects deleted!"); + + for (uint32_t P : Present) { + if (auto EC = Stream.readInteger(Buckets[P].first)) + return EC; + if (auto EC = Stream.readInteger(Buckets[P].second)) + return EC; + } + + return Error::success(); +} + +uint32_t HashTable::calculateSerializedLength() const { + uint32_t Size = sizeof(Header); + + int NumBitsP = Present.find_last() + 1; + int NumBitsD = Deleted.find_last() + 1; + + // Present bit set number of words, followed by that many actual words. + Size += sizeof(uint32_t); + Size += alignTo(NumBitsP, sizeof(uint32_t)); + + // Deleted bit set number of words, followed by that many actual words. + Size += sizeof(uint32_t); + Size += alignTo(NumBitsD, sizeof(uint32_t)); + + // One (Key, Value) pair for each entry Present. + Size += 2 * sizeof(uint32_t) * size(); + + return Size; +} + +Error HashTable::commit(msf::StreamWriter &Writer) const { + Header H; + H.Size = size(); + H.Capacity = capacity(); + if (auto EC = Writer.writeObject(H)) + return EC; + + if (auto EC = writeSparseBitVector(Writer, Present)) + return EC; + + if (auto EC = writeSparseBitVector(Writer, Deleted)) + return EC; + + for (const auto &Entry : *this) { + if (auto EC = Writer.writeInteger(Entry.first)) + return EC; + if (auto EC = Writer.writeInteger(Entry.second)) + return EC; + } + return Error::success(); +} + +void HashTable::clear() { + Buckets.resize(8); + Present.clear(); + Deleted.clear(); +} + +uint32_t HashTable::capacity() const { return Buckets.size(); } +uint32_t HashTable::size() const { return Present.count(); } + +HashTableIterator HashTable::begin() const { return HashTableIterator(*this); } +HashTableIterator HashTable::end() const { + return HashTableIterator(*this, 0, true); +} + +HashTableIterator HashTable::find(uint32_t K) { + uint32_t H = K % capacity(); + uint32_t I = H; + Optional FirstUnused; + do { + if (isPresent(I)) { + if (Buckets[I].first == K) + return HashTableIterator(*this, I, false); + } else { + if (!FirstUnused) + FirstUnused = I; + // Insertion occurs via linear probing from the slot hint, and will be + // inserted at the first empty / deleted location. Therefore, if we are + // probing and find a location that is neither present nor deleted, then + // nothing must have EVER been inserted at this location, and thus it is + // not possible for a matching value to occur later. + if (!isDeleted(I)) + break; + } + I = (I + 1) % capacity(); + } while (I != H); + + // The only way FirstUnused would not be set is if every single entry in the + // table were Present. But this would violate the load factor constraints + // that we impose, so it should never happen. + assert(FirstUnused); + return HashTableIterator(*this, *FirstUnused, true); +} + +void HashTable::set(uint32_t K, uint32_t V) { + auto Entry = find(K); + if (Entry != end()) { + assert(isPresent(Entry.index())); + assert(Buckets[Entry.index()].first == K); + // We're updating, no need to do anything special. + Buckets[Entry.index()].second = V; + return; + } + + auto &B = Buckets[Entry.index()]; + assert(!isPresent(Entry.index())); + assert(Entry.isEnd()); + B.first = K; + B.second = V; + Present.set(Entry.index()); + Deleted.reset(Entry.index()); + + grow(); + + assert(find(K) != end()); +} + +void HashTable::remove(uint32_t K) { + auto Iter = find(K); + // It wasn't here to begin with, just exit. + if (Iter == end()) + return; + + assert(Present.test(Iter.index())); + assert(!Deleted.test(Iter.index())); + Deleted.set(Iter.index()); + Present.reset(Iter.index()); +} + +uint32_t HashTable::get(uint32_t K) { + auto I = find(K); + assert(I != end()); + return (*I).second; +} + +uint32_t HashTable::maxLoad(uint32_t capacity) { return capacity * 2 / 3 + 1; } + +void HashTable::grow() { + uint32_t S = size(); + if (S < maxLoad(capacity())) + return; + assert(capacity() != UINT32_MAX && "Can't grow Hash table!"); + + uint32_t NewCapacity = + (capacity() <= INT32_MAX) ? capacity() * 2 : UINT32_MAX; + + // Growing requires rebuilding the table and re-hashing every item. Make a + // copy with a larger capacity, insert everything into the copy, then swap + // it in. + HashTable NewMap(NewCapacity); + for (auto I : Present) { + NewMap.set(Buckets[I].first, Buckets[I].second); + } + + Buckets.swap(NewMap.Buckets); + std::swap(Present, NewMap.Present); + std::swap(Deleted, NewMap.Deleted); + assert(capacity() == NewCapacity); + assert(size() == S); +} + +Error HashTable::readSparseBitVector(msf::StreamReader &Stream, + SparseBitVector<> &V) { + uint32_t NumWords; + if (auto EC = Stream.readInteger(NumWords)) + return joinErrors( + std::move(EC), + make_error(raw_error_code::corrupt_file, + "Expected hash table number of words")); + + for (uint32_t I = 0; I != NumWords; ++I) { + uint32_t Word; + if (auto EC = Stream.readInteger(Word)) + return joinErrors(std::move(EC), + make_error(raw_error_code::corrupt_file, + "Expected hash table word")); + for (unsigned Idx = 0; Idx < 32; ++Idx) + if (Word & (1U << Idx)) + V.set((I * 32) + Idx); + } + return Error::success(); +} + +Error HashTable::writeSparseBitVector(msf::StreamWriter &Writer, + SparseBitVector<> &Vec) { + int ReqBits = Vec.find_last() + 1; + uint32_t NumWords = alignTo(ReqBits, sizeof(uint32_t)) / sizeof(uint32_t); + if (auto EC = Writer.writeInteger(NumWords)) + return joinErrors( + std::move(EC), + make_error(raw_error_code::corrupt_file, + "Could not write linear map number of words")); + + uint32_t Idx = 0; + for (uint32_t I = 0; I != NumWords; ++I) { + uint32_t Word = 0; + for (uint32_t WordIdx = 0; WordIdx < 32; ++WordIdx, ++Idx) { + if (Vec.test(Idx)) + Word |= (1 << WordIdx); + } + if (auto EC = Writer.writeInteger(Word)) + return joinErrors(std::move(EC), make_error( + raw_error_code::corrupt_file, + "Could not write linear map word")); + } + return Error::success(); +} + +HashTableIterator::HashTableIterator(const HashTable &Map, uint32_t Index, + bool IsEnd) + : Map(&Map), Index(Index), IsEnd(IsEnd) {} + +HashTableIterator::HashTableIterator(const HashTable &Map) : Map(&Map) { + int I = Map.Present.find_first(); + if (I == -1) { + Index = 0; + IsEnd = true; + } else { + Index = static_cast(I); + IsEnd = false; + } +} + +HashTableIterator &HashTableIterator::operator=(const HashTableIterator &R) { + Map = R.Map; + return *this; +} + +bool HashTableIterator::operator==(const HashTableIterator &R) const { + if (IsEnd && R.IsEnd) + return true; + if (IsEnd != R.IsEnd) + return false; + + return (Map == R.Map) && (Index == R.Index); +} + +const std::pair &HashTableIterator::operator*() const { + assert(Map->Present.test(Index)); + return Map->Buckets[Index]; +} + +HashTableIterator &HashTableIterator::operator++() { + while (Index < Map->Buckets.size()) { + ++Index; + if (Map->Present.test(Index)) + return *this; + } + + IsEnd = true; + return *this; +} Index: llvm/trunk/lib/DebugInfo/PDB/Native/InfoStream.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/InfoStream.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/InfoStream.cpp @@ -0,0 +1,81 @@ +//===- InfoStream.cpp - PDB Info Stream (Stream 1) Access -------*- C++ -*-===// +// +// 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/Native/InfoStream.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/DebugInfo/MSF/StreamReader.h" +#include "llvm/DebugInfo/MSF/StreamWriter.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +InfoStream::InfoStream(std::unique_ptr Stream) + : Stream(std::move(Stream)) {} + +Error InfoStream::reload() { + StreamReader Reader(*Stream); + + const InfoStreamHeader *H; + if (auto EC = Reader.readObject(H)) + return joinErrors( + std::move(EC), + make_error(raw_error_code::corrupt_file, + "PDB Stream does not contain a header.")); + + switch (H->Version) { + case PdbImplVC70: + case PdbImplVC80: + case PdbImplVC110: + case PdbImplVC140: + break; + default: + return make_error(raw_error_code::corrupt_file, + "Unsupported PDB stream version."); + } + + Version = H->Version; + Signature = H->Signature; + Age = H->Age; + Guid = H->Guid; + + return NamedStreams.load(Reader); +} + +uint32_t InfoStream::getNamedStreamIndex(llvm::StringRef Name) const { + uint32_t Result; + if (!NamedStreams.get(Name, Result)) + return 0; + return Result; +} + +iterator_range> +InfoStream::named_streams() const { + return NamedStreams.entries(); +} + +PdbRaw_ImplVer InfoStream::getVersion() const { + return static_cast(Version); +} + +uint32_t InfoStream::getSignature() const { return Signature; } + +uint32_t InfoStream::getAge() const { return Age; } + +PDB_UniqueId InfoStream::getGuid() const { return Guid; } + +const NamedStreamMap &InfoStream::getNamedStreams() const { + return NamedStreams; +} Index: llvm/trunk/lib/DebugInfo/PDB/Native/InfoStreamBuilder.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/InfoStreamBuilder.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/InfoStreamBuilder.cpp @@ -0,0 +1,61 @@ +//===- InfoStreamBuilder.cpp - PDB Info Stream Creation ---------*- C++ -*-===// +// +// 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/Native/InfoStreamBuilder.h" + +#include "llvm/DebugInfo/MSF/MSFBuilder.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/MSF/StreamWriter.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h" +#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +InfoStreamBuilder::InfoStreamBuilder(msf::MSFBuilder &Msf, + NamedStreamMap &NamedStreams) + : Msf(Msf), Ver(PdbRaw_ImplVer::PdbImplVC70), Sig(-1), Age(0), + NamedStreams(NamedStreams) {} + +void InfoStreamBuilder::setVersion(PdbRaw_ImplVer V) { Ver = V; } + +void InfoStreamBuilder::setSignature(uint32_t S) { Sig = S; } + +void InfoStreamBuilder::setAge(uint32_t A) { Age = A; } + +void InfoStreamBuilder::setGuid(PDB_UniqueId G) { Guid = G; } + +Error InfoStreamBuilder::finalizeMsfLayout() { + uint32_t Length = sizeof(InfoStreamHeader) + NamedStreams.finalize(); + if (auto EC = Msf.setStreamSize(StreamPDB, Length)) + return EC; + return Error::success(); +} + +Error InfoStreamBuilder::commit(const msf::MSFLayout &Layout, + const msf::WritableStream &Buffer) const { + auto InfoS = + WritableMappedBlockStream::createIndexedStream(Layout, Buffer, StreamPDB); + StreamWriter Writer(*InfoS); + + InfoStreamHeader H; + H.Age = Age; + H.Signature = Sig; + H.Version = Ver; + H.Guid = Guid; + if (auto EC = Writer.writeObject(H)) + return EC; + + return NamedStreams.commit(Writer); +} Index: llvm/trunk/lib/DebugInfo/PDB/Native/ModInfo.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/ModInfo.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/ModInfo.cpp @@ -0,0 +1,81 @@ +//===- ModInfo.cpp - PDB module information -------------------------------===// +// +// 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/Native/ModInfo.h" +#include "llvm/DebugInfo/MSF/StreamReader.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MathExtras.h" +#include + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::pdb; +using namespace llvm::support; + +ModInfo::ModInfo() = default; + +ModInfo::ModInfo(const ModInfo &Info) = default; + +ModInfo::~ModInfo() = default; + +Error ModInfo::initialize(ReadableStreamRef Stream, ModInfo &Info) { + StreamReader Reader(Stream); + if (auto EC = Reader.readObject(Info.Layout)) + return EC; + + if (auto EC = Reader.readZeroString(Info.ModuleName)) + return EC; + + if (auto EC = Reader.readZeroString(Info.ObjFileName)) + return EC; + return Error::success(); +} + +bool ModInfo::hasECInfo() const { + return (Layout->Flags & ModInfoFlags::HasECFlagMask) != 0; +} + +uint16_t ModInfo::getTypeServerIndex() const { + return (Layout->Flags & ModInfoFlags::TypeServerIndexMask) >> + ModInfoFlags::TypeServerIndexShift; +} + +uint16_t ModInfo::getModuleStreamIndex() const { return Layout->ModDiStream; } + +uint32_t ModInfo::getSymbolDebugInfoByteSize() const { + return Layout->SymBytes; +} + +uint32_t ModInfo::getLineInfoByteSize() const { return Layout->LineBytes; } + +uint32_t ModInfo::getC13LineInfoByteSize() const { return Layout->C13Bytes; } + +uint32_t ModInfo::getNumberOfFiles() const { return Layout->NumFiles; } + +uint32_t ModInfo::getSourceFileNameIndex() const { + return Layout->SrcFileNameNI; +} + +uint32_t ModInfo::getPdbFilePathNameIndex() const { + return Layout->PdbFilePathNI; +} + +StringRef ModInfo::getModuleName() const { return ModuleName; } + +StringRef ModInfo::getObjFileName() const { return ObjFileName; } + +uint32_t ModInfo::getRecordLength() const { + uint32_t M = ModuleName.str().size() + 1; + uint32_t O = ObjFileName.str().size() + 1; + uint32_t Size = sizeof(ModuleInfoHeader) + M + O; + Size = alignTo(Size, 4); + return Size; +} Index: llvm/trunk/lib/DebugInfo/PDB/Native/ModStream.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/ModStream.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/ModStream.cpp @@ -0,0 +1,85 @@ +//===- ModStream.cpp - PDB Module Info Stream Access ----------------------===// +// +// 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/Native/ModStream.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/MSF/StreamReader.h" +#include "llvm/DebugInfo/MSF/StreamRef.h" +#include "llvm/DebugInfo/PDB/Native/ModInfo.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/Error.h" +#include +#include + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::pdb; + +ModStream::ModStream(const ModInfo &Module, + std::unique_ptr Stream) + : Mod(Module), Stream(std::move(Stream)) {} + +ModStream::~ModStream() = default; + +Error ModStream::reload() { + StreamReader Reader(*Stream); + + uint32_t SymbolSize = Mod.getSymbolDebugInfoByteSize(); + uint32_t C11Size = Mod.getLineInfoByteSize(); + uint32_t C13Size = Mod.getC13LineInfoByteSize(); + + if (C11Size > 0 && C13Size > 0) + return make_error(raw_error_code::corrupt_file, + "Module has both C11 and C13 line info"); + + ReadableStreamRef S; + + if (auto EC = Reader.readInteger(Signature)) + return EC; + if (auto EC = Reader.readArray(SymbolsSubstream, SymbolSize - 4)) + return EC; + + if (auto EC = Reader.readStreamRef(LinesSubstream, C11Size)) + return EC; + if (auto EC = Reader.readStreamRef(C13LinesSubstream, C13Size)) + return EC; + + StreamReader LineReader(C13LinesSubstream); + if (auto EC = LineReader.readArray(LineInfo, LineReader.bytesRemaining())) + return EC; + + uint32_t GlobalRefsSize; + if (auto EC = Reader.readInteger(GlobalRefsSize)) + return EC; + if (auto EC = Reader.readStreamRef(GlobalRefsSubstream, GlobalRefsSize)) + return EC; + if (Reader.bytesRemaining() > 0) + return make_error(raw_error_code::corrupt_file, + "Unexpected bytes in module stream."); + + return Error::success(); +} + +iterator_range +ModStream::symbols(bool *HadError) const { + // It's OK if the stream is empty. + if (SymbolsSubstream.getUnderlyingStream().getLength() == 0) + return make_range(SymbolsSubstream.end(), SymbolsSubstream.end()); + return make_range(SymbolsSubstream.begin(HadError), SymbolsSubstream.end()); +} + +iterator_range +ModStream::lines(bool *HadError) const { + return make_range(LineInfo.begin(HadError), LineInfo.end()); +} + +Error ModStream::commit() { return Error::success(); } Index: llvm/trunk/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp @@ -0,0 +1,135 @@ +//===- NamedStreamMap.cpp - PDB Named Stream Map ----------------*- C++ -*-===// +// +// 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/Native/NamedStreamMap.h" + +#include "llvm/ADT/SparseBitVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/DebugInfo/MSF/StreamReader.h" +#include "llvm/DebugInfo/PDB/Native/HashTable.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Support/Error.h" +#include +#include + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::pdb; + +NamedStreamMap::NamedStreamMap() = default; + +Error NamedStreamMap::load(StreamReader &Stream) { + Mapping.clear(); + FinalizedHashTable.clear(); + FinalizedInfo.reset(); + + uint32_t StringBufferSize; + if (auto EC = Stream.readInteger(StringBufferSize)) + return joinErrors(std::move(EC), + make_error(raw_error_code::corrupt_file, + "Expected string buffer size")); + + msf::ReadableStreamRef StringsBuffer; + if (auto EC = Stream.readStreamRef(StringsBuffer, StringBufferSize)) + return EC; + + HashTable OffsetIndexMap; + if (auto EC = OffsetIndexMap.load(Stream)) + return EC; + + uint32_t NameOffset; + uint32_t NameIndex; + for (const auto &Entry : OffsetIndexMap) { + std::tie(NameOffset, NameIndex) = Entry; + + // Compute the offset of the start of the string relative to the stream. + msf::StreamReader NameReader(StringsBuffer); + NameReader.setOffset(NameOffset); + // Pump out our c-string from the stream. + StringRef Str; + if (auto EC = NameReader.readZeroString(Str)) + return joinErrors(std::move(EC), + make_error(raw_error_code::corrupt_file, + "Expected name map name")); + + // Add this to a string-map from name to stream number. + Mapping.insert({Str, NameIndex}); + } + + return Error::success(); +} + +Error NamedStreamMap::commit(msf::StreamWriter &Writer) const { + assert(FinalizedInfo.hasValue()); + + // The first field is the number of bytes of string data. + if (auto EC = Writer.writeInteger( + FinalizedInfo->StringDataBytes)) // Number of bytes of string data + return EC; + + // Now all of the string data itself. + for (const auto &Item : Mapping) { + if (auto EC = Writer.writeZeroString(Item.getKey())) + return EC; + } + + // And finally the Offset Index map. + if (auto EC = FinalizedHashTable.commit(Writer)) + return EC; + + return Error::success(); +} + +uint32_t NamedStreamMap::finalize() { + if (FinalizedInfo.hasValue()) + return FinalizedInfo->SerializedLength; + + // Build the finalized hash table. + FinalizedHashTable.clear(); + FinalizedInfo.emplace(); + for (const auto &Item : Mapping) { + FinalizedHashTable.set(FinalizedInfo->StringDataBytes, Item.getValue()); + FinalizedInfo->StringDataBytes += Item.getKeyLength() + 1; + } + + // Number of bytes of string data. + FinalizedInfo->SerializedLength += sizeof(support::ulittle32_t); + // Followed by that many actual bytes of string data. + FinalizedInfo->SerializedLength += FinalizedInfo->StringDataBytes; + // Followed by the mapping from Offset to Index. + FinalizedInfo->SerializedLength += + FinalizedHashTable.calculateSerializedLength(); + return FinalizedInfo->SerializedLength; +} + +iterator_range> +NamedStreamMap::entries() const { + return make_range>(Mapping.begin(), + Mapping.end()); +} + +bool NamedStreamMap::get(StringRef Stream, uint32_t &StreamNo) const { + auto Iter = Mapping.find(Stream); + if (Iter == Mapping.end()) + return false; + StreamNo = Iter->second; + return true; +} + +void NamedStreamMap::set(StringRef Stream, uint32_t StreamNo) { + FinalizedInfo.reset(); + Mapping[Stream] = StreamNo; +} + +void NamedStreamMap::remove(StringRef Stream) { + FinalizedInfo.reset(); + Mapping.erase(Stream); +} Index: llvm/trunk/lib/DebugInfo/PDB/Native/NativeSession.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/NativeSession.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/NativeSession.cpp @@ -0,0 +1,139 @@ +//===- NativeSession.cpp - Native implementation of IPDBSession -*- C++ -*-===// +// +// 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/Native/NativeSession.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/MSF/ByteStream.h" +#include "llvm/DebugInfo/PDB/GenericError.h" +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/IPDBSourceFile.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/MemoryBuffer.h" +#include +#include + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::pdb; + +NativeSession::NativeSession(std::unique_ptr PdbFile, + std::unique_ptr Allocator) + : Pdb(std::move(PdbFile)), Allocator(std::move(Allocator)) {} + +NativeSession::~NativeSession() = default; + +Error NativeSession::createFromPdb(StringRef Path, + std::unique_ptr &Session) { + ErrorOr> ErrorOrBuffer = + MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + if (!ErrorOrBuffer) + return make_error(generic_error_code::invalid_path); + + std::unique_ptr Buffer = std::move(*ErrorOrBuffer); + auto Stream = llvm::make_unique(std::move(Buffer)); + + auto Allocator = llvm::make_unique(); + auto File = llvm::make_unique(std::move(Stream), *Allocator); + if (auto EC = File->parseFileHeaders()) + return EC; + if (auto EC = File->parseStreamData()) + return EC; + + Session = + llvm::make_unique(std::move(File), std::move(Allocator)); + + return Error::success(); +} + +Error NativeSession::createFromExe(StringRef Path, + std::unique_ptr &Session) { + return make_error(raw_error_code::feature_unsupported); +} + +uint64_t NativeSession::getLoadAddress() const { return 0; } + +void NativeSession::setLoadAddress(uint64_t Address) {} + +std::unique_ptr NativeSession::getGlobalScope() const { + return nullptr; +} + +std::unique_ptr +NativeSession::getSymbolById(uint32_t SymbolId) const { + return nullptr; +} + +std::unique_ptr +NativeSession::findSymbolByAddress(uint64_t Address, PDB_SymType Type) const { + return nullptr; +} + +std::unique_ptr +NativeSession::findLineNumbers(const PDBSymbolCompiland &Compiland, + const IPDBSourceFile &File) const { + return nullptr; +} + +std::unique_ptr +NativeSession::findLineNumbersByAddress(uint64_t Address, + uint32_t Length) const { + return nullptr; +} + +std::unique_ptr +NativeSession::findSourceFiles(const PDBSymbolCompiland *Compiland, + StringRef Pattern, + PDB_NameSearchFlags Flags) const { + return nullptr; +} + +std::unique_ptr +NativeSession::findOneSourceFile(const PDBSymbolCompiland *Compiland, + StringRef Pattern, + PDB_NameSearchFlags Flags) const { + return nullptr; +} + +std::unique_ptr> +NativeSession::findCompilandsForSourceFile(StringRef Pattern, + PDB_NameSearchFlags Flags) const { + return nullptr; +} + +std::unique_ptr +NativeSession::findOneCompilandForSourceFile(StringRef Pattern, + PDB_NameSearchFlags Flags) const { + return nullptr; +} + +std::unique_ptr NativeSession::getAllSourceFiles() const { + return nullptr; +} + +std::unique_ptr NativeSession::getSourceFilesForCompiland( + const PDBSymbolCompiland &Compiland) const { + return nullptr; +} + +std::unique_ptr +NativeSession::getSourceFileById(uint32_t FileId) const { + return nullptr; +} + +std::unique_ptr NativeSession::getDebugStreams() const { + return nullptr; +} Index: llvm/trunk/lib/DebugInfo/PDB/Native/PDBFile.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/PDBFile.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/PDBFile.cpp @@ -0,0 +1,404 @@ +//===- PDBFile.cpp - Low level interface to a PDB file ----------*- C++ -*-===// +// +// 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/Native/PDBFile.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/MSF/MSFCommon.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/MSF/StreamArray.h" +#include "llvm/DebugInfo/MSF/StreamInterface.h" +#include "llvm/DebugInfo/MSF/StreamReader.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/PublicsStream.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/StringTable.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include +#include +#include + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +namespace { +typedef FixedStreamArray ulittle_array; +} // end anonymous namespace + +PDBFile::PDBFile(std::unique_ptr PdbFileBuffer, + BumpPtrAllocator &Allocator) + : Allocator(Allocator), Buffer(std::move(PdbFileBuffer)) {} + +PDBFile::~PDBFile() = default; + +uint32_t PDBFile::getBlockSize() const { return ContainerLayout.SB->BlockSize; } + +uint32_t PDBFile::getFreeBlockMapBlock() const { + return ContainerLayout.SB->FreeBlockMapBlock; +} + +uint32_t PDBFile::getBlockCount() const { + return ContainerLayout.SB->NumBlocks; +} + +uint32_t PDBFile::getNumDirectoryBytes() const { + return ContainerLayout.SB->NumDirectoryBytes; +} + +uint32_t PDBFile::getBlockMapIndex() const { + return ContainerLayout.SB->BlockMapAddr; +} + +uint32_t PDBFile::getUnknown1() const { return ContainerLayout.SB->Unknown1; } + +uint32_t PDBFile::getNumDirectoryBlocks() const { + return msf::bytesToBlocks(ContainerLayout.SB->NumDirectoryBytes, + ContainerLayout.SB->BlockSize); +} + +uint64_t PDBFile::getBlockMapOffset() const { + return (uint64_t)ContainerLayout.SB->BlockMapAddr * + ContainerLayout.SB->BlockSize; +} + +uint32_t PDBFile::getNumStreams() const { + return ContainerLayout.StreamSizes.size(); +} + +uint32_t PDBFile::getStreamByteSize(uint32_t StreamIndex) const { + return ContainerLayout.StreamSizes[StreamIndex]; +} + +ArrayRef +PDBFile::getStreamBlockList(uint32_t StreamIndex) const { + return ContainerLayout.StreamMap[StreamIndex]; +} + +uint32_t PDBFile::getFileSize() const { return Buffer->getLength(); } + +Expected> PDBFile::getBlockData(uint32_t BlockIndex, + uint32_t NumBytes) const { + uint64_t StreamBlockOffset = msf::blockToOffset(BlockIndex, getBlockSize()); + + ArrayRef Result; + if (auto EC = Buffer->readBytes(StreamBlockOffset, NumBytes, Result)) + return std::move(EC); + return Result; +} + +Error PDBFile::setBlockData(uint32_t BlockIndex, uint32_t Offset, + ArrayRef Data) const { + return make_error(raw_error_code::not_writable, + "PDBFile is immutable"); +} + +Error PDBFile::parseFileHeaders() { + StreamReader Reader(*Buffer); + + // Initialize SB. + const msf::SuperBlock *SB = nullptr; + if (auto EC = Reader.readObject(SB)) { + consumeError(std::move(EC)); + return make_error(raw_error_code::corrupt_file, + "Does not contain superblock"); + } + + if (auto EC = msf::validateSuperBlock(*SB)) + return EC; + + if (Buffer->getLength() % SB->BlockSize != 0) + return make_error(raw_error_code::corrupt_file, + "File size is not a multiple of block size"); + ContainerLayout.SB = SB; + + // Initialize Free Page Map. + ContainerLayout.FreePageMap.resize(SB->NumBlocks); + // The Fpm exists either at block 1 or block 2 of the MSF. However, this + // allows for a maximum of getBlockSize() * 8 blocks bits in the Fpm, and + // thusly an equal number of total blocks in the file. For a block size + // of 4KiB (very common), this would yield 32KiB total blocks in file, for a + // maximum file size of 32KiB * 4KiB = 128MiB. Obviously this won't do, so + // the Fpm is split across the file at `getBlockSize()` intervals. As a + // result, every block whose index is of the form |{1,2} + getBlockSize() * k| + // for any non-negative integer k is an Fpm block. In theory, we only really + // need to reserve blocks of the form |{1,2} + getBlockSize() * 8 * k|, but + // current versions of the MSF format already expect the Fpm to be arranged + // at getBlockSize() intervals, so we have to be compatible. + // See the function fpmPn() for more information: + // https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/msf/msf.cpp#L489 + auto FpmStream = MappedBlockStream::createFpmStream(ContainerLayout, *Buffer); + StreamReader FpmReader(*FpmStream); + ArrayRef FpmBytes; + if (auto EC = FpmReader.readBytes(FpmBytes, + msf::getFullFpmByteSize(ContainerLayout))) + return EC; + uint32_t BlocksRemaining = getBlockCount(); + uint32_t BI = 0; + for (auto Byte : FpmBytes) { + uint32_t BlocksThisByte = std::min(BlocksRemaining, 8U); + for (uint32_t I = 0; I < BlocksThisByte; ++I) { + if (Byte & (1 << I)) + ContainerLayout.FreePageMap[BI] = true; + --BlocksRemaining; + ++BI; + } + } + + Reader.setOffset(getBlockMapOffset()); + if (auto EC = Reader.readArray(ContainerLayout.DirectoryBlocks, + getNumDirectoryBlocks())) + return EC; + + return Error::success(); +} + +Error PDBFile::parseStreamData() { + assert(ContainerLayout.SB); + if (DirectoryStream) + return Error::success(); + + uint32_t NumStreams = 0; + + // Normally you can't use a MappedBlockStream without having fully parsed the + // PDB file, because it accesses the directory and various other things, which + // is exactly what we are attempting to parse. By specifying a custom + // subclass of IPDBStreamData which only accesses the fields that have already + // been parsed, we can avoid this and reuse MappedBlockStream. + auto DS = MappedBlockStream::createDirectoryStream(ContainerLayout, *Buffer); + StreamReader Reader(*DS); + if (auto EC = Reader.readInteger(NumStreams)) + return EC; + + if (auto EC = Reader.readArray(ContainerLayout.StreamSizes, NumStreams)) + return EC; + for (uint32_t I = 0; I < NumStreams; ++I) { + uint32_t StreamSize = getStreamByteSize(I); + // FIXME: What does StreamSize ~0U mean? + uint64_t NumExpectedStreamBlocks = + StreamSize == UINT32_MAX + ? 0 + : msf::bytesToBlocks(StreamSize, ContainerLayout.SB->BlockSize); + + // For convenience, we store the block array contiguously. This is because + // if someone calls setStreamMap(), it is more convenient to be able to call + // it with an ArrayRef instead of setting up a StreamRef. Since the + // DirectoryStream is cached in the class and thus lives for the life of the + // class, we can be guaranteed that readArray() will return a stable + // reference, even if it has to allocate from its internal pool. + ArrayRef Blocks; + if (auto EC = Reader.readArray(Blocks, NumExpectedStreamBlocks)) + return EC; + for (uint32_t Block : Blocks) { + uint64_t BlockEndOffset = + (uint64_t)(Block + 1) * ContainerLayout.SB->BlockSize; + if (BlockEndOffset > getFileSize()) + return make_error(raw_error_code::corrupt_file, + "Stream block map is corrupt."); + } + ContainerLayout.StreamMap.push_back(Blocks); + } + + // We should have read exactly SB->NumDirectoryBytes bytes. + assert(Reader.bytesRemaining() == 0); + DirectoryStream = std::move(DS); + return Error::success(); +} + +ArrayRef PDBFile::getDirectoryBlockArray() const { + return ContainerLayout.DirectoryBlocks; +} + +Expected PDBFile::getPDBGlobalsStream() { + if (!Globals) { + auto DbiS = getPDBDbiStream(); + if (!DbiS) + return DbiS.takeError(); + + auto GlobalS = safelyCreateIndexedStream( + ContainerLayout, *Buffer, DbiS->getGlobalSymbolStreamIndex()); + if (!GlobalS) + return GlobalS.takeError(); + auto TempGlobals = llvm::make_unique(std::move(*GlobalS)); + if (auto EC = TempGlobals->reload()) + return std::move(EC); + Globals = std::move(TempGlobals); + } + return *Globals; +} + +Expected PDBFile::getPDBInfoStream() { + if (!Info) { + auto InfoS = safelyCreateIndexedStream(ContainerLayout, *Buffer, StreamPDB); + if (!InfoS) + return InfoS.takeError(); + auto TempInfo = llvm::make_unique(std::move(*InfoS)); + if (auto EC = TempInfo->reload()) + return std::move(EC); + Info = std::move(TempInfo); + } + return *Info; +} + +Expected PDBFile::getPDBDbiStream() { + if (!Dbi) { + auto DbiS = safelyCreateIndexedStream(ContainerLayout, *Buffer, StreamDBI); + if (!DbiS) + return DbiS.takeError(); + auto TempDbi = llvm::make_unique(*this, std::move(*DbiS)); + if (auto EC = TempDbi->reload()) + return std::move(EC); + Dbi = std::move(TempDbi); + } + return *Dbi; +} + +Expected PDBFile::getPDBTpiStream() { + if (!Tpi) { + auto TpiS = safelyCreateIndexedStream(ContainerLayout, *Buffer, StreamTPI); + if (!TpiS) + return TpiS.takeError(); + auto TempTpi = llvm::make_unique(*this, std::move(*TpiS)); + if (auto EC = TempTpi->reload()) + return std::move(EC); + Tpi = std::move(TempTpi); + } + return *Tpi; +} + +Expected PDBFile::getPDBIpiStream() { + if (!Ipi) { + auto IpiS = safelyCreateIndexedStream(ContainerLayout, *Buffer, StreamIPI); + if (!IpiS) + return IpiS.takeError(); + auto TempIpi = llvm::make_unique(*this, std::move(*IpiS)); + if (auto EC = TempIpi->reload()) + return std::move(EC); + Ipi = std::move(TempIpi); + } + return *Ipi; +} + +Expected PDBFile::getPDBPublicsStream() { + if (!Publics) { + auto DbiS = getPDBDbiStream(); + if (!DbiS) + return DbiS.takeError(); + + auto PublicS = safelyCreateIndexedStream( + ContainerLayout, *Buffer, DbiS->getPublicSymbolStreamIndex()); + if (!PublicS) + return PublicS.takeError(); + auto TempPublics = + llvm::make_unique(*this, std::move(*PublicS)); + if (auto EC = TempPublics->reload()) + return std::move(EC); + Publics = std::move(TempPublics); + } + return *Publics; +} + +Expected PDBFile::getPDBSymbolStream() { + if (!Symbols) { + auto DbiS = getPDBDbiStream(); + if (!DbiS) + return DbiS.takeError(); + + uint32_t SymbolStreamNum = DbiS->getSymRecordStreamIndex(); + auto SymbolS = + safelyCreateIndexedStream(ContainerLayout, *Buffer, SymbolStreamNum); + if (!SymbolS) + return SymbolS.takeError(); + + auto TempSymbols = llvm::make_unique(std::move(*SymbolS)); + if (auto EC = TempSymbols->reload()) + return std::move(EC); + Symbols = std::move(TempSymbols); + } + return *Symbols; +} + +Expected PDBFile::getStringTable() { + if (!Strings || !StringTableStream) { + auto IS = getPDBInfoStream(); + if (!IS) + return IS.takeError(); + + uint32_t NameStreamIndex = IS->getNamedStreamIndex("/names"); + + auto NS = + safelyCreateIndexedStream(ContainerLayout, *Buffer, NameStreamIndex); + if (!NS) + return NS.takeError(); + + StreamReader Reader(**NS); + auto N = llvm::make_unique(); + if (auto EC = N->load(Reader)) + return std::move(EC); + Strings = std::move(N); + StringTableStream = std::move(*NS); + } + return *Strings; +} + +bool PDBFile::hasPDBDbiStream() const { return StreamDBI < getNumStreams(); } + +bool PDBFile::hasPDBGlobalsStream() { + auto DbiS = getPDBDbiStream(); + if (!DbiS) + return false; + return DbiS->getGlobalSymbolStreamIndex() < getNumStreams(); +} + +bool PDBFile::hasPDBInfoStream() { return StreamPDB < getNumStreams(); } + +bool PDBFile::hasPDBIpiStream() const { return StreamIPI < getNumStreams(); } + +bool PDBFile::hasPDBPublicsStream() { + auto DbiS = getPDBDbiStream(); + if (!DbiS) + return false; + return DbiS->getPublicSymbolStreamIndex() < getNumStreams(); +} + +bool PDBFile::hasPDBSymbolStream() { + auto DbiS = getPDBDbiStream(); + if (!DbiS) + return false; + return DbiS->getSymRecordStreamIndex() < getNumStreams(); +} + +bool PDBFile::hasPDBTpiStream() const { return StreamTPI < getNumStreams(); } + +bool PDBFile::hasStringTable() { + auto IS = getPDBInfoStream(); + if (!IS) + return false; + return IS->getNamedStreamIndex("/names") < getNumStreams(); +} + +/// Wrapper around MappedBlockStream::createIndexedStream() +/// that checks if a stream with that index actually exists. +/// If it does not, the return value will have an MSFError with +/// code msf_error_code::no_stream. Else, the return value will +/// contain the stream returned by createIndexedStream(). +Expected> +PDBFile::safelyCreateIndexedStream(const MSFLayout &Layout, + const ReadableStream &MsfData, + uint32_t StreamIndex) const { + if (StreamIndex >= getNumStreams()) + return make_error(raw_error_code::no_stream); + return MappedBlockStream::createIndexedStream(Layout, MsfData, StreamIndex); +} Index: llvm/trunk/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp @@ -0,0 +1,178 @@ +//===- PDBFileBuilder.cpp - PDB File Creation -------------------*- C++ -*-===// +// +// 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/Native/PDBFileBuilder.h" + +#include "llvm/ADT/BitVector.h" + +#include "llvm/DebugInfo/MSF/MSFBuilder.h" +#include "llvm/DebugInfo/MSF/StreamInterface.h" +#include "llvm/DebugInfo/MSF/StreamWriter.h" +#include "llvm/DebugInfo/PDB/GenericError.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/StringTableBuilder.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; +using namespace llvm::support; + +PDBFileBuilder::PDBFileBuilder(BumpPtrAllocator &Allocator) + : Allocator(Allocator) {} + +Error PDBFileBuilder::initialize(uint32_t BlockSize) { + auto ExpectedMsf = MSFBuilder::create(Allocator, BlockSize); + if (!ExpectedMsf) + return ExpectedMsf.takeError(); + Msf = llvm::make_unique(std::move(*ExpectedMsf)); + return Error::success(); +} + +MSFBuilder &PDBFileBuilder::getMsfBuilder() { return *Msf; } + +InfoStreamBuilder &PDBFileBuilder::getInfoBuilder() { + if (!Info) + Info = llvm::make_unique(*Msf, NamedStreams); + return *Info; +} + +DbiStreamBuilder &PDBFileBuilder::getDbiBuilder() { + if (!Dbi) + Dbi = llvm::make_unique(*Msf); + return *Dbi; +} + +TpiStreamBuilder &PDBFileBuilder::getTpiBuilder() { + if (!Tpi) + Tpi = llvm::make_unique(*Msf, StreamTPI); + return *Tpi; +} + +TpiStreamBuilder &PDBFileBuilder::getIpiBuilder() { + if (!Ipi) + Ipi = llvm::make_unique(*Msf, StreamIPI); + return *Ipi; +} + +StringTableBuilder &PDBFileBuilder::getStringTableBuilder() { return Strings; } + +Error PDBFileBuilder::addNamedStream(StringRef Name, uint32_t Size) { + auto ExpectedStream = Msf->addStream(Size); + if (!ExpectedStream) + return ExpectedStream.takeError(); + NamedStreams.set(Name, *ExpectedStream); + return Error::success(); +} + +Expected PDBFileBuilder::finalizeMsfLayout() { + uint32_t StringTableSize = Strings.finalize(); + + if (auto EC = addNamedStream("/names", StringTableSize)) + return std::move(EC); + if (auto EC = addNamedStream("/LinkInfo", 0)) + return std::move(EC); + if (auto EC = addNamedStream("/src/headerblock", 0)) + return std::move(EC); + + if (Info) { + if (auto EC = Info->finalizeMsfLayout()) + return std::move(EC); + } + if (Dbi) { + if (auto EC = Dbi->finalizeMsfLayout()) + return std::move(EC); + } + if (Tpi) { + if (auto EC = Tpi->finalizeMsfLayout()) + return std::move(EC); + } + if (Ipi) { + if (auto EC = Ipi->finalizeMsfLayout()) + return std::move(EC); + } + + return Msf->build(); +} + +Error PDBFileBuilder::commit(StringRef Filename) { + auto ExpectedLayout = finalizeMsfLayout(); + if (!ExpectedLayout) + return ExpectedLayout.takeError(); + auto &Layout = *ExpectedLayout; + + uint64_t Filesize = Layout.SB->BlockSize * Layout.SB->NumBlocks; + auto OutFileOrError = FileOutputBuffer::create(Filename, Filesize); + if (OutFileOrError.getError()) + return llvm::make_error(generic_error_code::invalid_path, + Filename); + FileBufferByteStream Buffer(std::move(*OutFileOrError)); + StreamWriter Writer(Buffer); + + if (auto EC = Writer.writeObject(*Layout.SB)) + return EC; + uint32_t BlockMapOffset = + msf::blockToOffset(Layout.SB->BlockMapAddr, Layout.SB->BlockSize); + Writer.setOffset(BlockMapOffset); + if (auto EC = Writer.writeArray(Layout.DirectoryBlocks)) + return EC; + + auto DirStream = + WritableMappedBlockStream::createDirectoryStream(Layout, Buffer); + StreamWriter DW(*DirStream); + if (auto EC = + DW.writeInteger(static_cast(Layout.StreamSizes.size()))) + return EC; + + if (auto EC = DW.writeArray(Layout.StreamSizes)) + return EC; + + for (const auto &Blocks : Layout.StreamMap) { + if (auto EC = DW.writeArray(Blocks)) + return EC; + } + + uint32_t StringTableStreamNo = 0; + if (!NamedStreams.get("/names", StringTableStreamNo)) + return llvm::make_error(raw_error_code::no_stream); + + auto NS = WritableMappedBlockStream::createIndexedStream(Layout, Buffer, + StringTableStreamNo); + StreamWriter NSWriter(*NS); + if (auto EC = Strings.commit(NSWriter)) + return EC; + + if (Info) { + if (auto EC = Info->commit(Layout, Buffer)) + return EC; + } + + if (Dbi) { + if (auto EC = Dbi->commit(Layout, Buffer)) + return EC; + } + + if (Tpi) { + if (auto EC = Tpi->commit(Layout, Buffer)) + return EC; + } + + if (Ipi) { + if (auto EC = Ipi->commit(Layout, Buffer)) + return EC; + } + + return Buffer.commit(); +} Index: llvm/trunk/lib/DebugInfo/PDB/Native/PublicsStream.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/PublicsStream.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/PublicsStream.cpp @@ -0,0 +1,131 @@ +//===- PublicsStream.cpp - PDB Public Symbol Stream -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// The data structures defined in this file are based on the reference +// implementation which is available at +// https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h +// +// When you are reading the reference source code, you'd find the +// information below useful. +// +// - ppdb1->m_fMinimalDbgInfo seems to be always true. +// - SMALLBUCKETS macro is defined. +// +// The reference doesn't compile, so I learned just by reading code. +// It's not guaranteed to be correct. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/PublicsStream.h" +#include "GSI.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/MSF/StreamReader.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include +#include + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::support; +using namespace llvm::pdb; + +// This is PSGSIHDR struct defined in +// https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h +struct PublicsStream::HeaderInfo { + ulittle32_t SymHash; + ulittle32_t AddrMap; + ulittle32_t NumThunks; + ulittle32_t SizeOfThunk; + ulittle16_t ISectThunkTable; + char Padding[2]; + ulittle32_t OffThunkTable; + ulittle32_t NumSections; +}; + +PublicsStream::PublicsStream(PDBFile &File, + std::unique_ptr Stream) + : Pdb(File), Stream(std::move(Stream)) {} + +PublicsStream::~PublicsStream() = default; + +uint32_t PublicsStream::getSymHash() const { return Header->SymHash; } +uint32_t PublicsStream::getAddrMap() const { return Header->AddrMap; } + +// Publics stream contains fixed-size headers and a serialized hash table. +// This implementation is not complete yet. It reads till the end of the +// stream so that we verify the stream is at least not corrupted. However, +// we skip over the hash table which we believe contains information about +// public symbols. +Error PublicsStream::reload() { + StreamReader Reader(*Stream); + + // Check stream size. + if (Reader.bytesRemaining() < sizeof(HeaderInfo) + sizeof(GSIHashHeader)) + return make_error(raw_error_code::corrupt_file, + "Publics Stream does not contain a header."); + + // Read PSGSIHDR and GSIHashHdr structs. + if (Reader.readObject(Header)) + return make_error(raw_error_code::corrupt_file, + "Publics Stream does not contain a header."); + + if (auto EC = readGSIHashHeader(HashHdr, Reader)) + return EC; + + if (auto EC = readGSIHashRecords(HashRecords, HashHdr, Reader)) + return EC; + + if (auto EC = readGSIHashBuckets(HashBuckets, HashHdr, Reader)) + return EC; + NumBuckets = HashBuckets.size(); + + // Something called "address map" follows. + uint32_t NumAddressMapEntries = Header->AddrMap / sizeof(uint32_t); + if (auto EC = Reader.readArray(AddressMap, NumAddressMapEntries)) + return joinErrors(std::move(EC), + make_error(raw_error_code::corrupt_file, + "Could not read an address map.")); + + // Something called "thunk map" follows. + if (auto EC = Reader.readArray(ThunkMap, Header->NumThunks)) + return joinErrors(std::move(EC), + make_error(raw_error_code::corrupt_file, + "Could not read a thunk map.")); + + // Something called "section map" follows. + if (auto EC = Reader.readArray(SectionOffsets, Header->NumSections)) + return joinErrors(std::move(EC), + make_error(raw_error_code::corrupt_file, + "Could not read a section map.")); + + if (Reader.bytesRemaining() > 0) + return make_error(raw_error_code::corrupt_file, + "Corrupted publics stream."); + return Error::success(); +} + +iterator_range +PublicsStream::getSymbols(bool *HadError) const { + auto SymbolS = Pdb.getPDBSymbolStream(); + if (SymbolS.takeError()) { + codeview::CVSymbolArray::Iterator Iter; + return make_range(Iter, Iter); + } + SymbolStream &SS = SymbolS.get(); + + return SS.getSymbols(HadError); +} + +Error PublicsStream::commit() { return Error::success(); } Index: llvm/trunk/lib/DebugInfo/PDB/Native/RawError.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/RawError.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/RawError.cpp @@ -0,0 +1,73 @@ +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ManagedStatic.h" + +using namespace llvm; +using namespace llvm::pdb; + +namespace { +// FIXME: This class is only here to support the transition to llvm::Error. It +// will be removed once this transition is complete. Clients should prefer to +// deal with the Error value directly, rather than converting to error_code. +class RawErrorCategory : public std::error_category { +public: + const char *name() const noexcept override { return "llvm.pdb.raw"; } + + std::string message(int Condition) const override { + switch (static_cast(Condition)) { + case raw_error_code::unspecified: + return "An unknown error has occurred."; + case raw_error_code::feature_unsupported: + return "The feature is unsupported by the implementation."; + case raw_error_code::invalid_format: + return "The record is in an unexpected format."; + case raw_error_code::corrupt_file: + return "The PDB file is corrupt."; + case raw_error_code::insufficient_buffer: + return "The buffer is not large enough to read the requested number of " + "bytes."; + case raw_error_code::no_stream: + return "The specified stream could not be loaded."; + case raw_error_code::index_out_of_bounds: + return "The specified item does not exist in the array."; + case raw_error_code::invalid_block_address: + return "The specified block address is not valid."; + case raw_error_code::duplicate_entry: + return "The entry already exists."; + case raw_error_code::no_entry: + return "The entry does not exist."; + case raw_error_code::not_writable: + return "The PDB does not support writing."; + case raw_error_code::invalid_tpi_hash: + return "The Type record has an invalid hash value."; + } + llvm_unreachable("Unrecognized raw_error_code"); + } +}; +} // end anonymous namespace + +static ManagedStatic Category; + +char RawError::ID = 0; + +RawError::RawError(raw_error_code C) : RawError(C, "") {} + +RawError::RawError(const std::string &Context) + : RawError(raw_error_code::unspecified, Context) {} + +RawError::RawError(raw_error_code C, const std::string &Context) : Code(C) { + ErrMsg = "Native PDB Error: "; + std::error_code EC = convertToErrorCode(); + if (Code != raw_error_code::unspecified) + ErrMsg += EC.message() + " "; + if (!Context.empty()) + ErrMsg += Context; +} + +void RawError::log(raw_ostream &OS) const { OS << ErrMsg << "\n"; } + +const std::string &RawError::getErrorMessage() const { return ErrMsg; } + +std::error_code RawError::convertToErrorCode() const { + return std::error_code(static_cast(Code), *Category); +} Index: llvm/trunk/lib/DebugInfo/PDB/Native/StringTable.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/StringTable.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/StringTable.cpp @@ -0,0 +1,99 @@ +//===- StringTable.cpp - PDB String Table -----------------------*- C++ -*-===// +// +// 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/Native/StringTable.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/MSF/StreamReader.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::support; +using namespace llvm::pdb; + +StringTable::StringTable() : Signature(0), HashVersion(0), NameCount(0) {} + +Error StringTable::load(StreamReader &Stream) { + const StringTableHeader *H; + if (auto EC = Stream.readObject(H)) + return EC; + + if (H->Signature != StringTableSignature) + return make_error(raw_error_code::corrupt_file, + "Invalid hash table signature"); + if (H->HashVersion != 1 && H->HashVersion != 2) + return make_error(raw_error_code::corrupt_file, + "Unsupported hash version"); + + Signature = H->Signature; + HashVersion = H->HashVersion; + if (auto EC = Stream.readStreamRef(NamesBuffer, H->ByteSize)) + return joinErrors(std::move(EC), + make_error(raw_error_code::corrupt_file, + "Invalid hash table byte length")); + + const support::ulittle32_t *HashCount; + if (auto EC = Stream.readObject(HashCount)) + return EC; + + if (auto EC = Stream.readArray(IDs, *HashCount)) + return joinErrors(std::move(EC), + make_error(raw_error_code::corrupt_file, + "Could not read bucket array")); + + if (Stream.bytesRemaining() < sizeof(support::ulittle32_t)) + return make_error(raw_error_code::corrupt_file, + "Missing name count"); + + if (auto EC = Stream.readInteger(NameCount)) + return EC; + return Error::success(); +} + +StringRef StringTable::getStringForID(uint32_t ID) const { + if (ID == IDs[0]) + return StringRef(); + + // NamesBuffer is a buffer of null terminated strings back to back. ID is + // the starting offset of the string we're looking for. So just seek into + // the desired offset and a read a null terminated stream from that offset. + StringRef Result; + StreamReader NameReader(NamesBuffer); + NameReader.setOffset(ID); + if (auto EC = NameReader.readZeroString(Result)) + consumeError(std::move(EC)); + return Result; +} + +uint32_t StringTable::getIDForString(StringRef Str) const { + uint32_t Hash = (HashVersion == 1) ? hashStringV1(Str) : hashStringV2(Str); + size_t Count = IDs.size(); + uint32_t Start = Hash % Count; + for (size_t I = 0; I < Count; ++I) { + // The hash is just a starting point for the search, but if it + // doesn't work we should find the string no matter what, because + // we iterate the entire array. + uint32_t Index = (Start + I) % Count; + + uint32_t ID = IDs[Index]; + StringRef S = getStringForID(ID); + if (S == Str) + return ID; + } + // IDs[0] contains the ID of the "invalid" entry. + return IDs[0]; +} + +FixedStreamArray StringTable::name_ids() const { + return IDs; +} Index: llvm/trunk/lib/DebugInfo/PDB/Native/StringTableBuilder.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/StringTableBuilder.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/StringTableBuilder.cpp @@ -0,0 +1,102 @@ +//===- StringTableBuilder.cpp - PDB String Table ----------------*- C++ -*-===// +// +// 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/Native/StringTableBuilder.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/MSF/StreamWriter.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::support; +using namespace llvm::support::endian; +using namespace llvm::pdb; + +uint32_t StringTableBuilder::insert(StringRef S) { + auto P = Strings.insert({S, StringSize}); + + // If a given string didn't exist in the string table, we want to increment + // the string table size. + if (P.second) + StringSize += S.size() + 1; // +1 for '\0' + return P.first->second; +} + +static uint32_t computeBucketCount(uint32_t NumStrings) { + // The /names stream is basically an on-disk open-addressing hash table. + // Hash collisions are resolved by linear probing. We cannot make + // utilization 100% because it will make the linear probing extremely + // slow. But lower utilization wastes disk space. As a reasonable + // load factor, we choose 80%. We need +1 because slot 0 is reserved. + return (NumStrings + 1) * 1.25; +} + +uint32_t StringTableBuilder::finalize() { + uint32_t Size = 0; + Size += sizeof(StringTableHeader); + Size += StringSize; + Size += sizeof(uint32_t); // Hash table begins with 4-byte size field. + + uint32_t BucketCount = computeBucketCount(Strings.size()); + Size += BucketCount * sizeof(uint32_t); + + Size += + sizeof(uint32_t); // The /names stream ends with the number of strings. + return Size; +} + +Error StringTableBuilder::commit(msf::StreamWriter &Writer) const { + // Write a header + StringTableHeader H; + H.Signature = StringTableSignature; + H.HashVersion = 1; + H.ByteSize = StringSize; + if (auto EC = Writer.writeObject(H)) + return EC; + + // Write a string table. + uint32_t StringStart = Writer.getOffset(); + for (auto Pair : Strings) { + StringRef S = Pair.first; + uint32_t Offset = Pair.second; + Writer.setOffset(StringStart + Offset); + if (auto EC = Writer.writeZeroString(S)) + return EC; + } + Writer.setOffset(StringStart + StringSize); + + // Write a hash table. + uint32_t BucketCount = computeBucketCount(Strings.size()); + if (auto EC = Writer.writeInteger(BucketCount)) + return EC; + std::vector Buckets(BucketCount); + + for (auto Pair : Strings) { + StringRef S = Pair.first; + uint32_t Offset = Pair.second; + uint32_t Hash = hashStringV1(S); + + for (uint32_t I = 0; I != BucketCount; ++I) { + uint32_t Slot = (Hash + I) % BucketCount; + if (Slot == 0) + continue; // Skip reserved slot + if (Buckets[Slot] != 0) + continue; + Buckets[Slot] = Offset; + break; + } + } + + if (auto EC = Writer.writeArray(ArrayRef(Buckets))) + return EC; + if (auto EC = Writer.writeInteger(static_cast(Strings.size()))) + return EC; + return Error::success(); +} Index: llvm/trunk/lib/DebugInfo/PDB/Native/SymbolStream.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/SymbolStream.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/SymbolStream.cpp @@ -0,0 +1,46 @@ +//===- SymbolStream.cpp - PDB Symbol Stream Access ------------------------===// +// +// 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/Native/SymbolStream.h" + +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/MSF/StreamReader.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" + +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::support; +using namespace llvm::pdb; + +SymbolStream::SymbolStream(std::unique_ptr Stream) + : Stream(std::move(Stream)) {} + +SymbolStream::~SymbolStream() {} + +Error SymbolStream::reload() { + StreamReader Reader(*Stream); + + if (auto EC = Reader.readArray(SymbolRecords, Stream->getLength())) + return EC; + + return Error::success(); +} + +iterator_range +SymbolStream::getSymbols(bool *HadError) const { + return llvm::make_range(SymbolRecords.begin(HadError), SymbolRecords.end()); +} + +Error SymbolStream::commit() { return Error::success(); } Index: llvm/trunk/lib/DebugInfo/PDB/Native/TpiHashing.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/TpiHashing.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/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/Native/TpiHashing.h" + +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/DebugInfo/PDB/Native/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.data()); +} + +void TpiHashUpdater::visitKnownRecordImpl(CVType &CVR, EnumRecord &Rec) { + CVR.Hash = getTpiHash(Rec, CVR.data()); +} + +void TpiHashUpdater::visitKnownRecordImpl(CVType &CVR, UnionRecord &Rec) { + CVR.Hash = getTpiHash(Rec, CVR.data()); +} + +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.data()) % NumHashBuckets != HashValues[Index]) + return errorInvalidHash(); + return Error::success(); +} +Error TpiHashVerifier::visitKnownRecord(CVType &CVR, EnumRecord &Rec) { + if (getTpiHash(Rec, CVR.data()) % NumHashBuckets != HashValues[Index]) + return errorInvalidHash(); + return Error::success(); +} +Error TpiHashVerifier::visitKnownRecord(CVType &CVR, UnionRecord &Rec) { + if (getTpiHash(Rec, CVR.data()) % 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: llvm/trunk/lib/DebugInfo/PDB/Native/TpiStream.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/TpiStream.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/TpiStream.cpp @@ -0,0 +1,171 @@ +//===- TpiStream.cpp - PDB Type Info (TPI) Stream 2 Access ----------------===// +// +// 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/Native/TpiStream.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/MSF/StreamReader.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/DebugInfo/PDB/Native/TpiHashing.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include +#include +#include + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::support; +using namespace llvm::msf; +using namespace llvm::pdb; + +TpiStream::TpiStream(const PDBFile &File, + std::unique_ptr Stream) + : Pdb(File), Stream(std::move(Stream)) {} + +TpiStream::~TpiStream() = default; + +// Verifies that a given type record matches with a given hash value. +// Currently we only verify SRC_LINE records. +Error TpiStream::verifyHashValues() { + TpiHashVerifier Verifier(HashValues, Header->NumHashBuckets); + TypeDeserializer Deserializer; + + TypeVisitorCallbackPipeline Pipeline; + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Verifier); + + CVTypeVisitor Visitor(Pipeline); + return Visitor.visitTypeStream(TypeRecords); +} + +Error TpiStream::reload() { + StreamReader Reader(*Stream); + + if (Reader.bytesRemaining() < sizeof(TpiStreamHeader)) + return make_error(raw_error_code::corrupt_file, + "TPI Stream does not contain a header."); + + if (Reader.readObject(Header)) + return make_error(raw_error_code::corrupt_file, + "TPI Stream does not contain a header."); + + if (Header->Version != PdbTpiV80) + return make_error(raw_error_code::corrupt_file, + "Unsupported TPI Version."); + + if (Header->HeaderSize != sizeof(TpiStreamHeader)) + return make_error(raw_error_code::corrupt_file, + "Corrupt TPI Header size."); + + if (Header->HashKeySize != sizeof(ulittle32_t)) + return make_error(raw_error_code::corrupt_file, + "TPI Stream expected 4 byte hash key size."); + + if (Header->NumHashBuckets < MinTpiHashBuckets || + Header->NumHashBuckets > MaxTpiHashBuckets) + return make_error(raw_error_code::corrupt_file, + "TPI Stream Invalid number of hash buckets."); + + // The actual type records themselves come from this stream + if (auto EC = Reader.readArray(TypeRecords, Header->TypeRecordBytes)) + return EC; + + // Hash indices, hash values, etc come from the hash stream. + if (Header->HashStreamIndex != kInvalidStreamIndex) { + if (Header->HashStreamIndex >= Pdb.getNumStreams()) + return make_error(raw_error_code::corrupt_file, + "Invalid TPI hash stream index."); + + auto HS = MappedBlockStream::createIndexedStream( + Pdb.getMsfLayout(), Pdb.getMsfBuffer(), Header->HashStreamIndex); + StreamReader HSR(*HS); + + uint32_t NumHashValues = + Header->HashValueBuffer.Length / sizeof(ulittle32_t); + if (NumHashValues != NumTypeRecords()) + return make_error( + 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; + std::vector HashValueList; + for (auto I : HashValues) + HashValueList.push_back(I); + + HSR.setOffset(Header->IndexOffsetBuffer.Off); + uint32_t NumTypeIndexOffsets = + Header->IndexOffsetBuffer.Length / sizeof(TypeIndexOffset); + if (auto EC = HSR.readArray(TypeIndexOffsets, NumTypeIndexOffsets)) + return EC; + + if (Header->HashAdjBuffer.Length > 0) { + HSR.setOffset(Header->HashAdjBuffer.Off); + if (auto EC = HashAdjusters.load(HSR)) + 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(); +} + +PdbRaw_TpiVer TpiStream::getTpiVersion() const { + uint32_t Value = Header->Version; + return static_cast(Value); +} + +uint32_t TpiStream::TypeIndexBegin() const { return Header->TypeIndexBegin; } + +uint32_t TpiStream::TypeIndexEnd() const { return Header->TypeIndexEnd; } + +uint32_t TpiStream::NumTypeRecords() const { + return TypeIndexEnd() - TypeIndexBegin(); +} + +uint16_t TpiStream::getTypeHashStreamIndex() const { + return Header->HashStreamIndex; +} + +uint16_t TpiStream::getTypeHashStreamAuxIndex() const { + return Header->HashAuxStreamIndex; +} + +uint32_t TpiStream::NumHashBuckets() const { return Header->NumHashBuckets; } +uint32_t TpiStream::getHashKeySize() const { return Header->HashKeySize; } + +FixedStreamArray TpiStream::getHashValues() const { + return HashValues; +} + +FixedStreamArray TpiStream::getTypeIndexOffsets() const { + return TypeIndexOffsets; +} + +HashTable &TpiStream::getHashAdjusters() { return HashAdjusters; } + +iterator_range TpiStream::types(bool *HadError) const { + return make_range(TypeRecords.begin(HadError), TypeRecords.end()); +} + +Error TpiStream::commit() { return Error::success(); } Index: llvm/trunk/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp +++ llvm/trunk/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp @@ -0,0 +1,145 @@ +//===- TpiStreamBuilder.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/Native/TpiStreamBuilder.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/MSF/ByteStream.h" +#include "llvm/DebugInfo/MSF/MSFBuilder.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/MSF/StreamArray.h" +#include "llvm/DebugInfo/MSF/StreamReader.h" +#include "llvm/DebugInfo/MSF/StreamWriter.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include +#include + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::pdb; +using namespace llvm::support; + +TpiStreamBuilder::TpiStreamBuilder(MSFBuilder &Msf, uint32_t StreamIdx) + : Msf(Msf), Allocator(Msf.getAllocator()), Header(nullptr), Idx(StreamIdx) { +} + +TpiStreamBuilder::~TpiStreamBuilder() = default; + +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(); + + uint32_t Count = TypeRecords.size(); + uint32_t HashBufferSize = calculateHashBufferSize(); + + H->Version = *VerHeader; + H->HeaderSize = sizeof(TpiStreamHeader); + H->TypeIndexBegin = codeview::TypeIndex::FirstNonSimpleIndex; + H->TypeIndexEnd = H->TypeIndexBegin + Count; + H->TypeRecordBytes = TypeRecordStream.getLength(); + + H->HashStreamIndex = HashStreamIndex; + H->HashAuxStreamIndex = kInvalidStreamIndex; + H->HashKeySize = sizeof(ulittle32_t); + H->NumHashBuckets = MinTpiHashBuckets; + + // 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; + return Error::success(); +} + +uint32_t TpiStreamBuilder::calculateSerializedLength() const { + return sizeof(TpiStreamHeader) + TypeRecordStream.getLength(); +} + +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(Idx, Length)) + return 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(); +} + +Error TpiStreamBuilder::commit(const msf::MSFLayout &Layout, + const msf::WritableStream &Buffer) { + if (auto EC = finalize()) + return EC; + + auto InfoS = + WritableMappedBlockStream::createIndexedStream(Layout, Buffer, Idx); + + StreamWriter Writer(*InfoS); + if (auto EC = Writer.writeObject(*Header)) + return EC; + + auto RecordArray = VarStreamArray(TypeRecordStream); + 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: llvm/trunk/lib/DebugInfo/PDB/PDB.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/PDB.cpp +++ llvm/trunk/lib/DebugInfo/PDB/PDB.cpp @@ -17,7 +17,7 @@ #if LLVM_ENABLE_DIA_SDK #include "llvm/DebugInfo/PDB/DIA/DIASession.h" #endif -#include "llvm/DebugInfo/PDB/Raw/RawSession.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ManagedStatic.h" @@ -28,7 +28,7 @@ std::unique_ptr &Session) { // Create the correct concrete instance type based on the value of Type. if (Type == PDB_ReaderType::Raw) - return RawSession::createFromPdb(Path, Session); + return NativeSession::createFromPdb(Path, Session); #if LLVM_ENABLE_DIA_SDK return DIASession::createFromPdb(Path, Session); @@ -41,7 +41,7 @@ std::unique_ptr &Session) { // Create the correct concrete instance type based on the value of Type. if (Type == PDB_ReaderType::Raw) - return RawSession::createFromExe(Path, Session); + return NativeSession::createFromExe(Path, Session); #if LLVM_ENABLE_DIA_SDK return DIASession::createFromExe(Path, Session); Index: llvm/trunk/tools/llvm-pdbdump/LLVMOutputStyle.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbdump/LLVMOutputStyle.cpp +++ llvm/trunk/tools/llvm-pdbdump/LLVMOutputStyle.cpp @@ -23,18 +23,18 @@ #include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" #include "llvm/DebugInfo/MSF/StreamReader.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/EnumTables.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/ModInfo.h" +#include "llvm/DebugInfo/PDB/Native/ModStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PublicsStream.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/DebugInfo/PDB/PDBExtras.h" -#include "llvm/DebugInfo/PDB/Raw/DbiStream.h" -#include "llvm/DebugInfo/PDB/Raw/EnumTables.h" -#include "llvm/DebugInfo/PDB/Raw/GlobalsStream.h" -#include "llvm/DebugInfo/PDB/Raw/ISectionContribVisitor.h" -#include "llvm/DebugInfo/PDB/Raw/InfoStream.h" -#include "llvm/DebugInfo/PDB/Raw/ModInfo.h" -#include "llvm/DebugInfo/PDB/Raw/ModStream.h" -#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" -#include "llvm/DebugInfo/PDB/Raw/PublicsStream.h" -#include "llvm/DebugInfo/PDB/Raw/RawError.h" -#include "llvm/DebugInfo/PDB/Raw/TpiStream.h" #include "llvm/Object/COFF.h" #include "llvm/Support/FormatVariadic.h" Index: llvm/trunk/tools/llvm-pdbdump/PdbYaml.h =================================================================== --- llvm/trunk/tools/llvm-pdbdump/PdbYaml.h +++ llvm/trunk/tools/llvm-pdbdump/PdbYaml.h @@ -16,9 +16,9 @@ #include "llvm/DebugInfo/CodeView/SymbolRecord.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/MSF/MSFCommon.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" #include "llvm/DebugInfo/PDB/PDBTypes.h" -#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" -#include "llvm/DebugInfo/PDB/Raw/RawConstants.h" #include "llvm/Support/Endian.h" #include "llvm/Support/YAMLTraits.h" Index: llvm/trunk/tools/llvm-pdbdump/PdbYaml.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbdump/PdbYaml.cpp +++ llvm/trunk/tools/llvm-pdbdump/PdbYaml.cpp @@ -20,10 +20,10 @@ #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" #include "llvm/DebugInfo/CodeView/TypeSerializer.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/TpiHashing.h" #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; Index: llvm/trunk/tools/llvm-pdbdump/YAMLOutputStyle.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbdump/YAMLOutputStyle.cpp +++ llvm/trunk/tools/llvm-pdbdump/YAMLOutputStyle.cpp @@ -13,12 +13,12 @@ #include "llvm-pdbdump.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" -#include "llvm/DebugInfo/PDB/Raw/DbiStream.h" -#include "llvm/DebugInfo/PDB/Raw/InfoStream.h" -#include "llvm/DebugInfo/PDB/Raw/ModStream.h" -#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" -#include "llvm/DebugInfo/PDB/Raw/RawConstants.h" -#include "llvm/DebugInfo/PDB/Raw/TpiStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/ModStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" using namespace llvm; using namespace llvm::pdb; Index: llvm/trunk/tools/llvm-pdbdump/YamlTypeDumper.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbdump/YamlTypeDumper.cpp +++ llvm/trunk/tools/llvm-pdbdump/YamlTypeDumper.cpp @@ -17,7 +17,7 @@ #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/CodeView/TypeSerializer.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" -#include "llvm/DebugInfo/PDB/Raw/TpiHashing.h" +#include "llvm/DebugInfo/PDB/Native/TpiHashing.h" using namespace llvm; using namespace llvm::codeview; Index: llvm/trunk/tools/llvm-pdbdump/llvm-pdbdump.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbdump/llvm-pdbdump.cpp +++ llvm/trunk/tools/llvm-pdbdump/llvm-pdbdump.cpp @@ -35,24 +35,24 @@ #include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" #include "llvm/DebugInfo/PDB/IPDBRawSymbol.h" #include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/StringTableBuilder.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h" #include "llvm/DebugInfo/PDB/PDB.h" #include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" #include "llvm/DebugInfo/PDB/PDBSymbolData.h" #include "llvm/DebugInfo/PDB/PDBSymbolExe.h" #include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" #include "llvm/DebugInfo/PDB/PDBSymbolThunk.h" -#include "llvm/DebugInfo/PDB/Raw/DbiStream.h" -#include "llvm/DebugInfo/PDB/Raw/DbiStreamBuilder.h" -#include "llvm/DebugInfo/PDB/Raw/InfoStream.h" -#include "llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h" -#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" -#include "llvm/DebugInfo/PDB/Raw/PDBFileBuilder.h" -#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/StringTableBuilder.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" @@ -404,7 +404,7 @@ std::unique_ptr Session; ExitOnErr(loadDataForPDB(PDB_ReaderType::Raw, Path, Session)); - RawSession *RS = static_cast(Session.get()); + NativeSession *RS = static_cast(Session.get()); PDBFile &File = RS->getPDBFile(); auto O = llvm::make_unique(File); O = llvm::make_unique(File); @@ -416,7 +416,7 @@ std::unique_ptr Session; ExitOnErr(loadDataForPDB(PDB_ReaderType::Raw, Path, Session)); - RawSession *RS = static_cast(Session.get()); + NativeSession *RS = static_cast(Session.get()); PDBFile &File = RS->getPDBFile(); auto O = llvm::make_unique(File);