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 | |
77 | raw_pwrite_stream &getOS() { return OS; } |
78 | size_t getWrittenSize() const { return PhysicalRecords * GOFF::RecordLength; } |
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. |
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(Ptr: (const char *)&Value, Size: 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 | |
110 | GOFFOstream::GOFFOstream(raw_pwrite_stream &OS) : OS(OS) {} |
111 | |
112 | GOFFOstream::~GOFFOstream() { finalizeRecord(); } |
113 | |
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 | |
130 | size_t GOFFOstream::getRemainingSize() { |
131 | return size_t(&Buffer[BufferSize] - BufferPtr); |
132 | } |
133 | |
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(dest: BufferPtr, src: Ptr, n: 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(Ptr: Buffer, Size: size_t(BufferPtr - Buffer)); |
148 | if (RemainingSize > 0) { |
149 | OS.write(Ptr, Size: RemainingSize); |
150 | Ptr += RemainingSize; |
151 | Size -= RemainingSize; |
152 | } |
153 | |
154 | while (Size > BufferSize) { |
155 | updateFlagsAndWritePrefix(/*IsContinued=*/true); |
156 | OS.write(Ptr, Size: BufferSize); |
157 | Ptr += BufferSize; |
158 | Size -= BufferSize; |
159 | } |
160 | |
161 | // The remaining bytes fit into the buffer. |
162 | memcpy(dest: Buffer, src: Ptr, n: Size); |
163 | BufferPtr = &Buffer[Size]; |
164 | } |
165 | |
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(s: BufferPtr, c: 0, n: 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(Ptr: Zeros, Size: NumZeros); |
182 | } |
183 | |
184 | void GOFFOstream::newRecord(GOFF::RecordType Type) { |
185 | finalizeRecord(); |
186 | TypeAndFlags = Type << 4; |
187 | ++LogicalRecords; |
188 | } |
189 | |
190 | void GOFFOstream::finalizeRecord() { |
191 | if (Buffer == BufferPtr) |
192 | return; |
193 | updateFlagsAndWritePrefix(/*IsContinued=*/false); |
194 | OS.write(Ptr: Buffer, Size: size_t(BufferPtr - Buffer)); |
195 | OS.write_zeros(NumZeros: 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 | |
221 | GOFFSymbol() : EsdId(0), ParentEsdId(0) {} |
222 | |
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 | |
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 | |
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 | |
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 | |
291 | GOFFWriter::GOFFWriter(raw_pwrite_stream &OS, MCAssembler &Asm) |
292 | : OS(OS), Asm(Asm) {} |
293 | |
294 | void GOFFWriter::defineSectionSymbols(const MCSectionGOFF &Section) { |
295 | if (Section.isSD()) { |
296 | GOFFSymbol SD(Section.getName(), Section.getOrdinal(), |
297 | Section.getSDAttributes()); |
298 | writeSymbol(Symbol: 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(Sec: Section); |
305 | writeSymbol(Symbol: 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(Sec: 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(Symbol: PR); |
323 | } |
324 | } |
325 | |
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(Symbol: LD); |
333 | } |
334 | |
335 | void GOFFWriter::defineSymbols() { |
336 | unsigned Ordinal = 0; |
337 | // Process all sections. |
338 | for (MCSection &S : Asm) { |
339 | auto &Section = cast<MCSectionGOFF>(Val&: 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>(Val: Sym); |
349 | if (Symbol.hasLDAttributes()) { |
350 | Symbol.setIndex(++Ordinal); |
351 | defineLabel(Symbol); |
352 | } |
353 | } |
354 | } |
355 | |
356 | void GOFFWriter::() { |
357 | OS.newRecord(Type: GOFF::RT_HDR); |
358 | OS.write_zeros(NumZeros: 1); // Reserved |
359 | OS.writebe<uint32_t>(Value: 0); // Target Hardware Environment |
360 | OS.writebe<uint32_t>(Value: 0); // Target Operating System Environment |
361 | OS.write_zeros(NumZeros: 2); // Reserved |
362 | OS.writebe<uint16_t>(Value: 0); // CCSID |
363 | OS.write_zeros(NumZeros: 16); // Character Set name |
364 | OS.write_zeros(NumZeros: 16); // Language Product Identifier |
365 | OS.writebe<uint32_t>(Value: 1); // Architecture Level |
366 | OS.writebe<uint16_t>(Value: 0); // Module Properties Length |
367 | OS.write_zeros(NumZeros: 6); // Reserved |
368 | } |
369 | |
370 | void GOFFWriter::writeSymbol(const GOFFSymbol &Symbol) { |
371 | if (Symbol.Offset >= (((uint64_t)1) << 31)) |
372 | report_fatal_error(reason: "ESD offset out of range" ); |
373 | |
374 | // All symbol names are in EBCDIC. |
375 | SmallString<256> Name; |
376 | ConverterEBCDIC::convertToEBCDIC(Source: Symbol.Name, Result&: 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(reason: "Symbol max name length exceeded" ); |
382 | uint16_t NameLength = Name.size(); |
383 | |
384 | OS.newRecord(Type: GOFF::RT_ESD); |
385 | OS.writebe<uint8_t>(Value: Symbol.SymbolType); // Symbol Type |
386 | OS.writebe<uint32_t>(Value: Symbol.EsdId); // ESDID |
387 | OS.writebe<uint32_t>(Value: Symbol.ParentEsdId); // Parent or Owning ESDID |
388 | OS.writebe<uint32_t>(Value: 0); // Reserved |
389 | OS.writebe<uint32_t>( |
390 | Value: static_cast<uint32_t>(Symbol.Offset)); // Offset or Address |
391 | OS.writebe<uint32_t>(Value: 0); // Reserved |
392 | OS.writebe<uint32_t>(Value: Symbol.SectionLength); // Length |
393 | OS.writebe<uint32_t>(Value: Symbol.EASectionEDEsdId); // Extended Attribute ESDID |
394 | OS.writebe<uint32_t>(Value: Symbol.EASectionOffset); // Extended Attribute Offset |
395 | OS.writebe<uint32_t>(Value: 0); // Reserved |
396 | OS.writebe<uint8_t>(Value: Symbol.NameSpace); // Name Space ID |
397 | OS.writebe<uint8_t>(Value: Symbol.SymbolFlags); // Flags |
398 | OS.writebe<uint8_t>(Value: Symbol.FillByteValue); // Fill-Byte Value |
399 | OS.writebe<uint8_t>(Value: 0); // Reserved |
400 | OS.writebe<uint32_t>(Value: Symbol.ADAEsdId); // ADA ESDID |
401 | OS.writebe<uint32_t>(Value: Symbol.SortKey); // Sort Priority |
402 | OS.writebe<uint64_t>(Value: 0); // Reserved |
403 | for (auto F : Symbol.BehavAttrs.Attr) |
404 | OS.writebe<uint8_t>(Value: F); // Behavioral Attributes |
405 | OS.writebe<uint16_t>(Value: NameLength); // Name Length |
406 | OS.write(Ptr: Name.data(), Size: 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 | |
435 | uint64_t current_pos() const override { return Offset; } |
436 | |
437 | public: |
438 | explicit TextStream(GOFFOstream &OS, uint32_t EsdId, |
439 | GOFF::ESDTextStyle RecordStyle) |
440 | : OS(OS), Offset(0), EsdId(EsdId), RecordStyle(RecordStyle) { |
441 | SetBuffer(BufferStart: Buffer, Size: sizeof(Buffer)); |
442 | } |
443 | |
444 | ~TextStream() { flush(); } |
445 | }; |
446 | } // namespace |
447 | |
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(reason: "TXT section too large" ); |
454 | |
455 | while (WrittenLength < Size) { |
456 | size_t ToWriteLength = |
457 | std::min(a: Size - WrittenLength, b: size_t(GOFF::MaxDataLength)); |
458 | |
459 | OS.newRecord(Type: GOFF::RT_TXT); |
460 | OS.writebe<uint8_t>(Value: GOFF::Flags(4, 4, RecordStyle)); // Text Record Style |
461 | OS.writebe<uint32_t>(Value: EsdId); // Element ESDID |
462 | OS.writebe<uint32_t>(Value: 0); // Reserved |
463 | OS.writebe<uint32_t>(Value: static_cast<uint32_t>(Offset)); // Offset |
464 | OS.writebe<uint32_t>(Value: 0); // Text Field True Length |
465 | OS.writebe<uint16_t>(Value: 0); // Text Encoding |
466 | OS.writebe<uint16_t>(Value: ToWriteLength); // Data Length |
467 | OS.write(Ptr: Ptr + WrittenLength, Size: ToWriteLength); // Data |
468 | |
469 | WrittenLength += ToWriteLength; |
470 | Offset += ToWriteLength; |
471 | } |
472 | } |
473 | |
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(OS&: S, Section); |
481 | } |
482 | |
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(Type: GOFF::RT_END); |
491 | OS.writebe<uint8_t>(Value: GOFF::Flags(6, 2, F)); // Indicator flags |
492 | OS.writebe<uint8_t>(Value: AMODE); // AMODE |
493 | OS.write_zeros(NumZeros: 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>(Value: 0); // Record Count |
498 | OS.writebe<uint32_t>(Value: ESDID); // ESDID (of entry point) |
499 | } |
500 | |
501 | uint64_t GOFFWriter::writeObject() { |
502 | writeHeader(); |
503 | |
504 | defineSymbols(); |
505 | |
506 | for (const MCSection &Section : Asm) |
507 | writeText(Section: 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 | |
520 | GOFFObjectWriter::GOFFObjectWriter( |
521 | std::unique_ptr<MCGOFFObjectTargetWriter> MOTW, raw_pwrite_stream &OS) |
522 | : TargetObjectWriter(std::move(MOTW)), OS(OS) {} |
523 | |
524 | GOFFObjectWriter::~GOFFObjectWriter() {} |
525 | |
526 | uint64_t GOFFObjectWriter::writeObject() { |
527 | uint64_t Size = GOFFWriter(OS, *Asm).writeObject(); |
528 | return Size; |
529 | } |
530 | |
531 | std::unique_ptr<MCObjectWriter> |
532 | llvm::createGOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW, |
533 | raw_pwrite_stream &OS) { |
534 | return std::make_unique<GOFFObjectWriter>(args: std::move(MOTW), args&: OS); |
535 | } |
536 | |