uint32_t getLength() const override;
+ Error commit() const override;
+
ArrayRef<uint8_t> data() const { return Data; }
StringRef str() const;
virtual Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Data) const = 0;
virtual uint32_t getLength() const = 0;
+
+ virtual Error commit() const = 0;
};
} // end namespace codeview
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();
Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Buffer) const override;
uint32_t getLength() const override;
+ Error commit() const override;
uint32_t getNumBytesCopied() const;
Expected<SymbolStream &> getPDBSymbolStream();
Expected<NameHashTable &> getStringTable();
- void setSuperBlock(const SuperBlock *Block);
+ Error setSuperBlock(const SuperBlock *Block);
void setStreamSizes(ArrayRef<support::ulittle32_t> Sizes);
- void setStreamMap(ArrayRef<ArrayRef<support::ulittle32_t>> Blocks);
- void commit();
+ void setStreamMap(ArrayRef<support::ulittle32_t> Directory,
+ std::vector<ArrayRef<support::ulittle32_t>> &Streams);
+ Error commit();
private:
std::unique_ptr<codeview::StreamInterface> Buffer;
const PDBFile::SuperBlock *SB;
ArrayRef<support::ulittle32_t> StreamSizes;
+ ArrayRef<support::ulittle32_t> DirectoryBlocks;
std::vector<ArrayRef<support::ulittle32_t>> StreamMap;
std::unique_ptr<InfoStream> Info;
return Data.size();
}
+template <bool Writable> Error ByteStream<Writable>::commit() const {
+ return Error::success();
+}
+
template <bool Writable> StringRef ByteStream<Writable>::str() const {
const char *CharData = reinterpret_cast<const char *>(Data.data());
return StringRef(CharData, Data.size());
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<uint8_t> &Buffer) const {
// Attempt to fulfill the request with a reference directly into the stream.
#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"
"Does not contain superblock");
}
- // Check the magic bytes.
- if (memcmp(SB->MagicBytes, MsfMagic, sizeof(MsfMagic)) != 0)
- return make_error<RawError>(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<RawError>(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<RawError>(raw_error_code::corrupt_file,
- "Unsupported block size.");
- }
-
- if (Buffer->getLength() % SB->BlockSize != 0)
- return make_error<RawError>(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<RawError>(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<RawError>(raw_error_code::corrupt_file,
- "Too many directory blocks.");
+ Reader.setOffset(getBlockMapOffset());
+ if (auto EC = Reader.readArray(DirectoryBlocks, getNumDirectoryBlocks()))
+ return EC;
return Error::success();
}
}
llvm::ArrayRef<support::ulittle32_t> PDBFile::getDirectoryBlockArray() const {
- StreamReader Reader(*Buffer);
- Reader.setOffset(getBlockMapOffset());
- llvm::ArrayRef<support::ulittle32_t> Result;
- if (auto EC = Reader.readArray(Result, getNumDirectoryBlocks()))
- consumeError(std::move(EC));
- return Result;
+ return DirectoryBlocks;
}
Expected<InfoStream &> PDBFile::getPDBInfoStream() {
return *StringTable;
}
-void PDBFile::setSuperBlock(const SuperBlock *Block) { SB = Block; }
+Error PDBFile::setSuperBlock(const SuperBlock *Block) {
+ SB = Block;
+
+ // Check the magic bytes.
+ if (memcmp(SB->MagicBytes, MsfMagic, sizeof(MsfMagic)) != 0)
+ return make_error<RawError>(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<RawError>(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<RawError>(raw_error_code::corrupt_file,
+ "Unsupported block size.");
+ }
+
+ if (Buffer->getLength() % SB->BlockSize != 0)
+ return make_error<RawError>(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<RawError>(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<RawError>(raw_error_code::corrupt_file,
+ "Too many directory blocks.");
+
+ return Error::success();
+}
void PDBFile::setStreamSizes(ArrayRef<support::ulittle32_t> Sizes) {
StreamSizes = Sizes;
}
-void PDBFile::setStreamMap(ArrayRef<ArrayRef<support::ulittle32_t>> Blocks) {
- StreamMap = Blocks;
+void PDBFile::setStreamMap(
+ ArrayRef<support::ulittle32_t> Directory,
+ std::vector<ArrayRef<support::ulittle32_t>> &Streams) {
+ DirectoryBlocks = Directory;
+ StreamMap = Streams;
}
-void PDBFile::commit() {}
+Error PDBFile::commit() {
+ 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
--- /dev/null
+; 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 pdb2yaml -stream-metadata -stream-directory %p/Inputs/empty.pdb > %t.1
+; RUN: llvm-pdbdump yaml2pdb -pdb=%t.2 %t.1
+; RUN: llvm-pdbdump pdb2yaml -stream-metadata -stream-directory %t.2 > %t.3
+; RUN: diff %t.1 %t.3
#include "PdbYaml.h"
#include "llvm-pdbdump.h"
-#include "llvm/DebugInfo/PDB/Raw/RawConstants.h"
#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Raw/RawConstants.h"
using namespace llvm;
using namespace llvm::pdb;
Obj.Headers.DirectoryBlocks.assign(Blocks.begin(), Blocks.end());
Obj.Headers.NumDirectoryBlocks = File.getNumDirectoryBlocks();
Obj.Headers.SuperBlock.NumDirectoryBytes = File.getNumDirectoryBytes();
- Obj.Headers.NumStreams = opts::pdb2yaml::StreamMetadata ? File.getNumStreams() : 0;
+ Obj.Headers.NumStreams =
+ opts::pdb2yaml::StreamMetadata ? File.getNumStreams() : 0;
Obj.Headers.SuperBlock.Unknown0 = File.getUnknown0();
Obj.Headers.SuperBlock.Unknown1 = File.getUnknown1();
Obj.Headers.FileSize = File.getFileSize();
Error dump() override;
private:
-
Error dumpFileHeaders();
Error dumpStreamMetadata();
Error dumpStreamDirectory();
Buffer->getBufferEnd())),
FileBuffer(std::move(Buffer)) {}
+ Error commit() const override {
+ if (FileBuffer->commit())
+ return llvm::make_error<RawError>(raw_error_code::not_writable);
+ return Error::success();
+ }
+
private:
std::unique_ptr<FileOutputBuffer> FileBuffer;
};
cl::SubCommand
PrettySubcommand("pretty",
"Dump semantic information about types and symbols");
-cl::SubCommand YamlToPdbSubcommand("yaml2pdb", "Generate a PDB file from a YAML description");
+cl::SubCommand
+ YamlToPdbSubcommand("yaml2pdb",
+ "Generate a PDB file from a YAML description");
cl::SubCommand
PdbToYamlSubcommand("pdb2yaml",
"Generate a detailed YAML description of a PDB File");
cl::opt<bool> DumpModuleFiles("module-files", cl::desc("dump file information"),
cl::cat(FileOptions), cl::sub(RawSubcommand));
cl::opt<bool> DumpLineInfo("line-info",
- cl::desc("dump file and line information"),
- cl::cat(FileOptions), cl::sub(RawSubcommand));
+ cl::desc("dump file and line information"),
+ cl::cat(FileOptions), cl::sub(RawSubcommand));
// SYMBOL OPTIONS
cl::opt<bool> DumpModuleSyms("module-syms", cl::desc("dump module symbols"),
// MISCELLANEOUS OPTIONS
cl::opt<bool> DumpSectionContribs("section-contribs",
cl::desc("dump section contributions"),
- cl::cat(MiscOptions),
- cl::sub(RawSubcommand));
+ cl::cat(MiscOptions), cl::sub(RawSubcommand));
cl::opt<bool> DumpSectionMap("section-map", cl::desc("dump section map"),
cl::cat(MiscOptions), cl::sub(RawSubcommand));
cl::opt<bool> DumpSectionHeaders("section-headers",
cl::desc("dump section headers"),
- cl::cat(MiscOptions),
- cl::sub(RawSubcommand));
-cl::opt<bool> DumpFpo("fpo", cl::desc("dump FPO records"),
- cl::cat(MiscOptions), cl::sub(RawSubcommand));
+ cl::cat(MiscOptions), cl::sub(RawSubcommand));
+cl::opt<bool> DumpFpo("fpo", cl::desc("dump FPO records"), cl::cat(MiscOptions),
+ cl::sub(RawSubcommand));
cl::opt<std::string> DumpStreamDataIdx("stream", cl::desc("dump stream data"),
- cl::cat(MiscOptions),
- cl::sub(RawSubcommand));
+ cl::cat(MiscOptions),
+ cl::sub(RawSubcommand));
cl::opt<std::string> DumpStreamDataName("stream-name",
- cl::desc("dump stream data"),
- cl::cat(MiscOptions),
- cl::sub(RawSubcommand));
+ cl::desc("dump stream data"),
+ cl::cat(MiscOptions),
+ cl::sub(RawSubcommand));
-cl::opt<bool>
- RawAll("all",
- cl::desc("Implies most other options."),
- cl::cat(MiscOptions), cl::sub(RawSubcommand));
+cl::opt<bool> RawAll("all", cl::desc("Implies most other options."),
+ cl::cat(MiscOptions), cl::sub(RawSubcommand));
cl::list<std::string> InputFilenames(cl::Positional,
cl::desc("<input PDB files>"),
}
namespace pdb2yaml {
- cl::opt<bool> StreamMetadata("stream-metadata", cl::desc("Dump the number of streams and each stream's size"), cl::sub(PdbToYamlSubcommand));
- cl::opt<bool> StreamDirectory("stream-directory", cl::desc("Dump each stream's block map (implies -stream-metadata)"), cl::sub(PdbToYamlSubcommand));
+cl::opt<bool> StreamMetadata(
+ "stream-metadata",
+ cl::desc("Dump the number of streams and each stream's size"),
+ cl::sub(PdbToYamlSubcommand));
+cl::opt<bool> StreamDirectory(
+ "stream-directory",
+ cl::desc("Dump each stream's block map (implies -stream-metadata)"),
+ cl::sub(PdbToYamlSubcommand));
cl::list<std::string> InputFilename(cl::Positional,
cl::desc("<input PDB file>"), cl::Required,
auto FileByteStream =
llvm::make_unique<FileBufferByteStream>(std::move(*OutFileOrError));
PDBFile Pdb(std::move(FileByteStream));
- Pdb.setSuperBlock(&YamlObj.Headers.SuperBlock);
+ ExitOnErr(Pdb.setSuperBlock(&YamlObj.Headers.SuperBlock));
if (YamlObj.StreamMap.hasValue()) {
std::vector<ArrayRef<support::ulittle32_t>> StreamMap;
for (auto &E : YamlObj.StreamMap.getValue()) {
StreamMap.push_back(E.Blocks);
}
- Pdb.setStreamMap(StreamMap);
+ Pdb.setStreamMap(YamlObj.Headers.DirectoryBlocks, StreamMap);
}
if (YamlObj.StreamSizes.hasValue()) {
Pdb.setStreamSizes(YamlObj.StreamSizes.getValue());
}
- Pdb.commit();
+ ExitOnErr(Pdb.commit());
}
static void dumpRaw(StringRef Path) {