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