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