brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.8 KiB · 82800b1 Raw
280 lines · cpp
1//===- yaml2goff - Convert YAML to a GOFF object file ---------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// The GOFF component of yaml2obj.11///12//===----------------------------------------------------------------------===//13 14#include "llvm/ObjectYAML/ObjectYAML.h"15#include "llvm/ObjectYAML/yaml2obj.h"16#include "llvm/Support/ConvertEBCDIC.h"17#include "llvm/Support/Endian.h"18#include "llvm/Support/raw_ostream.h"19 20using namespace llvm;21 22namespace {23 24// Common flag values on records.25enum {26  // Flag: This record is continued.27  Rec_Continued = 1,28 29  // Flag: This record is a continuation.30  Rec_Continuation = 1 << (8 - 6 - 1),31};32 33template <typename ValueType> struct BinaryBeImpl {34  ValueType Value;35  BinaryBeImpl(ValueType V) : Value(V) {}36};37 38template <typename ValueType>39raw_ostream &operator<<(raw_ostream &OS, const BinaryBeImpl<ValueType> &BBE) {40  char Buffer[sizeof(BBE.Value)];41  support::endian::write<ValueType, support::unaligned>(Buffer, BBE.Value,42                                                        llvm::endianness::big);43  OS.write(Buffer, sizeof(BBE.Value));44  return OS;45}46 47template <typename ValueType> BinaryBeImpl<ValueType> binaryBe(ValueType V) {48  return BinaryBeImpl<ValueType>(V);49}50 51struct ZerosImpl {52  size_t NumBytes;53};54 55raw_ostream &operator<<(raw_ostream &OS, const ZerosImpl &Z) {56  OS.write_zeros(Z.NumBytes);57  return OS;58}59 60ZerosImpl zeros(const size_t NumBytes) { return ZerosImpl{NumBytes}; }61 62// The GOFFOstream is responsible to write the data into the fixed physical63// records of the format. A user of this class announces the start of a new64// logical record and the size of its payload. While writing the payload, the65// physical records are created for the data. Possible fill bytes at the end of66// a physical record are written automatically.67class GOFFOstream : public raw_ostream {68public:69  explicit GOFFOstream(raw_ostream &OS)70      : OS(OS), LogicalRecords(0), RemainingSize(0), NewLogicalRecord(false) {71    SetBufferSize(GOFF::PayloadLength);72  }73 74  ~GOFFOstream() override { finalize(); }75 76  void makeNewRecord(GOFF::RecordType Type, size_t Size) {77    fillRecord();78    CurrentType = Type;79    RemainingSize = Size;80    if (size_t Gap = (RemainingSize % GOFF::PayloadLength))81      RemainingSize += GOFF::PayloadLength - Gap;82    NewLogicalRecord = true;83    ++LogicalRecords;84  }85 86  void finalize() { fillRecord(); }87 88  uint32_t logicalRecords() { return LogicalRecords; }89 90private:91  // The underlying raw_ostream.92  raw_ostream &OS;93 94  // The number of logical records emitted so far.95  uint32_t LogicalRecords;96 97  // The remaining size of this logical record, including fill bytes.98  size_t RemainingSize;99 100  // The type of the current (logical) record.101  GOFF::RecordType CurrentType;102 103  // Signals start of new record.104  bool NewLogicalRecord;105 106  // Return the number of bytes left to write until next physical record.107  // Please note that we maintain the total number of bytes left, not the108  // written size.109  size_t bytesToNextPhysicalRecord() {110    size_t Bytes = RemainingSize % GOFF::PayloadLength;111    return Bytes ? Bytes : GOFF::PayloadLength;112  }113 114  // Write the record prefix of a physical record, using the current record115  // type.116  static void writeRecordPrefix(raw_ostream &OS, GOFF::RecordType Type,117                                size_t RemainingSize,118                                uint8_t Flags = Rec_Continuation) {119    uint8_t TypeAndFlags = Flags | (Type << 4);120    if (RemainingSize > GOFF::RecordLength)121      TypeAndFlags |= Rec_Continued;122    OS << binaryBe(static_cast<unsigned char>(GOFF::PTVPrefix))123       << binaryBe(static_cast<unsigned char>(TypeAndFlags))124       << binaryBe(static_cast<unsigned char>(0));125  }126 127  // Fill the last physical record of a logical record with zero bytes.128  void fillRecord() {129    assert((GetNumBytesInBuffer() <= RemainingSize) &&130           "More bytes in buffer than expected");131    size_t Remains = RemainingSize - GetNumBytesInBuffer();132    if (Remains) {133      assert((Remains < GOFF::RecordLength) &&134             "Attempting to fill more than one physical record");135      raw_ostream::write_zeros(Remains);136    }137    flush();138    assert(RemainingSize == 0 && "Not fully flushed");139    assert(GetNumBytesInBuffer() == 0 && "Buffer not fully empty");140  }141 142  // See raw_ostream::write_impl.143  void write_impl(const char *Ptr, size_t Size) override {144    assert((RemainingSize >= Size) && "Attempt to write too much data");145    assert(RemainingSize && "Logical record overflow");146    if (!(RemainingSize % GOFF::PayloadLength)) {147      writeRecordPrefix(OS, CurrentType, RemainingSize,148                        NewLogicalRecord ? 0 : Rec_Continuation);149      NewLogicalRecord = false;150    }151    assert(!NewLogicalRecord &&152           "New logical record not on physical record boundary");153 154    size_t Idx = 0;155    while (Size > 0) {156      size_t BytesToWrite = bytesToNextPhysicalRecord();157      if (BytesToWrite > Size)158        BytesToWrite = Size;159      OS.write(Ptr + Idx, BytesToWrite);160      Idx += BytesToWrite;161      Size -= BytesToWrite;162      RemainingSize -= BytesToWrite;163      if (Size) {164        writeRecordPrefix(OS, CurrentType, RemainingSize);165      }166    }167  }168 169  // Return the current position within the stream, not counting the bytes170  // currently in the buffer.171  uint64_t current_pos() const override { return OS.tell(); }172};173 174class GOFFState {175  void writeHeader(GOFFYAML::FileHeader &FileHdr);176  void writeEnd();177 178  void reportError(const Twine &Msg) {179    ErrHandler(Msg);180    HasError = true;181  }182 183  GOFFState(raw_ostream &OS, GOFFYAML::Object &Doc,184            yaml::ErrorHandler ErrHandler)185      : GW(OS), Doc(Doc), ErrHandler(ErrHandler), HasError(false) {}186 187  ~GOFFState() { GW.finalize(); }188 189  bool writeObject();190 191public:192  static bool writeGOFF(raw_ostream &OS, GOFFYAML::Object &Doc,193                        yaml::ErrorHandler ErrHandler);194 195private:196  GOFFOstream GW;197  GOFFYAML::Object &Doc;198  yaml::ErrorHandler ErrHandler;199  bool HasError;200};201 202void GOFFState::writeHeader(GOFFYAML::FileHeader &FileHdr) {203  SmallString<16> CCSIDName;204  if (std::error_code EC =205          ConverterEBCDIC::convertToEBCDIC(FileHdr.CharacterSetName, CCSIDName))206    reportError("Conversion error on " + FileHdr.CharacterSetName);207  if (CCSIDName.size() > 16) {208    reportError("CharacterSetName too long");209    CCSIDName.resize(16);210  }211  SmallString<16> LangProd;212  if (std::error_code EC = ConverterEBCDIC::convertToEBCDIC(213          FileHdr.LanguageProductIdentifier, LangProd))214    reportError("Conversion error on " + FileHdr.LanguageProductIdentifier);215  if (LangProd.size() > 16) {216    reportError("LanguageProductIdentifier too long");217    LangProd.resize(16);218  }219 220  GW.makeNewRecord(GOFF::RT_HDR, GOFF::PayloadLength);221  GW << binaryBe(FileHdr.TargetEnvironment)     // TargetEnvironment222     << binaryBe(FileHdr.TargetOperatingSystem) // TargetOperatingSystem223     << zeros(2)                                // Reserved224     << binaryBe(FileHdr.CCSID)                 // CCSID225     << CCSIDName                               // CharacterSetName226     << zeros(16 - CCSIDName.size())            // Fill bytes227     << LangProd                                // LanguageProductIdentifier228     << zeros(16 - LangProd.size())             // Fill bytes229     << binaryBe(FileHdr.ArchitectureLevel);    // ArchitectureLevel230  // The module propties are optional. Figure out if we need to write them.231  uint16_t ModPropLen = 0;232  if (FileHdr.TargetSoftwareEnvironment)233    ModPropLen = 3;234  else if (FileHdr.InternalCCSID)235    ModPropLen = 2;236  if (ModPropLen) {237    GW << binaryBe(ModPropLen) << zeros(6);238    if (ModPropLen >= 2)239      GW << binaryBe(FileHdr.InternalCCSID.value_or(0));240    if (ModPropLen >= 3)241      GW << binaryBe(FileHdr.TargetSoftwareEnvironment.value_or(0));242  }243}244 245void GOFFState::writeEnd() {246  GW.makeNewRecord(GOFF::RT_END, GOFF::PayloadLength);247  GW << binaryBe(uint8_t(0)) // No entry point248     << binaryBe(uint8_t(0)) // No AMODE249     << zeros(3)             // Reserved250     << binaryBe(GW.logicalRecords());251  // No entry point yet. Automatically fill remaining space with zero bytes.252  GW.finalize();253}254 255bool GOFFState::writeObject() {256  writeHeader(Doc.Header);257  if (HasError)258    return false;259  writeEnd();260  return true;261}262 263bool GOFFState::writeGOFF(raw_ostream &OS, GOFFYAML::Object &Doc,264                          yaml::ErrorHandler ErrHandler) {265  GOFFState State(OS, Doc, ErrHandler);266  return State.writeObject();267}268} // namespace269 270namespace llvm {271namespace yaml {272 273bool yaml2goff(llvm::GOFFYAML::Object &Doc, raw_ostream &Out,274               ErrorHandler ErrHandler) {275  return GOFFState::writeGOFF(Out, Doc, ErrHandler);276}277 278} // namespace yaml279} // namespace llvm280