1//===- lib/MC/GOFFObjectWriter.cpp - GOFF File Writer ---------------------===//
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// This file implements GOFF object file writer information.
10//
11//===----------------------------------------------------------------------===//
12
13#include "llvm/BinaryFormat/GOFF.h"
14#include "llvm/MC/MCAssembler.h"
15#include "llvm/MC/MCGOFFObjectWriter.h"
16#include "llvm/MC/MCValue.h"
17#include "llvm/Support/Debug.h"
18#include "llvm/Support/Endian.h"
19#include "llvm/Support/Path.h"
20#include "llvm/Support/raw_ostream.h"
21
22using namespace llvm;
23
24#define DEBUG_TYPE "goff-writer"
25
26namespace {
27
28// The standard System/390 convention is to name the high-order (leftmost) bit
29// in a byte as bit zero. The Flags type helps to set bits in a byte according
30// to this numeration order.
31class Flags {
32 uint8_t Val;
33
34 constexpr static uint8_t bits(uint8_t BitIndex, uint8_t Length, uint8_t Value,
35 uint8_t OldValue) {
36 assert(BitIndex < 8 && "Bit index out of bounds!");
37 assert(Length + BitIndex <= 8 && "Bit length too long!");
38
39 uint8_t Mask = ((1 << Length) - 1) << (8 - BitIndex - Length);
40 Value = Value << (8 - BitIndex - Length);
41 assert((Value & Mask) == Value && "Bits set outside of range!");
42
43 return (OldValue & ~Mask) | Value;
44 }
45
46public:
47 constexpr Flags() : Val(0) {}
48 constexpr Flags(uint8_t BitIndex, uint8_t Length, uint8_t Value)
49 : Val(bits(BitIndex, Length, Value, OldValue: 0)) {}
50
51 void set(uint8_t BitIndex, uint8_t Length, uint8_t Value) {
52 Val = bits(BitIndex, Length, Value, OldValue: Val);
53 }
54
55 constexpr operator uint8_t() const { return Val; }
56};
57
58// Common flag values on records.
59
60// Flag: This record is continued.
61constexpr uint8_t RecContinued = Flags(7, 1, 1);
62
63// Flag: This record is a continuation.
64constexpr uint8_t RecContinuation = Flags(6, 1, 1);
65
66// The GOFFOstream is responsible to write the data into the fixed physical
67// records of the format. A user of this class announces the start of a new
68// logical record and the size of its content. While writing the content, the
69// physical records are created for the data. Possible fill bytes at the end of
70// a physical record are written automatically. In principle, the GOFFOstream
71// is agnostic of the endianness of the content. However, it also supports
72// writing data in big endian byte order.
73class GOFFOstream : public raw_ostream {
74 /// The underlying raw_pwrite_stream.
75 raw_pwrite_stream &OS;
76
77 /// The remaining size of this logical record, including fill bytes.
78 size_t RemainingSize;
79
80#ifndef NDEBUG
81 /// The number of bytes needed to fill up the last physical record.
82 size_t Gap = 0;
83#endif
84
85 /// The number of logical records emitted to far.
86 uint32_t LogicalRecords;
87
88 /// The type of the current (logical) record.
89 GOFF::RecordType CurrentType;
90
91 /// Signals start of new record.
92 bool NewLogicalRecord;
93
94 /// Static allocated buffer for the stream, used by the raw_ostream class. The
95 /// buffer is sized to hold the content of a physical record.
96 char Buffer[GOFF::RecordContentLength];
97
98 // Return the number of bytes left to write until next physical record.
99 // Please note that we maintain the total numbers of byte left, not the
100 // written size.
101 size_t bytesToNextPhysicalRecord() {
102 size_t Bytes = RemainingSize % GOFF::RecordContentLength;
103 return Bytes ? Bytes : GOFF::RecordContentLength;
104 }
105
106 /// Write the record prefix of a physical record, using the given record type.
107 static void writeRecordPrefix(raw_ostream &OS, GOFF::RecordType Type,
108 size_t RemainingSize,
109 uint8_t Flags = RecContinuation);
110
111 /// Fill the last physical record of a logical record with zero bytes.
112 void fillRecord();
113
114 /// See raw_ostream::write_impl.
115 void write_impl(const char *Ptr, size_t Size) override;
116
117 /// Return the current position within the stream, not counting the bytes
118 /// currently in the buffer.
119 uint64_t current_pos() const override { return OS.tell(); }
120
121public:
122 explicit GOFFOstream(raw_pwrite_stream &OS)
123 : OS(OS), RemainingSize(0), LogicalRecords(0), NewLogicalRecord(false) {
124 SetBuffer(BufferStart: Buffer, Size: sizeof(Buffer));
125 }
126
127 ~GOFFOstream() { finalize(); }
128
129 raw_pwrite_stream &getOS() { return OS; }
130
131 void newRecord(GOFF::RecordType Type, size_t Size);
132
133 void finalize() { fillRecord(); }
134
135 uint32_t logicalRecords() { return LogicalRecords; }
136
137 // Support for endian-specific data.
138 template <typename value_type> void writebe(value_type Value) {
139 Value =
140 support::endian::byte_swap<value_type>(Value, llvm::endianness::big);
141 write(Ptr: reinterpret_cast<const char *>(&Value), Size: sizeof(value_type));
142 }
143};
144
145void GOFFOstream::writeRecordPrefix(raw_ostream &OS, GOFF::RecordType Type,
146 size_t RemainingSize, uint8_t Flags) {
147 uint8_t TypeAndFlags = Flags | (Type << 4);
148 if (RemainingSize > GOFF::RecordLength)
149 TypeAndFlags |= RecContinued;
150 OS << static_cast<unsigned char>(GOFF::PTVPrefix) // Record Type
151 << static_cast<unsigned char>(TypeAndFlags) // Continuation
152 << static_cast<unsigned char>(0); // Version
153}
154
155void GOFFOstream::newRecord(GOFF::RecordType Type, size_t Size) {
156 fillRecord();
157 CurrentType = Type;
158 RemainingSize = Size;
159#ifdef NDEBUG
160 size_t Gap;
161#endif
162 Gap = (RemainingSize % GOFF::RecordContentLength);
163 if (Gap) {
164 Gap = GOFF::RecordContentLength - Gap;
165 RemainingSize += Gap;
166 }
167 NewLogicalRecord = true;
168 ++LogicalRecords;
169}
170
171void GOFFOstream::fillRecord() {
172 assert((GetNumBytesInBuffer() <= RemainingSize) &&
173 "More bytes in buffer than expected");
174 size_t Remains = RemainingSize - GetNumBytesInBuffer();
175 if (Remains) {
176 assert(Remains == Gap && "Wrong size of fill gap");
177 assert((Remains < GOFF::RecordLength) &&
178 "Attempt to fill more than one physical record");
179 raw_ostream::write_zeros(NumZeros: Remains);
180 }
181 flush();
182 assert(RemainingSize == 0 && "Not fully flushed");
183 assert(GetNumBytesInBuffer() == 0 && "Buffer not fully empty");
184}
185
186// This function is called from the raw_ostream implementation if:
187// - The internal buffer is full. Size is excactly the size of the buffer.
188// - Data larger than the internal buffer is written. Size is a multiple of the
189// buffer size.
190// - flush() has been called. Size is at most the buffer size.
191// The GOFFOstream implementation ensures that flush() is called before a new
192// logical record begins. Therefore it is sufficient to check for a new block
193// only once.
194void GOFFOstream::write_impl(const char *Ptr, size_t Size) {
195 assert((RemainingSize >= Size) && "Attempt to write too much data");
196 assert(RemainingSize && "Logical record overflow");
197 if (!(RemainingSize % GOFF::RecordContentLength)) {
198 writeRecordPrefix(OS, Type: CurrentType, RemainingSize,
199 Flags: NewLogicalRecord ? 0 : RecContinuation);
200 NewLogicalRecord = false;
201 }
202 assert(!NewLogicalRecord &&
203 "New logical record not on physical record boundary");
204
205 size_t Idx = 0;
206 while (Size > 0) {
207 size_t BytesToWrite = bytesToNextPhysicalRecord();
208 if (BytesToWrite > Size)
209 BytesToWrite = Size;
210 OS.write(Ptr: Ptr + Idx, Size: BytesToWrite);
211 Idx += BytesToWrite;
212 Size -= BytesToWrite;
213 RemainingSize -= BytesToWrite;
214 if (Size)
215 writeRecordPrefix(OS, Type: CurrentType, RemainingSize);
216 }
217}
218
219class GOFFObjectWriter : public MCObjectWriter {
220 // The target specific GOFF writer instance.
221 std::unique_ptr<MCGOFFObjectTargetWriter> TargetObjectWriter;
222
223 // The stream used to write the GOFF records.
224 GOFFOstream OS;
225
226public:
227 GOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW,
228 raw_pwrite_stream &OS)
229 : TargetObjectWriter(std::move(MOTW)), OS(OS) {}
230
231 ~GOFFObjectWriter() override {}
232
233 // Write GOFF records.
234 void writeHeader();
235 void writeEnd();
236
237 // Implementation of the MCObjectWriter interface.
238 void recordRelocation(MCAssembler &Asm, const MCFragment *Fragment,
239 const MCFixup &Fixup, MCValue Target,
240 uint64_t &FixedValue) override {}
241 uint64_t writeObject(MCAssembler &Asm) override;
242};
243} // end anonymous namespace
244
245void GOFFObjectWriter::writeHeader() {
246 OS.newRecord(Type: GOFF::RT_HDR, /*Size=*/57);
247 OS.write_zeros(NumZeros: 1); // Reserved
248 OS.writebe<uint32_t>(Value: 0); // Target Hardware Environment
249 OS.writebe<uint32_t>(Value: 0); // Target Operating System Environment
250 OS.write_zeros(NumZeros: 2); // Reserved
251 OS.writebe<uint16_t>(Value: 0); // CCSID
252 OS.write_zeros(NumZeros: 16); // Character Set name
253 OS.write_zeros(NumZeros: 16); // Language Product Identifier
254 OS.writebe<uint32_t>(Value: 1); // Architecture Level
255 OS.writebe<uint16_t>(Value: 0); // Module Properties Length
256 OS.write_zeros(NumZeros: 6); // Reserved
257}
258
259void GOFFObjectWriter::writeEnd() {
260 uint8_t F = GOFF::END_EPR_None;
261 uint8_t AMODE = 0;
262 uint32_t ESDID = 0;
263
264 // TODO Set Flags/AMODE/ESDID for entry point.
265
266 OS.newRecord(Type: GOFF::RT_END, /*Size=*/13);
267 OS.writebe<uint8_t>(Value: Flags(6, 2, F)); // Indicator flags
268 OS.writebe<uint8_t>(Value: AMODE); // AMODE
269 OS.write_zeros(NumZeros: 3); // Reserved
270 // The record count is the number of logical records. In principle, this value
271 // is available as OS.logicalRecords(). However, some tools rely on this field
272 // being zero.
273 OS.writebe<uint32_t>(Value: 0); // Record Count
274 OS.writebe<uint32_t>(Value: ESDID); // ESDID (of entry point)
275 OS.finalize();
276}
277
278uint64_t GOFFObjectWriter::writeObject(MCAssembler &Asm) {
279 uint64_t StartOffset = OS.tell();
280
281 writeHeader();
282 writeEnd();
283
284 LLVM_DEBUG(dbgs() << "Wrote " << OS.logicalRecords() << " logical records.");
285
286 return OS.tell() - StartOffset;
287}
288
289std::unique_ptr<MCObjectWriter>
290llvm::createGOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW,
291 raw_pwrite_stream &OS) {
292 return std::make_unique<GOFFObjectWriter>(args: std::move(MOTW), args&: OS);
293}
294