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