Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/lib/ObjectYAML/GOFFEmitter.cpp
35233 views
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
284