Index: include/llvm/DebugInfo/PDB/Raw/GlobalsStream.h =================================================================== --- /dev/null +++ include/llvm/DebugInfo/PDB/Raw/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/PDBTypes.h" +#include "llvm/DebugInfo/PDB/Raw/RawConstants.h" +#include "llvm/DebugInfo/PDB/Raw/RawTypes.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace pdb { +class DbiStream; +class PDBFile; + +class GlobalsStream { +public: + 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: include/llvm/DebugInfo/PDB/Raw/PDBFile.h =================================================================== --- include/llvm/DebugInfo/PDB/Raw/PDBFile.h +++ include/llvm/DebugInfo/PDB/Raw/PDBFile.h @@ -31,6 +31,7 @@ namespace pdb { class DbiStream; +class GlobalsStream; class InfoStream; class NameHashTable; class PDBFileBuilder; @@ -86,6 +87,7 @@ Expected getPDBInfoStream(); Expected getPDBDbiStream(); + Expected getPDBGlobalsStream(); Expected getPDBTpiStream(); Expected getPDBIpiStream(); Expected getPDBPublicsStream(); @@ -102,6 +104,7 @@ std::vector FpmPages; msf::MSFLayout ContainerLayout; + std::unique_ptr Globals; std::unique_ptr Info; std::unique_ptr Dbi; std::unique_ptr Tpi; Index: include/llvm/DebugInfo/PDB/Raw/PublicsStream.h =================================================================== --- include/llvm/DebugInfo/PDB/Raw/PublicsStream.h +++ include/llvm/DebugInfo/PDB/Raw/PublicsStream.h @@ -22,10 +22,10 @@ namespace llvm { namespace pdb { class DbiStream; +struct GSIHashHeader; class PDBFile; class PublicsStream { - struct GSIHashHeader; struct HeaderInfo; public: Index: lib/DebugInfo/PDB/CMakeLists.txt =================================================================== --- lib/DebugInfo/PDB/CMakeLists.txt +++ lib/DebugInfo/PDB/CMakeLists.txt @@ -31,6 +31,8 @@ Raw/DbiStream.cpp Raw/DbiStreamBuilder.cpp Raw/EnumTables.cpp + Raw/GlobalsStream.cpp + Raw/GSI.cpp Raw/Hash.cpp Raw/InfoStream.cpp Raw/InfoStreamBuilder.cpp Index: lib/DebugInfo/PDB/Raw/GSI.h =================================================================== --- /dev/null +++ lib/DebugInfo/PDB/Raw/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/Raw/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: lib/DebugInfo/PDB/Raw/GSI.cpp =================================================================== --- /dev/null +++ lib/DebugInfo/PDB/Raw/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/Raw/RawError.h" +#include "llvm/DebugInfo/PDB/Raw/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: lib/DebugInfo/PDB/Raw/GlobalsStream.cpp =================================================================== --- /dev/null +++ lib/DebugInfo/PDB/Raw/GlobalsStream.cpp @@ -0,0 +1,46 @@ +//===- 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/Raw/GlobalsStream.h" + +#include "GSI.h" +#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" +#include "llvm/DebugInfo/PDB/Raw/RawError.h" +#include "llvm/DebugInfo/PDB/Raw/RawTypes.h" + +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::pdb; + +GlobalsStream::GlobalsStream(std::unique_ptr Stream) + : Stream(std::move(Stream)) {} + +GlobalsStream::~GlobalsStream() {} + +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: lib/DebugInfo/PDB/Raw/PDBFile.cpp =================================================================== --- lib/DebugInfo/PDB/Raw/PDBFile.cpp +++ lib/DebugInfo/PDB/Raw/PDBFile.cpp @@ -15,6 +15,7 @@ #include "llvm/DebugInfo/MSF/StreamReader.h" #include "llvm/DebugInfo/MSF/StreamWriter.h" #include "llvm/DebugInfo/PDB/Raw/DbiStream.h" +#include "llvm/DebugInfo/PDB/Raw/GlobalsStream.h" #include "llvm/DebugInfo/PDB/Raw/InfoStream.h" #include "llvm/DebugInfo/PDB/Raw/NameHashTable.h" #include "llvm/DebugInfo/PDB/Raw/PublicsStream.h" @@ -217,6 +218,22 @@ return ContainerLayout.DirectoryBlocks; } +Expected PDBFile::getPDBGlobalsStream() { + if (!Globals) { + auto DbiS = getPDBDbiStream(); + if (!DbiS) + return DbiS.takeError(); + + auto GlobalS = MappedBlockStream::createIndexedStream( + ContainerLayout, *Buffer, DbiS->getGlobalSymbolStreamIndex()); + 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 = MappedBlockStream::createIndexedStream(ContainerLayout, Index: lib/DebugInfo/PDB/Raw/PublicsStream.cpp =================================================================== --- lib/DebugInfo/PDB/Raw/PublicsStream.cpp +++ lib/DebugInfo/PDB/Raw/PublicsStream.cpp @@ -24,6 +24,7 @@ #include "llvm/DebugInfo/PDB/Raw/PublicsStream.h" +#include "GSI.h" #include "llvm/DebugInfo/CodeView/CodeView.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" @@ -44,8 +45,6 @@ using namespace llvm::pdb; -static const unsigned IPHR_HASH = 4096; - // This is PSGSIHDR struct defined in // https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h struct PublicsStream::HeaderInfo { @@ -59,18 +58,6 @@ ulittle32_t NumSections; }; -// This is GSIHashHdr. -struct PublicsStream::GSIHashHeader { - enum : unsigned { - HdrSignature = ~0U, - HdrVersion = 0xeffe0000 + 19990810, - }; - ulittle32_t VerSignature; - ulittle32_t VerHdr; - ulittle32_t HrSize; - ulittle32_t NumBuckets; -}; - PublicsStream::PublicsStream(PDBFile &File, std::unique_ptr Stream) : Pdb(File), Stream(std::move(Stream)) {} @@ -98,40 +85,15 @@ return make_error(raw_error_code::corrupt_file, "Publics Stream does not contain a header."); - if (Reader.readObject(HashHdr)) - return make_error(raw_error_code::corrupt_file, - "Publics Stream does not contain a header."); - - // An array of HashRecord follows. Read them. - 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, - "Could not read an HR array")); - - // A bitmap of a fixed length follows. - 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.")); - for (uint8_t B : Bitmap) - NumBuckets += countPopulation(B); + if (auto EC = readGSIHashHeader(HashHdr, Reader)) + return EC; - // We don't yet understand the following data structures completely, - // but we at least know the types and sizes. Here we are trying - // to read the stream till end so that we at least can detect - // corrupted streams. + if (auto EC = readGSIHashRecords(HashRecords, HashHdr, Reader)) + return EC; - // 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.")); + 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); Index: test/DebugInfo/PDB/pdbdump-headers.test =================================================================== --- test/DebugInfo/PDB/pdbdump-headers.test +++ test/DebugInfo/PDB/pdbdump-headers.test @@ -794,7 +794,12 @@ ; EMPTY-NEXT: SecByteLength: 4294967295 ; EMPTY-NEXT: } ; EMPTY-NEXT: ] -; EMPTY: Publics Stream { +; EMPTY-NEXT: Globals Stream { +; EMPTY-NEXT: Stream number: 6 +; EMPTY-NEXT: Number of buckets: 2 +; EMPTY-NEXT: Hash Buckets: [0, 12] +; EMPTY-NEXT: } +; EMPTY-NEXT: Publics Stream { ; EMPTY-NEXT: Stream number: 7 ; EMPTY-NEXT: SymHash: 556 ; EMPTY-NEXT: AddrMap: 8 @@ -1552,6 +1557,11 @@ ; ALL: SecByteLength: 4294967295 ; ALL: } ; ALL: ] +; ALL: Globals Stream { +; ALL: Stream number: 6 +; ALL: Number of buckets: 2 +; ALL: Hash Buckets: [0, 12] +; ALL: } ; ALL: Publics Stream { ; ALL: Stream number: 7 ; ALL: SymHash: 556 Index: tools/llvm-pdbdump/LLVMOutputStyle.h =================================================================== --- tools/llvm-pdbdump/LLVMOutputStyle.h +++ tools/llvm-pdbdump/LLVMOutputStyle.h @@ -31,6 +31,7 @@ Error dumpStreamSummary(); Error dumpFreePageMap(); Error dumpBlockRanges(); + Error dumpGlobalsStream(); Error dumpStreamBytes(); Error dumpStreamBlocks(); Error dumpInfoStream(); Index: tools/llvm-pdbdump/LLVMOutputStyle.cpp =================================================================== --- tools/llvm-pdbdump/LLVMOutputStyle.cpp +++ tools/llvm-pdbdump/LLVMOutputStyle.cpp @@ -18,6 +18,7 @@ #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" @@ -122,6 +123,9 @@ if (auto EC = dumpSectionMap()) return EC; + if (auto EC = dumpGlobalsStream()) + return EC; + if (auto EC = dumpPublicsStream()) return EC; @@ -343,6 +347,26 @@ P.printList(Name, Vec); } +Error LLVMOutputStyle::dumpGlobalsStream() { + if (!opts::raw::DumpGlobals) + return Error::success(); + + DictScope D(P, "Globals Stream"); + auto Globals = File.getPDBGlobalsStream(); + if (!Globals) + return Globals.takeError(); + + auto Dbi = File.getPDBDbiStream(); + if (!Dbi) + return Dbi.takeError(); + + P.printNumber("Stream number", Dbi->getGlobalSymbolStreamIndex()); + P.printNumber("Number of buckets", Globals->getNumBuckets()); + P.printList("Hash Buckets", Globals->getHashBuckets()); + + return Error::success(); +} + Error LLVMOutputStyle::dumpStreamBlocks() { if (!opts::raw::DumpStreamBlocks) return Error::success(); Index: tools/llvm-pdbdump/llvm-pdbdump.h =================================================================== --- tools/llvm-pdbdump/llvm-pdbdump.h +++ tools/llvm-pdbdump/llvm-pdbdump.h @@ -43,6 +43,7 @@ extern llvm::Optional DumpBlockRange; extern llvm::cl::list DumpStreamData; +extern llvm::cl::opt DumpGlobals; extern llvm::cl::opt DumpHeaders; extern llvm::cl::opt DumpStreamBlocks; extern llvm::cl::opt DumpStreamSummary; Index: tools/llvm-pdbdump/llvm-pdbdump.cpp =================================================================== --- tools/llvm-pdbdump/llvm-pdbdump.cpp +++ tools/llvm-pdbdump/llvm-pdbdump.cpp @@ -215,6 +215,8 @@ cl::cat(FileOptions), cl::sub(RawSubcommand)); // SYMBOL OPTIONS +cl::opt DumpGlobals("globals", cl::desc("dump globals stream data"), + cl::cat(SymbolOptions), cl::sub(RawSubcommand)); cl::opt DumpModuleSyms("module-syms", cl::desc("dump module symbols"), cl::cat(SymbolOptions), cl::sub(RawSubcommand)); cl::opt DumpPublics("publics", cl::desc("dump Publics stream data"), @@ -559,6 +561,7 @@ opts::raw::DumpModules = true; opts::raw::DumpModuleFiles = true; opts::raw::DumpModuleSyms = true; + opts::raw::DumpGlobals = true; opts::raw::DumpPublics = true; opts::raw::DumpSectionHeaders = true; opts::raw::DumpStreamSummary = true;