Index: lib/Bitcode/NaCl/TestUtils/NaClBitcodeMunge.cpp |
diff --git a/lib/Bitcode/NaCl/TestUtils/NaClBitcodeMunge.cpp b/lib/Bitcode/NaCl/TestUtils/NaClBitcodeMunge.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6cb093238bdd40abd6c7d41f6f46c5ac9802a14e |
--- /dev/null |
+++ b/lib/Bitcode/NaCl/TestUtils/NaClBitcodeMunge.cpp |
@@ -0,0 +1,377 @@ |
+//===--- Bitcode/NaCl/TestUtils/NaClBitcodeMunge.cpp - Bitcode Munger -----===// |
+// |
+// The LLVM Compiler Infrastructure |
+// |
+// This file is distributed under the University of Illinois Open Source |
+// License. See LICENSE.TXT for details. |
+// |
+//===----------------------------------------------------------------------===// |
+// |
+// Bitcode writer/munger implementation for testing. |
+// |
+//===----------------------------------------------------------------------===// |
+ |
+#include "llvm/Bitcode/NaCl/NaClBitcodeMunge.h" |
+ |
+#include "llvm/Bitcode/NaCl/NaClBitcodeHeader.h" |
+#include "llvm/Bitcode/NaCl/NaClBitcodeParser.h" |
+#include "llvm/Bitcode/NaCl/NaClBitstreamWriter.h" |
+#include "llvm/Bitcode/NaCl/NaClReaderWriter.h" |
+#include "llvm/IR/LLVMContext.h" |
+#include "llvm/IR/Module.h" |
+#include "llvm/Support/ErrorHandling.h" |
+#include "llvm/Support/MemoryBuffer.h" |
+ |
+#include <memory> |
+ |
+using namespace llvm; |
+ |
+// For debugging. When true, shows each PNaCl record that is |
+// emitted to the bitcode file. |
+static bool DebugEmitRecord = false; |
+ |
+void NaClBitcodeMunger::setupTest( |
+ const char *TestName, const uint64_t Munges[], size_t MungesSize, |
+ bool AddHeader) { |
+ assert(DumpStream == nullptr && "Test run with DumpStream already defined"); |
+ assert(MungedInput.get() == nullptr |
+ && "Test run with MungedInput already defined"); |
+ FoundErrors = false; |
+ DumpResults.clear(); // Throw away any previous results. |
+ std::string DumpBuffer; |
+ DumpStream = new raw_string_ostream(DumpResults); |
+ SmallVector<char, 0> StreamBuffer; |
+ StreamBuffer.reserve(256*1024); |
+ NaClBitstreamWriter OutStream(StreamBuffer); |
+ Writer = &OutStream; |
+ |
+ if (DebugEmitRecord) { |
+ errs() << "*** Run test: " << TestName << "\n"; |
+ } |
+ |
+ WriteBlockID = -1; |
+ SetBID = -1; |
+ writeMungedData(Munges, MungesSize, AddHeader); |
+ |
+ std::string OutBuffer; |
+ raw_string_ostream BitcodeStrm(OutBuffer); |
+ for (SmallVectorImpl<char>::const_iterator |
+ Iter = StreamBuffer.begin(), IterEnd = StreamBuffer.end(); |
+ Iter != IterEnd; ++Iter) { |
+ BitcodeStrm << *Iter; |
+ } |
+ MungedInput = MemoryBuffer::getMemBufferCopy(BitcodeStrm.str(), TestName); |
+} |
+ |
+void NaClBitcodeMunger::cleanupTest() { |
+ MungedInput.reset(); |
+ assert(DumpStream && "Dump stream removed before cleanup!"); |
+ DumpStream->flush(); |
+ delete DumpStream; |
+ DumpStream = nullptr; |
+ Writer = nullptr; |
+} |
+ |
+// Return the next line of input (including eoln), starting from |
+// Pos. Then increment Pos past the end of that line. |
+static std::string getLine(const std::string &Input, size_t &Pos) { |
+ std::string Line; |
+ if (Pos >= Input.size()) { |
+ Pos = std::string::npos; |
+ return Line; |
+ } |
+ size_t Eoln = Input.find_first_of("\n", Pos); |
+ if (Eoln != std::string::npos) { |
+ for (size_t i = Pos; i <= Eoln; ++i) |
+ Line.push_back(Input[i]); |
+ Pos = Eoln + 1; |
+ return Line; |
+ } |
+ Pos = std::string::npos; |
+ return Input.substr(Pos); |
+} |
+ |
+std::string NaClBitcodeMunger:: |
+getLinesWithTextMatch(const std::string &Substring, bool MustBePrefix) const { |
+ std::string Messages; |
+ size_t LastLinePos = 0; |
+ while (1) { |
+ std::string Line = getLine(DumpResults, LastLinePos); |
+ if (LastLinePos == std::string::npos) break; |
+ size_t Pos = Line.find(Substring); |
+ if (Pos != std::string::npos && (!MustBePrefix || Pos == 0)) { |
+ Messages.append(Line); |
+ } |
+ } |
+ return Messages; |
+} |
+ |
+void NaClBitcodeMunger::writeMungedData(const uint64_t Munges[], |
+ size_t MungesSize, bool AddHeader) { |
+ uint64_t RecordCount = 0; |
+ size_t MungesIndex = 0; |
+ if (AddHeader) { |
+ NaClWriteHeader(*Writer, true); |
+ } |
+ for (size_t RecordsIndex = 0; RecordsIndex < RecordsSize;) { |
+ if (MungesIndex < MungesSize && Munges[MungesIndex] == RecordCount) { |
+ if (MungesIndex + 2 > MungesSize) { |
+ Fatal() << "Munges entry must contain at least 2 elements. Found: " |
+ << MungesIndex; |
+ ReportFatalError(); |
+ } |
+ ++MungesIndex; |
+ switch (Munges[MungesIndex++]) { |
+ case NaClBitcodeMunger::AddBefore: |
+ emitRecordAtIndex(Munges, MungesSize, MungesIndex); |
+ break; |
+ case NaClBitcodeMunger::AddAfter: |
+ emitRecordAtIndex(Records, RecordsSize, RecordsIndex); |
+ ++RecordCount; |
+ emitRecordAtIndex(Munges, MungesSize, MungesIndex); |
+ break; |
+ case NaClBitcodeMunger::Remove: |
+ deleteRecord(Records, RecordsSize, RecordsIndex); |
+ ++RecordCount; |
+ break; |
+ case NaClBitcodeMunger::Replace: |
+ deleteRecord(Records, RecordsSize, RecordsIndex); |
+ emitRecordAtIndex(Munges, MungesSize, MungesIndex); |
+ ++RecordCount; |
+ break; |
+ default: |
+ Fatal() << "Munge directive not understood: " << Munges[MungesIndex]; |
+ ReportFatalError(); |
+ break; |
+ } |
+ } else { |
+ emitRecordAtIndex(Records, RecordsSize, RecordsIndex); |
+ ++RecordCount; |
+ } |
+ } |
+ if (MungesIndex < MungesSize) { |
+ Fatal() << "Unprocessed modifications. At index " << MungesIndex << "\n"; |
+ ReportFatalError(); |
+ } |
+} |
+ |
+void NaClBitcodeMunger::deleteRecord( |
+ const uint64_t Record[], size_t RecordSize, size_t &Index) { |
+ while (Index < RecordSize) { |
+ if (Record[Index++] == RecordTerminator) |
+ break; |
+ } |
+} |
+ |
+void NaClBitcodeMunger::emitRecordAtIndex( |
+ const uint64_t Record[], size_t RecordSize, size_t &Index) { |
+ if (Index + 3 > RecordSize) { |
+ Fatal() << "Last record doesn't contain at least 3 elements. Found: " |
+ << (RecordSize - Index); |
+ ReportFatalError(); |
+ } |
+ SmallVector<uint64_t, 32> RecordValues; |
+ unsigned AbbrevIndex = static_cast<unsigned>(Record[Index++]); |
+ unsigned RecordCode = static_cast<unsigned>(Record[Index++]); |
+ while (Index < RecordSize && Record[Index] != RecordTerminator) { |
+ RecordValues.push_back(Record[Index++]); |
+ } |
+ emitRecord(AbbrevIndex, RecordCode, RecordValues); |
+ if (Index == RecordSize) { |
+ Fatal() << "Last record not followed by terminator.\n"; |
+ ReportFatalError(); |
+ } |
+ ++Index; |
+} |
+ |
+void NaClBitcodeMunger::emitRecord(unsigned AbbrevIndex, |
+ unsigned RecordCode, |
+ SmallVectorImpl<uint64_t> &Values) { |
+ if (DebugEmitRecord) { |
+ errs() << "Emit " << AbbrevIndex << ": <" << RecordCode; |
+ for (size_t i = 0; i < Values.size(); ++i) { |
+ errs() << ", " << Values[i]; |
+ } |
+ errs() << ">\n"; |
+ } |
+ |
+ switch (RecordCode) { |
+ case naclbitc::BLK_CODE_ENTER: { |
+ unsigned NumBits = naclbitc::DEFAULT_MAX_ABBREV; |
+ WriteBlockID = -1; |
+ if (AbbrevIndex != naclbitc::ENTER_SUBBLOCK) { |
+ Fatal() << "Enter block record code " << RecordCode |
+ << " uses illegal abbreviation index " << AbbrevIndex << "\n"; |
+ ReportFatalError(); |
+ } |
+ if (Values.size() == 2) { |
+ WriteBlockID = Values[0]; |
+ NumBits = Values[1]; |
+ if (NumBits > 32) { |
+ Fatal() << "Error: Bit size " << NumBits |
+ << " for record should be <= 32\n"; |
+ ReportFatalError(); |
+ } |
+ } else { |
+ Fatal() << "Error: Values for enter record should be of size 2. Found: " |
+ << Values.size(); |
+ ReportFatalError(); |
+ } |
+ if (WriteBlockID == naclbitc::BLOCKINFO_BLOCK_ID) { |
+ if (NumBits != naclbitc::DEFAULT_MAX_ABBREV) { |
+ Fatal() |
+ << "Error: Numbits entry for abbreviations record not 2. Found: " |
+ << NumBits << "\n"; |
+ ReportFatalError(); |
+ } |
+ Writer->EnterBlockInfoBlock(); |
+ } else { |
+ Writer->EnterSubblock(WriteBlockID, NumBits); |
+ } |
+ return; |
+ } |
+ case naclbitc::BLK_CODE_EXIT: |
+ if (AbbrevIndex != naclbitc::END_BLOCK) { |
+ Fatal() << "Error: Exit block record code " << RecordCode |
+ << " uses illegal abbreviation index " << AbbrevIndex << "\n"; |
+ ReportFatalError(); |
+ } |
+ if (!Values.empty()) { |
+ Fatal() << "Error: Exit block should not have values. Found: " |
+ << Values.size() << "\n"; |
+ ReportFatalError(); |
+ } |
+ Writer->ExitBlock(); |
+ return; |
+ case naclbitc::BLK_CODE_DEFINE_ABBREV: { |
+ if (AbbrevIndex != naclbitc::DEFINE_ABBREV) { |
+ Fatal() << "Error: Define abbreviation record code " << RecordCode |
+ << " uses illegal abbreviation index " << AbbrevIndex << "\n"; |
+ ReportFatalError(); |
+ } |
+ NaClBitCodeAbbrev *Abbrev = buildAbbrev(RecordCode, Values); |
+ if (Abbrev == NULL) return; |
+ if (WriteBlockID == naclbitc::BLOCKINFO_BLOCK_ID) { |
+ Writer->EmitBlockInfoAbbrev(SetBID, Abbrev); |
+ } else { |
+ Writer->EmitAbbrev(Abbrev); |
+ } |
+ return; |
+ } |
+ case naclbitc::BLK_CODE_HEADER: |
+ // Note: There is no abbreviation index here. Ignore. |
+ for (SmallVectorImpl<uint64_t>::const_iterator |
+ Iter = Values.begin(), IterEnd = Values.end(); |
+ Iter != IterEnd; ++Iter) { |
+ Writer->Emit(*Iter, 8); |
+ } |
+ return; |
+ default: |
+ if ((AbbrevIndex != naclbitc::UNABBREV_RECORD |
+ && !Writer->isUserRecordAbbreviation(AbbrevIndex))) { |
+ Fatal() << "Error: Record code " << RecordCode |
+ << " uses illegal abbreviation index " << AbbrevIndex << "\n"; |
+ ReportFatalError(); |
+ } |
+ if (AbbrevIndex == naclbitc::UNABBREV_RECORD) |
+ Writer->EmitRecord(RecordCode, Values); |
+ else |
+ Writer->EmitRecord(RecordCode, Values, AbbrevIndex); |
+ return; |
+ } |
+ Fatal() << "emitRecord on unimplemented code" << "\n"; |
+ ReportFatalError(); |
+} |
+ |
+NaClBitCodeAbbrev *NaClBitcodeMunger::buildAbbrev( |
+ unsigned RecordCode, SmallVectorImpl<uint64_t> &Values) { |
+ NaClBitCodeAbbrev *Abbrev = new NaClBitCodeAbbrev(); |
+ for (size_t Index = 0; Index < Values.size(); ) { |
+ switch (Values[Index]) { |
+ case 1: |
+ if (Index + 1 >= Values.size()) { |
+ Fatal() << "Malformed literal abbreviation.\n"; |
+ ReportFatalError(); |
+ } |
+ Abbrev->Add(NaClBitCodeAbbrevOp(Values[++Index])); |
+ break; |
+ case 0: { |
+ if (Index >= Values.size()) { |
+ Fatal() << "Malformed abbreviation found.\n"; |
+ ReportFatalError(); |
+ } |
+ unsigned Kind = Values[++Index]; |
+ switch (Kind) { |
+ case NaClBitCodeAbbrevOp::Fixed: |
+ if (Index >= Values.size()) { |
+ Fatal() << "Malformed fixed abbreviation found.\n"; |
+ ReportFatalError(); |
+ } |
+ Abbrev->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::Fixed, |
+ Values[++Index])); |
+ break; |
+ case NaClBitCodeAbbrevOp::VBR: |
+ if (Index >= Values.size()) { |
+ Fatal() << "Malformed vbr abbreviation found.\n"; |
+ ReportFatalError(); |
+ } |
+ Abbrev->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::VBR, |
+ Values[++Index])); |
+ break; |
+ case NaClBitCodeAbbrevOp::Array: |
+ if (Index >= Values.size()) { |
+ Fatal() << "Malformed array abbreviation found.\n"; |
+ ReportFatalError(); |
+ } |
+ Abbrev->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::Array)); |
+ break; |
+ case NaClBitCodeAbbrevOp::Char6: |
+ Abbrev->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::Char6)); |
+ break; |
+ default: |
+ Fatal() << "Unknown abbreviation kind. Found: " << Kind << "\n"; |
+ ReportFatalError(); |
+ } |
+ break; |
+ } |
+ default: |
+ Fatal() << "Error: Bad literal flag in abbreviation. Found: " |
+ << Values[Index]; |
+ ReportFatalError(); |
+ } |
+ } |
+ return Abbrev; |
+} |
+ |
+bool NaClObjDumpMunger::runTestWithFlags( |
+ const char *Name, const uint64_t Munges[], size_t MungesSize, |
+ bool AddHeader, bool NoRecords, bool NoAssembly) { |
+ setupTest(Name, Munges, MungesSize, AddHeader); |
+ // TODO(jvoung,kschimpf): Should NaClObjDump take a MemoryBufferRef |
+ // like the parser? |
+ if (NaClObjDump(MungedInput.get(), *DumpStream, NoRecords, NoAssembly)) |
+ FoundErrors = true; |
+ cleanupTest(); |
+ return !FoundErrors; |
+} |
+ |
+bool NaClParseBitcodeMunger::runTest( |
+ const char *Name, const uint64_t Munges[], size_t MungesSize, |
+ bool VerboseErrors) { |
+ bool AddHeader = true; |
+ setupTest(Name, Munges, MungesSize, AddHeader); |
+ LLVMContext &Context = getGlobalContext(); |
+ raw_ostream *VerboseStrm = VerboseErrors ? DumpStream : nullptr; |
+ ErrorOr<Module *> ModuleOrError = |
+ NaClParseBitcodeFile(MungedInput->getMemBufferRef(), Context, |
+ VerboseStrm); |
+ if (ModuleOrError) { |
+ if (VerboseErrors) |
+ *DumpStream << "Successful parse!\n"; |
+ delete ModuleOrError.get(); |
+ } else { |
+ Error() << ModuleOrError.getError().message() << "\n"; |
+ } |
+ cleanupTest(); |
+ return !FoundErrors; |
+} |