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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 /// 9 /// \file 10 /// 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 20 using namespace llvm; 21 22 namespace { 23 24 // Common flag values on records. 25 enum { 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 33 template <typename ValueType> struct BinaryBeImpl { 34 ValueType Value; 35 BinaryBeImpl(ValueType V) : Value(V) {} 36 }; 37 38 template <typename ValueType> 39 raw_ostream &operator<<(raw_ostream &OS, const BinaryBeImpl<ValueType> &BBE) { 40 char Buffer[sizeof(BBE.Value)]; 41 support::endian::write<ValueType, llvm::endianness::big, support::unaligned>( 42 Buffer, BBE.Value); 43 OS.write(Buffer, sizeof(BBE.Value)); 44 return OS; 45 } 46 47 template <typename ValueType> BinaryBeImpl<ValueType> binaryBe(ValueType V) { 48 return BinaryBeImpl<ValueType>(V); 49 } 50 51 struct ZerosImpl { 52 size_t NumBytes; 53 }; 54 55 raw_ostream &operator<<(raw_ostream &OS, const ZerosImpl &Z) { 56 OS.write_zeros(Z.NumBytes); 57 return OS; 58 } 59 60 ZerosImpl zeros(const size_t NumBytes) { return ZerosImpl{NumBytes}; } 61 62 // The GOFFOstream is responsible to write the data into the fixed physical 63 // records of the format. A user of this class announces the start of a new 64 // logical record and the size of its payload. While writing the payload, the 65 // physical records are created for the data. Possible fill bytes at the end of 66 // a physical record are written automatically. 67 class GOFFOstream : public raw_ostream { 68 public: 69 explicit GOFFOstream(raw_ostream &OS) 70 : OS(OS), LogicalRecords(0), RemainingSize(0), NewLogicalRecord(false) { 71 SetBufferSize(GOFF::PayloadLength); 72 } 73 74 ~GOFFOstream() { 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 90 private: 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 the 108 // 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 record 115 // 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 bytes 170 // currently in the buffer. 171 uint64_t current_pos() const override { return OS.tell(); } 172 }; 173 174 class 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 191 public: 192 static bool writeGOFF(raw_ostream &OS, GOFFYAML::Object &Doc, 193 yaml::ErrorHandler ErrHandler); 194 195 private: 196 GOFFOstream GW; 197 GOFFYAML::Object &Doc; 198 yaml::ErrorHandler ErrHandler; 199 bool HasError; 200 }; 201 202 void 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) // TargetEnvironment 222 << binaryBe(FileHdr.TargetOperatingSystem) // TargetOperatingSystem 223 << zeros(2) // Reserved 224 << binaryBe(FileHdr.CCSID) // CCSID 225 << CCSIDName // CharacterSetName 226 << zeros(16 - CCSIDName.size()) // Fill bytes 227 << LangProd // LanguageProductIdentifier 228 << zeros(16 - LangProd.size()) // Fill bytes 229 << binaryBe(FileHdr.ArchitectureLevel); // ArchitectureLevel 230 // 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 245 void GOFFState::writeEnd() { 246 GW.makeNewRecord(GOFF::RT_END, GOFF::PayloadLength); 247 GW << binaryBe(uint8_t(0)) // No entry point 248 << binaryBe(uint8_t(0)) // No AMODE 249 << zeros(3) // Reserved 250 << binaryBe(GW.logicalRecords()); 251 // No entry point yet. Automatically fill remaining space with zero bytes. 252 GW.finalize(); 253 } 254 255 bool GOFFState::writeObject() { 256 writeHeader(Doc.Header); 257 if (HasError) 258 return false; 259 writeEnd(); 260 return true; 261 } 262 263 bool GOFFState::writeGOFF(raw_ostream &OS, GOFFYAML::Object &Doc, 264 yaml::ErrorHandler ErrHandler) { 265 GOFFState State(OS, Doc, ErrHandler); 266 return State.writeObject(); 267 } 268 } // namespace 269 270 namespace llvm { 271 namespace yaml { 272 273 bool yaml2goff(llvm::GOFFYAML::Object &Doc, raw_ostream &Out, 274 ErrorHandler ErrHandler) { 275 return GOFFState::writeGOFF(Out, Doc, ErrHandler); 276 } 277 278 } // namespace yaml 279 } // namespace llvm 280