15f757f3fSDimitry Andric //===- lib/MC/GOFFObjectWriter.cpp - GOFF File Writer ---------------------===// 25f757f3fSDimitry Andric // 35f757f3fSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 45f757f3fSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 55f757f3fSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65f757f3fSDimitry Andric // 75f757f3fSDimitry Andric //===----------------------------------------------------------------------===// 85f757f3fSDimitry Andric // 95f757f3fSDimitry Andric // This file implements GOFF object file writer information. 105f757f3fSDimitry Andric // 115f757f3fSDimitry Andric //===----------------------------------------------------------------------===// 125f757f3fSDimitry Andric 135f757f3fSDimitry Andric #include "llvm/BinaryFormat/GOFF.h" 145f757f3fSDimitry Andric #include "llvm/MC/MCAssembler.h" 155f757f3fSDimitry Andric #include "llvm/MC/MCGOFFObjectWriter.h" 165f757f3fSDimitry Andric #include "llvm/MC/MCValue.h" 175f757f3fSDimitry Andric #include "llvm/Support/Debug.h" 185f757f3fSDimitry Andric #include "llvm/Support/Endian.h" 195f757f3fSDimitry Andric #include "llvm/Support/Path.h" 205f757f3fSDimitry Andric #include "llvm/Support/raw_ostream.h" 215f757f3fSDimitry Andric 225f757f3fSDimitry Andric using namespace llvm; 235f757f3fSDimitry Andric 245f757f3fSDimitry Andric #define DEBUG_TYPE "goff-writer" 255f757f3fSDimitry Andric 265f757f3fSDimitry Andric namespace { 275f757f3fSDimitry Andric 285f757f3fSDimitry Andric // The standard System/390 convention is to name the high-order (leftmost) bit 295f757f3fSDimitry Andric // in a byte as bit zero. The Flags type helps to set bits in a byte according 305f757f3fSDimitry Andric // to this numeration order. 315f757f3fSDimitry Andric class Flags { 325f757f3fSDimitry Andric uint8_t Val; 335f757f3fSDimitry Andric 345f757f3fSDimitry Andric constexpr static uint8_t bits(uint8_t BitIndex, uint8_t Length, uint8_t Value, 355f757f3fSDimitry Andric uint8_t OldValue) { 365f757f3fSDimitry Andric assert(BitIndex < 8 && "Bit index out of bounds!"); 375f757f3fSDimitry Andric assert(Length + BitIndex <= 8 && "Bit length too long!"); 385f757f3fSDimitry Andric 395f757f3fSDimitry Andric uint8_t Mask = ((1 << Length) - 1) << (8 - BitIndex - Length); 405f757f3fSDimitry Andric Value = Value << (8 - BitIndex - Length); 415f757f3fSDimitry Andric assert((Value & Mask) == Value && "Bits set outside of range!"); 425f757f3fSDimitry Andric 435f757f3fSDimitry Andric return (OldValue & ~Mask) | Value; 445f757f3fSDimitry Andric } 455f757f3fSDimitry Andric 465f757f3fSDimitry Andric public: 475f757f3fSDimitry Andric constexpr Flags() : Val(0) {} 485f757f3fSDimitry Andric constexpr Flags(uint8_t BitIndex, uint8_t Length, uint8_t Value) 495f757f3fSDimitry Andric : Val(bits(BitIndex, Length, Value, 0)) {} 505f757f3fSDimitry Andric 515f757f3fSDimitry Andric void set(uint8_t BitIndex, uint8_t Length, uint8_t Value) { 525f757f3fSDimitry Andric Val = bits(BitIndex, Length, Value, Val); 535f757f3fSDimitry Andric } 545f757f3fSDimitry Andric 555f757f3fSDimitry Andric constexpr operator uint8_t() const { return Val; } 565f757f3fSDimitry Andric }; 575f757f3fSDimitry Andric 585f757f3fSDimitry Andric // Common flag values on records. 595f757f3fSDimitry Andric 605f757f3fSDimitry Andric // Flag: This record is continued. 615f757f3fSDimitry Andric constexpr uint8_t RecContinued = Flags(7, 1, 1); 625f757f3fSDimitry Andric 635f757f3fSDimitry Andric // Flag: This record is a continuation. 645f757f3fSDimitry Andric constexpr uint8_t RecContinuation = Flags(6, 1, 1); 655f757f3fSDimitry Andric 665f757f3fSDimitry Andric // The GOFFOstream is responsible to write the data into the fixed physical 675f757f3fSDimitry Andric // records of the format. A user of this class announces the start of a new 685f757f3fSDimitry Andric // logical record and the size of its content. While writing the content, the 695f757f3fSDimitry Andric // physical records are created for the data. Possible fill bytes at the end of 705f757f3fSDimitry Andric // a physical record are written automatically. In principle, the GOFFOstream 715f757f3fSDimitry Andric // is agnostic of the endianness of the content. However, it also supports 725f757f3fSDimitry Andric // writing data in big endian byte order. 735f757f3fSDimitry Andric class GOFFOstream : public raw_ostream { 745f757f3fSDimitry Andric /// The underlying raw_pwrite_stream. 755f757f3fSDimitry Andric raw_pwrite_stream &OS; 765f757f3fSDimitry Andric 775f757f3fSDimitry Andric /// The remaining size of this logical record, including fill bytes. 785f757f3fSDimitry Andric size_t RemainingSize; 795f757f3fSDimitry Andric 805f757f3fSDimitry Andric #ifndef NDEBUG 815f757f3fSDimitry Andric /// The number of bytes needed to fill up the last physical record. 825f757f3fSDimitry Andric size_t Gap = 0; 835f757f3fSDimitry Andric #endif 845f757f3fSDimitry Andric 855f757f3fSDimitry Andric /// The number of logical records emitted to far. 865f757f3fSDimitry Andric uint32_t LogicalRecords; 875f757f3fSDimitry Andric 885f757f3fSDimitry Andric /// The type of the current (logical) record. 895f757f3fSDimitry Andric GOFF::RecordType CurrentType; 905f757f3fSDimitry Andric 915f757f3fSDimitry Andric /// Signals start of new record. 925f757f3fSDimitry Andric bool NewLogicalRecord; 935f757f3fSDimitry Andric 945f757f3fSDimitry Andric /// Static allocated buffer for the stream, used by the raw_ostream class. The 955f757f3fSDimitry Andric /// buffer is sized to hold the content of a physical record. 965f757f3fSDimitry Andric char Buffer[GOFF::RecordContentLength]; 975f757f3fSDimitry Andric 985f757f3fSDimitry Andric // Return the number of bytes left to write until next physical record. 995f757f3fSDimitry Andric // Please note that we maintain the total numbers of byte left, not the 1005f757f3fSDimitry Andric // written size. 1015f757f3fSDimitry Andric size_t bytesToNextPhysicalRecord() { 1025f757f3fSDimitry Andric size_t Bytes = RemainingSize % GOFF::RecordContentLength; 1035f757f3fSDimitry Andric return Bytes ? Bytes : GOFF::RecordContentLength; 1045f757f3fSDimitry Andric } 1055f757f3fSDimitry Andric 1065f757f3fSDimitry Andric /// Write the record prefix of a physical record, using the given record type. 1075f757f3fSDimitry Andric static void writeRecordPrefix(raw_ostream &OS, GOFF::RecordType Type, 1085f757f3fSDimitry Andric size_t RemainingSize, 1095f757f3fSDimitry Andric uint8_t Flags = RecContinuation); 1105f757f3fSDimitry Andric 1115f757f3fSDimitry Andric /// Fill the last physical record of a logical record with zero bytes. 1125f757f3fSDimitry Andric void fillRecord(); 1135f757f3fSDimitry Andric 1145f757f3fSDimitry Andric /// See raw_ostream::write_impl. 1155f757f3fSDimitry Andric void write_impl(const char *Ptr, size_t Size) override; 1165f757f3fSDimitry Andric 1175f757f3fSDimitry Andric /// Return the current position within the stream, not counting the bytes 1185f757f3fSDimitry Andric /// currently in the buffer. 1195f757f3fSDimitry Andric uint64_t current_pos() const override { return OS.tell(); } 1205f757f3fSDimitry Andric 1215f757f3fSDimitry Andric public: 1225f757f3fSDimitry Andric explicit GOFFOstream(raw_pwrite_stream &OS) 1235f757f3fSDimitry Andric : OS(OS), RemainingSize(0), LogicalRecords(0), NewLogicalRecord(false) { 1245f757f3fSDimitry Andric SetBuffer(Buffer, sizeof(Buffer)); 1255f757f3fSDimitry Andric } 1265f757f3fSDimitry Andric 1275f757f3fSDimitry Andric ~GOFFOstream() { finalize(); } 1285f757f3fSDimitry Andric 1295f757f3fSDimitry Andric raw_pwrite_stream &getOS() { return OS; } 1305f757f3fSDimitry Andric 1315f757f3fSDimitry Andric void newRecord(GOFF::RecordType Type, size_t Size); 1325f757f3fSDimitry Andric 1335f757f3fSDimitry Andric void finalize() { fillRecord(); } 1345f757f3fSDimitry Andric 1355f757f3fSDimitry Andric uint32_t logicalRecords() { return LogicalRecords; } 1365f757f3fSDimitry Andric 1375f757f3fSDimitry Andric // Support for endian-specific data. 1385f757f3fSDimitry Andric template <typename value_type> void writebe(value_type Value) { 1395f757f3fSDimitry Andric Value = 1405f757f3fSDimitry Andric support::endian::byte_swap<value_type>(Value, llvm::endianness::big); 1415f757f3fSDimitry Andric write(reinterpret_cast<const char *>(&Value), sizeof(value_type)); 1425f757f3fSDimitry Andric } 1435f757f3fSDimitry Andric }; 1445f757f3fSDimitry Andric 1455f757f3fSDimitry Andric void GOFFOstream::writeRecordPrefix(raw_ostream &OS, GOFF::RecordType Type, 1465f757f3fSDimitry Andric size_t RemainingSize, uint8_t Flags) { 1475f757f3fSDimitry Andric uint8_t TypeAndFlags = Flags | (Type << 4); 1485f757f3fSDimitry Andric if (RemainingSize > GOFF::RecordLength) 1495f757f3fSDimitry Andric TypeAndFlags |= RecContinued; 1505f757f3fSDimitry Andric OS << static_cast<unsigned char>(GOFF::PTVPrefix) // Record Type 1515f757f3fSDimitry Andric << static_cast<unsigned char>(TypeAndFlags) // Continuation 1525f757f3fSDimitry Andric << static_cast<unsigned char>(0); // Version 1535f757f3fSDimitry Andric } 1545f757f3fSDimitry Andric 1555f757f3fSDimitry Andric void GOFFOstream::newRecord(GOFF::RecordType Type, size_t Size) { 1565f757f3fSDimitry Andric fillRecord(); 1575f757f3fSDimitry Andric CurrentType = Type; 1585f757f3fSDimitry Andric RemainingSize = Size; 1595f757f3fSDimitry Andric #ifdef NDEBUG 1605f757f3fSDimitry Andric size_t Gap; 1615f757f3fSDimitry Andric #endif 1625f757f3fSDimitry Andric Gap = (RemainingSize % GOFF::RecordContentLength); 1635f757f3fSDimitry Andric if (Gap) { 1645f757f3fSDimitry Andric Gap = GOFF::RecordContentLength - Gap; 1655f757f3fSDimitry Andric RemainingSize += Gap; 1665f757f3fSDimitry Andric } 1675f757f3fSDimitry Andric NewLogicalRecord = true; 1685f757f3fSDimitry Andric ++LogicalRecords; 1695f757f3fSDimitry Andric } 1705f757f3fSDimitry Andric 1715f757f3fSDimitry Andric void GOFFOstream::fillRecord() { 1725f757f3fSDimitry Andric assert((GetNumBytesInBuffer() <= RemainingSize) && 1735f757f3fSDimitry Andric "More bytes in buffer than expected"); 1745f757f3fSDimitry Andric size_t Remains = RemainingSize - GetNumBytesInBuffer(); 1755f757f3fSDimitry Andric if (Remains) { 1765f757f3fSDimitry Andric assert(Remains == Gap && "Wrong size of fill gap"); 1775f757f3fSDimitry Andric assert((Remains < GOFF::RecordLength) && 1785f757f3fSDimitry Andric "Attempt to fill more than one physical record"); 1795f757f3fSDimitry Andric raw_ostream::write_zeros(Remains); 1805f757f3fSDimitry Andric } 1815f757f3fSDimitry Andric flush(); 1825f757f3fSDimitry Andric assert(RemainingSize == 0 && "Not fully flushed"); 1835f757f3fSDimitry Andric assert(GetNumBytesInBuffer() == 0 && "Buffer not fully empty"); 1845f757f3fSDimitry Andric } 1855f757f3fSDimitry Andric 1865f757f3fSDimitry Andric // This function is called from the raw_ostream implementation if: 1875f757f3fSDimitry Andric // - The internal buffer is full. Size is excactly the size of the buffer. 1885f757f3fSDimitry Andric // - Data larger than the internal buffer is written. Size is a multiple of the 1895f757f3fSDimitry Andric // buffer size. 1905f757f3fSDimitry Andric // - flush() has been called. Size is at most the buffer size. 1915f757f3fSDimitry Andric // The GOFFOstream implementation ensures that flush() is called before a new 1925f757f3fSDimitry Andric // logical record begins. Therefore it is sufficient to check for a new block 1935f757f3fSDimitry Andric // only once. 1945f757f3fSDimitry Andric void GOFFOstream::write_impl(const char *Ptr, size_t Size) { 1955f757f3fSDimitry Andric assert((RemainingSize >= Size) && "Attempt to write too much data"); 1965f757f3fSDimitry Andric assert(RemainingSize && "Logical record overflow"); 1975f757f3fSDimitry Andric if (!(RemainingSize % GOFF::RecordContentLength)) { 1985f757f3fSDimitry Andric writeRecordPrefix(OS, CurrentType, RemainingSize, 1995f757f3fSDimitry Andric NewLogicalRecord ? 0 : RecContinuation); 2005f757f3fSDimitry Andric NewLogicalRecord = false; 2015f757f3fSDimitry Andric } 2025f757f3fSDimitry Andric assert(!NewLogicalRecord && 2035f757f3fSDimitry Andric "New logical record not on physical record boundary"); 2045f757f3fSDimitry Andric 2055f757f3fSDimitry Andric size_t Idx = 0; 2065f757f3fSDimitry Andric while (Size > 0) { 2075f757f3fSDimitry Andric size_t BytesToWrite = bytesToNextPhysicalRecord(); 2085f757f3fSDimitry Andric if (BytesToWrite > Size) 2095f757f3fSDimitry Andric BytesToWrite = Size; 2105f757f3fSDimitry Andric OS.write(Ptr + Idx, BytesToWrite); 2115f757f3fSDimitry Andric Idx += BytesToWrite; 2125f757f3fSDimitry Andric Size -= BytesToWrite; 2135f757f3fSDimitry Andric RemainingSize -= BytesToWrite; 2145f757f3fSDimitry Andric if (Size) 2155f757f3fSDimitry Andric writeRecordPrefix(OS, CurrentType, RemainingSize); 2165f757f3fSDimitry Andric } 2175f757f3fSDimitry Andric } 2185f757f3fSDimitry Andric 2195f757f3fSDimitry Andric class GOFFObjectWriter : public MCObjectWriter { 2205f757f3fSDimitry Andric // The target specific GOFF writer instance. 2215f757f3fSDimitry Andric std::unique_ptr<MCGOFFObjectTargetWriter> TargetObjectWriter; 2225f757f3fSDimitry Andric 2235f757f3fSDimitry Andric // The stream used to write the GOFF records. 2245f757f3fSDimitry Andric GOFFOstream OS; 2255f757f3fSDimitry Andric 2265f757f3fSDimitry Andric public: 2275f757f3fSDimitry Andric GOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW, 2285f757f3fSDimitry Andric raw_pwrite_stream &OS) 2295f757f3fSDimitry Andric : TargetObjectWriter(std::move(MOTW)), OS(OS) {} 2305f757f3fSDimitry Andric 2315f757f3fSDimitry Andric ~GOFFObjectWriter() override {} 2325f757f3fSDimitry Andric 2335f757f3fSDimitry Andric // Write GOFF records. 2345f757f3fSDimitry Andric void writeHeader(); 2355f757f3fSDimitry Andric void writeEnd(); 2365f757f3fSDimitry Andric 2375f757f3fSDimitry Andric // Implementation of the MCObjectWriter interface. 238*0fca6ea1SDimitry Andric void recordRelocation(MCAssembler &Asm, const MCFragment *Fragment, 239*0fca6ea1SDimitry Andric const MCFixup &Fixup, MCValue Target, 240*0fca6ea1SDimitry Andric uint64_t &FixedValue) override {} 241*0fca6ea1SDimitry Andric uint64_t writeObject(MCAssembler &Asm) override; 2425f757f3fSDimitry Andric }; 2435f757f3fSDimitry Andric } // end anonymous namespace 2445f757f3fSDimitry Andric 2455f757f3fSDimitry Andric void GOFFObjectWriter::writeHeader() { 2465f757f3fSDimitry Andric OS.newRecord(GOFF::RT_HDR, /*Size=*/57); 2475f757f3fSDimitry Andric OS.write_zeros(1); // Reserved 2485f757f3fSDimitry Andric OS.writebe<uint32_t>(0); // Target Hardware Environment 2495f757f3fSDimitry Andric OS.writebe<uint32_t>(0); // Target Operating System Environment 2505f757f3fSDimitry Andric OS.write_zeros(2); // Reserved 2515f757f3fSDimitry Andric OS.writebe<uint16_t>(0); // CCSID 2525f757f3fSDimitry Andric OS.write_zeros(16); // Character Set name 2535f757f3fSDimitry Andric OS.write_zeros(16); // Language Product Identifier 2545f757f3fSDimitry Andric OS.writebe<uint32_t>(1); // Architecture Level 2555f757f3fSDimitry Andric OS.writebe<uint16_t>(0); // Module Properties Length 2565f757f3fSDimitry Andric OS.write_zeros(6); // Reserved 2575f757f3fSDimitry Andric } 2585f757f3fSDimitry Andric 2595f757f3fSDimitry Andric void GOFFObjectWriter::writeEnd() { 2605f757f3fSDimitry Andric uint8_t F = GOFF::END_EPR_None; 2615f757f3fSDimitry Andric uint8_t AMODE = 0; 2625f757f3fSDimitry Andric uint32_t ESDID = 0; 2635f757f3fSDimitry Andric 2645f757f3fSDimitry Andric // TODO Set Flags/AMODE/ESDID for entry point. 2655f757f3fSDimitry Andric 2665f757f3fSDimitry Andric OS.newRecord(GOFF::RT_END, /*Size=*/13); 2675f757f3fSDimitry Andric OS.writebe<uint8_t>(Flags(6, 2, F)); // Indicator flags 2685f757f3fSDimitry Andric OS.writebe<uint8_t>(AMODE); // AMODE 2695f757f3fSDimitry Andric OS.write_zeros(3); // Reserved 2705f757f3fSDimitry Andric // The record count is the number of logical records. In principle, this value 2715f757f3fSDimitry Andric // is available as OS.logicalRecords(). However, some tools rely on this field 2725f757f3fSDimitry Andric // being zero. 2735f757f3fSDimitry Andric OS.writebe<uint32_t>(0); // Record Count 2745f757f3fSDimitry Andric OS.writebe<uint32_t>(ESDID); // ESDID (of entry point) 2755f757f3fSDimitry Andric OS.finalize(); 2765f757f3fSDimitry Andric } 2775f757f3fSDimitry Andric 278*0fca6ea1SDimitry Andric uint64_t GOFFObjectWriter::writeObject(MCAssembler &Asm) { 2795f757f3fSDimitry Andric uint64_t StartOffset = OS.tell(); 2805f757f3fSDimitry Andric 2815f757f3fSDimitry Andric writeHeader(); 2825f757f3fSDimitry Andric writeEnd(); 2835f757f3fSDimitry Andric 2845f757f3fSDimitry Andric LLVM_DEBUG(dbgs() << "Wrote " << OS.logicalRecords() << " logical records."); 2855f757f3fSDimitry Andric 2865f757f3fSDimitry Andric return OS.tell() - StartOffset; 2875f757f3fSDimitry Andric } 2885f757f3fSDimitry Andric 2895f757f3fSDimitry Andric std::unique_ptr<MCObjectWriter> 2905f757f3fSDimitry Andric llvm::createGOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW, 2915f757f3fSDimitry Andric raw_pwrite_stream &OS) { 2925f757f3fSDimitry Andric return std::make_unique<GOFFObjectWriter>(std::move(MOTW), OS); 2935f757f3fSDimitry Andric } 294