Index: include/llvm/DebugInfo/CodeView/ByteStream.h =================================================================== --- include/llvm/DebugInfo/CodeView/ByteStream.h +++ include/llvm/DebugInfo/CodeView/ByteStream.h @@ -40,6 +40,8 @@ uint32_t getLength() const override; + Error commit() const override; + ArrayRef data() const { return Data; } StringRef str() const; Index: include/llvm/DebugInfo/CodeView/StreamInterface.h =================================================================== --- include/llvm/DebugInfo/CodeView/StreamInterface.h +++ include/llvm/DebugInfo/CodeView/StreamInterface.h @@ -44,6 +44,8 @@ // only writes into existing allocated space. virtual Error writeBytes(uint32_t Offset, ArrayRef Data) const = 0; + virtual Error commit() const = 0; + virtual uint32_t getLength() const = 0; }; Index: include/llvm/DebugInfo/CodeView/StreamRef.h =================================================================== --- include/llvm/DebugInfo/CodeView/StreamRef.h +++ include/llvm/DebugInfo/CodeView/StreamRef.h @@ -60,6 +60,8 @@ uint32_t getLength() const override { return Length; } + Error commit() const override { return Stream->commit(); } + StreamRef drop_front(uint32_t N) const { if (!Stream) return StreamRef(); @@ -91,6 +93,13 @@ bool operator!=(const StreamRef &Other) const { return !(*this == Other); } + StreamRef &operator=(const StreamRef &Other) { + Stream = Other.Stream; + ViewOffset = Other.ViewOffset; + Length = Other.Length; + return *this; + } + private: const StreamInterface *Stream; uint32_t ViewOffset; Index: include/llvm/DebugInfo/CodeView/StreamWriter.h =================================================================== --- include/llvm/DebugInfo/CodeView/StreamWriter.h +++ include/llvm/DebugInfo/CodeView/StreamWriter.h @@ -41,7 +41,8 @@ static_cast::type>(Num)); } - template Error writeObject(const T &Obj) { + template ::value>> + Error writeObject(const T &Obj) { return writeBytes( ArrayRef(reinterpret_cast(&Obj), sizeof(T))); } Index: include/llvm/DebugInfo/PDB/Raw/IPDBFile.h =================================================================== --- include/llvm/DebugInfo/PDB/Raw/IPDBFile.h +++ include/llvm/DebugInfo/PDB/Raw/IPDBFile.h @@ -37,6 +37,7 @@ uint32_t NumBytes) const = 0; virtual Error setBlockData(uint32_t BlockIndex, uint32_t Offset, ArrayRef Data) const = 0; + virtual Error commit() const = 0; }; } } Index: include/llvm/DebugInfo/PDB/Raw/MappedBlockStream.h =================================================================== --- include/llvm/DebugInfo/PDB/Raw/MappedBlockStream.h +++ include/llvm/DebugInfo/PDB/Raw/MappedBlockStream.h @@ -37,6 +37,8 @@ uint32_t getLength() const override; + Error commit() const override; + uint32_t getNumBytesCopied() const; static Expected> Index: include/llvm/DebugInfo/PDB/Raw/PDBFile.h =================================================================== --- include/llvm/DebugInfo/PDB/Raw/PDBFile.h +++ include/llvm/DebugInfo/PDB/Raw/PDBFile.h @@ -82,11 +82,18 @@ uint32_t getStreamByteSize(uint32_t StreamIndex) const override; ArrayRef getStreamBlockList(uint32_t StreamIndex) const override; + size_t getFileSize() const; ArrayRef getBlockData(uint32_t BlockIndex, uint32_t NumBytes) const override; Error setBlockData(uint32_t BlockIndex, uint32_t Offset, ArrayRef Data) const override; + Error commit() const override; + + ArrayRef getStreamSizes() const { return StreamSizes; } + ArrayRef> getStreamMap() const { + return StreamMap; + } ArrayRef getDirectoryBlockArray() const; @@ -109,10 +116,16 @@ Expected getPDBSymbolStream(); Expected getStringTable(); + Error setSuperBlock(const SuperBlock *Block); + void setStreamSizes(ArrayRef Sizes); + void setStreamMap(ArrayRef Directory, + std::vector> &Streams); + private: std::unique_ptr Buffer; const PDBFile::SuperBlock *SB; ArrayRef StreamSizes; + ArrayRef DirectoryBlocks; std::vector> StreamMap; std::unique_ptr Info; Index: lib/DebugInfo/CodeView/ByteStream.cpp =================================================================== --- lib/DebugInfo/CodeView/ByteStream.cpp +++ lib/DebugInfo/CodeView/ByteStream.cpp @@ -62,6 +62,10 @@ return Data.size(); } +template Error ByteStream::commit() const { + return Error::success(); +} + template StringRef ByteStream::str() const { const char *CharData = reinterpret_cast(Data.data()); return StringRef(CharData, Data.size()); Index: lib/DebugInfo/PDB/Raw/MappedBlockStream.cpp =================================================================== --- lib/DebugInfo/PDB/Raw/MappedBlockStream.cpp +++ lib/DebugInfo/PDB/Raw/MappedBlockStream.cpp @@ -147,6 +147,8 @@ uint32_t MappedBlockStream::getLength() const { return Data->getLength(); } +Error MappedBlockStream::commit() const { return Error::success(); } + bool MappedBlockStream::tryReadContiguously(uint32_t Offset, uint32_t Size, ArrayRef &Buffer) const { // Attempt to fulfill the request with a reference directly into the stream. Index: lib/DebugInfo/PDB/Raw/PDBFile.cpp =================================================================== --- lib/DebugInfo/PDB/Raw/PDBFile.cpp +++ lib/DebugInfo/PDB/Raw/PDBFile.cpp @@ -13,6 +13,7 @@ #include "llvm/DebugInfo/CodeView/StreamArray.h" #include "llvm/DebugInfo/CodeView/StreamInterface.h" #include "llvm/DebugInfo/CodeView/StreamReader.h" +#include "llvm/DebugInfo/CodeView/StreamWriter.h" #include "llvm/DebugInfo/PDB/Raw/DbiStream.h" #include "llvm/DebugInfo/PDB/Raw/DirectoryStreamData.h" #include "llvm/DebugInfo/PDB/Raw/IndexedStreamData.h" @@ -69,6 +70,8 @@ return StreamMap[StreamIndex]; } +size_t PDBFile::getFileSize() const { return Buffer->getLength(); } + ArrayRef PDBFile::getBlockData(uint32_t BlockIndex, uint32_t NumBytes) const { uint64_t StreamBlockOffset = blockToOffset(BlockIndex, getBlockSize()); @@ -81,11 +84,11 @@ Error PDBFile::setBlockData(uint32_t BlockIndex, uint32_t Offset, ArrayRef Data) const { - if (Offset >= getBlockSize()) + if (Offset > getBlockSize()) return make_error( raw_error_code::invalid_block_address, "setBlockData attempted to write out of block bounds."); - if (Data.size() >= getBlockSize() - Offset) + if (Data.size() > getBlockSize() - Offset) return make_error( raw_error_code::invalid_block_address, "setBlockData attempted to write out of block bounds."); @@ -104,44 +107,12 @@ "Does not contain superblock"); } - // Check the magic bytes. - if (memcmp(SB->MagicBytes, MsfMagic, sizeof(MsfMagic)) != 0) - return make_error(raw_error_code::corrupt_file, - "MSF magic header doesn't match"); - - // We don't support blocksizes which aren't a multiple of four bytes. - if (SB->BlockSize % sizeof(support::ulittle32_t) != 0) - return make_error(raw_error_code::corrupt_file, - "Block size is not multiple of 4."); - - switch (SB->BlockSize) { - case 512: case 1024: case 2048: case 4096: - break; - default: - // An invalid block size suggests a corrupt PDB file. - return make_error(raw_error_code::corrupt_file, - "Unsupported block size."); - } - - if (Buffer->getLength() % SB->BlockSize != 0) - return make_error(raw_error_code::corrupt_file, - "File size is not a multiple of block size"); - - // We don't support directories whose sizes aren't a multiple of four bytes. - if (SB->NumDirectoryBytes % sizeof(support::ulittle32_t) != 0) - return make_error(raw_error_code::corrupt_file, - "Directory size is not multiple of 4."); - - // The number of blocks which comprise the directory is a simple function of - // the number of bytes it contains. - uint64_t NumDirectoryBlocks = getNumDirectoryBlocks(); + if (auto EC = setSuperBlock(SB)) + return EC; - // The directory, as we understand it, is a block which consists of a list of - // block numbers. It is unclear what would happen if the number of blocks - // couldn't fit on a single block. - if (NumDirectoryBlocks > SB->BlockSize / sizeof(support::ulittle32_t)) - return make_error(raw_error_code::corrupt_file, - "Too many directory blocks."); + Reader.setOffset(getBlockMapOffset()); + if (auto EC = Reader.readArray(DirectoryBlocks, getNumDirectoryBlocks())) + return EC; return Error::success(); } @@ -190,12 +161,7 @@ } llvm::ArrayRef PDBFile::getDirectoryBlockArray() const { - StreamReader Reader(*Buffer); - Reader.setOffset(getBlockMapOffset()); - llvm::ArrayRef Result; - if (auto EC = Reader.readArray(Result, getNumDirectoryBlocks())) - consumeError(std::move(EC)); - return Result; + return DirectoryBlocks; } Expected PDBFile::getPDBInfoStream() { @@ -317,3 +283,90 @@ } return *StringTable; } + +Error PDBFile::setSuperBlock(const SuperBlock *Block) { + SB = Block; + + // Check the magic bytes. + if (memcmp(SB->MagicBytes, MsfMagic, sizeof(MsfMagic)) != 0) + return make_error(raw_error_code::corrupt_file, + "MSF magic header doesn't match"); + + // We don't support blocksizes which aren't a multiple of four bytes. + if (SB->BlockSize % sizeof(support::ulittle32_t) != 0) + return make_error(raw_error_code::corrupt_file, + "Block size is not multiple of 4."); + + switch (SB->BlockSize) { + case 512: + case 1024: + case 2048: + case 4096: + break; + default: + // An invalid block size suggests a corrupt PDB file. + return make_error(raw_error_code::corrupt_file, + "Unsupported block size."); + } + + if (Buffer->getLength() % SB->BlockSize != 0) + return make_error(raw_error_code::corrupt_file, + "File size is not a multiple of block size"); + + // We don't support directories whose sizes aren't a multiple of four bytes. + if (SB->NumDirectoryBytes % sizeof(support::ulittle32_t) != 0) + return make_error(raw_error_code::corrupt_file, + "Directory size is not multiple of 4."); + + // The number of blocks which comprise the directory is a simple function of + // the number of bytes it contains. + uint64_t NumDirectoryBlocks = getNumDirectoryBlocks(); + + // The directory, as we understand it, is a block which consists of a list of + // block numbers. It is unclear what would happen if the number of blocks + // couldn't fit on a single block. + if (NumDirectoryBlocks > SB->BlockSize / sizeof(support::ulittle32_t)) + return make_error(raw_error_code::corrupt_file, + "Too many directory blocks."); + + return Error::success(); +} + +void PDBFile::setStreamSizes(ArrayRef Sizes) { + StreamSizes = Sizes; +} + +void PDBFile::setStreamMap( + ArrayRef Directory, + std::vector> &Streams) { + DirectoryBlocks = Directory; + StreamMap = Streams; +} + +Error PDBFile::commit() const { + StreamWriter Writer(*Buffer); + + if (auto EC = Writer.writeObject(*SB)) + return EC; + Writer.setOffset(getBlockMapOffset()); + if (auto EC = Writer.writeArray(DirectoryBlocks)) + return EC; + + auto DS = MappedBlockStream::createDirectoryStream(*this); + if (!DS) + return DS.takeError(); + auto DirStream = std::move(*DS); + StreamWriter DW(*DirStream); + if (auto EC = DW.writeInteger(this->getNumStreams())) + return EC; + + if (auto EC = DW.writeArray(StreamSizes)) + return EC; + + for (const auto &Blocks : StreamMap) { + if (auto EC = DW.writeArray(Blocks)) + return EC; + } + + return Buffer->commit(); +} \ No newline at end of file Index: lib/DebugInfo/PDB/Raw/RawSession.cpp =================================================================== --- lib/DebugInfo/PDB/Raw/RawSession.cpp +++ lib/DebugInfo/PDB/Raw/RawSession.cpp @@ -52,6 +52,9 @@ MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1, /*RequiresNullTerminator=*/false); + if (ErrorOrBuffer.getError()) + return make_error(generic_error_code::invalid_path, Path); + std::unique_ptr Buffer = std::move(*ErrorOrBuffer); auto Stream = llvm::make_unique(std::move(Buffer)); Index: test/DebugInfo/PDB/pdbdump-headers.test =================================================================== --- test/DebugInfo/PDB/pdbdump-headers.test +++ test/DebugInfo/PDB/pdbdump-headers.test @@ -2,9 +2,10 @@ ; RUN: -raw-sym-record-bytes -raw-publics -raw-module-files -raw-stream-name=/names \ ; RUN: -raw-stream-summary -raw-stream-blocks -raw-ipi-records -raw-ipi-record-bytes \ ; RUN: -raw-section-contribs -raw-section-map -raw-section-headers -raw-line-info \ -; RUN: -raw-tpi-hash -raw-fpo %p/Inputs/empty.pdb | FileCheck -check-prefix=EMPTY %s +; RUN: -raw-dbi-stream -raw-pdb-stream -raw-tpi-hash -raw-fpo %p/Inputs/empty.pdb \ +; RUN: | FileCheck -check-prefix=EMPTY %s ; RUN: llvm-pdbdump -raw-all %p/Inputs/empty.pdb | FileCheck -check-prefix=ALL %s -; RUN: llvm-pdbdump -raw-headers -raw-stream-name=/names -raw-modules -raw-module-files \ +; RUN: llvm-pdbdump -raw-headers -raw-pdb-stream -raw-stream-name=/names -raw-modules -raw-module-files \ ; RUN: %p/Inputs/big-read.pdb | FileCheck -check-prefix=BIG %s ; RUN: not llvm-pdbdump -raw-headers %p/Inputs/bad-block-size.pdb 2>&1 | FileCheck -check-prefix=BAD-BLOCK-SIZE %s Index: test/DebugInfo/PDB/pdbdump-write.test =================================================================== --- /dev/null +++ test/DebugInfo/PDB/pdbdump-write.test @@ -0,0 +1,15 @@ +; This testcase checks to make sure that we can write PDB files. It +; works by first reading a known good PDB file and dumping the contents +; to YAML. Then it tries to reconstruct as much of the original PDB as +; possible, although depending on what flags are specified when generating +; the YAML, the PDB might be missing data required for any standard tool +; to recognize it. Finally, it dumps the same set of fields from the newly +; constructed PDB to YAML, and verifies that the YAML is the same as the +; original YAML generated from the good PDB. +; +; RUN: llvm-pdbdump -raw-headers -raw-stream-summary -raw-stream-blocks \ +; RUN: -raw-output-style=YAML %p/Inputs/empty.pdb > %t.1 +; RUN: llvm-pdbdump -yaml-to-pdb -pdb-output=%t.2 %t.1 +; RUN: llvm-pdbdump -raw-headers -raw-stream-summary -raw-stream-blocks \ +; RUN: -raw-output-style=YAML %t.2 > %t.3 +; RUN: diff %t.1 %t.3 Index: test/DebugInfo/PDB/pdbdump-yaml.test =================================================================== --- test/DebugInfo/PDB/pdbdump-yaml.test +++ test/DebugInfo/PDB/pdbdump-yaml.test @@ -3,15 +3,17 @@ ; YAML: --- ; YAML-NEXT: MSF: -; YAML-NEXT: BlockSize: 4096 -; YAML-NEXT: Unknown0: 2 -; YAML-NEXT: NumBlocks: 25 -; YAML-NEXT: NumDirectoryBytes: 136 -; YAML-NEXT: Unknown1: 0 -; YAML-NEXT: BlockMapAddr: 24 +; YAML-NEXT: SuperBlock: +; YAML-NEXT: BlockSize: 4096 +; YAML-NEXT: Unknown0: 2 +; YAML-NEXT: NumBlocks: 25 +; YAML-NEXT: NumDirectoryBytes: 136 +; YAML-NEXT: Unknown1: 0 +; YAML-NEXT: BlockMapAddr: 24 ; YAML-NEXT: NumDirectoryBlocks: 1 ; YAML-NEXT: BlockMapOffset: 98304 ; YAML-NEXT: DirectoryBlocks: ; YAML-NEXT: - 23 ; YAML-NEXT: NumStreams: 17 +; YAML-NEXT: FileSize: 102400 ; YAML-NEXT: ... Index: tools/llvm-pdbdump/LLVMOutputStyle.cpp =================================================================== --- tools/llvm-pdbdump/LLVMOutputStyle.cpp +++ tools/llvm-pdbdump/LLVMOutputStyle.cpp @@ -68,32 +68,25 @@ return Error::success(); auto Dbi = File.getPDBDbiStream(); - if (!Dbi) - return Dbi.takeError(); - auto Tpi = File.getPDBTpiStream(); - if (!Tpi) - return Tpi.takeError(); - auto Ipi = File.getPDBIpiStream(); - if (!Ipi) - return Ipi.takeError(); - auto Info = File.getPDBInfoStream(); - if (!Info) - return Info.takeError(); ListScope L(P, "Streams"); uint32_t StreamCount = File.getNumStreams(); std::unordered_map ModStreams; std::unordered_map NamedStreams; - for (auto &ModI : Dbi->modules()) { - uint16_t SN = ModI.Info.getModuleStreamIndex(); - ModStreams[SN] = &ModI; + if (Dbi) { + for (auto &ModI : Dbi->modules()) { + uint16_t SN = ModI.Info.getModuleStreamIndex(); + ModStreams[SN] = &ModI; + } } - for (auto &NSE : Info->named_streams()) { - NamedStreams[NSE.second] = NSE.first(); + if (Info) { + for (auto &NSE : Info->named_streams()) { + NamedStreams[NSE.second] = NSE.first(); + } } for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) { @@ -110,42 +103,49 @@ Value = "TPI Stream"; else if (StreamIdx == StreamIPI) Value = "IPI Stream"; - else if (StreamIdx == Dbi->getGlobalSymbolStreamIndex()) + else if (Dbi && StreamIdx == Dbi->getGlobalSymbolStreamIndex()) Value = "Global Symbol Hash"; - else if (StreamIdx == Dbi->getPublicSymbolStreamIndex()) + else if (Dbi && StreamIdx == Dbi->getPublicSymbolStreamIndex()) Value = "Public Symbol Hash"; - else if (StreamIdx == Dbi->getSymRecordStreamIndex()) + else if (Dbi && StreamIdx == Dbi->getSymRecordStreamIndex()) Value = "Public Symbol Records"; - else if (StreamIdx == Tpi->getTypeHashStreamIndex()) + else if (Tpi && StreamIdx == Tpi->getTypeHashStreamIndex()) Value = "TPI Hash"; - else if (StreamIdx == Tpi->getTypeHashStreamAuxIndex()) + else if (Tpi && StreamIdx == Tpi->getTypeHashStreamAuxIndex()) Value = "TPI Aux Hash"; - else if (StreamIdx == Ipi->getTypeHashStreamIndex()) + else if (Ipi && StreamIdx == Ipi->getTypeHashStreamIndex()) Value = "IPI Hash"; - else if (StreamIdx == Ipi->getTypeHashStreamAuxIndex()) + else if (Ipi && StreamIdx == Ipi->getTypeHashStreamAuxIndex()) Value = "IPI Aux Hash"; - else if (StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Exception)) + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Exception)) Value = "Exception Data"; - else if (StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Fixup)) + else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Fixup)) Value = "Fixup Data"; - else if (StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::FPO)) + else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::FPO)) Value = "FPO Data"; - else if (StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::NewFPO)) + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::NewFPO)) Value = "New FPO Data"; - else if (StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapFromSrc)) + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapFromSrc)) Value = "Omap From Source Data"; - else if (StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapToSrc)) + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapToSrc)) Value = "Omap To Source Data"; - else if (StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Pdata)) + else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Pdata)) Value = "Pdata"; - else if (StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdr)) + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdr)) Value = "Section Header Data"; - else if (StreamIdx == - Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdrOrig)) + else if (Dbi && + StreamIdx == + Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdrOrig)) Value = "Section Header Original Data"; - else if (StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::TokenRidMap)) + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::TokenRidMap)) Value = "Token Rid Data"; - else if (StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Xdata)) + else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Xdata)) Value = "Xdata"; else { auto ModIter = ModStreams.find(StreamIdx); @@ -169,6 +169,11 @@ P.printString(Label, Value); } P.flush(); + + consumeError(std::move(Dbi.takeError())); + consumeError(std::move(Tpi.takeError())); + consumeError(std::move(Ipi.takeError())); + consumeError(std::move(Info.takeError())); return Error::success(); } @@ -215,7 +220,7 @@ } Error LLVMOutputStyle::dumpInfoStream() { - if (!opts::DumpHeaders) + if (!opts::DumpPdbStream) return Error::success(); auto IS = File.getPDBInfoStream(); if (!IS) @@ -363,7 +368,7 @@ Error LLVMOutputStyle::dumpDbiStream() { bool DumpModules = opts::DumpModules || opts::DumpModuleSyms || opts::DumpModuleFiles || opts::DumpLineInfo; - if (!opts::DumpHeaders && !DumpModules) + if (!opts::DumpDbiStream && !DumpModules) return Error::success(); auto DS = File.getPDBDbiStream(); Index: tools/llvm-pdbdump/PdbYaml.h =================================================================== --- tools/llvm-pdbdump/PdbYaml.h +++ tools/llvm-pdbdump/PdbYaml.h @@ -13,6 +13,7 @@ #include "OutputStyle.h" #include "llvm/ADT/Optional.h" +#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" #include "llvm/Support/Endian.h" #include "llvm/Support/YAMLTraits.h" @@ -20,34 +21,25 @@ namespace llvm { namespace pdb { -class PDBFile; namespace yaml { struct MsfHeaders { - uint32_t BlockSize; - uint32_t Unknown0; - uint32_t BlockCount; - uint32_t NumDirectoryBytes; - uint32_t Unknown1; - uint32_t BlockMapIndex; + PDBFile::SuperBlock SuperBlock; uint32_t NumDirectoryBlocks; uint32_t BlockMapOffset; - std::vector DirectoryBlocks; + std::vector DirectoryBlocks; uint32_t NumStreams; + uint32_t FileSize; }; -struct StreamSizeEntry { - uint32_t Size; -}; - -struct StreamMapEntry { - std::vector Blocks; +struct StreamBlockList { + std::vector Blocks; }; struct PdbObject { - Optional Headers; - Optional> StreamSizes; - Optional> StreamMap; + MsfHeaders Headers; + Optional> StreamSizes; + Optional> StreamMap; }; } } @@ -55,25 +47,26 @@ namespace llvm { namespace yaml { -template <> struct MappingTraits { - static void mapping(IO &IO, pdb::yaml::StreamSizeEntry &Obj); + +template <> struct MappingTraits { + static void mapping(IO &IO, pdb::PDBFile::SuperBlock &SB); }; -template <> struct MappingTraits { - static void mapping(IO &IO, pdb::yaml::StreamMapEntry &Obj); +template <> struct MappingTraits { + static void mapping(IO &IO, pdb::yaml::StreamBlockList &SB); }; template <> struct MappingTraits { static void mapping(IO &IO, pdb::yaml::MsfHeaders &Obj); }; + template <> struct MappingTraits { static void mapping(IO &IO, pdb::yaml::PdbObject &Obj); }; } } -LLVM_YAML_IS_SEQUENCE_VECTOR(uint32_t) -LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::StreamSizeEntry) -LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::StreamMapEntry) +LLVM_YAML_IS_SEQUENCE_VECTOR(support::ulittle32_t) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::StreamBlockList) #endif // LLVM_TOOLS_LLVMPDBDUMP_PDBYAML_H Index: tools/llvm-pdbdump/PdbYaml.cpp =================================================================== --- tools/llvm-pdbdump/PdbYaml.cpp +++ tools/llvm-pdbdump/PdbYaml.cpp @@ -16,29 +16,35 @@ using namespace llvm::pdb; using namespace llvm::pdb::yaml; +void MappingTraits::mapping(IO &IO, + PDBFile::SuperBlock &SB) { + if (!IO.outputting()) { + ::memcpy(SB.MagicBytes, MsfMagic, sizeof(MsfMagic)); + } + + IO.mapRequired("BlockSize", SB.BlockSize); + IO.mapRequired("Unknown0", SB.Unknown0); + IO.mapRequired("NumBlocks", SB.NumBlocks); + IO.mapRequired("NumDirectoryBytes", SB.NumDirectoryBytes); + IO.mapRequired("Unknown1", SB.Unknown1); + IO.mapRequired("BlockMapAddr", SB.BlockMapAddr); +} + +void MappingTraits::mapping(IO &IO, StreamBlockList &SB) { + IO.mapRequired("Stream", SB.Blocks); +} + void MappingTraits::mapping(IO &IO, MsfHeaders &Obj) { - IO.mapRequired("BlockSize", Obj.BlockSize); - IO.mapRequired("Unknown0", Obj.Unknown0); - IO.mapRequired("NumBlocks", Obj.BlockCount); - IO.mapRequired("NumDirectoryBytes", Obj.NumDirectoryBytes); - IO.mapRequired("Unknown1", Obj.Unknown1); - IO.mapRequired("BlockMapAddr", Obj.BlockMapIndex); + IO.mapRequired("SuperBlock", Obj.SuperBlock); IO.mapRequired("NumDirectoryBlocks", Obj.NumDirectoryBlocks); IO.mapRequired("BlockMapOffset", Obj.BlockMapOffset); IO.mapRequired("DirectoryBlocks", Obj.DirectoryBlocks); IO.mapRequired("NumStreams", Obj.NumStreams); + IO.mapRequired("FileSize", Obj.FileSize); } void MappingTraits::mapping(IO &IO, PdbObject &Obj) { - IO.mapOptional("MSF", Obj.Headers); + IO.mapRequired("MSF", Obj.Headers); IO.mapOptional("StreamSizes", Obj.StreamSizes); IO.mapOptional("StreamMap", Obj.StreamMap); } - -void MappingTraits::mapping(IO &IO, StreamSizeEntry &Obj) { - IO.mapRequired("Size", Obj.Size); -} - -void MappingTraits::mapping(IO &IO, StreamMapEntry &Obj) { - IO.mapRequired("Blocks", Obj.Blocks); -} Index: tools/llvm-pdbdump/YAMLOutputStyle.cpp =================================================================== --- tools/llvm-pdbdump/YAMLOutputStyle.cpp +++ tools/llvm-pdbdump/YAMLOutputStyle.cpp @@ -24,19 +24,18 @@ return Error::success(); yaml::MsfHeaders Headers; - Headers.BlockCount = File.getBlockCount(); - Headers.BlockMapIndex = File.getBlockMapIndex(); - Headers.BlockMapOffset = File.getBlockMapOffset(); - Headers.BlockSize = File.getBlockSize(); + Obj.Headers.SuperBlock.NumBlocks = File.getBlockCount(); + Obj.Headers.SuperBlock.BlockMapAddr = File.getBlockMapIndex(); + Obj.Headers.BlockMapOffset = File.getBlockMapOffset(); + Obj.Headers.SuperBlock.BlockSize = File.getBlockSize(); auto Blocks = File.getDirectoryBlockArray(); - Headers.DirectoryBlocks.assign(Blocks.begin(), Blocks.end()); - Headers.NumDirectoryBlocks = File.getNumDirectoryBlocks(); - Headers.NumDirectoryBytes = File.getNumDirectoryBytes(); - Headers.NumStreams = File.getNumStreams(); - Headers.Unknown0 = File.getUnknown0(); - Headers.Unknown1 = File.getUnknown1(); - - Obj.Headers.emplace(Headers); + Obj.Headers.DirectoryBlocks.assign(Blocks.begin(), Blocks.end()); + Obj.Headers.NumDirectoryBlocks = File.getNumDirectoryBlocks(); + Obj.Headers.SuperBlock.NumDirectoryBytes = File.getNumDirectoryBytes(); + Obj.Headers.NumStreams = File.getNumStreams(); + Obj.Headers.SuperBlock.Unknown0 = File.getUnknown0(); + Obj.Headers.SuperBlock.Unknown1 = File.getUnknown1(); + Obj.Headers.FileSize = File.getFileSize(); return Error::success(); } @@ -45,13 +44,7 @@ if (!opts::DumpStreamSummary) return Error::success(); - std::vector Sizes; - for (uint32_t I = 0; I < File.getNumStreams(); ++I) { - yaml::StreamSizeEntry Entry; - Entry.Size = File.getStreamByteSize(I); - Sizes.push_back(Entry); - } - Obj.StreamSizes.emplace(Sizes); + Obj.StreamSizes = File.getStreamSizes(); return Error::success(); } @@ -59,14 +52,13 @@ if (!opts::DumpStreamBlocks) return Error::success(); - std::vector Blocks; - for (uint32_t I = 0; I < File.getNumStreams(); ++I) { - yaml::StreamMapEntry Entry; - auto BlockList = File.getStreamBlockList(I); - Entry.Blocks.assign(BlockList.begin(), BlockList.end()); - Blocks.push_back(Entry); + auto StreamMap = File.getStreamMap(); + Obj.StreamMap.emplace(); + for (auto &Stream : StreamMap) { + pdb::yaml::StreamBlockList BlockList; + BlockList.Blocks = Stream; + Obj.StreamMap->push_back(BlockList); } - Obj.StreamMap.emplace(Blocks); return Error::success(); } Index: tools/llvm-pdbdump/llvm-pdbdump.h =================================================================== --- tools/llvm-pdbdump/llvm-pdbdump.h +++ tools/llvm-pdbdump/llvm-pdbdump.h @@ -25,6 +25,8 @@ extern llvm::cl::opt RawOutputStyle; extern llvm::cl::opt DumpHeaders; +extern llvm::cl::opt DumpPdbStream; +extern llvm::cl::opt DumpDbiStream; extern llvm::cl::opt DumpStreamBlocks; extern llvm::cl::opt DumpStreamSummary; extern llvm::cl::opt DumpTpiHash; Index: tools/llvm-pdbdump/llvm-pdbdump.cpp =================================================================== --- tools/llvm-pdbdump/llvm-pdbdump.cpp +++ tools/llvm-pdbdump/llvm-pdbdump.cpp @@ -29,6 +29,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Config/config.h" +#include "llvm/DebugInfo/CodeView/ByteStream.h" #include "llvm/DebugInfo/PDB/GenericError.h" #include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" #include "llvm/DebugInfo/PDB/IPDBRawSymbol.h" @@ -46,6 +47,7 @@ #include "llvm/Support/COM.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" #include "llvm/Support/ManagedStatic.h" @@ -60,6 +62,27 @@ using namespace llvm::codeview; using namespace llvm::pdb; +namespace { +// A simple adapter that acts like a ByteStream but holds ownership over +// and underlying FileOutputBuffer. +class FileBufferByteStream : public ByteStream { +public: + FileBufferByteStream(std::unique_ptr Buffer) + : ByteStream(MutableArrayRef(Buffer->getBufferStart(), + Buffer->getBufferEnd())), + FileBuffer(std::move(Buffer)) {} + + Error commit() const override { + if (auto EC = FileBuffer->commit()) + return llvm::make_error(raw_error_code::not_writable); + return Error::success(); + } + +private: + std::unique_ptr FileBuffer; +}; +} + namespace opts { enum class PDB_DumpType { ByType, ByObjFile, Both }; @@ -100,6 +123,10 @@ cl::opt DumpHeaders("raw-headers", cl::desc("dump PDB headers"), cl::cat(NativeOptions)); +cl::opt DumpPdbStream("raw-pdb-stream", cl::desc("dump PDB stream"), + cl::cat(NativeOptions)); +cl::opt DumpDbiStream("raw-dbi-stream", cl::desc("dump DBI stream"), + cl::cat(NativeOptions)); cl::opt DumpStreamBlocks("raw-stream-blocks", cl::desc("dump PDB stream blocks"), cl::cat(NativeOptions)); @@ -163,6 +190,14 @@ cl::desc("Implies most other options in 'Native Options' category"), cl::cat(NativeOptions)); +cl::opt + YamlToPdb("yaml-to-pdb", + cl::desc("The input file is yaml, and the tool outputs a pdb"), + cl::cat(NativeOptions)); +cl::opt YamlPdbOutputFile( + "pdb-output", cl::desc("When yaml-to-pdb is specified, the output file"), + cl::cat(NativeOptions)); + cl::list ExcludeTypes("exclude-types", cl::desc("Exclude types by regular expression"), @@ -266,6 +301,10 @@ bool isRawDumpEnabled() { if (opts::DumpHeaders) return true; + if (opts::DumpPdbStream) + return true; + if (opts::DumpDbiStream) + return true; if (opts::DumpModules) return true; if (opts::DumpModuleFiles) @@ -307,6 +346,47 @@ return false; } +static void yamlToPdb(StringRef Path) { + ErrorOr> ErrorOrBuffer = + MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + + if (ErrorOrBuffer.getError()) { + ExitOnErr(make_error(generic_error_code::invalid_path, Path)); + } + + std::unique_ptr &Buffer = ErrorOrBuffer.get(); + + llvm::yaml::Input In(Buffer->getBuffer()); + pdb::yaml::PdbObject YamlObj; + In >> YamlObj; + + auto OutFileOrError = FileOutputBuffer::create(opts::YamlPdbOutputFile, + YamlObj.Headers.FileSize); + if (OutFileOrError.getError()) + ExitOnErr(make_error(generic_error_code::invalid_path, + opts::YamlPdbOutputFile)); + + auto FileByteStream = + llvm::make_unique(std::move(*OutFileOrError)); + PDBFile Pdb(std::move(FileByteStream)); + ExitOnErr(Pdb.setSuperBlock(&YamlObj.Headers.SuperBlock)); + + if (YamlObj.StreamMap.hasValue()) { + std::vector> StreamMap; + for (auto &E : YamlObj.StreamMap.getValue()) { + StreamMap.push_back(E.Blocks); + } + + Pdb.setStreamMap(YamlObj.Headers.DirectoryBlocks, StreamMap); + } + if (YamlObj.StreamSizes.hasValue()) { + Pdb.setStreamSizes(YamlObj.StreamSizes.getValue()); + } + + ExitOnErr(Pdb.commit()); +} + static void dumpInput(StringRef Path) { std::unique_ptr Session; if (isRawDumpEnabled()) { @@ -460,6 +540,8 @@ if (opts::RawAll) { opts::DumpHeaders = true; + opts::DumpPdbStream = true; + opts::DumpDbiStream = true; opts::DumpModules = true; opts::DumpModuleFiles = true; opts::DumpModuleSyms = true; @@ -494,8 +576,13 @@ llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded); - std::for_each(opts::InputFilenames.begin(), opts::InputFilenames.end(), - dumpInput); + if (opts::YamlToPdb) { + std::for_each(opts::InputFilenames.begin(), opts::InputFilenames.end(), + yamlToPdb); + } else { + std::for_each(opts::InputFilenames.begin(), opts::InputFilenames.end(), + dumpInput); + } outs().flush(); return 0; Index: unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp =================================================================== --- unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp +++ unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp @@ -55,6 +55,7 @@ uint32_t getStreamByteSize(uint32_t StreamIndex) const override { return getBlockCount() * getBlockSize(); } + Error commit() const override { return Error::success(); } ArrayRef getStreamBlockList(uint32_t StreamIndex) const override { if (StreamIndex != 0)