From d11328a1bbf6a4d6f62fe0d7b401e5760cafdf68 Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Mon, 2 Apr 2018 18:35:21 +0000 Subject: [PATCH] [llvm-pdbutil] Add an export subcommand. This command can dump the binary contents of a stream to a file. This is useful when you want to do side-by-side comparisons of a specific stream from two PDBs to examine the differences between them. You can export both of them to a file, then open them up side by side in a hex editor (for example), so as to eliminate any differences that might arise from the contents being on different blocks in the PDB. In subsequent patches I plan to improve the "explain" subcommand so that you can explain the contents of a binary file that isn't necessarily a full PDB, but one of these dumped streams, by telling the subcommand how to interpret the contents. llvm-svn: 329002 --- .../llvm/DebugInfo/PDB/Native/InfoStream.h | 2 +- llvm/lib/DebugInfo/PDB/Native/InfoStream.cpp | 4 +- llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp | 13 +++- llvm/test/tools/llvm-pdbdump/Inputs/tpi.bin | Bin 0 -> 6000 bytes .../tools/llvm-pdbdump/export-stream.test | 2 + llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp | 64 ++++++++++++++++++ llvm/tools/llvm-pdbutil/llvm-pdbutil.h | 6 ++ 7 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 llvm/test/tools/llvm-pdbdump/Inputs/tpi.bin create mode 100644 llvm/test/tools/llvm-pdbdump/export-stream.test diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/InfoStream.h b/llvm/include/llvm/DebugInfo/PDB/Native/InfoStream.h index caeb423af724..f49e7afc07e4 100644 --- a/llvm/include/llvm/DebugInfo/PDB/Native/InfoStream.h +++ b/llvm/include/llvm/DebugInfo/PDB/Native/InfoStream.h @@ -52,7 +52,7 @@ public: BinarySubstreamRef getNamedStreamsBuffer() const; - uint32_t getNamedStreamIndex(llvm::StringRef Name) const; + Expected getNamedStreamIndex(llvm::StringRef Name) const; StringMap named_streams() const; private: diff --git a/llvm/lib/DebugInfo/PDB/Native/InfoStream.cpp b/llvm/lib/DebugInfo/PDB/Native/InfoStream.cpp index e9ae5a1a1933..0bb543e8c646 100644 --- a/llvm/lib/DebugInfo/PDB/Native/InfoStream.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/InfoStream.cpp @@ -86,10 +86,10 @@ Error InfoStream::reload() { uint32_t InfoStream::getStreamSize() const { return Stream->getLength(); } -uint32_t InfoStream::getNamedStreamIndex(llvm::StringRef Name) const { +Expected InfoStream::getNamedStreamIndex(llvm::StringRef Name) const { uint32_t Result; if (!NamedStreams.get(Name, Result)) - return 0; + return make_error(raw_error_code::no_stream); return Result; } diff --git a/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp b/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp index 15b31d821b1c..6b1d28a3731a 100644 --- a/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp @@ -370,7 +370,10 @@ Expected PDBFile::getStringTable() { if (!IS) return IS.takeError(); - uint32_t NameStreamIndex = IS->getNamedStreamIndex("/names"); + Expected ExpectedNSI = IS->getNamedStreamIndex("/names"); + if (!ExpectedNSI) + return ExpectedNSI.takeError(); + uint32_t NameStreamIndex = *ExpectedNSI; auto NS = safelyCreateIndexedStream(ContainerLayout, *Buffer, NameStreamIndex); @@ -445,7 +448,13 @@ bool PDBFile::hasPDBStringTable() { auto IS = getPDBInfoStream(); if (!IS) return false; - return IS->getNamedStreamIndex("/names") < getNumStreams(); + Expected ExpectedNSI = IS->getNamedStreamIndex("/names"); + if (!ExpectedNSI) { + consumeError(ExpectedNSI.takeError()); + return false; + } + assert(*ExpectedNSI < getNumStreams()); + return true; } /// Wrapper around MappedBlockStream::createIndexedStream() that checks if a diff --git a/llvm/test/tools/llvm-pdbdump/Inputs/tpi.bin b/llvm/test/tools/llvm-pdbdump/Inputs/tpi.bin new file mode 100644 index 0000000000000000000000000000000000000000..269a93d15ac457096e2b36bf07dc50094aaef5c9 GIT binary patch literal 6000 zcmcIoZFdvL6~0;tqc{YF2@R!zx+HCBdP+@7Q|h)s;FsVO7at6i(+^QrtFf${)vmfP z4$gd3HPDOrC0&zO z^9afr_$D8s|x%?pyxqYIZnM z9mRqFdZ+l`uGDPL7=(-Cs}mp7k%AY6f9c>GCF5`4k7xX8{ty1dqiisQ1?$t4p5L#p z5MPlarpI|^CU8nbbPDE;0CGZ1EMk7=8a3v8iBLHU`l`IuV)iI|8?SgZImZ^y0`%CH zb$}zIvm^^k?EemEwinQTi+Hy%Guk(K8j5|{74M`)mD_LzDf$(6SWz} zerCUqx~TOy?}iHV{3kHjFc`c9pG%(Zt8k=}6Q9Y!?T#O2jPC0{k_x?c$y~)p^WA*d zESXwetr$^*wSqsFCk1vdBQ7p>-vw=PN!btZ61(rQ#q+7bAoVN;F9J_jKv#Go#2R%i z);Rw68bYtv%i*^pZV4P=&zM z&pSZH?a+dD(v4b9l>6e^hUoMH)u7?q|N^qcoKh5o4z{o%fEP_(O&0xP4a4TxW#zUD|4E{n2*Z z%R>{)!lrj5N61DT9S-B%iGSmWYpHsQ53{HCia}_HeB|a?G>D^uTi7ALA!;MahXvcr zk9_21UF{7b_0;dr_CbDjvm_6)LZEZTKFHCdIPk++edK2+iBUx|10ZJ{!>pB_fReiBEMfKBWj|Kpwz+X%Ip&v&Sa z*G*0Q5x&8BYGTeW!zqWFwhOc&+9#FfszBeqaV#S3#e?Ml6XVuid_d%Pf3+vyqr6+MJ)==6*W%R>bVf`*@-%pG!O6IG5Cnxu&^Znec zsWtIOev%CVO*jTRdf1c`tVpy4=hC0xkD9+`G~4#4Ch_d$*5V-Xt0Yo~Uo}#8@=2mi|)n$gjlH z#QfA0&lzQQRVOKkiG zt;GiI+C3I=aR+pzt7cD6eY?+MOo)x&Y99HO*r+x~cvMQNW5QZ6$JsMcfx<~Pd41N4 zLrY1wV&ri&UoAMrqR#v&vWn46?(ZU=%)LGT{|@vDVrz3u`S0DeyYb}t)*9(OV0FQ)TMsaw(kzC0OZs|@&gfLkV*JH?*qpJX zF*(Dlux_xp4*yLZYK@Q+-Zim4ZITb_0qz+tfWHR%Gw0M`*jXVvVGI7uVaMXM&{hOg$IR^uLMw zvmy2}#5d`dpaP{hEFe~vcSZ7i&?|HpgJlT=ORs?b5O;1-mc+!O?Q>IjL{D+Q7WKLA zkzd4b%4aY2GSj|s?7L1MBHP+(YyRE}k4wizIkU73M+>YM|IgNN#|o^_?hQSOpljz$ zy1|^A+>vf#zuR}{a}soSbxaE1sMC^Yu*r;=*pc@xc#peZ>AaD?0{ZzN(RWWAdl|g< zB~1cc(T))2)Qmy?HR>q2IT>Cz$Is*<<${n`<4DUCkT$yw9b*i|BV<(j71~UB}(? zvWyO%DAqsKyKVF?L67`T?pVpk@(E&k9x;9D#Pp`@Cc#7O%2o0>F9Pc_+HKgg`Jzp6 Z@$zjQg?ybzSV3r;{{c7sCT;)# literal 0 HcmV?d00001 diff --git a/llvm/test/tools/llvm-pdbdump/export-stream.test b/llvm/test/tools/llvm-pdbdump/export-stream.test new file mode 100644 index 000000000000..f60606dd1198 --- /dev/null +++ b/llvm/test/tools/llvm-pdbdump/export-stream.test @@ -0,0 +1,2 @@ +; RUN: llvm-pdbutil export -stream=2 -out=%t.tpi.bin %p/Inputs/InjectedSource.pdb +; RUN: diff %t.tpi.bin %p/Inputs/tpi.bin diff --git a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp index 3cb023772f58..a694d47bd010 100644 --- a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp @@ -50,6 +50,7 @@ #include "llvm/DebugInfo/PDB/IPDBSession.h" #include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h" #include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" #include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h" #include "llvm/DebugInfo/PDB/Native/NativeSession.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" @@ -115,6 +116,9 @@ cl::SubCommand MergeSubcommand("merge", cl::SubCommand ExplainSubcommand("explain", "Explain the meaning of a file offset"); +cl::SubCommand ExportSubcommand("export", + "Write binary data from a stream to a file"); + cl::OptionCategory TypeCategory("Symbol Type Options"); cl::OptionCategory FilterCategory("Filtering and Sorting Options"); cl::OptionCategory OtherOptions("Other Options"); @@ -618,6 +622,24 @@ cl::list InputFilename(cl::Positional, cl::list Offsets("offset", cl::desc("The file offset to explain"), cl::sub(ExplainSubcommand), cl::OneOrMore); } // namespace explain + +namespace exportstream { +cl::list InputFilename(cl::Positional, + cl::desc(""), cl::Required, + cl::sub(ExportSubcommand)); +cl::opt OutputFile("out", + cl::desc("The file to write the stream to"), + cl::Required, cl::sub(ExportSubcommand)); +cl::opt + Stream("stream", cl::Required, + cl::desc("The index or name of the stream whose contents to export"), + cl::sub(ExportSubcommand)); +cl::opt ForceName("name", + cl::desc("Force the interpretation of -stream as a " + "string, even if it is a valid integer"), + cl::sub(ExportSubcommand), cl::Optional, + cl::init(false)); +} // namespace exportstream } static ExitOnError ExitOnErr; @@ -1098,6 +1120,46 @@ static void explain() { } } +static void exportStream() { + std::unique_ptr Session; + PDBFile &File = loadPDB(opts::exportstream::InputFilename.front(), Session); + + std::unique_ptr SourceStream; + uint32_t Index = 0; + bool Success = false; + std::string OutFileName = opts::exportstream::OutputFile; + + if (!opts::exportstream::ForceName) { + // First try to parse it as an integer, if it fails fall back to treating it + // as a named stream. + if (to_integer(opts::exportstream::Stream, Index)) { + if (Index >= File.getNumStreams()) { + errs() << "Error: " << Index << " is not a valid stream index.\n"; + exit(1); + } + Success = true; + outs() << "Dumping contents of stream index " << Index << " to file " + << OutFileName << ".\n"; + } + } + + if (!Success) { + InfoStream &IS = cantFail(File.getPDBInfoStream()); + Index = ExitOnErr(IS.getNamedStreamIndex(opts::exportstream::Stream)); + outs() << "Dumping contents of stream '" << opts::exportstream::Stream + << "' (index " << Index << ") to file " << OutFileName << ".\n"; + } + + SourceStream = MappedBlockStream::createIndexedStream( + File.getMsfLayout(), File.getMsfBuffer(), Index, File.getAllocator()); + auto OutFile = ExitOnErr( + FileOutputBuffer::create(OutFileName, SourceStream->getLength())); + FileBufferByteStream DestStream(std::move(OutFile), llvm::support::little); + BinaryStreamWriter Writer(DestStream); + ExitOnErr(Writer.writeStreamRef(*SourceStream)); + ExitOnErr(DestStream.commit()); +} + static bool parseRange(StringRef Str, Optional &Parsed) { if (Str.empty()) @@ -1274,6 +1336,8 @@ int main(int argc_, const char *argv_[]) { mergePdbs(); } else if (opts::ExplainSubcommand) { explain(); + } else if (opts::ExportSubcommand) { + exportStream(); } outs().flush(); diff --git a/llvm/tools/llvm-pdbutil/llvm-pdbutil.h b/llvm/tools/llvm-pdbutil/llvm-pdbutil.h index e7ad269444e2..82f4e52c1e47 100644 --- a/llvm/tools/llvm-pdbutil/llvm-pdbutil.h +++ b/llvm/tools/llvm-pdbutil/llvm-pdbutil.h @@ -193,6 +193,12 @@ namespace explain { extern llvm::cl::list InputFilename; extern llvm::cl::list Offsets; } // namespace explain + +namespace exportstream { +extern llvm::cl::opt OutputFile; +extern llvm::cl::opt Stream; +extern llvm::cl::opt ForceName; +} // namespace exportstream } #endif