Index: include/llvm/DebugInfo/PDB/Raw/DbiStream.h =================================================================== --- include/llvm/DebugInfo/PDB/Raw/DbiStream.h +++ include/llvm/DebugInfo/PDB/Raw/DbiStream.h @@ -33,6 +33,7 @@ PdbRaw_DbiVer getDbiVersion() const; uint32_t getAge() const; + uint16_t getPublicSymbolStreamIndex() const; bool isIncrementallyLinked() const; bool hasCTypes() const; Index: include/llvm/DebugInfo/PDB/Raw/PDBFile.h =================================================================== --- include/llvm/DebugInfo/PDB/Raw/PDBFile.h +++ include/llvm/DebugInfo/PDB/Raw/PDBFile.h @@ -24,6 +24,7 @@ struct PDBFileContext; class DbiStream; class InfoStream; +class PublicsStream; class TpiStream; class PDBFile { @@ -62,12 +63,14 @@ Expected getPDBInfoStream(); Expected getPDBDbiStream(); Expected getPDBTpiStream(); + Expected getPDBPublicsStream(); private: std::unique_ptr Context; std::unique_ptr Info; std::unique_ptr Dbi; std::unique_ptr Tpi; + std::unique_ptr Publics; }; } } Index: include/llvm/DebugInfo/PDB/Raw/PublicsStream.h =================================================================== --- /dev/null +++ include/llvm/DebugInfo/PDB/Raw/PublicsStream.h @@ -0,0 +1,51 @@ +//===- 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/TypeStream.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" +#include "llvm/DebugInfo/PDB/Raw/ByteStream.h" +#include "llvm/DebugInfo/PDB/Raw/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Raw/RawConstants.h" + +#include "llvm/Support/Error.h" + +namespace llvm { +namespace pdb { +class PDBFile; + +class PublicsStream { + struct HeaderInfo; + struct GSIHashHeader; + struct HRFile; + +public: + PublicsStream(PDBFile &File, uint32_t StreamNum); + ~PublicsStream(); + Error reload(); + + uint32_t getStreamNum() const { return StreamNum; } + uint32_t getSymHash() const; + uint32_t getAddrMap() const; + uint32_t getNumBuckets() const { return NumBuckets; } + +private: + uint32_t StreamNum; + MappedBlockStream Stream; + uint32_t NumBuckets = 0; + + std::unique_ptr Header; + std::unique_ptr HashHdr; +}; +} +} + +#endif Index: lib/DebugInfo/PDB/CMakeLists.txt =================================================================== --- lib/DebugInfo/PDB/CMakeLists.txt +++ lib/DebugInfo/PDB/CMakeLists.txt @@ -37,6 +37,7 @@ Raw/NameHashTable.cpp Raw/NameMap.cpp Raw/PDBFile.cpp + Raw/PublicsStream.cpp Raw/RawError.cpp Raw/RawSession.cpp Raw/StreamReader.cpp Index: lib/DebugInfo/PDB/Raw/DbiStream.cpp =================================================================== --- lib/DebugInfo/PDB/Raw/DbiStream.cpp +++ lib/DebugInfo/PDB/Raw/DbiStream.cpp @@ -52,17 +52,17 @@ struct DbiStream::HeaderInfo { little32_t VersionSignature; ulittle32_t VersionHeader; - ulittle32_t Age; // Should match InfoStream. - ulittle16_t GSSyms; // Number of global symbols - ulittle16_t BuildNumber; // See DbiBuildNo structure. - ulittle16_t PSSyms; // Number of public symbols - ulittle16_t PdbDllVersion; // version of mspdbNNN.dll - ulittle16_t SymRecords; // Number of symbols - ulittle16_t PdbDllRbld; // rbld number of mspdbNNN.dll - little32_t ModiSubstreamSize; // Size of module info stream - little32_t SecContrSubstreamSize; // Size of sec. contribution stream - little32_t SectionMapSize; // Size of sec. map substream - little32_t FileInfoSize; // Size of file info substream + ulittle32_t Age; // Should match InfoStream. + ulittle16_t GSSyms; // Number of global symbols + ulittle16_t BuildNumber; // See DbiBuildNo structure. + ulittle16_t PublicSymbolStreamIndex; // Number of public symbols + ulittle16_t PdbDllVersion; // version of mspdbNNN.dll + ulittle16_t SymRecords; // Number of symbols + ulittle16_t PdbDllRbld; // rbld number of mspdbNNN.dll + little32_t ModiSubstreamSize; // Size of module info stream + little32_t SecContrSubstreamSize; // Size of sec. contribution stream + little32_t SectionMapSize; // Size of sec. map substream + little32_t FileInfoSize; // Size of file info substream little32_t TypeServerSize; // Size of type server map ulittle32_t MFCTypeServerIndex; // Index of MFC Type Server little32_t OptionalDbgHdrSize; // Size of DbgHeader info @@ -184,6 +184,10 @@ uint32_t DbiStream::getAge() const { return Header->Age; } +uint16_t DbiStream::getPublicSymbolStreamIndex() const { + return Header->PublicSymbolStreamIndex; +} + bool DbiStream::isIncrementallyLinked() const { return (Header->Flags & FlagIncrementalMask) != 0; } Index: lib/DebugInfo/PDB/Raw/PDBFile.cpp =================================================================== --- lib/DebugInfo/PDB/Raw/PDBFile.cpp +++ lib/DebugInfo/PDB/Raw/PDBFile.cpp @@ -11,6 +11,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/DebugInfo/PDB/Raw/DbiStream.h" #include "llvm/DebugInfo/PDB/Raw/InfoStream.h" +#include "llvm/DebugInfo/PDB/Raw/PublicsStream.h" #include "llvm/DebugInfo/PDB/Raw/RawError.h" #include "llvm/DebugInfo/PDB/Raw/TpiStream.h" #include "llvm/Support/Endian.h" @@ -292,3 +293,17 @@ } return *Tpi; } + +Expected PDBFile::getPDBPublicsStream() { + if (!Publics) { + auto DbiS = getPDBDbiStream(); + if (auto EC = DbiS.takeError()) + return std::move(EC); + uint32_t PublicsStreamNum = DbiS->getPublicSymbolStreamIndex(); + + Publics.reset(new PublicsStream(*this, PublicsStreamNum)); + if (auto EC = Publics->reload()) + return std::move(EC); + } + return *Publics; +} Index: lib/DebugInfo/PDB/Raw/PublicsStream.cpp =================================================================== --- /dev/null +++ lib/DebugInfo/PDB/Raw/PublicsStream.cpp @@ -0,0 +1,132 @@ +//===- 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/Raw/PublicsStream.h" + +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/PDB/Raw/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Raw/RawConstants.h" +#include "llvm/DebugInfo/PDB/Raw/RawError.h" +#include "llvm/DebugInfo/PDB/Raw/StreamReader.h" + +#include "llvm/ADT/BitVector.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MathExtras.h" + +using namespace llvm; +using namespace llvm::support; +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 { + ulittle32_t SymHash; + ulittle32_t AddrMap; + ulittle32_t NumThunks; + ulittle32_t SizeOfThunk; + ulittle16_t ISectThunkTable; + char Padding[2]; + ulittle32_t OffThunkTable; + ulittle32_t NumSects; +}; + + +// This is GSIHashHdr struct defined in +struct PublicsStream::GSIHashHeader { + enum { + HdrSignature = -1, + HdrVersion = 0xeffe0000 + 19990810, + }; + ulittle32_t VerSignature; + ulittle32_t VerHdr; + ulittle32_t HrSize; + ulittle32_t NumBuckets; +}; + +struct PublicsStream::HRFile { + ulittle32_t Off; + ulittle32_t CRef; +}; + +PublicsStream::PublicsStream(PDBFile &File, uint32_t StreamNum) + : StreamNum(StreamNum), Stream(StreamNum, File) {} + +PublicsStream::~PublicsStream() {} + +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. + Header.reset(new HeaderInfo()); + if (Reader.readObject(Header.get())) + return make_error(raw_error_code::corrupt_file, + "Publics Stream does not contain a header."); + HashHdr.reset(new GSIHashHeader()); + if (Reader.readObject(HashHdr.get())) + return make_error(raw_error_code::corrupt_file, + "Publics Stream does not contain a header."); + + // An array of HRFile follows. Read them. + if (HashHdr->HrSize % sizeof(HRFile)) + return make_error(raw_error_code::corrupt_file, + "Invalid HR array size."); + std::vector HRs(HashHdr->HrSize / sizeof(HRFile)); + if (auto EC = Reader.readArray(HRs)) + return 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); + std::vector Bitmap(BitmapSizeInBits / 8); + if (auto EC = Reader.readArray(Bitmap)) + return make_error(raw_error_code::corrupt_file, + "Could not read a bitmap."); + for (uint8_t B : Bitmap) + NumBuckets += countPopulation(B); + + // Buckets follow. + if (Reader.bytesRemaining() < NumBuckets * sizeof(uint32_t)) + return make_error(raw_error_code::corrupt_file, + "Hash buckets corrupted."); + + return Error::success(); +} Index: test/DebugInfo/PDB/pdbdump-headers.test =================================================================== --- test/DebugInfo/PDB/pdbdump-headers.test +++ test/DebugInfo/PDB/pdbdump-headers.test @@ -1,5 +1,5 @@ ; RUN: llvm-pdbdump --dump-headers -dump-tpi-records -dump-tpi-record-bytes -dump-module-syms \ -; RUN: %p/Inputs/empty.pdb | FileCheck -check-prefix=EMPTY %s +; RUN: --dump-publics %p/Inputs/empty.pdb | FileCheck -check-prefix=EMPTY %s ; RUN: llvm-pdbdump --dump-headers %p/Inputs/big-read.pdb | FileCheck -check-prefix=BIG %s ; RUN: llvm-pdbdump --dump-headers %p/Inputs/bad-block-size.pdb | FileCheck -check-prefix=BAD-BLOCK-SIZE %s @@ -308,6 +308,13 @@ ; EMPTY-NEXT: ) ; EMPTY-NEXT: } +; EMPTY: Publics Stream { +; EMPTY-NEXT: Stream number: 7 +; EMPTY-NEXT: SymHash: 556 +; EMPTY-NEXT: AddrMap: 8 +; EMPTY-NEXT: Number of buckets: 2 +; EMPTY-NEXT: } + ; BIG: FileHeaders { ; BIG-NEXT: BlockSize: 4096 ; BIG-NEXT: Unknown0: 2 Index: tools/llvm-pdbdump/llvm-pdbdump.h =================================================================== --- tools/llvm-pdbdump/llvm-pdbdump.h +++ tools/llvm-pdbdump/llvm-pdbdump.h @@ -32,4 +32,4 @@ extern llvm::cl::list IncludeCompilands; } -#endif \ No newline at end of file +#endif Index: tools/llvm-pdbdump/llvm-pdbdump.cpp =================================================================== --- tools/llvm-pdbdump/llvm-pdbdump.cpp +++ tools/llvm-pdbdump/llvm-pdbdump.cpp @@ -44,6 +44,7 @@ #include "llvm/DebugInfo/PDB/Raw/ModStream.h" #include "llvm/DebugInfo/PDB/Raw/NameHashTable.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/RawSession.h" #include "llvm/DebugInfo/PDB/Raw/StreamReader.h" @@ -122,6 +123,9 @@ cl::opt DumpModuleSyms("dump-module-syms", cl::desc("dump module symbols"), cl::cat(NativeOtions)); +cl::opt DumpPublics("dump-publics", + cl::desc("dump Publics stream data"), + cl::cat(NativeOtions)); cl::list ExcludeTypes("exclude-types", @@ -394,6 +398,22 @@ return Error::success(); } +static Error dumpPublicsStream(ScopedPrinter &P, PDBFile &File) { + if (!opts::DumpPublics) + return Error::success(); + + DictScope D(P, "Publics Stream"); + auto PublicsS = File.getPDBPublicsStream(); + if (auto EC = PublicsS.takeError()) + return EC; + PublicsStream &Publics = PublicsS.get(); + P.printNumber("Stream number", Publics.getStreamNum()); + P.printNumber("SymHash", Publics.getSymHash()); + P.printNumber("AddrMap", Publics.getAddrMap()); + P.printNumber("Number of buckets", Publics.getNumBuckets()); + return Error::success(); +} + static Error dumpStructure(RawSession &RS) { PDBFile &File = RS.getPDBFile(); ScopedPrinter P(outs()); @@ -421,7 +441,10 @@ if (auto EC = dumpTpiStream(P, File)) return EC; - return Error::success(); + + if (auto EC = dumpPublicsStream(P, File)) + return EC; +return Error::success(); } static void dumpInput(StringRef Path) {