blob: 272634654b8aeace28b6cc5bd7917a65b62d43ef [file] [log] [blame] [edit]
//===--- 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/NaClCompress.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);
MungedInputBuffer.clear();
NaClBitstreamWriter OutStream(MungedInputBuffer);
Writer = &OutStream;
if (DebugEmitRecord) {
errs() << "*** Run test: " << TestName << "\n";
}
WriteBlockID = -1;
SetBID = -1;
writeMungedData(Munges, MungesSize, AddHeader);
// Add null terminator, so that we meet the requirements of the
// MemoryBuffer API.
MungedInputBuffer.push_back('\0');
MungedInput = MemoryBuffer::getMemBuffer(
StringRef(MungedInputBuffer.data(), MungedInputBuffer.size()-1),
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();
}
if (NumBits < 2) {
Fatal() << "Error: Bit size " << NumBits
<< " for record should be >= 2\n";
ReportFatalError();
}
} else {
Fatal() << "Error: Values for enter record should be of size 2. Found: "
<< Values.size();
ReportFatalError();
}
uint64_t MaxAbbrev = (static_cast<uint64_t>(1) << NumBits) - 1;
AbbrevIndexLimitStack.push_back(MaxAbbrev);
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 {
NaClBitcodeSelectorAbbrev CurCodeLen(MaxAbbrev);
Writer->EnterSubblock(WriteBlockID, CurCodeLen);
}
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();
}
if (!AbbrevIndexLimitStack.empty())
AbbrevIndexLimitStack.pop_back();
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))) {
uint64_t BlockAbbrevIndexLimit = 0;
if (!AbbrevIndexLimitStack.empty())
BlockAbbrevIndexLimit = AbbrevIndexLimitStack.back();
if (AbbrevIndex > BlockAbbrevIndexLimit) {
Fatal() << "Error: Record code " << RecordCode
<< " uses illegal abbreviation index " << AbbrevIndex
<< ". Must not exceed " << BlockAbbrevIndexLimit << "\n";
ReportFatalError();
}
// Note: If this point is reached, the abbreviation is
// bad. However, that may be the point of munge being
// applied. Hence, emit the bad abbreviation and the data so
// that the reader can be tested on this bad input. For
// simplicity, we output the record data using the default
// abbreviation pattern.
errs() << "Warning: Record code " << RecordCode
<< " uses illegal abbreviation index " << AbbrevIndex << "\n";
Writer->EmitCode(AbbrevIndex);
Writer->EmitVBR(RecordCode, 6);
uint32_t NumValues = static_cast<uint32_t>(Values.size());
Writer->EmitVBR(NumValues, 6);
for (uint32_t i = 0; i < NumValues; ++i) {
Writer->EmitVBR64(Values[i], 6);
}
return;
}
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();
size_t Index = 0;
if (Values.empty()) {
Fatal() << "Empty abbreviation record not allowed\n";
ReportFatalError();
}
size_t NumAbbreviations = Values[Index++];
if (NumAbbreviations == 0) {
Fatal() << "Abbreviation must contain at least one operator\n";
ReportFatalError();
}
for (size_t Count = 0; Count < NumAbbreviations; ++Count) {
if (Index >= Values.size()) {
Fatal() << "Malformed abbreviation found. Expects "
<< NumAbbreviations << " operands. Found: "
<< Count << "\n";
ReportFatalError();
}
switch (Values[Index++]) {
case 1:
if (Index >= 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);
/// If running in death mode, redirect output directly to the
/// error stream (rather than buffering in DumpStream), so that
/// output can be seen in gtest death test.
raw_ostream &Output = RunAsDeathTest ? errs() : *DumpStream;
if (NaClObjDump(MungedInput.get()->getMemBufferRef(),
Output, 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;
}
bool NaClCompressMunger::runTest(const char* Name, const uint64_t Munges[],
size_t MungesSize) {
bool AddHeader = true;
setupTest(Name, Munges, MungesSize, AddHeader);
NaClBitcodeCompressor Compressor;
bool Result = Compressor.compress(MungedInput.get(), *DumpStream);
cleanupTest();
return Result;
}