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