brintos

brintos / llvm-project-archived public Read only

0
0
Text · 17.8 KiB · a3eaaa7 Raw
535 lines · cpp
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-exception6//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/MCGOFFAttributes.h"16#include "llvm/MC/MCGOFFObjectWriter.h"17#include "llvm/MC/MCSectionGOFF.h"18#include "llvm/MC/MCSymbolGOFF.h"19#include "llvm/MC/MCValue.h"20#include "llvm/Support/ConvertEBCDIC.h"21#include "llvm/Support/Debug.h"22#include "llvm/Support/Endian.h"23#include "llvm/Support/raw_ostream.h"24 25using namespace llvm;26 27#define DEBUG_TYPE "goff-writer"28 29namespace {30// Common flag values on records.31 32// Flag: This record is continued.33constexpr uint8_t RecContinued = GOFF::Flags(7, 1, 1);34 35// Flag: This record is a continuation.36constexpr uint8_t RecContinuation = GOFF::Flags(6, 1, 1);37 38// The GOFFOstream is responsible to write the data into the fixed physical39// records of the format. A user of this class announces the begin of a new40// logical record. While writing the payload, the physical records are created41// for the data. Possible fill bytes at the end of a physical record are written42// automatically. In principle, the GOFFOstream is agnostic of the endianness of43// the payload. However, it also supports writing data in big endian byte order.44//45// The physical records use the flag field to indicate if the there is a46// successor and predecessor record. To be able to set these flags while47// writing, the basic implementation idea is to always buffer the last seen48// physical record.49class GOFFOstream {50  /// The underlying raw_pwrite_stream.51  raw_pwrite_stream &OS;52 53  /// The number of logical records emitted so far.54  uint32_t LogicalRecords = 0;55 56  /// The number of physical records emitted so far.57  uint32_t PhysicalRecords = 0;58 59  /// The size of the buffer. Same as the payload size of a physical record.60  static constexpr uint8_t BufferSize = GOFF::PayloadLength;61 62  /// Current position in buffer.63  char *BufferPtr = Buffer;64 65  /// Static allocated buffer for the stream.66  char Buffer[BufferSize];67 68  /// The type of the current logical record, and the flags (aka continued and69  /// continuation indicators) for the previous (physical) record.70  uint8_t TypeAndFlags = 0;71 72public:73  GOFFOstream(raw_pwrite_stream &OS);74  ~GOFFOstream();75 76  raw_pwrite_stream &getOS() { return OS; }77  size_t getWrittenSize() const { return PhysicalRecords * GOFF::RecordLength; }78  uint32_t getNumLogicalRecords() { return LogicalRecords; }79 80  /// Write the specified bytes.81  void write(const char *Ptr, size_t Size);82 83  /// Write zeroes, up to a maximum of 16 bytes.84  void write_zeros(unsigned NumZeros);85 86  /// Support for endian-specific data.87  template <typename value_type> void writebe(value_type Value) {88    Value =89        support::endian::byte_swap<value_type>(Value, llvm::endianness::big);90    write((const char *)&Value, sizeof(value_type));91  }92 93  /// Begin a new logical record. Implies finalizing the previous record.94  void newRecord(GOFF::RecordType Type);95 96  /// Ends a logical record.97  void finalizeRecord();98 99private:100  /// Updates the continued/continuation flags, and writes the record prefix of101  /// a physical record.102  void updateFlagsAndWritePrefix(bool IsContinued);103 104  /// Returns the remaining size in the buffer.105  size_t getRemainingSize();106};107} // namespace108 109GOFFOstream::GOFFOstream(raw_pwrite_stream &OS) : OS(OS) {}110 111GOFFOstream::~GOFFOstream() { finalizeRecord(); }112 113void GOFFOstream::updateFlagsAndWritePrefix(bool IsContinued) {114  // Update the flags based on the previous state and the flag IsContinued.115  if (TypeAndFlags & RecContinued)116    TypeAndFlags |= RecContinuation;117  if (IsContinued)118    TypeAndFlags |= RecContinued;119  else120    TypeAndFlags &= ~RecContinued;121 122  OS << static_cast<unsigned char>(GOFF::PTVPrefix) // Record Type123     << static_cast<unsigned char>(TypeAndFlags)    // Continuation124     << static_cast<unsigned char>(0);              // Version125 126  ++PhysicalRecords;127}128 129size_t GOFFOstream::getRemainingSize() {130  return size_t(&Buffer[BufferSize] - BufferPtr);131}132 133void GOFFOstream::write(const char *Ptr, size_t Size) {134  size_t RemainingSize = getRemainingSize();135 136  // Data fits into the buffer.137  if (LLVM_LIKELY(Size <= RemainingSize)) {138    memcpy(BufferPtr, Ptr, Size);139    BufferPtr += Size;140    return;141  }142 143  // Otherwise the buffer is partially filled or full, and data does not fit144  // into it.145  updateFlagsAndWritePrefix(/*IsContinued=*/true);146  OS.write(Buffer, size_t(BufferPtr - Buffer));147  if (RemainingSize > 0) {148    OS.write(Ptr, RemainingSize);149    Ptr += RemainingSize;150    Size -= RemainingSize;151  }152 153  while (Size > BufferSize) {154    updateFlagsAndWritePrefix(/*IsContinued=*/true);155    OS.write(Ptr, BufferSize);156    Ptr += BufferSize;157    Size -= BufferSize;158  }159 160  // The remaining bytes fit into the buffer.161  memcpy(Buffer, Ptr, Size);162  BufferPtr = &Buffer[Size];163}164 165void GOFFOstream::write_zeros(unsigned NumZeros) {166  assert(NumZeros <= 16 && "Range for zeros too large");167 168  // Handle the common case first: all fits in the buffer.169  size_t RemainingSize = getRemainingSize();170  if (LLVM_LIKELY(RemainingSize >= NumZeros)) {171    memset(BufferPtr, 0, NumZeros);172    BufferPtr += NumZeros;173    return;174  }175 176  // Otherwise some field value is cleared.177  static char Zeros[16] = {178      0,179  };180  write(Zeros, NumZeros);181}182 183void GOFFOstream::newRecord(GOFF::RecordType Type) {184  finalizeRecord();185  TypeAndFlags = Type << 4;186  ++LogicalRecords;187}188 189void GOFFOstream::finalizeRecord() {190  if (Buffer == BufferPtr)191    return;192  updateFlagsAndWritePrefix(/*IsContinued=*/false);193  OS.write(Buffer, size_t(BufferPtr - Buffer));194  OS.write_zeros(getRemainingSize());195  BufferPtr = Buffer;196}197 198namespace {199// A GOFFSymbol holds all the data required for writing an ESD record.200class GOFFSymbol {201public:202  std::string Name;203  uint32_t EsdId;204  uint32_t ParentEsdId;205  uint64_t Offset = 0; // Offset of the symbol into the section. LD only.206                       // Offset is only 32 bit, the larger type is used to207                       // enable error checking.208  GOFF::ESDSymbolType SymbolType;209  GOFF::ESDNameSpaceId NameSpace = GOFF::ESD_NS_ProgramManagementBinder;210 211  GOFF::BehavioralAttributes BehavAttrs;212  GOFF::SymbolFlags SymbolFlags;213  uint32_t SortKey = 0;214  uint32_t SectionLength = 0;215  uint32_t ADAEsdId = 0;216  uint32_t EASectionEDEsdId = 0;217  uint32_t EASectionOffset = 0;218  uint8_t FillByteValue = 0;219 220  GOFFSymbol() : EsdId(0), ParentEsdId(0) {}221 222  GOFFSymbol(StringRef Name, uint32_t EsdID, const GOFF::SDAttr &Attr)223      : Name(Name.data(), Name.size()), EsdId(EsdID), ParentEsdId(0),224        SymbolType(GOFF::ESD_ST_SectionDefinition) {225    BehavAttrs.setTaskingBehavior(Attr.TaskingBehavior);226    BehavAttrs.setBindingScope(Attr.BindingScope);227  }228 229  GOFFSymbol(StringRef Name, uint32_t EsdID, uint32_t ParentEsdID,230             const GOFF::EDAttr &Attr)231      : Name(Name.data(), Name.size()), EsdId(EsdID), ParentEsdId(ParentEsdID),232        SymbolType(GOFF::ESD_ST_ElementDefinition) {233    this->NameSpace = Attr.NameSpace;234    // We always set a fill byte value.235    this->FillByteValue = Attr.FillByteValue;236    SymbolFlags.setFillBytePresence(1);237    SymbolFlags.setReservedQwords(Attr.ReservedQwords);238    // TODO Do we need/should set the "mangled" flag?239    BehavAttrs.setReadOnly(Attr.IsReadOnly);240    BehavAttrs.setRmode(Attr.Rmode);241    BehavAttrs.setTextStyle(Attr.TextStyle);242    BehavAttrs.setBindingAlgorithm(Attr.BindAlgorithm);243    BehavAttrs.setLoadingBehavior(Attr.LoadBehavior);244    BehavAttrs.setAlignment(Attr.Alignment);245  }246 247  GOFFSymbol(StringRef Name, uint32_t EsdID, uint32_t ParentEsdID,248             GOFF::ESDNameSpaceId NameSpace, const GOFF::LDAttr &Attr)249      : Name(Name.data(), Name.size()), EsdId(EsdID), ParentEsdId(ParentEsdID),250        SymbolType(GOFF::ESD_ST_LabelDefinition), NameSpace(NameSpace) {251    SymbolFlags.setRenameable(Attr.IsRenamable);252    BehavAttrs.setExecutable(Attr.Executable);253    BehavAttrs.setBindingStrength(Attr.BindingStrength);254    BehavAttrs.setLinkageType(Attr.Linkage);255    BehavAttrs.setAmode(Attr.Amode);256    BehavAttrs.setBindingScope(Attr.BindingScope);257  }258 259  GOFFSymbol(StringRef Name, uint32_t EsdID, uint32_t ParentEsdID,260             const GOFF::EDAttr &EDAttr, const GOFF::PRAttr &Attr)261      : Name(Name.data(), Name.size()), EsdId(EsdID), ParentEsdId(ParentEsdID),262        SymbolType(GOFF::ESD_ST_PartReference), NameSpace(EDAttr.NameSpace) {263    SymbolFlags.setRenameable(Attr.IsRenamable);264    BehavAttrs.setExecutable(Attr.Executable);265    BehavAttrs.setLinkageType(Attr.Linkage);266    BehavAttrs.setBindingScope(Attr.BindingScope);267    BehavAttrs.setAlignment(EDAttr.Alignment);268  }269};270 271class GOFFWriter {272  GOFFOstream OS;273  MCAssembler &Asm;274 275  void writeHeader();276  void writeSymbol(const GOFFSymbol &Symbol);277  void writeText(const MCSectionGOFF *MC);278  void writeEnd();279 280  void defineSectionSymbols(const MCSectionGOFF &Section);281  void defineLabel(const MCSymbolGOFF &Symbol);282  void defineSymbols();283 284public:285  GOFFWriter(raw_pwrite_stream &OS, MCAssembler &Asm);286  uint64_t writeObject();287};288} // namespace289 290GOFFWriter::GOFFWriter(raw_pwrite_stream &OS, MCAssembler &Asm)291    : OS(OS), Asm(Asm) {}292 293void GOFFWriter::defineSectionSymbols(const MCSectionGOFF &Section) {294  if (Section.isSD()) {295    GOFFSymbol SD(Section.getName(), Section.getOrdinal(),296                  Section.getSDAttributes());297    writeSymbol(SD);298  }299 300  if (Section.isED()) {301    GOFFSymbol ED(Section.getName(), Section.getOrdinal(),302                  Section.getParent()->getOrdinal(), Section.getEDAttributes());303    ED.SectionLength = Asm.getSectionAddressSize(Section);304    writeSymbol(ED);305  }306 307  if (Section.isPR()) {308    MCSectionGOFF *Parent = Section.getParent();309    GOFFSymbol PR(Section.getName(), Section.getOrdinal(), Parent->getOrdinal(),310                  Parent->getEDAttributes(), Section.getPRAttributes());311    PR.SectionLength = Asm.getSectionAddressSize(Section);312    if (Section.requiresNonZeroLength()) {313      // We cannot have a zero-length section for data.  If we do,314      // artificially inflate it. Use 2 bytes to avoid odd alignments. Note:315      // if this is ever changed, you will need to update the code in316      // SystemZAsmPrinter::emitCEEMAIN and SystemZAsmPrinter::emitCELQMAIN to317      // generate -1 if there is no ADA318      if (!PR.SectionLength)319        PR.SectionLength = 2;320    }321    writeSymbol(PR);322  }323}324 325void GOFFWriter::defineLabel(const MCSymbolGOFF &Symbol) {326  MCSectionGOFF &Section = static_cast<MCSectionGOFF &>(Symbol.getSection());327  GOFFSymbol LD(Symbol.getName(), Symbol.getIndex(), Section.getOrdinal(),328                Section.getEDAttributes().NameSpace, Symbol.getLDAttributes());329  if (Symbol.getADA())330    LD.ADAEsdId = Symbol.getADA()->getOrdinal();331  writeSymbol(LD);332}333 334void GOFFWriter::defineSymbols() {335  unsigned Ordinal = 0;336  // Process all sections.337  for (MCSection &S : Asm) {338    auto &Section = static_cast<MCSectionGOFF &>(S);339    Section.setOrdinal(++Ordinal);340    defineSectionSymbols(Section);341  }342 343  // Process all symbols344  for (const MCSymbol &Sym : Asm.symbols()) {345    if (Sym.isTemporary())346      continue;347    auto &Symbol = static_cast<const MCSymbolGOFF &>(Sym);348    if (Symbol.hasLDAttributes()) {349      Symbol.setIndex(++Ordinal);350      defineLabel(Symbol);351    }352  }353}354 355void GOFFWriter::writeHeader() {356  OS.newRecord(GOFF::RT_HDR);357  OS.write_zeros(1);       // Reserved358  OS.writebe<uint32_t>(0); // Target Hardware Environment359  OS.writebe<uint32_t>(0); // Target Operating System Environment360  OS.write_zeros(2);       // Reserved361  OS.writebe<uint16_t>(0); // CCSID362  OS.write_zeros(16);      // Character Set name363  OS.write_zeros(16);      // Language Product Identifier364  OS.writebe<uint32_t>(1); // Architecture Level365  OS.writebe<uint16_t>(0); // Module Properties Length366  OS.write_zeros(6);       // Reserved367}368 369void GOFFWriter::writeSymbol(const GOFFSymbol &Symbol) {370  if (Symbol.Offset >= (((uint64_t)1) << 31))371    report_fatal_error("ESD offset out of range");372 373  // All symbol names are in EBCDIC.374  SmallString<256> Name;375  ConverterEBCDIC::convertToEBCDIC(Symbol.Name, Name);376 377  // Check length here since this number is technically signed but we need uint378  // for writing to records.379  if (Name.size() >= GOFF::MaxDataLength)380    report_fatal_error("Symbol max name length exceeded");381  uint16_t NameLength = Name.size();382 383  OS.newRecord(GOFF::RT_ESD);384  OS.writebe<uint8_t>(Symbol.SymbolType);   // Symbol Type385  OS.writebe<uint32_t>(Symbol.EsdId);       // ESDID386  OS.writebe<uint32_t>(Symbol.ParentEsdId); // Parent or Owning ESDID387  OS.writebe<uint32_t>(0);                  // Reserved388  OS.writebe<uint32_t>(389      static_cast<uint32_t>(Symbol.Offset));     // Offset or Address390  OS.writebe<uint32_t>(0);                       // Reserved391  OS.writebe<uint32_t>(Symbol.SectionLength);    // Length392  OS.writebe<uint32_t>(Symbol.EASectionEDEsdId); // Extended Attribute ESDID393  OS.writebe<uint32_t>(Symbol.EASectionOffset);  // Extended Attribute Offset394  OS.writebe<uint32_t>(0);                       // Reserved395  OS.writebe<uint8_t>(Symbol.NameSpace);         // Name Space ID396  OS.writebe<uint8_t>(Symbol.SymbolFlags);       // Flags397  OS.writebe<uint8_t>(Symbol.FillByteValue);     // Fill-Byte Value398  OS.writebe<uint8_t>(0);                        // Reserved399  OS.writebe<uint32_t>(Symbol.ADAEsdId);         // ADA ESDID400  OS.writebe<uint32_t>(Symbol.SortKey);          // Sort Priority401  OS.writebe<uint64_t>(0);                       // Reserved402  for (auto F : Symbol.BehavAttrs.Attr)403    OS.writebe<uint8_t>(F);          // Behavioral Attributes404  OS.writebe<uint16_t>(NameLength);  // Name Length405  OS.write(Name.data(), NameLength); // Name406}407 408namespace {409/// Adapter stream to write a text section.410class TextStream : public raw_ostream {411  /// The underlying GOFFOstream.412  GOFFOstream &OS;413 414  /// The buffer size is the maximum number of bytes in a TXT section.415  static constexpr size_t BufferSize = GOFF::MaxDataLength;416 417  /// Static allocated buffer for the stream, used by the raw_ostream class. The418  /// buffer is sized to hold the payload of a logical TXT record.419  char Buffer[BufferSize];420 421  /// The offset for the next TXT record. This is equal to the number of bytes422  /// written.423  size_t Offset;424 425  /// The Esdid of the GOFF section.426  const uint32_t EsdId;427 428  /// The record style.429  const GOFF::ESDTextStyle RecordStyle;430 431  /// See raw_ostream::write_impl.432  void write_impl(const char *Ptr, size_t Size) override;433 434  uint64_t current_pos() const override { return Offset; }435 436public:437  explicit TextStream(GOFFOstream &OS, uint32_t EsdId,438                      GOFF::ESDTextStyle RecordStyle)439      : OS(OS), Offset(0), EsdId(EsdId), RecordStyle(RecordStyle) {440    SetBuffer(Buffer, sizeof(Buffer));441  }442 443  ~TextStream() override { flush(); }444};445} // namespace446 447void TextStream::write_impl(const char *Ptr, size_t Size) {448  size_t WrittenLength = 0;449 450  // We only have signed 32bits of offset.451  if (Offset + Size > std::numeric_limits<int32_t>::max())452    report_fatal_error("TXT section too large");453 454  while (WrittenLength < Size) {455    size_t ToWriteLength =456        std::min(Size - WrittenLength, size_t(GOFF::MaxDataLength));457 458    OS.newRecord(GOFF::RT_TXT);459    OS.writebe<uint8_t>(GOFF::Flags(4, 4, RecordStyle)); // Text Record Style460    OS.writebe<uint32_t>(EsdId);                         // Element ESDID461    OS.writebe<uint32_t>(0);                             // Reserved462    OS.writebe<uint32_t>(static_cast<uint32_t>(Offset)); // Offset463    OS.writebe<uint32_t>(0);                      // Text Field True Length464    OS.writebe<uint16_t>(0);                      // Text Encoding465    OS.writebe<uint16_t>(ToWriteLength);          // Data Length466    OS.write(Ptr + WrittenLength, ToWriteLength); // Data467 468    WrittenLength += ToWriteLength;469    Offset += ToWriteLength;470  }471}472 473void GOFFWriter::writeText(const MCSectionGOFF *Section) {474  // A BSS section contains only zeros, no need to write this.475  if (Section->isBSS())476    return;477 478  TextStream S(OS, Section->getOrdinal(), Section->getTextStyle());479  Asm.writeSectionData(S, Section);480}481 482void GOFFWriter::writeEnd() {483  uint8_t F = GOFF::END_EPR_None;484  uint8_t AMODE = 0;485  uint32_t ESDID = 0;486 487  // TODO Set Flags/AMODE/ESDID for entry point.488 489  OS.newRecord(GOFF::RT_END);490  OS.writebe<uint8_t>(GOFF::Flags(6, 2, F)); // Indicator flags491  OS.writebe<uint8_t>(AMODE);                // AMODE492  OS.write_zeros(3);                         // Reserved493  // The record count is the number of logical records. In principle, this value494  // is available as OS.logicalRecords(). However, some tools rely on this field495  // being zero.496  OS.writebe<uint32_t>(0);     // Record Count497  OS.writebe<uint32_t>(ESDID); // ESDID (of entry point)498}499 500uint64_t GOFFWriter::writeObject() {501  writeHeader();502 503  defineSymbols();504 505  for (const MCSection &Section : Asm)506    writeText(static_cast<const MCSectionGOFF *>(&Section));507 508  writeEnd();509 510  // Make sure all records are written.511  OS.finalizeRecord();512 513  LLVM_DEBUG(dbgs() << "Wrote " << OS.getNumLogicalRecords()514                    << " logical records.");515 516  return OS.getWrittenSize();517}518 519GOFFObjectWriter::GOFFObjectWriter(520    std::unique_ptr<MCGOFFObjectTargetWriter> MOTW, raw_pwrite_stream &OS)521    : TargetObjectWriter(std::move(MOTW)), OS(OS) {}522 523GOFFObjectWriter::~GOFFObjectWriter() = default;524 525uint64_t GOFFObjectWriter::writeObject() {526  uint64_t Size = GOFFWriter(OS, *Asm).writeObject();527  return Size;528}529 530std::unique_ptr<MCObjectWriter>531llvm::createGOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW,532                             raw_pwrite_stream &OS) {533  return std::make_unique<GOFFObjectWriter>(std::move(MOTW), OS);534}535