//===- NaClBitcodeHeader.cpp ----------------------------------------------===// // PNaCl bitcode header reader. // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "llvm/Bitcode/NaCl/NaClBitcodeHeader.h" #include "llvm/ADT/SmallSet.h" #include "llvm/Bitcode/NaCl/NaClReaderWriter.h" #include "llvm/Bitcode/ReaderWriter.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/StreamingMemoryObject.h" #include <cstring> #include <iomanip> #include <limits> using namespace llvm; namespace { // The name for each ID tag. static const char *TagName[] = { "Invalid", // kInvalid "PNaCl Version", // kPNaClVersion "Align bitcode records" // kAlignBitcodeRecords }; // The name for each field type. static const char *FieldTypeName[] = { "uint8[]", // kBufferType "uint32", // kUInt32Type "flag", // kFlagType "unknown" // kUnknownType }; // The type associated with each ID tag. static const NaClBitcodeHeaderField::FieldType ExpectedType[] = { NaClBitcodeHeaderField::kUnknownType, // kInvalid NaClBitcodeHeaderField::kUInt32Type, // kPNaClVersion NaClBitcodeHeaderField::kFlagType // kAlignBitcodeRecords }; } // end of anonymous namespace const char *NaClBitcodeHeaderField::IDName(Tag ID) { return ID > kTag_MAX ? "???" : TagName[ID]; } const char *NaClBitcodeHeaderField::TypeName(FieldType FType) { return FType > kFieldType_MAX ? "???" : FieldTypeName[FType]; } NaClBitcodeHeaderField::NaClBitcodeHeaderField() : ID(kInvalid), FType(kBufferType), Len(0), Data(0) {} NaClBitcodeHeaderField::NaClBitcodeHeaderField(Tag MyID) : ID(MyID), FType(kFlagType), Len(0), Data(0) { assert(MyID <= kTag_MAX); } NaClBitcodeHeaderField::NaClBitcodeHeaderField(Tag MyID, uint32_t MyValue) : ID(MyID), FType(kUInt32Type), Len(4), Data(new uint8_t[4]) { assert(MyID <= kTag_MAX); Data[0] = static_cast<uint8_t>(MyValue & 0xFF); Data[1] = static_cast<uint8_t>((MyValue >> 8) & 0xFF); Data[2] = static_cast<uint8_t>((MyValue >> 16) & 0xFF); Data[3] = static_cast<uint8_t>((MyValue >> 24) & 0xFF); } uint32_t NaClBitcodeHeaderField::GetUInt32Value() const { assert(FType == kUInt32Type && "Header field must be uint32"); return static_cast<uint32_t>(Data[0]) | (static_cast<uint32_t>(Data[1]) << 8) | (static_cast<uint32_t>(Data[2]) << 16) | (static_cast<uint32_t>(Data[2]) << 24); } NaClBitcodeHeaderField::NaClBitcodeHeaderField(Tag MyID, size_t MyLen, uint8_t *MyData) : ID(MyID), FType(kBufferType), Len(MyLen), Data(new uint8_t[MyLen]) { assert(MyID <= kTag_MAX); for (size_t i = 0; i < MyLen; ++i) { Data[i] = MyData[i]; } } bool NaClBitcodeHeaderField::Write(uint8_t *Buf, size_t BufLen) const { size_t FieldsLen = kTagLenSize + Len; size_t PadLen = (WordSize - (FieldsLen & (WordSize-1))) & (WordSize-1); // Ensure buffer is large enough and that length can be represented // in 32 bits if (BufLen < FieldsLen + PadLen || Len > std::numeric_limits<FixedSubfield>::max()) return false; WriteFixedSubfield(EncodeTypedID(), Buf); WriteFixedSubfield(static_cast<FixedSubfield>(Len), Buf + sizeof(FixedSubfield)); memcpy(Buf + kTagLenSize, Data, Len); // Pad out to word alignment if (PadLen) { memset(Buf + FieldsLen, 0, PadLen); } return true; } bool NaClBitcodeHeaderField::Read(const uint8_t *Buf, size_t BufLen) { if (BufLen < kTagLenSize) return false; FixedSubfield IdField; ReadFixedSubfield(&IdField, Buf); FixedSubfield LengthField; ReadFixedSubfield(&LengthField, Buf + sizeof(FixedSubfield)); size_t Length = static_cast<size_t>(LengthField); if (BufLen < kTagLenSize + Length) return false; if (Len != Length) { // Need to reallocate data buffer. if (Data) delete[] Data; Data = new uint8_t[Length]; } Len = Length; DecodeTypedID(IdField, ID, FType); memcpy(Data, Buf + kTagLenSize, Len); return true; } std::string NaClBitcodeHeaderField::Contents() const { std::string buffer; raw_string_ostream ss(buffer); ss << IDName() << ": "; switch (FType) { case kFlagType: ss << "true"; break; case kUInt32Type: ss << GetUInt32Value(); break; case kBufferType: ss << "["; for (size_t i = 0; i < Len; ++i) { if (i) ss << " "; ss << format("%02x", Data[i]); } ss << "]"; break; case kUnknownType: ss << "unknown value"; break; } return ss.str(); } NaClBitcodeHeader::NaClBitcodeHeader() : HeaderSize(0), UnsupportedMessage(), IsSupportedFlag(false), IsReadableFlag(false), PNaClVersion(0) {} NaClBitcodeHeader::~NaClBitcodeHeader() { for (std::vector<NaClBitcodeHeaderField *>::const_iterator Iter = Fields.begin(), IterEnd = Fields.end(); Iter != IterEnd; ++Iter) { delete *Iter; } } bool NaClBitcodeHeader::ReadPrefix(const unsigned char *BufPtr, const unsigned char *BufEnd, unsigned &NumFields, unsigned &NumBytes) { // Must contain PEXE. if (!isNaClBitcode(BufPtr, BufEnd)) { UnsupportedMessage = "Invalid PNaCl bitcode header"; if (isBitcode(BufPtr, BufEnd)) { UnsupportedMessage += " (to run in Chrome, bitcode files must be " "finalized using pnacl-finalize)"; } return true; } BufPtr += WordSize; // Read #Fields and number of bytes needed for the header. if (BufPtr + WordSize > BufEnd) return UnsupportedError("Bitcode read failure"); NumFields = static_cast<unsigned>(BufPtr[0]) | (static_cast<unsigned>(BufPtr[1]) << 8); NumBytes = static_cast<unsigned>(BufPtr[2]) | (static_cast<unsigned>(BufPtr[3]) << 8); BufPtr += WordSize; return false; } bool NaClBitcodeHeader::ReadFields(const unsigned char *BufPtr, const unsigned char *BufEnd, unsigned NumFields, unsigned NumBytes) { HeaderSize = NumBytes + (2 * WordSize); // Read in each field. for (size_t i = 0; i < NumFields; ++i) { NaClBitcodeHeaderField *Field = new NaClBitcodeHeaderField(); Fields.push_back(Field); if (!Field->Read(BufPtr, BufEnd - BufPtr)) return UnsupportedError("Bitcode read failure"); size_t FieldSize = Field->GetTotalSize(); BufPtr += FieldSize; } return false; } bool NaClBitcodeHeader::Read(const unsigned char *BufPtr, const unsigned char *BufEnd) { unsigned NumFields; unsigned NumBytes; if (ReadPrefix(BufPtr, BufEnd, NumFields, NumBytes)) return true; // ReadPrefix sets UnsupportedMessage BufPtr += 2 * WordSize; if (ReadFields(BufPtr, BufEnd, NumFields, NumBytes)) return true; // ReadFields sets UnsupportedMessage BufPtr += NumBytes; InstallFields(); return false; } bool NaClBitcodeHeader::Read(MemoryObject *Bytes) { unsigned NumFields; unsigned NumBytes; // First, read the prefix, which is 2 * WordSize, to determine the // NumBytes and NumFields. { unsigned char Buffer[2 * WordSize]; if (Bytes->readBytes(Buffer, sizeof(Buffer), 0) != sizeof(Buffer)) return UnsupportedError("Bitcode read failure"); if (ReadPrefix(Buffer, Buffer + sizeof(Buffer), NumFields, NumBytes)) return true; // ReadPrefix sets UnsupportedMessage } // Then read the rest, starting after the 2 * WordSize of the prefix. uint8_t *Header = new uint8_t[NumBytes]; bool failed = Bytes->readBytes(Header, NumBytes, 2 * WordSize) != NumBytes || ReadFields(Header, Header + NumBytes, NumFields, NumBytes); delete[] Header; if (failed) return UnsupportedError("Bitcode read failure"); InstallFields(); return false; } NaClBitcodeHeaderField * NaClBitcodeHeader::GetTaggedField(NaClBitcodeHeaderField::Tag ID) const { for (std::vector<NaClBitcodeHeaderField *>::const_iterator Iter = Fields.begin(), IterEnd = Fields.end(); Iter != IterEnd; ++Iter) { if ((*Iter)->GetID() == ID) { return *Iter; } } return 0; } NaClBitcodeHeaderField *NaClBitcodeHeader::GetField(size_t index) const { if (index >= Fields.size()) return 0; return Fields[index]; } NaClBitcodeHeaderField *GetPNaClVersionPtr(NaClBitcodeHeader *Header) { if (NaClBitcodeHeaderField *Version = Header->GetTaggedField(NaClBitcodeHeaderField::kPNaClVersion)) { if (Version->GetType() == NaClBitcodeHeaderField::kUInt32Type) { return Version; } } return 0; } void NaClBitcodeHeader::InstallFields() { IsSupportedFlag = true; IsReadableFlag = true; AlignBitcodeRecords = false; PNaClVersion = 0; UnsupportedMessage.clear(); SmallSet<unsigned, NaClBitcodeHeaderField::kTag_MAX> FieldIDs; auto ReportProblem = [&](bool IsReadable) { UnsupportedMessage.append("\n"); IsSupportedFlag = false; IsReadableFlag = IsReadableFlag && IsReadable; }; auto ReportProblemWithContents = [&](NaClBitcodeHeaderField *Field, bool IsReadable) { UnsupportedMessage.append(": "); UnsupportedMessage.append(Field->Contents()); ReportProblem(IsReadable); }; for (size_t i = 0, e = NumberFields(); i < e; ++i) { // Start by checking expected properties for any field NaClBitcodeHeaderField *Field = GetField(i); if (!FieldIDs.insert(Field->GetID()).second) { UnsupportedMessage.append("Specified multiple times: "); UnsupportedMessage.append(Field->IDName()); ReportProblem(false); continue; } NaClBitcodeHeaderField::FieldType ExpectedTy = ExpectedType[Field->GetID()]; if (Field->GetType() != ExpectedTy) { UnsupportedMessage.append("Expects type "); UnsupportedMessage.append(NaClBitcodeHeaderField::TypeName(ExpectedTy)); ReportProblemWithContents(Field, false); continue; } if (Field->GetType() == NaClBitcodeHeaderField::kUnknownType) { UnsupportedMessage.append("Unknown value"); ReportProblemWithContents(Field, false); continue; } // Check specific ID values and install. switch (Field->GetID()) { case NaClBitcodeHeaderField::kInvalid: UnsupportedMessage.append("Unsupported"); ReportProblemWithContents(Field, false); continue; case NaClBitcodeHeaderField::kPNaClVersion: PNaClVersion = Field->GetUInt32Value(); if (PNaClVersion != 2) { UnsupportedMessage.append("Unsupported"); ReportProblemWithContents(Field, false); continue; } break; case NaClBitcodeHeaderField::kAlignBitcodeRecords: AlignBitcodeRecords = true; UnsupportedMessage.append("Unsupported"); ReportProblemWithContents(Field, true); continue; } } }