//===- 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;
    }
  }
}
