Index: include/llvm/DebugInfo/PDB/Raw/ModInfo.h =================================================================== --- /dev/null +++ include/llvm/DebugInfo/PDB/Raw/ModInfo.h @@ -0,0 +1,62 @@ +//===- 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 + +namespace llvm { +class PDBFile; + +class ModInfo { +private: + struct FileLayout; + +public: + ModInfo(const uint8_t *Bytes); + ~ModInfo(); + + 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; + + llvm::StringRef getModuleName() const; + llvm::StringRef getObjFileName() const; + +private: + const FileLayout *Layout; +}; + +class ModInfoIterator { +public: + ModInfoIterator(const uint8_t *Stream); + ModInfoIterator(const ModInfoIterator &Other); + + ModInfo operator*(); + ModInfoIterator &operator++(); + ModInfoIterator operator++(int); + bool operator==(const ModInfoIterator &Other); + bool operator!=(const ModInfoIterator &Other); + ModInfoIterator &operator=(const ModInfoIterator &Other); + +private: + const uint8_t *Bytes; +}; +} + +#endif Index: include/llvm/DebugInfo/PDB/Raw/PDBDbiStream.h =================================================================== --- include/llvm/DebugInfo/PDB/Raw/PDBDbiStream.h +++ include/llvm/DebugInfo/PDB/Raw/PDBDbiStream.h @@ -11,6 +11,7 @@ #define LLVM_DEBUGINFO_PDB_RAW_PDBDBISTREAM_H #include "llvm/DebugInfo/PDB/PDBTypes.h" +#include "llvm/DebugInfo/PDB/Raw/ModInfo.h" #include "llvm/DebugInfo/PDB/Raw/PDBRawConstants.h" #include "llvm/DebugInfo/PDB/Raw/PDBStream.h" #include "llvm/Support/Endian.h" @@ -42,9 +43,13 @@ PDB_Machine getMachineType() const; + llvm::iterator_range modules() const; + private: PDBFile &Pdb; PDBStream Stream; + + std::vector ModInfoSubstream; std::unique_ptr Header; }; } Index: lib/DebugInfo/PDB/CMakeLists.txt =================================================================== --- lib/DebugInfo/PDB/CMakeLists.txt +++ lib/DebugInfo/PDB/CMakeLists.txt @@ -27,6 +27,7 @@ endif() add_pdb_impl_folder(Raw + Raw/ModInfo.cpp Raw/PDBFile.cpp Raw/PDBDbiStream.cpp Raw/PDBInfoStream.cpp Index: lib/DebugInfo/PDB/Raw/ModInfo.cpp =================================================================== --- /dev/null +++ lib/DebugInfo/PDB/Raw/ModInfo.cpp @@ -0,0 +1,140 @@ +//===- 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/Raw/ModInfo.h" +#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::support; + +namespace { +struct SCBytes { + ulittle16_t Section; + little32_t Offset; + little32_t Size; + ulittle32_t Characteristics; + ulittle16_t ModuleIndex; + ulittle32_t DataCrc; + ulittle32_t RelocCrc; +}; + +// struct Flags { +// 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 +//}; +const uint16_t HasECFlagMask = 0x2; + +const uint16_t TypeServerIndexMask = 0xFF00; +const uint16_t TypeServerIndexShift = 8; +} + +struct ModInfo::FileLayout { + ulittle32_t Mod; // 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. + SCBytes SC; // First section contribution of this module. + ulittle16_t Flags; // See Flags definition. + ulittle16_t ModDiStream; // Stream Number of module debug info + ulittle32_t SymBytes; // Size of local symbol debug info in above stream + ulittle32_t LineBytes; // Size of line number debug info in above stream + ulittle32_t C13Bytes; // Size of C13 line number info in above stream + ulittle16_t NumFiles; // Number of files contributing to this module + ulittle16_t Padding; // Padding so the next field is 4-byte aligned. + ulittle32_t FileNameOffs; // 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. + ulittle32_t SrcFileNameNI; // Name Index for src file name + ulittle32_t PdbFilePathNI; // Name Index for path to compiler PDB + char VarInfo[1]; // Module name followed by Obj File Name + + StringRef getModuleName() const { return StringRef(VarInfo); } + + StringRef getObjectFileName() const { + return StringRef(getModuleName().end() + 1); + } +}; + +ModInfo::ModInfo(const uint8_t *Bytes) + : Layout(reinterpret_cast(Bytes)) {} + +ModInfo::~ModInfo() {} + +bool ModInfo::hasECInfo() const { return (Layout->Flags & HasECFlagMask) != 0; } + +uint16_t ModInfo::getTypeServerIndex() const { + return (Layout->Flags & TypeServerIndexMask) >> 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; +} + +llvm::StringRef ModInfo::getModuleName() const { + return Layout->getModuleName(); +} + +llvm::StringRef ModInfo::getObjFileName() const { + return Layout->getObjectFileName(); +} + +ModInfoIterator::ModInfoIterator(const uint8_t *Stream) : Bytes(Stream) {} + +ModInfoIterator::ModInfoIterator(const ModInfoIterator &Other) + : Bytes(Other.Bytes) {} + +ModInfo ModInfoIterator::operator*() { return ModInfo(Bytes); } + +ModInfoIterator &ModInfoIterator::operator++() { + StringRef Obj = ModInfo(Bytes).getObjFileName(); + Bytes = Obj.bytes_end() + 1; + Bytes = reinterpret_cast(llvm::alignAddr(Bytes, 4)); + + return *this; +} + +ModInfoIterator ModInfoIterator::operator++(int) { + ModInfoIterator Copy(*this); + ++(*this); + return Copy; +} + +bool ModInfoIterator::operator==(const ModInfoIterator &Other) { + return Bytes == Other.Bytes; +} + +bool ModInfoIterator::operator!=(const ModInfoIterator &Other) { + return !(*this == Other); +} + +ModInfoIterator &ModInfoIterator::operator=(const ModInfoIterator &Other) { + Bytes = Other.Bytes; + return *this; +} Index: lib/DebugInfo/PDB/Raw/PDBDbiStream.cpp =================================================================== --- lib/DebugInfo/PDB/Raw/PDBDbiStream.cpp +++ lib/DebugInfo/PDB/Raw/PDBDbiStream.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/PDB/Raw/PDBDbiStream.h" +#include "llvm/DebugInfo/PDB/Raw/ModInfo.h" #include "llvm/DebugInfo/PDB/Raw/PDBFile.h" #include "llvm/DebugInfo/PDB/Raw/PDBInfoStream.h" #include "llvm/DebugInfo/PDB/Raw/PDBRawConstants.h" @@ -84,8 +85,10 @@ if (Header->VersionSignature != -1) return std::make_error_code(std::errc::illegal_byte_sequence); - // Prior to VC50 an old style header was used. We don't support this. - if (Header->VersionHeader < PdbDbiV50) + // 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 std::make_error_code(std::errc::not_supported); if (Header->Age != Pdb.getPDBInfoStream().getAge()) @@ -98,6 +101,14 @@ Header->OptionalDbgHdrSize + Header->ECSubstreamSize) return std::make_error_code(std::errc::illegal_byte_sequence); + if (Header->ModiSubstreamSize % sizeof(uint32_t) != 0) + return std::make_error_code(std::errc::illegal_byte_sequence); + + ModInfoSubstream.resize(Header->ModiSubstreamSize); + if (auto EC = + Stream.readBytes(&ModInfoSubstream[0], Header->ModiSubstreamSize)) + return EC; + return std::error_code(); } @@ -138,3 +149,8 @@ uint16_t Machine = Header->MachineType; return static_cast(Machine); } + +llvm::iterator_range PDBDbiStream::modules() const { + return llvm::make_range(ModInfoIterator(&ModInfoSubstream.front()), + ModInfoIterator(&ModInfoSubstream.back() + 1)); +} Index: test/DebugInfo/PDB/pdbdump-headers.test =================================================================== --- test/DebugInfo/PDB/pdbdump-headers.test +++ test/DebugInfo/PDB/pdbdump-headers.test @@ -28,3 +28,27 @@ ; CHECK-NEXT: Number of Symbols: 8 ; CHECK-NEXT: Toolchain Version: 12.0 ; CHECK-NEXT: mspdb120.dll version: 12.0.31101 + +; CHECK: Modules: +; CHECK-NEXT: d:\src\llvm\test\DebugInfo\PDB\Inputs\empty.obj +; CHECK-NEXT: Debug Stream Index: 12 +; CHECK-NEXT: Object File: d:\src\llvm\test\DebugInfo\PDB\Inputs\empty.obj +; CHECK-NEXT: Num Files: 1 +; CHECK-NEXT: Source File Name Idx: 0 +; CHECK-NEXT: Pdb File Name Idx: 0 +; CHECK-NEXT: Line Info Byte Size: 0 +; CHECK-NEXT: C13 Line Info Byte Size: 88 +; CHECK-NEXT: Symbol Byte Size: 208 +; CHECK-NEXT: Type Server Index: 0 +; CHECK-NEXT: Has EC Info: 0 +; CHECK-NEXT: * Linker * +; CHECK-NEXT: Debug Stream Index: 14 +; CHECK-NEXT: Object File: +; CHECK-NEXT: Num Files: 0 +; CHECK-NEXT: Source File Name Idx: 0 +; CHECK-NEXT: Pdb File Name Idx: 1 +; CHECK-NEXT: Line Info Byte Size: 0 +; CHECK-NEXT: C13 Line Info Byte Size: 0 +; CHECK-NEXT: Symbol Byte Size: 516 +; CHECK-NEXT: Type Server Index: 0 +; CHECK-NEXT: Has EC Info: 0 Index: tools/llvm-pdbdump/llvm-pdbdump.cpp =================================================================== --- tools/llvm-pdbdump/llvm-pdbdump.cpp +++ tools/llvm-pdbdump/llvm-pdbdump.cpp @@ -35,6 +35,7 @@ #include "llvm/DebugInfo/PDB/PDBSymbolExe.h" #include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" #include "llvm/DebugInfo/PDB/PDBSymbolThunk.h" +#include "llvm/DebugInfo/PDB/Raw/ModInfo.h" #include "llvm/DebugInfo/PDB/Raw/PDBDbiStream.h" #include "llvm/DebugInfo/PDB/Raw/PDBFile.h" #include "llvm/DebugInfo/PDB/Raw/PDBInfoStream.h" @@ -278,6 +279,28 @@ outs() << "Toolchain Version: " << Major << "." << Minor << '\n'; outs() << "mspdb" << Major << Minor << ".dll version: " << Major << "." << Minor << "." << DbiStream.getPdbDllVersion() << '\n'; + + outs() << "Modules: \n"; + for (auto Modi : DbiStream.modules()) { + outs() << Modi.getModuleName() << '\n'; + outs().indent(4) << "Debug Stream Index: " << Modi.getModuleStreamIndex() + << '\n'; + outs().indent(4) << "Object File: " << Modi.getObjFileName() << '\n'; + outs().indent(4) << "Num Files: " << Modi.getNumberOfFiles() << '\n'; + outs().indent(4) << "Source File Name Idx: " + << Modi.getSourceFileNameIndex() << '\n'; + outs().indent(4) << "Pdb File Name Idx: " << Modi.getPdbFilePathNameIndex() + << '\n'; + outs().indent(4) << "Line Info Byte Size: " << Modi.getLineInfoByteSize() + << '\n'; + outs().indent(4) << "C13 Line Info Byte Size: " + << Modi.getC13LineInfoByteSize() << '\n'; + outs().indent(4) << "Symbol Byte Size: " + << Modi.getSymbolDebugInfoByteSize() << '\n'; + outs().indent(4) << "Type Server Index: " << Modi.getTypeServerIndex() + << '\n'; + outs().indent(4) << "Has EC Info: " << Modi.hasECInfo() << '\n'; + } } static void dumpInput(StringRef Path) {