xref: /llvm-project/llvm/lib/ObjectYAML/GOFFEmitter.cpp (revision 5b7102d1f37eab7a8f17b7bf4124ca76fbdbd66d)
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