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