Path: blob/main/contrib/llvm-project/llvm/lib/MC/GOFFObjectWriter.cpp
35234 views
//===- lib/MC/GOFFObjectWriter.cpp - GOFF File Writer ---------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements GOFF object file writer information.9//10//===----------------------------------------------------------------------===//1112#include "llvm/BinaryFormat/GOFF.h"13#include "llvm/MC/MCAssembler.h"14#include "llvm/MC/MCGOFFObjectWriter.h"15#include "llvm/MC/MCValue.h"16#include "llvm/Support/Debug.h"17#include "llvm/Support/Endian.h"18#include "llvm/Support/Path.h"19#include "llvm/Support/raw_ostream.h"2021using namespace llvm;2223#define DEBUG_TYPE "goff-writer"2425namespace {2627// The standard System/390 convention is to name the high-order (leftmost) bit28// in a byte as bit zero. The Flags type helps to set bits in a byte according29// to this numeration order.30class Flags {31uint8_t Val;3233constexpr static uint8_t bits(uint8_t BitIndex, uint8_t Length, uint8_t Value,34uint8_t OldValue) {35assert(BitIndex < 8 && "Bit index out of bounds!");36assert(Length + BitIndex <= 8 && "Bit length too long!");3738uint8_t Mask = ((1 << Length) - 1) << (8 - BitIndex - Length);39Value = Value << (8 - BitIndex - Length);40assert((Value & Mask) == Value && "Bits set outside of range!");4142return (OldValue & ~Mask) | Value;43}4445public:46constexpr Flags() : Val(0) {}47constexpr Flags(uint8_t BitIndex, uint8_t Length, uint8_t Value)48: Val(bits(BitIndex, Length, Value, 0)) {}4950void set(uint8_t BitIndex, uint8_t Length, uint8_t Value) {51Val = bits(BitIndex, Length, Value, Val);52}5354constexpr operator uint8_t() const { return Val; }55};5657// Common flag values on records.5859// Flag: This record is continued.60constexpr uint8_t RecContinued = Flags(7, 1, 1);6162// Flag: This record is a continuation.63constexpr uint8_t RecContinuation = Flags(6, 1, 1);6465// The GOFFOstream is responsible to write the data into the fixed physical66// records of the format. A user of this class announces the start of a new67// logical record and the size of its content. While writing the content, the68// physical records are created for the data. Possible fill bytes at the end of69// a physical record are written automatically. In principle, the GOFFOstream70// is agnostic of the endianness of the content. However, it also supports71// writing data in big endian byte order.72class GOFFOstream : public raw_ostream {73/// The underlying raw_pwrite_stream.74raw_pwrite_stream &OS;7576/// The remaining size of this logical record, including fill bytes.77size_t RemainingSize;7879#ifndef NDEBUG80/// The number of bytes needed to fill up the last physical record.81size_t Gap = 0;82#endif8384/// The number of logical records emitted to far.85uint32_t LogicalRecords;8687/// The type of the current (logical) record.88GOFF::RecordType CurrentType;8990/// Signals start of new record.91bool NewLogicalRecord;9293/// Static allocated buffer for the stream, used by the raw_ostream class. The94/// buffer is sized to hold the content of a physical record.95char Buffer[GOFF::RecordContentLength];9697// Return the number of bytes left to write until next physical record.98// Please note that we maintain the total numbers of byte left, not the99// written size.100size_t bytesToNextPhysicalRecord() {101size_t Bytes = RemainingSize % GOFF::RecordContentLength;102return Bytes ? Bytes : GOFF::RecordContentLength;103}104105/// Write the record prefix of a physical record, using the given record type.106static void writeRecordPrefix(raw_ostream &OS, GOFF::RecordType Type,107size_t RemainingSize,108uint8_t Flags = RecContinuation);109110/// Fill the last physical record of a logical record with zero bytes.111void fillRecord();112113/// See raw_ostream::write_impl.114void write_impl(const char *Ptr, size_t Size) override;115116/// Return the current position within the stream, not counting the bytes117/// currently in the buffer.118uint64_t current_pos() const override { return OS.tell(); }119120public:121explicit GOFFOstream(raw_pwrite_stream &OS)122: OS(OS), RemainingSize(0), LogicalRecords(0), NewLogicalRecord(false) {123SetBuffer(Buffer, sizeof(Buffer));124}125126~GOFFOstream() { finalize(); }127128raw_pwrite_stream &getOS() { return OS; }129130void newRecord(GOFF::RecordType Type, size_t Size);131132void finalize() { fillRecord(); }133134uint32_t logicalRecords() { return LogicalRecords; }135136// Support for endian-specific data.137template <typename value_type> void writebe(value_type Value) {138Value =139support::endian::byte_swap<value_type>(Value, llvm::endianness::big);140write(reinterpret_cast<const char *>(&Value), sizeof(value_type));141}142};143144void GOFFOstream::writeRecordPrefix(raw_ostream &OS, GOFF::RecordType Type,145size_t RemainingSize, uint8_t Flags) {146uint8_t TypeAndFlags = Flags | (Type << 4);147if (RemainingSize > GOFF::RecordLength)148TypeAndFlags |= RecContinued;149OS << static_cast<unsigned char>(GOFF::PTVPrefix) // Record Type150<< static_cast<unsigned char>(TypeAndFlags) // Continuation151<< static_cast<unsigned char>(0); // Version152}153154void GOFFOstream::newRecord(GOFF::RecordType Type, size_t Size) {155fillRecord();156CurrentType = Type;157RemainingSize = Size;158#ifdef NDEBUG159size_t Gap;160#endif161Gap = (RemainingSize % GOFF::RecordContentLength);162if (Gap) {163Gap = GOFF::RecordContentLength - Gap;164RemainingSize += Gap;165}166NewLogicalRecord = true;167++LogicalRecords;168}169170void GOFFOstream::fillRecord() {171assert((GetNumBytesInBuffer() <= RemainingSize) &&172"More bytes in buffer than expected");173size_t Remains = RemainingSize - GetNumBytesInBuffer();174if (Remains) {175assert(Remains == Gap && "Wrong size of fill gap");176assert((Remains < GOFF::RecordLength) &&177"Attempt to fill more than one physical record");178raw_ostream::write_zeros(Remains);179}180flush();181assert(RemainingSize == 0 && "Not fully flushed");182assert(GetNumBytesInBuffer() == 0 && "Buffer not fully empty");183}184185// This function is called from the raw_ostream implementation if:186// - The internal buffer is full. Size is excactly the size of the buffer.187// - Data larger than the internal buffer is written. Size is a multiple of the188// buffer size.189// - flush() has been called. Size is at most the buffer size.190// The GOFFOstream implementation ensures that flush() is called before a new191// logical record begins. Therefore it is sufficient to check for a new block192// only once.193void GOFFOstream::write_impl(const char *Ptr, size_t Size) {194assert((RemainingSize >= Size) && "Attempt to write too much data");195assert(RemainingSize && "Logical record overflow");196if (!(RemainingSize % GOFF::RecordContentLength)) {197writeRecordPrefix(OS, CurrentType, RemainingSize,198NewLogicalRecord ? 0 : RecContinuation);199NewLogicalRecord = false;200}201assert(!NewLogicalRecord &&202"New logical record not on physical record boundary");203204size_t Idx = 0;205while (Size > 0) {206size_t BytesToWrite = bytesToNextPhysicalRecord();207if (BytesToWrite > Size)208BytesToWrite = Size;209OS.write(Ptr + Idx, BytesToWrite);210Idx += BytesToWrite;211Size -= BytesToWrite;212RemainingSize -= BytesToWrite;213if (Size)214writeRecordPrefix(OS, CurrentType, RemainingSize);215}216}217218class GOFFObjectWriter : public MCObjectWriter {219// The target specific GOFF writer instance.220std::unique_ptr<MCGOFFObjectTargetWriter> TargetObjectWriter;221222// The stream used to write the GOFF records.223GOFFOstream OS;224225public:226GOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW,227raw_pwrite_stream &OS)228: TargetObjectWriter(std::move(MOTW)), OS(OS) {}229230~GOFFObjectWriter() override {}231232// Write GOFF records.233void writeHeader();234void writeEnd();235236// Implementation of the MCObjectWriter interface.237void recordRelocation(MCAssembler &Asm, const MCFragment *Fragment,238const MCFixup &Fixup, MCValue Target,239uint64_t &FixedValue) override {}240uint64_t writeObject(MCAssembler &Asm) override;241};242} // end anonymous namespace243244void GOFFObjectWriter::writeHeader() {245OS.newRecord(GOFF::RT_HDR, /*Size=*/57);246OS.write_zeros(1); // Reserved247OS.writebe<uint32_t>(0); // Target Hardware Environment248OS.writebe<uint32_t>(0); // Target Operating System Environment249OS.write_zeros(2); // Reserved250OS.writebe<uint16_t>(0); // CCSID251OS.write_zeros(16); // Character Set name252OS.write_zeros(16); // Language Product Identifier253OS.writebe<uint32_t>(1); // Architecture Level254OS.writebe<uint16_t>(0); // Module Properties Length255OS.write_zeros(6); // Reserved256}257258void GOFFObjectWriter::writeEnd() {259uint8_t F = GOFF::END_EPR_None;260uint8_t AMODE = 0;261uint32_t ESDID = 0;262263// TODO Set Flags/AMODE/ESDID for entry point.264265OS.newRecord(GOFF::RT_END, /*Size=*/13);266OS.writebe<uint8_t>(Flags(6, 2, F)); // Indicator flags267OS.writebe<uint8_t>(AMODE); // AMODE268OS.write_zeros(3); // Reserved269// The record count is the number of logical records. In principle, this value270// is available as OS.logicalRecords(). However, some tools rely on this field271// being zero.272OS.writebe<uint32_t>(0); // Record Count273OS.writebe<uint32_t>(ESDID); // ESDID (of entry point)274OS.finalize();275}276277uint64_t GOFFObjectWriter::writeObject(MCAssembler &Asm) {278uint64_t StartOffset = OS.tell();279280writeHeader();281writeEnd();282283LLVM_DEBUG(dbgs() << "Wrote " << OS.logicalRecords() << " logical records.");284285return OS.tell() - StartOffset;286}287288std::unique_ptr<MCObjectWriter>289llvm::createGOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW,290raw_pwrite_stream &OS) {291return std::make_unique<GOFFObjectWriter>(std::move(MOTW), OS);292}293294295