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