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/MCAsmBackend.h"
15#include "llvm/MC/MCAssembler.h"
16#include "llvm/MC/MCGOFFAttributes.h"
17#include "llvm/MC/MCGOFFObjectWriter.h"
18#include "llvm/MC/MCObjectWriter.h"
19#include "llvm/MC/MCSectionGOFF.h"
20#include "llvm/MC/MCSymbolGOFF.h"
21#include "llvm/MC/MCValue.h"
22#include "llvm/Support/ConvertEBCDIC.h"
23#include "llvm/Support/Debug.h"
24#include "llvm/Support/Endian.h"
25#include "llvm/Support/raw_ostream.h"
26
27using namespace llvm;
28
29#define DEBUG_TYPE "goff-writer"
30
31namespace {
32// Common flag values on records.
33
34// Flag: This record is continued.
35constexpr uint8_t RecContinued = GOFF::Flags(7, 1, 1);
36
37// Flag: This record is a continuation.
38constexpr uint8_t RecContinuation = GOFF::Flags(6, 1, 1);
39
40// The GOFFOstream is responsible to write the data into the fixed physical
41// records of the format. A user of this class announces the begin of a new
42// logical record. While writing the payload, the physical records are created
43// for the data. Possible fill bytes at the end of a physical record are written
44// automatically. In principle, the GOFFOstream is agnostic of the endianness of
45// the payload. However, it also supports writing data in big endian byte order.
46//
47// The physical records use the flag field to indicate if the there is a
48// successor and predecessor record. To be able to set these flags while
49// writing, the basic implementation idea is to always buffer the last seen
50// physical record.
51class GOFFOstream {
52 /// The underlying raw_pwrite_stream.
53 raw_pwrite_stream &OS;
54
55 /// The number of logical records emitted so far.
56 uint32_t LogicalRecords = 0;
57
58 /// The number of physical records emitted so far.
59 uint32_t PhysicalRecords = 0;
60
61 /// The size of the buffer. Same as the payload size of a physical record.
62 static constexpr uint8_t BufferSize = GOFF::PayloadLength;
63
64 /// Current position in buffer.
65 char *BufferPtr = Buffer;
66
67 /// Static allocated buffer for the stream.
68 char Buffer[BufferSize];
69
70 /// The type of the current logical record, and the flags (aka continued and
71 /// continuation indicators) for the previous (physical) record.
72 uint8_t TypeAndFlags = 0;
73
74public:
75 GOFFOstream(raw_pwrite_stream &OS);
76 ~GOFFOstream();
77
78 raw_pwrite_stream &getOS() { return OS; }
79 size_t getWrittenSize() const { return PhysicalRecords * GOFF::RecordLength; }
80 uint32_t getNumLogicalRecords() { return LogicalRecords; }
81
82 /// Write the specified bytes.
83 void write(const char *Ptr, size_t Size);
84
85 /// Write zeroes, up to a maximum of 16 bytes.
86 void write_zeros(unsigned NumZeros);
87
88 /// Support for endian-specific data.
89 template <typename value_type> void writebe(value_type Value) {
90 Value =
91 support::endian::byte_swap<value_type>(Value, llvm::endianness::big);
92 write(Ptr: (const char *)&Value, Size: sizeof(value_type));
93 }
94
95 /// Begin a new logical record. Implies finalizing the previous record.
96 void newRecord(GOFF::RecordType Type);
97
98 /// Ends a logical record.
99 void finalizeRecord();
100
101private:
102 /// Updates the continued/continuation flags, and writes the record prefix of
103 /// a physical record.
104 void updateFlagsAndWritePrefix(bool IsContinued);
105
106 /// Returns the remaining size in the buffer.
107 size_t getRemainingSize();
108};
109} // namespace
110
111GOFFOstream::GOFFOstream(raw_pwrite_stream &OS) : OS(OS) {}
112
113GOFFOstream::~GOFFOstream() { finalizeRecord(); }
114
115void GOFFOstream::updateFlagsAndWritePrefix(bool IsContinued) {
116 // Update the flags based on the previous state and the flag IsContinued.
117 if (TypeAndFlags & RecContinued)
118 TypeAndFlags |= RecContinuation;
119 if (IsContinued)
120 TypeAndFlags |= RecContinued;
121 else
122 TypeAndFlags &= ~RecContinued;
123
124 OS << static_cast<unsigned char>(GOFF::PTVPrefix) // Record Type
125 << static_cast<unsigned char>(TypeAndFlags) // Continuation
126 << static_cast<unsigned char>(0); // Version
127
128 ++PhysicalRecords;
129}
130
131size_t GOFFOstream::getRemainingSize() {
132 return size_t(&Buffer[BufferSize] - BufferPtr);
133}
134
135void GOFFOstream::write(const char *Ptr, size_t Size) {
136 size_t RemainingSize = getRemainingSize();
137
138 // Data fits into the buffer.
139 if (LLVM_LIKELY(Size <= RemainingSize)) {
140 memcpy(dest: BufferPtr, src: Ptr, n: Size);
141 BufferPtr += Size;
142 return;
143 }
144
145 // Otherwise the buffer is partially filled or full, and data does not fit
146 // into it.
147 updateFlagsAndWritePrefix(/*IsContinued=*/true);
148 OS.write(Ptr: Buffer, Size: size_t(BufferPtr - Buffer));
149 if (RemainingSize > 0) {
150 OS.write(Ptr, Size: RemainingSize);
151 Ptr += RemainingSize;
152 Size -= RemainingSize;
153 }
154
155 while (Size > BufferSize) {
156 updateFlagsAndWritePrefix(/*IsContinued=*/true);
157 OS.write(Ptr, Size: BufferSize);
158 Ptr += BufferSize;
159 Size -= BufferSize;
160 }
161
162 // The remaining bytes fit into the buffer.
163 memcpy(dest: Buffer, src: Ptr, n: Size);
164 BufferPtr = &Buffer[Size];
165}
166
167void GOFFOstream::write_zeros(unsigned NumZeros) {
168 assert(NumZeros <= 16 && "Range for zeros too large");
169
170 // Handle the common case first: all fits in the buffer.
171 size_t RemainingSize = getRemainingSize();
172 if (LLVM_LIKELY(RemainingSize >= NumZeros)) {
173 memset(s: BufferPtr, c: 0, n: NumZeros);
174 BufferPtr += NumZeros;
175 return;
176 }
177
178 // Otherwise some field value is cleared.
179 static char Zeros[16] = {
180 0,
181 };
182 write(Ptr: Zeros, Size: NumZeros);
183}
184
185void GOFFOstream::newRecord(GOFF::RecordType Type) {
186 finalizeRecord();
187 TypeAndFlags = Type << 4;
188 ++LogicalRecords;
189}
190
191void GOFFOstream::finalizeRecord() {
192 if (Buffer == BufferPtr)
193 return;
194 updateFlagsAndWritePrefix(/*IsContinued=*/false);
195 OS.write(Ptr: Buffer, Size: size_t(BufferPtr - Buffer));
196 OS.write_zeros(NumZeros: getRemainingSize());
197 BufferPtr = Buffer;
198}
199
200namespace {
201// A GOFFSymbol holds all the data required for writing an ESD record.
202class GOFFSymbol {
203public:
204 std::string Name;
205 uint32_t EsdId;
206 uint32_t ParentEsdId;
207 uint64_t Offset = 0; // Offset of the symbol into the section. LD only.
208 // Offset is only 32 bit, the larger type is used to
209 // enable error checking.
210 GOFF::ESDSymbolType SymbolType;
211 GOFF::ESDNameSpaceId NameSpace = GOFF::ESD_NS_ProgramManagementBinder;
212
213 GOFF::BehavioralAttributes BehavAttrs;
214 GOFF::SymbolFlags SymbolFlags;
215 uint32_t SortKey = 0;
216 uint32_t SectionLength = 0;
217 uint32_t ADAEsdId = 0;
218 uint32_t EASectionEDEsdId = 0;
219 uint32_t EASectionOffset = 0;
220 uint8_t FillByteValue = 0;
221
222 GOFFSymbol() : EsdId(0), ParentEsdId(0) {}
223
224 GOFFSymbol(StringRef Name, uint32_t EsdID, const GOFF::SDAttr &Attr)
225 : Name(Name.data(), Name.size()), EsdId(EsdID), ParentEsdId(0),
226 SymbolType(GOFF::ESD_ST_SectionDefinition) {
227 BehavAttrs.setTaskingBehavior(Attr.TaskingBehavior);
228 BehavAttrs.setBindingScope(Attr.BindingScope);
229 }
230
231 GOFFSymbol(StringRef Name, uint32_t EsdID, uint32_t ParentEsdID,
232 const GOFF::EDAttr &Attr, GOFF::ESDAlignment Alignment)
233 : Name(Name.data(), Name.size()), EsdId(EsdID), ParentEsdId(ParentEsdID),
234 SymbolType(GOFF::ESD_ST_ElementDefinition) {
235 this->NameSpace = Attr.NameSpace;
236 // We always set a fill byte value.
237 this->FillByteValue = Attr.FillByteValue;
238 SymbolFlags.setFillBytePresence(1);
239 SymbolFlags.setReservedQwords(Attr.ReservedQwords);
240 // TODO Do we need/should set the "mangled" flag?
241 BehavAttrs.setReadOnly(Attr.IsReadOnly);
242 BehavAttrs.setRmode(Attr.Rmode);
243 BehavAttrs.setTextStyle(Attr.TextStyle);
244 BehavAttrs.setBindingAlgorithm(Attr.BindAlgorithm);
245 BehavAttrs.setLoadingBehavior(Attr.LoadBehavior);
246 BehavAttrs.setAlignment(Alignment);
247 }
248
249 GOFFSymbol(StringRef Name, uint32_t EsdID, uint32_t ParentEsdID,
250 GOFF::ESDNameSpaceId NameSpace, const GOFF::LDAttr &Attr)
251 : Name(Name.data(), Name.size()), EsdId(EsdID), ParentEsdId(ParentEsdID),
252 SymbolType(GOFF::ESD_ST_LabelDefinition), NameSpace(NameSpace) {
253 SymbolFlags.setRenameable(Attr.IsRenamable);
254 BehavAttrs.setExecutable(Attr.Executable);
255 BehavAttrs.setBindingStrength(Attr.BindingStrength);
256 BehavAttrs.setLinkageType(Attr.Linkage);
257 BehavAttrs.setAmode(Attr.Amode);
258 BehavAttrs.setBindingScope(Attr.BindingScope);
259 }
260
261 GOFFSymbol(StringRef Name, uint32_t EsdID, uint32_t ParentEsdID,
262 const GOFF::EDAttr &EDAttr, GOFF::ESDAlignment Alignment,
263 const GOFF::PRAttr &Attr)
264 : Name(Name.data(), Name.size()), EsdId(EsdID), ParentEsdId(ParentEsdID),
265 SymbolType(GOFF::ESD_ST_PartReference), NameSpace(EDAttr.NameSpace) {
266 SymbolFlags.setRenameable(Attr.IsRenamable);
267 BehavAttrs.setExecutable(Attr.Executable);
268 BehavAttrs.setBindingStrength(Attr.BindingStrength);
269 BehavAttrs.setLinkageType(Attr.Linkage);
270 BehavAttrs.setBindingScope(Attr.BindingScope);
271 BehavAttrs.setAlignment(Alignment);
272 }
273
274 GOFFSymbol(StringRef Name, uint32_t EsdID, uint32_t ParentEsdID,
275 const GOFF::ERAttr &Attr)
276 : Name(Name.data(), Name.size()), EsdId(EsdID), ParentEsdId(ParentEsdID),
277 SymbolType(GOFF::ESD_ST_ExternalReference),
278 NameSpace(GOFF::ESD_NS_NormalName) {
279 BehavAttrs.setExecutable(Attr.Executable);
280 BehavAttrs.setBindingStrength(Attr.BindingStrength);
281 BehavAttrs.setLinkageType(Attr.Linkage);
282 BehavAttrs.setAmode(Attr.Amode);
283 BehavAttrs.setBindingScope(Attr.BindingScope);
284 BehavAttrs.setIndirectReference(Attr.IsIndirectReference);
285 }
286};
287
288class GOFFWriter {
289 GOFFOstream OS;
290 MCAssembler &Asm;
291 MCSectionGOFF *RootSD;
292
293 /// Saved relocation data collected in recordRelocations().
294 std::vector<GOFFRelocationEntry> &Relocations;
295
296public:
297 enum DwoMode {
298 AllSections,
299 NonDwoOnly,
300 DwoOnly,
301 };
302
303private:
304 const DwoMode Mode;
305
306 void writeHeader();
307 void writeSymbol(const GOFFSymbol &Symbol);
308 void writeText(const MCSectionGOFF *MC);
309 void writeRelocations();
310 void writeEnd();
311
312 void defineSectionSymbols(const MCSectionGOFF &Section);
313 void defineLabel(const MCSymbolGOFF &Symbol);
314 void defineExtern(const MCSymbolGOFF &Symbol);
315 void defineSymbols();
316
317public:
318 GOFFWriter(raw_pwrite_stream &OS, MCAssembler &Asm, MCSectionGOFF *RootSD,
319 std::vector<GOFFRelocationEntry> &Relocations, DwoMode Mode);
320 uint64_t writeObject();
321};
322} // namespace
323
324GOFFWriter::GOFFWriter(raw_pwrite_stream &OS, MCAssembler &Asm,
325 MCSectionGOFF *RootSD,
326 std::vector<GOFFRelocationEntry> &Relocations,
327 DwoMode Mode)
328 : OS(OS), Asm(Asm), RootSD(RootSD), Relocations(Relocations), Mode(Mode) {}
329
330namespace {
331bool isDwoSection(const MCSection &Section) {
332 StringRef Name = Section.getName();
333 return (Name.ends_with(Suffix: ".dwo") && Name.starts_with(Prefix: ".debug_")) ||
334 (Name.ends_with(Suffix: "_DWO") && Name.starts_with(Prefix: "D_"));
335}
336
337bool isSectionToSkip(const MCSection &Section, GOFFWriter::DwoMode Mode) {
338 if (Mode == GOFFWriter::DwoOnly) {
339 if (!isDwoSection(Section))
340 return true;
341 } else if (Mode == GOFFWriter::NonDwoOnly) {
342 if (isDwoSection(Section))
343 return true;
344 }
345 return false;
346}
347} // namespace
348
349void GOFFWriter::defineSectionSymbols(const MCSectionGOFF &Section) {
350 if (isSectionToSkip(Section, Mode))
351 return;
352
353 if (Section.isSD()) {
354 GOFFSymbol SD(Section.getExternalName(), Section.getOrdinal(),
355 Section.getSDAttributes());
356 writeSymbol(Symbol: SD);
357 }
358
359 if (Section.isED()) {
360 GOFFSymbol ED(Section.getExternalName(), Section.getOrdinal(),
361 Section.getParent()->getOrdinal(), Section.getEDAttributes(),
362 Section.getEDAlignment());
363 ED.SectionLength = Asm.getSectionAddressSize(Sec: Section);
364 writeSymbol(Symbol: ED);
365 }
366
367 if (Section.isPR()) {
368 MCSectionGOFF *Parent = Section.getParent();
369 GOFFSymbol PR(Section.getExternalName(), Section.getOrdinal(),
370 Parent->getOrdinal(), Parent->getEDAttributes(),
371 Parent->getEDAlignment(), Section.getPRAttributes());
372 PR.SectionLength = Asm.getSectionAddressSize(Sec: Section);
373 if (Section.requiresNonZeroLength()) {
374 // We cannot have a zero-length section for data. If we do,
375 // artificially inflate it. Use 2 bytes to avoid odd alignments. Note:
376 // if this is ever changed, you will need to update the code in
377 // SystemZAsmPrinter::emitCEEMAIN and SystemZAsmPrinter::emitCELQMAIN to
378 // generate -1 if there is no ADA
379 if (!PR.SectionLength)
380 PR.SectionLength = 2;
381 }
382 writeSymbol(Symbol: PR);
383 }
384}
385
386void GOFFWriter::defineLabel(const MCSymbolGOFF &Symbol) {
387 MCSectionGOFF &Section = static_cast<MCSectionGOFF &>(Symbol.getSection());
388 GOFFSymbol LD(Symbol.getExternalName(), Symbol.getIndex(),
389 Section.getOrdinal(), Section.getEDAttributes().NameSpace,
390 GOFF::LDAttr{.IsRenamable: false, .Executable: Symbol.getCodeData(),
391 .BindingStrength: Symbol.getBindingStrength(), .Linkage: Symbol.getLinkage(),
392 .Amode: GOFF::ESD_AMODE_64, .BindingScope: Symbol.getBindingScope()});
393 if (Symbol.getADA())
394 LD.ADAEsdId = Symbol.getADA()->getOrdinal();
395 LD.Offset = Asm.getSymbolOffset(S: Symbol);
396 writeSymbol(Symbol: LD);
397}
398
399void GOFFWriter::defineExtern(const MCSymbolGOFF &Symbol) {
400 if (Symbol.getCodeData() == GOFF::ESD_EXE_DATA) {
401 MCSectionGOFF *ED = Symbol.getADA()->getParent();
402 GOFFSymbol PR(Symbol.getExternalName(), Symbol.getIndex(), ED->getOrdinal(),
403 ED->getEDAttributes(), ED->getEDAlignment(),
404 GOFF::PRAttr{/*IsRenamable*/ false, .Executable: Symbol.getCodeData(),
405 .BindingStrength: Symbol.getBindingStrength(), .Linkage: Symbol.getLinkage(),
406 .BindingScope: Symbol.getBindingScope(), .SortKey: 0});
407 writeSymbol(Symbol: PR);
408 } else {
409 GOFFSymbol ER(Symbol.getExternalName(), Symbol.getIndex(),
410 RootSD->getOrdinal(),
411 GOFF::ERAttr{.IsIndirectReference: Symbol.isIndirect(), .Executable: Symbol.getCodeData(),
412 .BindingStrength: Symbol.getBindingStrength(), .Linkage: Symbol.getLinkage(),
413 .Amode: GOFF::ESD_AMODE_64, .BindingScope: Symbol.getBindingScope()});
414 writeSymbol(Symbol: ER);
415 }
416}
417
418void GOFFWriter::defineSymbols() {
419 unsigned Ordinal = 0;
420 // Process all sections.
421 for (MCSection &S : Asm) {
422 auto &Section = static_cast<MCSectionGOFF &>(S);
423 Section.setOrdinal(++Ordinal);
424 defineSectionSymbols(Section);
425 }
426
427 // Process all symbols
428 for (const MCSymbol &Sym : Asm.symbols()) {
429 if (Sym.isTemporary())
430 continue;
431 auto &Symbol = static_cast<const MCSymbolGOFF &>(Sym);
432 if (!Symbol.isDefined()) {
433 if (Mode != DwoOnly) {
434 Symbol.setIndex(++Ordinal);
435 defineExtern(Symbol);
436 }
437 } else {
438 if (isSectionToSkip(Section: Symbol.getSection(), Mode))
439 continue;
440 if (Symbol.isInEDSection()) {
441 Symbol.setIndex(++Ordinal);
442 defineLabel(Symbol);
443 } else {
444 // Symbol is in PR section, the symbol refers to the section.
445 Symbol.setIndex(Symbol.getSection().getOrdinal());
446 }
447 }
448 }
449}
450
451void GOFFWriter::writeHeader() {
452 OS.newRecord(Type: GOFF::RT_HDR);
453 OS.write_zeros(NumZeros: 1); // Reserved
454 OS.writebe<uint32_t>(Value: 0); // Target Hardware Environment
455 OS.writebe<uint32_t>(Value: 0); // Target Operating System Environment
456 OS.write_zeros(NumZeros: 2); // Reserved
457 OS.writebe<uint16_t>(Value: 0); // CCSID
458 OS.write_zeros(NumZeros: 16); // Character Set name
459 OS.write_zeros(NumZeros: 16); // Language Product Identifier
460 OS.writebe<uint32_t>(Value: 1); // Architecture Level
461 OS.writebe<uint16_t>(Value: 0); // Module Properties Length
462 OS.write_zeros(NumZeros: 6); // Reserved
463}
464
465void GOFFWriter::writeSymbol(const GOFFSymbol &Symbol) {
466 if (Symbol.Offset >= (((uint64_t)1) << 31))
467 report_fatal_error(reason: "ESD offset out of range");
468
469 // All symbol names are in EBCDIC.
470 SmallString<256> Name;
471 ConverterEBCDIC::convertToEBCDIC(Source: Symbol.Name, Result&: Name);
472
473 // Check length here since this number is technically signed but we need uint
474 // for writing to records.
475 if (Name.size() >= GOFF::MaxDataLength)
476 report_fatal_error(reason: "Symbol max name length exceeded");
477 uint16_t NameLength = Name.size();
478
479 OS.newRecord(Type: GOFF::RT_ESD);
480 OS.writebe<uint8_t>(Value: Symbol.SymbolType); // Symbol Type
481 OS.writebe<uint32_t>(Value: Symbol.EsdId); // ESDID
482 OS.writebe<uint32_t>(Value: Symbol.ParentEsdId); // Parent or Owning ESDID
483 OS.writebe<uint32_t>(Value: 0); // Reserved
484 OS.writebe<uint32_t>(
485 Value: static_cast<uint32_t>(Symbol.Offset)); // Offset or Address
486 OS.writebe<uint32_t>(Value: 0); // Reserved
487 OS.writebe<uint32_t>(Value: Symbol.SectionLength); // Length
488 OS.writebe<uint32_t>(Value: Symbol.EASectionEDEsdId); // Extended Attribute ESDID
489 OS.writebe<uint32_t>(Value: Symbol.EASectionOffset); // Extended Attribute Offset
490 OS.writebe<uint32_t>(Value: 0); // Reserved
491 OS.writebe<uint8_t>(Value: Symbol.NameSpace); // Name Space ID
492 OS.writebe<uint8_t>(Value: Symbol.SymbolFlags); // Flags
493 OS.writebe<uint8_t>(Value: Symbol.FillByteValue); // Fill-Byte Value
494 OS.writebe<uint8_t>(Value: 0); // Reserved
495 OS.writebe<uint32_t>(Value: Symbol.ADAEsdId); // ADA ESDID
496 OS.writebe<uint32_t>(Value: Symbol.SortKey); // Sort Priority
497 OS.writebe<uint64_t>(Value: 0); // Reserved
498 for (auto F : Symbol.BehavAttrs.Attr)
499 OS.writebe<uint8_t>(Value: F); // Behavioral Attributes
500 OS.writebe<uint16_t>(Value: NameLength); // Name Length
501 OS.write(Ptr: Name.data(), Size: NameLength); // Name
502}
503
504namespace {
505/// Adapter stream to write a text section.
506class TextStream : public raw_ostream {
507 /// The underlying GOFFOstream.
508 GOFFOstream &OS;
509
510 /// The buffer size is the maximum number of bytes in a TXT section.
511 static constexpr size_t BufferSize = GOFF::MaxDataLength;
512
513 /// Static allocated buffer for the stream, used by the raw_ostream class. The
514 /// buffer is sized to hold the payload of a logical TXT record.
515 char Buffer[BufferSize];
516
517 /// The offset for the next TXT record. This is equal to the number of bytes
518 /// written.
519 size_t Offset;
520
521 /// The Esdid of the GOFF section.
522 const uint32_t EsdId;
523
524 /// The record style.
525 const GOFF::ESDTextStyle RecordStyle;
526
527 /// See raw_ostream::write_impl.
528 void write_impl(const char *Ptr, size_t Size) override;
529
530 uint64_t current_pos() const override { return Offset; }
531
532public:
533 explicit TextStream(GOFFOstream &OS, uint32_t EsdId,
534 GOFF::ESDTextStyle RecordStyle)
535 : OS(OS), Offset(0), EsdId(EsdId), RecordStyle(RecordStyle) {
536 SetBuffer(BufferStart: Buffer, Size: sizeof(Buffer));
537 }
538
539 ~TextStream() override { flush(); }
540};
541} // namespace
542
543void TextStream::write_impl(const char *Ptr, size_t Size) {
544 size_t WrittenLength = 0;
545
546 // We only have signed 32bits of offset.
547 if (Offset + Size > std::numeric_limits<int32_t>::max())
548 report_fatal_error(reason: "TXT section too large");
549
550 while (WrittenLength < Size) {
551 size_t ToWriteLength =
552 std::min(a: Size - WrittenLength, b: size_t(GOFF::MaxDataLength));
553
554 OS.newRecord(Type: GOFF::RT_TXT);
555 OS.writebe<uint8_t>(Value: GOFF::Flags(4, 4, RecordStyle)); // Text Record Style
556 OS.writebe<uint32_t>(Value: EsdId); // Element ESDID
557 OS.writebe<uint32_t>(Value: 0); // Reserved
558 OS.writebe<uint32_t>(Value: static_cast<uint32_t>(Offset)); // Offset
559 OS.writebe<uint32_t>(Value: 0); // Text Field True Length
560 OS.writebe<uint16_t>(Value: 0); // Text Encoding
561 OS.writebe<uint16_t>(Value: ToWriteLength); // Data Length
562 OS.write(Ptr: Ptr + WrittenLength, Size: ToWriteLength); // Data
563
564 WrittenLength += ToWriteLength;
565 Offset += ToWriteLength;
566 }
567}
568
569void GOFFWriter::writeText(const MCSectionGOFF *Section) {
570 // A BSS section contains only zeros, no need to write this.
571 if (Section->isBSS())
572 return;
573
574 TextStream S(OS, Section->getOrdinal(), Section->getTextStyle());
575 Asm.writeSectionData(OS&: S, Section);
576}
577
578namespace {
579// RelocDataItemBuffer provides a static buffer for relocation data items.
580class RelocDataItemBuffer {
581 char Buffer[GOFF::MaxDataLength];
582 char *Ptr;
583
584public:
585 RelocDataItemBuffer() : Ptr(Buffer) {}
586 const char *data() { return Buffer; }
587 size_t size() { return Ptr - Buffer; }
588 void reset() { Ptr = Buffer; }
589 bool fits(size_t S) { return size() + S < GOFF::MaxDataLength; }
590 template <typename T> void writebe(T Val) {
591 assert(fits(sizeof(T)) && "Out-of-bounds write");
592 support::endian::write<T, llvm::endianness::big>(Ptr, Val);
593 Ptr += sizeof(T);
594 }
595};
596} // namespace
597
598void GOFFWriter::writeRelocations() {
599 // Set the IDs in the relocation entries.
600 for (auto &RelocEntry : Relocations) {
601 auto GetRptr = [](const MCSymbolGOFF *Sym) -> uint32_t {
602 if (Sym->isTemporary())
603 return static_cast<MCSectionGOFF &>(Sym->getSection()).getOrdinal();
604 return Sym->getIndex();
605 };
606
607 RelocEntry.PEsdId = RelocEntry.Pptr->getOrdinal();
608 RelocEntry.REsdId = GetRptr(RelocEntry.Rptr);
609 }
610
611 // Sort relocation data items by the P pointer to save space.
612 std::sort(
613 first: Relocations.begin(), last: Relocations.end(),
614 comp: [](const GOFFRelocationEntry &Left, const GOFFRelocationEntry &Right) {
615 return std::tie(args: Left.PEsdId, args: Left.REsdId, args: Left.POffset) <
616 std::tie(args: Right.PEsdId, args: Right.REsdId, args: Right.POffset);
617 });
618
619 // Construct the compressed relocation data items, and write them out.
620 RelocDataItemBuffer Buffer;
621 for (auto I = Relocations.begin(), E = Relocations.end(); I != E;) {
622 Buffer.reset();
623
624 uint32_t PrevResdId = -1;
625 uint32_t PrevPesdId = -1;
626 uint64_t PrevPOffset = -1;
627 for (; I != E; ++I) {
628 const GOFFRelocationEntry &Rel = *I;
629
630 bool SameREsdId = (Rel.REsdId == PrevResdId);
631 bool SamePEsdId = (Rel.PEsdId == PrevPesdId);
632 bool SamePOffset = (Rel.POffset == PrevPOffset);
633 bool EightByteOffset = ((Rel.POffset >> 32) & 0xffffffff);
634
635 // Calculate size of relocation data item, and check if it still fits into
636 // the record.
637 size_t ItemSize = 8; // Smallest size of a relocation data item.
638 if (!SameREsdId)
639 ItemSize += 4;
640 if (!SamePEsdId)
641 ItemSize += 4;
642 if (!SamePOffset)
643 ItemSize += (EightByteOffset ? 8 : 4);
644 if (!Buffer.fits(S: ItemSize))
645 break;
646
647 GOFF::Flags RelocFlags[6];
648 RelocFlags[0].set(BitIndex: 0, Length: 1, NewValue: SameREsdId);
649 RelocFlags[0].set(BitIndex: 1, Length: 1, NewValue: SamePEsdId);
650 RelocFlags[0].set(BitIndex: 2, Length: 1, NewValue: SamePOffset);
651 RelocFlags[0].set(BitIndex: 6, Length: 1, NewValue: EightByteOffset);
652
653 RelocFlags[1].set(BitIndex: 0, Length: 4, NewValue: Rel.ReferenceType);
654 RelocFlags[1].set(BitIndex: 4, Length: 4, NewValue: Rel.ReferentType);
655
656 RelocFlags[2].set(BitIndex: 0, Length: 7, NewValue: Rel.Action);
657 RelocFlags[2].set(BitIndex: 7, Length: 1, NewValue: Rel.FetchStore);
658
659 RelocFlags[4].set(BitIndex: 0, Length: 8, NewValue: Rel.TargetLength);
660
661 for (auto F : RelocFlags)
662 Buffer.writebe<uint8_t>(Val: F);
663 Buffer.writebe<uint16_t>(Val: 0); // Reserved.
664 if (!SameREsdId)
665 Buffer.writebe<uint32_t>(Val: Rel.REsdId);
666 if (!SamePEsdId)
667 Buffer.writebe<uint32_t>(Val: Rel.PEsdId);
668 if (!SamePOffset) {
669 if (EightByteOffset)
670 Buffer.writebe<uint64_t>(Val: Rel.POffset);
671 else
672 Buffer.writebe<uint32_t>(Val: Rel.POffset);
673 }
674
675 PrevResdId = Rel.REsdId;
676 PrevPesdId = Rel.PEsdId;
677 PrevPOffset = Rel.POffset;
678 }
679
680 OS.newRecord(Type: GOFF::RT_RLD);
681 OS.writebe<uint8_t>(Value: 0); // Reserved.
682 OS.writebe<uint16_t>(Value: Buffer.size()); // Length (of the relocation data).
683 OS.write(Ptr: Buffer.data(), Size: Buffer.size()); // Relocation Directory Data Items.
684 }
685}
686
687void GOFFWriter::writeEnd() {
688 uint8_t F = GOFF::END_EPR_None;
689 uint8_t AMODE = 0;
690 uint32_t ESDID = 0;
691
692 // TODO Set Flags/AMODE/ESDID for entry point.
693
694 OS.newRecord(Type: GOFF::RT_END);
695 OS.writebe<uint8_t>(Value: GOFF::Flags(6, 2, F)); // Indicator flags
696 OS.writebe<uint8_t>(Value: AMODE); // AMODE
697 OS.write_zeros(NumZeros: 3); // Reserved
698 // The record count is the number of logical records. In principle, this value
699 // is available as OS.logicalRecords(). However, some tools rely on this field
700 // being zero.
701 OS.writebe<uint32_t>(Value: 0); // Record Count
702 OS.writebe<uint32_t>(Value: ESDID); // ESDID (of entry point)
703}
704
705uint64_t GOFFWriter::writeObject() {
706 writeHeader();
707
708 defineSymbols();
709
710 for (const MCSection &Section : Asm)
711 writeText(Section: static_cast<const MCSectionGOFF *>(&Section));
712
713 // Do not write relocations into the dwo file.
714 if (Mode != GOFFWriter::DwoOnly)
715 writeRelocations();
716
717 writeEnd();
718
719 // Make sure all records are written.
720 OS.finalizeRecord();
721
722 LLVM_DEBUG(dbgs() << "Wrote " << OS.getNumLogicalRecords()
723 << " logical records.");
724
725 return OS.getWrittenSize();
726}
727
728GOFFObjectWriter::GOFFObjectWriter(
729 std::unique_ptr<MCGOFFObjectTargetWriter> MOTW, raw_pwrite_stream &OS)
730 : TargetObjectWriter(std::move(MOTW)), OS(OS) {}
731
732GOFFObjectWriter::GOFFObjectWriter(
733 std::unique_ptr<MCGOFFObjectTargetWriter> MOTW, raw_pwrite_stream &OS,
734 raw_pwrite_stream &DwoOS)
735 : TargetObjectWriter(std::move(MOTW)), OS(OS), DwoOS(&DwoOS) {}
736
737GOFFObjectWriter::~GOFFObjectWriter() = default;
738
739void GOFFObjectWriter::reset() {
740 Relocations.clear();
741 RootSD = nullptr;
742 MCObjectWriter::reset();
743}
744
745void GOFFObjectWriter::recordRelocation(const MCFragment &F,
746 const MCFixup &Fixup, MCValue Target,
747 uint64_t &FixedValue) {
748 const MCFixupKindInfo &FKI =
749 Asm->getBackend().getFixupKindInfo(Kind: Fixup.getKind());
750 const uint32_t Length = FKI.TargetSize / 8;
751 assert(FKI.TargetSize % 8 == 0 && "Target Size not multiple of 8");
752 const uint64_t FixupOffset = Asm->getFragmentOffset(F) + Fixup.getOffset();
753
754 unsigned RelocType = TargetObjectWriter->getRelocType(Target, Fixup);
755
756 const MCSectionGOFF *PSection = static_cast<MCSectionGOFF *>(F.getParent());
757 const auto &A = *static_cast<const MCSymbolGOFF *>(Target.getAddSym());
758 const MCSymbolGOFF *B = static_cast<const MCSymbolGOFF *>(Target.getSubSym());
759 if (RelocType == MCGOFFObjectTargetWriter::Reloc_Type_RICon) {
760 if (A.isUndefined()) {
761 Asm->reportError(
762 L: Fixup.getLoc(),
763 Msg: Twine("symbol ")
764 .concat(Suffix: A.getExternalName())
765 .concat(Suffix: " must be defined for a relative immediate relocation"));
766 return;
767 }
768 if (&A.getSection() != PSection) {
769 MCSectionGOFF &GOFFSection = static_cast<MCSectionGOFF &>(A.getSection());
770 Asm->reportError(L: Fixup.getLoc(),
771 Msg: Twine("relative immediate relocation section mismatch: ")
772 .concat(Suffix: GOFFSection.getExternalName())
773 .concat(Suffix: " of symbol ")
774 .concat(Suffix: A.getExternalName())
775 .concat(Suffix: " <-> ")
776 .concat(Suffix: PSection->getExternalName()));
777 return;
778 }
779 if (B) {
780 Asm->reportError(
781 L: Fixup.getLoc(),
782 Msg: Twine("subtractive symbol ")
783 .concat(Suffix: B->getExternalName())
784 .concat(Suffix: " not supported for a relative immediate relocation"));
785 return;
786 }
787 FixedValue = Asm->getSymbolOffset(S: A) - FixupOffset + Target.getConstant();
788 return;
789 }
790 FixedValue = Target.getConstant();
791
792 // The symbol only has a section-relative offset if it is a temporary symbol.
793 FixedValue += A.isTemporary() ? Asm->getSymbolOffset(S: A) : 0;
794 A.setUsedInReloc();
795 if (B) {
796 FixedValue -= B->isTemporary() ? Asm->getSymbolOffset(S: *B) : 0;
797 B->setUsedInReloc();
798 }
799
800 // UseQCon causes class offsets versus absolute addresses to be used. This
801 // is analogous to using QCONs in older OBJ object file format.
802 bool UseQCon = RelocType == MCGOFFObjectTargetWriter::Reloc_Type_QCon;
803
804 GOFF::RLDFetchStore FetchStore =
805 (RelocType == MCGOFFObjectTargetWriter::Reloc_Type_RCon ||
806 RelocType == MCGOFFObjectTargetWriter::Reloc_Type_VCon)
807 ? GOFF::RLDFetchStore::RLD_FS_Store
808 : GOFF::RLDFetchStore::RLD_FS_Fetch;
809 assert((FetchStore == GOFF::RLDFetchStore::RLD_FS_Fetch || B == nullptr) &&
810 "No dependent relocations expected");
811
812 enum GOFF::RLDReferenceType ReferenceType = GOFF::RLD_RT_RAddress;
813 enum GOFF::RLDReferentType ReferentType = GOFF::RLD_RO_Label;
814 if (UseQCon) {
815 ReferenceType = GOFF::RLD_RT_ROffset;
816 ReferentType = GOFF::RLD_RO_Class;
817 }
818 if (RelocType == MCGOFFObjectTargetWriter::Reloc_Type_RCon)
819 ReferenceType = GOFF::RLD_RT_RTypeConstant;
820
821 auto DumpReloc = [&PSection, &ReferenceType, &FixupOffset,
822 &FixedValue](const char *N, const MCSymbolGOFF *Sym) {
823 const char *Con;
824 switch (ReferenceType) {
825 case GOFF::RLDReferenceType::RLD_RT_RAddress:
826 Con = "ACon";
827 break;
828 case GOFF::RLDReferenceType::RLD_RT_ROffset:
829 Con = "QCon";
830 break;
831 case GOFF::RLDReferenceType::RLD_RT_RTypeConstant:
832 Con = "VCon";
833 break;
834 default:
835 Con = "(unknown)";
836 }
837 dbgs() << "Reloc " << N << ": " << Con
838 << " Rptr: " << Sym->getExternalName()
839 << " Pptr: " << PSection->getExternalName()
840 << " Offset: " << FixupOffset << " Fixed Imm: " << FixedValue
841 << "\n";
842 };
843 (void)DumpReloc;
844
845 // Save relocation data for later writing.
846 LLVM_DEBUG(DumpReloc("A", &A));
847 Relocations.emplace_back(args&: PSection, args: &A, args&: ReferenceType, args&: ReferentType,
848 args: GOFF::RLD_ACT_Add, args&: FetchStore, args: FixupOffset, args: Length);
849 if (B) {
850 LLVM_DEBUG(DumpReloc("B", B));
851 Relocations.emplace_back(
852 args&: PSection, args&: B, args&: ReferenceType, args&: ReferentType, args: GOFF::RLD_ACT_Subtract,
853 args: GOFF::RLDFetchStore::RLD_FS_Fetch, args: FixupOffset, args: Length);
854 }
855}
856
857uint64_t GOFFObjectWriter::writeObject() {
858 uint64_t Size = 0;
859 if (DwoOS)
860 Size += GOFFWriter(*DwoOS, *Asm, RootSD, Relocations, GOFFWriter::DwoOnly)
861 .writeObject();
862 Size += GOFFWriter(OS, *Asm, RootSD, Relocations,
863 DwoOS ? GOFFWriter::NonDwoOnly : GOFFWriter::AllSections)
864 .writeObject();
865 return Size;
866}
867
868std::unique_ptr<MCObjectWriter>
869llvm::createGOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW,
870 raw_pwrite_stream &OS) {
871 return std::make_unique<GOFFObjectWriter>(args: std::move(MOTW), args&: OS);
872}
873
874std::unique_ptr<MCObjectWriter>
875llvm::createGOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW,
876 raw_pwrite_stream &OS, raw_pwrite_stream &DwoOS) {
877 return std::make_unique<GOFFObjectWriter>(args: std::move(MOTW), args&: OS, args&: DwoOS);
878}
879