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