xref: /freebsd-src/contrib/llvm-project/llvm/lib/MC/GOFFObjectWriter.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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