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(NumZeros: Z.NumBytes); |
57 | return OS; |
58 | } |
59 | |
60 | ZerosImpl zeros(const size_t NumBytes) { return ZerosImpl{.NumBytes: 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(V: static_cast<unsigned char>(GOFF::PTVPrefix)) |
123 | << binaryBe(V: static_cast<unsigned char>(TypeAndFlags)) |
124 | << binaryBe(V: 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(NumZeros: 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, Type: CurrentType, RemainingSize, |
148 | Flags: 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: Ptr + Idx, Size: BytesToWrite); |
160 | Idx += BytesToWrite; |
161 | Size -= BytesToWrite; |
162 | RemainingSize -= BytesToWrite; |
163 | if (Size) { |
164 | writeRecordPrefix(OS, Type: 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::(GOFFYAML::FileHeader &FileHdr) { |
203 | SmallString<16> CCSIDName; |
204 | if (std::error_code EC = |
205 | ConverterEBCDIC::convertToEBCDIC(Source: FileHdr.CharacterSetName, Result&: CCSIDName)) |
206 | reportError(Msg: "Conversion error on " + FileHdr.CharacterSetName); |
207 | if (CCSIDName.size() > 16) { |
208 | reportError(Msg: "CharacterSetName too long" ); |
209 | CCSIDName.resize(N: 16); |
210 | } |
211 | SmallString<16> LangProd; |
212 | if (std::error_code EC = ConverterEBCDIC::convertToEBCDIC( |
213 | Source: FileHdr.LanguageProductIdentifier, Result&: LangProd)) |
214 | reportError(Msg: "Conversion error on " + FileHdr.LanguageProductIdentifier); |
215 | if (LangProd.size() > 16) { |
216 | reportError(Msg: "LanguageProductIdentifier too long" ); |
217 | LangProd.resize(N: 16); |
218 | } |
219 | |
220 | GW.makeNewRecord(Type: GOFF::RT_HDR, Size: GOFF::PayloadLength); |
221 | GW << binaryBe(V: FileHdr.TargetEnvironment) // TargetEnvironment |
222 | << binaryBe(V: FileHdr.TargetOperatingSystem) // TargetOperatingSystem |
223 | << zeros(NumBytes: 2) // Reserved |
224 | << binaryBe(V: FileHdr.CCSID) // CCSID |
225 | << CCSIDName // CharacterSetName |
226 | << zeros(NumBytes: 16 - CCSIDName.size()) // Fill bytes |
227 | << LangProd // LanguageProductIdentifier |
228 | << zeros(NumBytes: 16 - LangProd.size()) // Fill bytes |
229 | << binaryBe(V: 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(V: ModPropLen) << zeros(NumBytes: 6); |
238 | if (ModPropLen >= 2) |
239 | GW << binaryBe(V: FileHdr.InternalCCSID.value_or(u: 0)); |
240 | if (ModPropLen >= 3) |
241 | GW << binaryBe(V: FileHdr.TargetSoftwareEnvironment.value_or(u: 0)); |
242 | } |
243 | } |
244 | |
245 | void GOFFState::writeEnd() { |
246 | GW.makeNewRecord(Type: GOFF::RT_END, Size: GOFF::PayloadLength); |
247 | GW << binaryBe(V: uint8_t(0)) // No entry point |
248 | << binaryBe(V: uint8_t(0)) // No AMODE |
249 | << zeros(NumBytes: 3) // Reserved |
250 | << binaryBe(V: 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(FileHdr&: 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(OS&: Out, Doc, ErrHandler); |
276 | } |
277 | |
278 | } // namespace yaml |
279 | } // namespace llvm |
280 | |