xref: /freebsd/contrib/llvm-project/llvm/lib/MC/GOFFObjectWriter.cpp (revision aa1a8ff2d6dbc51ef058f46f3db5a8bb77967145)
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 
23 using namespace llvm;
24 
25 #define DEBUG_TYPE "goff-writer"
26 
27 namespace {
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.
32 class 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 
47 public:
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.
62 constexpr uint8_t RecContinued = Flags(7, 1, 1);
63 
64 // Flag: This record is a continuation.
65 constexpr 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.
74 class 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 
122 public:
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 
146 void 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 
156 void 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 
172 void 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.
195 void 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 
220 class 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 
227 public:
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 
248 void 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 
262 void 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 
281 uint64_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 
293 std::unique_ptr<MCObjectWriter>
294 llvm::createGOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW,
295                              raw_pwrite_stream &OS) {
296   return std::make_unique<GOFFObjectWriter>(std::move(MOTW), OS);
297 }
298