Path: blob/main/contrib/llvm-project/llvm/lib/ObjectYAML/GOFFEmitter.cpp
35233 views
//===- yaml2goff - Convert YAML to a GOFF object file ---------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file9/// The GOFF component of yaml2obj.10///11//===----------------------------------------------------------------------===//1213#include "llvm/ADT/IndexedMap.h"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"1920using namespace llvm;2122namespace {2324// Common flag values on records.25enum {26// Flag: This record is continued.27Rec_Continued = 1,2829// Flag: This record is a continuation.30Rec_Continuation = 1 << (8 - 6 - 1),31};3233template <typename ValueType> struct BinaryBeImpl {34ValueType Value;35BinaryBeImpl(ValueType V) : Value(V) {}36};3738template <typename ValueType>39raw_ostream &operator<<(raw_ostream &OS, const BinaryBeImpl<ValueType> &BBE) {40char Buffer[sizeof(BBE.Value)];41support::endian::write<ValueType, llvm::endianness::big, support::unaligned>(42Buffer, BBE.Value);43OS.write(Buffer, sizeof(BBE.Value));44return OS;45}4647template <typename ValueType> BinaryBeImpl<ValueType> binaryBe(ValueType V) {48return BinaryBeImpl<ValueType>(V);49}5051struct ZerosImpl {52size_t NumBytes;53};5455raw_ostream &operator<<(raw_ostream &OS, const ZerosImpl &Z) {56OS.write_zeros(Z.NumBytes);57return OS;58}5960ZerosImpl zeros(const size_t NumBytes) { return ZerosImpl{NumBytes}; }6162// 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:69explicit GOFFOstream(raw_ostream &OS)70: OS(OS), LogicalRecords(0), RemainingSize(0), NewLogicalRecord(false) {71SetBufferSize(GOFF::PayloadLength);72}7374~GOFFOstream() { finalize(); }7576void makeNewRecord(GOFF::RecordType Type, size_t Size) {77fillRecord();78CurrentType = Type;79RemainingSize = Size;80if (size_t Gap = (RemainingSize % GOFF::PayloadLength))81RemainingSize += GOFF::PayloadLength - Gap;82NewLogicalRecord = true;83++LogicalRecords;84}8586void finalize() { fillRecord(); }8788uint32_t logicalRecords() { return LogicalRecords; }8990private:91// The underlying raw_ostream.92raw_ostream &OS;9394// The number of logical records emitted so far.95uint32_t LogicalRecords;9697// The remaining size of this logical record, including fill bytes.98size_t RemainingSize;99100// The type of the current (logical) record.101GOFF::RecordType CurrentType;102103// Signals start of new record.104bool NewLogicalRecord;105106// 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.109size_t bytesToNextPhysicalRecord() {110size_t Bytes = RemainingSize % GOFF::PayloadLength;111return Bytes ? Bytes : GOFF::PayloadLength;112}113114// Write the record prefix of a physical record, using the current record115// type.116static void writeRecordPrefix(raw_ostream &OS, GOFF::RecordType Type,117size_t RemainingSize,118uint8_t Flags = Rec_Continuation) {119uint8_t TypeAndFlags = Flags | (Type << 4);120if (RemainingSize > GOFF::RecordLength)121TypeAndFlags |= Rec_Continued;122OS << binaryBe(static_cast<unsigned char>(GOFF::PTVPrefix))123<< binaryBe(static_cast<unsigned char>(TypeAndFlags))124<< binaryBe(static_cast<unsigned char>(0));125}126127// Fill the last physical record of a logical record with zero bytes.128void fillRecord() {129assert((GetNumBytesInBuffer() <= RemainingSize) &&130"More bytes in buffer than expected");131size_t Remains = RemainingSize - GetNumBytesInBuffer();132if (Remains) {133assert((Remains < GOFF::RecordLength) &&134"Attempting to fill more than one physical record");135raw_ostream::write_zeros(Remains);136}137flush();138assert(RemainingSize == 0 && "Not fully flushed");139assert(GetNumBytesInBuffer() == 0 && "Buffer not fully empty");140}141142// See raw_ostream::write_impl.143void write_impl(const char *Ptr, size_t Size) override {144assert((RemainingSize >= Size) && "Attempt to write too much data");145assert(RemainingSize && "Logical record overflow");146if (!(RemainingSize % GOFF::PayloadLength)) {147writeRecordPrefix(OS, CurrentType, RemainingSize,148NewLogicalRecord ? 0 : Rec_Continuation);149NewLogicalRecord = false;150}151assert(!NewLogicalRecord &&152"New logical record not on physical record boundary");153154size_t Idx = 0;155while (Size > 0) {156size_t BytesToWrite = bytesToNextPhysicalRecord();157if (BytesToWrite > Size)158BytesToWrite = Size;159OS.write(Ptr + Idx, BytesToWrite);160Idx += BytesToWrite;161Size -= BytesToWrite;162RemainingSize -= BytesToWrite;163if (Size) {164writeRecordPrefix(OS, CurrentType, RemainingSize);165}166}167}168169// Return the current position within the stream, not counting the bytes170// currently in the buffer.171uint64_t current_pos() const override { return OS.tell(); }172};173174class GOFFState {175void writeHeader(GOFFYAML::FileHeader &FileHdr);176void writeEnd();177178void reportError(const Twine &Msg) {179ErrHandler(Msg);180HasError = true;181}182183GOFFState(raw_ostream &OS, GOFFYAML::Object &Doc,184yaml::ErrorHandler ErrHandler)185: GW(OS), Doc(Doc), ErrHandler(ErrHandler), HasError(false) {}186187~GOFFState() { GW.finalize(); }188189bool writeObject();190191public:192static bool writeGOFF(raw_ostream &OS, GOFFYAML::Object &Doc,193yaml::ErrorHandler ErrHandler);194195private:196GOFFOstream GW;197GOFFYAML::Object &Doc;198yaml::ErrorHandler ErrHandler;199bool HasError;200};201202void GOFFState::writeHeader(GOFFYAML::FileHeader &FileHdr) {203SmallString<16> CCSIDName;204if (std::error_code EC =205ConverterEBCDIC::convertToEBCDIC(FileHdr.CharacterSetName, CCSIDName))206reportError("Conversion error on " + FileHdr.CharacterSetName);207if (CCSIDName.size() > 16) {208reportError("CharacterSetName too long");209CCSIDName.resize(16);210}211SmallString<16> LangProd;212if (std::error_code EC = ConverterEBCDIC::convertToEBCDIC(213FileHdr.LanguageProductIdentifier, LangProd))214reportError("Conversion error on " + FileHdr.LanguageProductIdentifier);215if (LangProd.size() > 16) {216reportError("LanguageProductIdentifier too long");217LangProd.resize(16);218}219220GW.makeNewRecord(GOFF::RT_HDR, GOFF::PayloadLength);221GW << 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.231uint16_t ModPropLen = 0;232if (FileHdr.TargetSoftwareEnvironment)233ModPropLen = 3;234else if (FileHdr.InternalCCSID)235ModPropLen = 2;236if (ModPropLen) {237GW << binaryBe(ModPropLen) << zeros(6);238if (ModPropLen >= 2)239GW << binaryBe(FileHdr.InternalCCSID ? *FileHdr.InternalCCSID : 0);240if (ModPropLen >= 3)241GW << binaryBe(FileHdr.TargetSoftwareEnvironment242? *FileHdr.TargetSoftwareEnvironment243: 0);244}245}246247void GOFFState::writeEnd() {248GW.makeNewRecord(GOFF::RT_END, GOFF::PayloadLength);249GW << binaryBe(uint8_t(0)) // No entry point250<< binaryBe(uint8_t(0)) // No AMODE251<< zeros(3) // Reserved252<< binaryBe(GW.logicalRecords());253// No entry point yet. Automatically fill remaining space with zero bytes.254GW.finalize();255}256257bool GOFFState::writeObject() {258writeHeader(Doc.Header);259if (HasError)260return false;261writeEnd();262return true;263}264265bool GOFFState::writeGOFF(raw_ostream &OS, GOFFYAML::Object &Doc,266yaml::ErrorHandler ErrHandler) {267GOFFState State(OS, Doc, ErrHandler);268return State.writeObject();269}270} // namespace271272namespace llvm {273namespace yaml {274275bool yaml2goff(llvm::GOFFYAML::Object &Doc, raw_ostream &Out,276ErrorHandler ErrHandler) {277return GOFFState::writeGOFF(Out, Doc, ErrHandler);278}279280} // namespace yaml281} // namespace llvm282283284