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