xref: /freebsd/contrib/llvm-project/llvm/lib/MC/GOFFObjectWriter.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 
22 using namespace llvm;
23 
24 #define DEBUG_TYPE "goff-writer"
25 
26 namespace {
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.
31 class Flags {
32   uint8_t Val;
33 
bits(uint8_t BitIndex,uint8_t Length,uint8_t Value,uint8_t OldValue)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 
46 public:
Flags()47   constexpr Flags() : Val(0) {}
Flags(uint8_t BitIndex,uint8_t Length,uint8_t Value)48   constexpr Flags(uint8_t BitIndex, uint8_t Length, uint8_t Value)
49       : Val(bits(BitIndex, Length, Value, 0)) {}
50 
set(uint8_t BitIndex,uint8_t Length,uint8_t Value)51   void set(uint8_t BitIndex, uint8_t Length, uint8_t Value) {
52     Val = bits(BitIndex, Length, Value, Val);
53   }
54 
operator uint8_t() const55   constexpr operator uint8_t() const { return Val; }
56 };
57 
58 // Common flag values on records.
59 
60 // Flag: This record is continued.
61 constexpr uint8_t RecContinued = Flags(7, 1, 1);
62 
63 // Flag: This record is a continuation.
64 constexpr 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.
73 class 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.
bytesToNextPhysicalRecord()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.
current_pos() const119   uint64_t current_pos() const override { return OS.tell(); }
120 
121 public:
GOFFOstream(raw_pwrite_stream & OS)122   explicit GOFFOstream(raw_pwrite_stream &OS)
123       : OS(OS), RemainingSize(0), LogicalRecords(0), NewLogicalRecord(false) {
124     SetBuffer(Buffer, sizeof(Buffer));
125   }
126 
~GOFFOstream()127   ~GOFFOstream() { finalize(); }
128 
getOS()129   raw_pwrite_stream &getOS() { return OS; }
130 
131   void newRecord(GOFF::RecordType Type, size_t Size);
132 
finalize()133   void finalize() { fillRecord(); }
134 
logicalRecords()135   uint32_t logicalRecords() { return LogicalRecords; }
136 
137   // Support for endian-specific data.
writebe(value_type Value)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(reinterpret_cast<const char *>(&Value), sizeof(value_type));
142   }
143 };
144 
writeRecordPrefix(raw_ostream & OS,GOFF::RecordType Type,size_t RemainingSize,uint8_t Flags)145 void 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 
newRecord(GOFF::RecordType Type,size_t Size)155 void 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 
fillRecord()171 void 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(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.
write_impl(const char * Ptr,size_t Size)194 void 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, CurrentType, RemainingSize,
199                       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 + Idx, BytesToWrite);
211     Idx += BytesToWrite;
212     Size -= BytesToWrite;
213     RemainingSize -= BytesToWrite;
214     if (Size)
215       writeRecordPrefix(OS, CurrentType, RemainingSize);
216   }
217 }
218 
219 class 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 
226 public:
GOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW,raw_pwrite_stream & OS)227   GOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW,
228                    raw_pwrite_stream &OS)
229       : TargetObjectWriter(std::move(MOTW)), OS(OS) {}
230 
~GOFFObjectWriter()231   ~GOFFObjectWriter() override {}
232 
233   // Write GOFF records.
234   void writeHeader();
235   void writeEnd();
236 
237   // Implementation of the MCObjectWriter interface.
recordRelocation(MCAssembler & Asm,const MCFragment * Fragment,const MCFixup & Fixup,MCValue Target,uint64_t & FixedValue)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 
writeHeader()245 void GOFFObjectWriter::writeHeader() {
246   OS.newRecord(GOFF::RT_HDR, /*Size=*/57);
247   OS.write_zeros(1);       // Reserved
248   OS.writebe<uint32_t>(0); // Target Hardware Environment
249   OS.writebe<uint32_t>(0); // Target Operating System Environment
250   OS.write_zeros(2);       // Reserved
251   OS.writebe<uint16_t>(0); // CCSID
252   OS.write_zeros(16);      // Character Set name
253   OS.write_zeros(16);      // Language Product Identifier
254   OS.writebe<uint32_t>(1); // Architecture Level
255   OS.writebe<uint16_t>(0); // Module Properties Length
256   OS.write_zeros(6);       // Reserved
257 }
258 
writeEnd()259 void 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(GOFF::RT_END, /*Size=*/13);
267   OS.writebe<uint8_t>(Flags(6, 2, F)); // Indicator flags
268   OS.writebe<uint8_t>(AMODE);          // AMODE
269   OS.write_zeros(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>(0);     // Record Count
274   OS.writebe<uint32_t>(ESDID); // ESDID (of entry point)
275   OS.finalize();
276 }
277 
writeObject(MCAssembler & Asm)278 uint64_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 
289 std::unique_ptr<MCObjectWriter>
createGOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW,raw_pwrite_stream & OS)290 llvm::createGOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW,
291                              raw_pwrite_stream &OS) {
292   return std::make_unique<GOFFObjectWriter>(std::move(MOTW), OS);
293 }
294