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/MCSectionGOFF.h"
19#include "llvm/MC/MCSymbolGOFF.h"
20#include "llvm/MC/MCValue.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
26using namespace llvm;
27
28#define DEBUG_TYPE "goff-writer"
29
30namespace {
31// Common flag values on records.
32
33// Flag: This record is continued.
34constexpr uint8_t RecContinued = GOFF::Flags(7, 1, 1);
35
36// Flag: This record is a continuation.
37constexpr 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.
50class 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
73public:
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
100private:
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
110GOFFOstream::GOFFOstream(raw_pwrite_stream &OS) : OS(OS) {}
111
112GOFFOstream::~GOFFOstream() { finalizeRecord(); }
113
114void 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
130size_t GOFFOstream::getRemainingSize() {
131 return size_t(&Buffer[BufferSize] - BufferPtr);
132}
133
134void 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
166void 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
184void GOFFOstream::newRecord(GOFF::RecordType Type) {
185 finalizeRecord();
186 TypeAndFlags = Type << 4;
187 ++LogicalRecords;
188}
189
190void 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
199namespace {
200// A GOFFSymbol holds all the data required for writing an ESD record.
201class GOFFSymbol {
202public:
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 GOFFSymbol(StringRef Name, uint32_t EsdID, uint32_t ParentEsdID,
272 const GOFF::ERAttr &Attr)
273 : Name(Name.data(), Name.size()), EsdId(EsdID), ParentEsdId(ParentEsdID),
274 SymbolType(GOFF::ESD_ST_ExternalReference),
275 NameSpace(GOFF::ESD_NS_NormalName) {
276 BehavAttrs.setExecutable(Attr.Executable);
277 BehavAttrs.setBindingStrength(Attr.BindingStrength);
278 BehavAttrs.setLinkageType(Attr.Linkage);
279 BehavAttrs.setAmode(Attr.Amode);
280 BehavAttrs.setBindingScope(Attr.BindingScope);
281 BehavAttrs.setIndirectReference(Attr.IsIndirectReference);
282 }
283};
284
285class GOFFWriter {
286 GOFFOstream OS;
287 MCAssembler &Asm;
288 MCSectionGOFF *RootSD;
289
290 /// Saved relocation data collected in recordRelocations().
291 std::vector<GOFFRelocationEntry> &Relocations;
292
293 void writeHeader();
294 void writeSymbol(const GOFFSymbol &Symbol);
295 void writeText(const MCSectionGOFF *MC);
296 void writeRelocations();
297 void writeEnd();
298
299 void defineSectionSymbols(const MCSectionGOFF &Section);
300 void defineLabel(const MCSymbolGOFF &Symbol);
301 void defineExtern(const MCSymbolGOFF &Symbol);
302 void defineSymbols();
303
304public:
305 GOFFWriter(raw_pwrite_stream &OS, MCAssembler &Asm, MCSectionGOFF *RootSD,
306 std::vector<GOFFRelocationEntry> &Relocations);
307 uint64_t writeObject();
308};
309} // namespace
310
311GOFFWriter::GOFFWriter(raw_pwrite_stream &OS, MCAssembler &Asm,
312 MCSectionGOFF *RootSD,
313 std::vector<GOFFRelocationEntry> &Relocations)
314 : OS(OS), Asm(Asm), RootSD(RootSD), Relocations(Relocations) {}
315
316void GOFFWriter::defineSectionSymbols(const MCSectionGOFF &Section) {
317 if (Section.isSD()) {
318 GOFFSymbol SD(Section.getExternalName(), Section.getOrdinal(),
319 Section.getSDAttributes());
320 writeSymbol(Symbol: SD);
321 }
322
323 if (Section.isED()) {
324 GOFFSymbol ED(Section.getExternalName(), Section.getOrdinal(),
325 Section.getParent()->getOrdinal(), Section.getEDAttributes());
326 ED.SectionLength = Asm.getSectionAddressSize(Sec: Section);
327 writeSymbol(Symbol: ED);
328 }
329
330 if (Section.isPR()) {
331 MCSectionGOFF *Parent = Section.getParent();
332 GOFFSymbol PR(Section.getExternalName(), Section.getOrdinal(),
333 Parent->getOrdinal(), Parent->getEDAttributes(),
334 Section.getPRAttributes());
335 PR.SectionLength = Asm.getSectionAddressSize(Sec: Section);
336 if (Section.requiresNonZeroLength()) {
337 // We cannot have a zero-length section for data. If we do,
338 // artificially inflate it. Use 2 bytes to avoid odd alignments. Note:
339 // if this is ever changed, you will need to update the code in
340 // SystemZAsmPrinter::emitCEEMAIN and SystemZAsmPrinter::emitCELQMAIN to
341 // generate -1 if there is no ADA
342 if (!PR.SectionLength)
343 PR.SectionLength = 2;
344 }
345 writeSymbol(Symbol: PR);
346 }
347}
348
349void GOFFWriter::defineLabel(const MCSymbolGOFF &Symbol) {
350 MCSectionGOFF &Section = static_cast<MCSectionGOFF &>(Symbol.getSection());
351 GOFFSymbol LD(Symbol.getExternalName(), Symbol.getIndex(),
352 Section.getOrdinal(), Section.getEDAttributes().NameSpace,
353 GOFF::LDAttr{.IsRenamable: false, .Executable: Symbol.getCodeData(),
354 .BindingStrength: Symbol.getBindingStrength(), .Linkage: Symbol.getLinkage(),
355 .Amode: GOFF::ESD_AMODE_64, .BindingScope: Symbol.getBindingScope()});
356 if (Symbol.getADA())
357 LD.ADAEsdId = Symbol.getADA()->getOrdinal();
358 LD.Offset = Asm.getSymbolOffset(S: Symbol);
359 writeSymbol(Symbol: LD);
360}
361
362void GOFFWriter::defineExtern(const MCSymbolGOFF &Symbol) {
363 GOFFSymbol ER(Symbol.getExternalName(), Symbol.getIndex(),
364 RootSD->getOrdinal(),
365 GOFF::ERAttr{.IsIndirectReference: Symbol.isIndirect(), .Executable: Symbol.getCodeData(),
366 .BindingStrength: Symbol.getBindingStrength(), .Linkage: Symbol.getLinkage(),
367 .Amode: GOFF::ESD_AMODE_64, .BindingScope: Symbol.getBindingScope()});
368 writeSymbol(Symbol: ER);
369}
370
371void GOFFWriter::defineSymbols() {
372 unsigned Ordinal = 0;
373 // Process all sections.
374 for (MCSection &S : Asm) {
375 auto &Section = static_cast<MCSectionGOFF &>(S);
376 Section.setOrdinal(++Ordinal);
377 defineSectionSymbols(Section);
378 }
379
380 // Process all symbols
381 for (const MCSymbol &Sym : Asm.symbols()) {
382 if (Sym.isTemporary())
383 continue;
384 auto &Symbol = static_cast<const MCSymbolGOFF &>(Sym);
385 if (!Symbol.isDefined()) {
386 Symbol.setIndex(++Ordinal);
387 defineExtern(Symbol);
388 } else if (Symbol.isInEDSection()) {
389 Symbol.setIndex(++Ordinal);
390 defineLabel(Symbol);
391 } else {
392 // Symbol is in PR section, the symbol refers to the section.
393 Symbol.setIndex(Symbol.getSection().getOrdinal());
394 }
395 }
396}
397
398void GOFFWriter::writeHeader() {
399 OS.newRecord(Type: GOFF::RT_HDR);
400 OS.write_zeros(NumZeros: 1); // Reserved
401 OS.writebe<uint32_t>(Value: 0); // Target Hardware Environment
402 OS.writebe<uint32_t>(Value: 0); // Target Operating System Environment
403 OS.write_zeros(NumZeros: 2); // Reserved
404 OS.writebe<uint16_t>(Value: 0); // CCSID
405 OS.write_zeros(NumZeros: 16); // Character Set name
406 OS.write_zeros(NumZeros: 16); // Language Product Identifier
407 OS.writebe<uint32_t>(Value: 1); // Architecture Level
408 OS.writebe<uint16_t>(Value: 0); // Module Properties Length
409 OS.write_zeros(NumZeros: 6); // Reserved
410}
411
412void GOFFWriter::writeSymbol(const GOFFSymbol &Symbol) {
413 if (Symbol.Offset >= (((uint64_t)1) << 31))
414 report_fatal_error(reason: "ESD offset out of range");
415
416 // All symbol names are in EBCDIC.
417 SmallString<256> Name;
418 ConverterEBCDIC::convertToEBCDIC(Source: Symbol.Name, Result&: Name);
419
420 // Check length here since this number is technically signed but we need uint
421 // for writing to records.
422 if (Name.size() >= GOFF::MaxDataLength)
423 report_fatal_error(reason: "Symbol max name length exceeded");
424 uint16_t NameLength = Name.size();
425
426 OS.newRecord(Type: GOFF::RT_ESD);
427 OS.writebe<uint8_t>(Value: Symbol.SymbolType); // Symbol Type
428 OS.writebe<uint32_t>(Value: Symbol.EsdId); // ESDID
429 OS.writebe<uint32_t>(Value: Symbol.ParentEsdId); // Parent or Owning ESDID
430 OS.writebe<uint32_t>(Value: 0); // Reserved
431 OS.writebe<uint32_t>(
432 Value: static_cast<uint32_t>(Symbol.Offset)); // Offset or Address
433 OS.writebe<uint32_t>(Value: 0); // Reserved
434 OS.writebe<uint32_t>(Value: Symbol.SectionLength); // Length
435 OS.writebe<uint32_t>(Value: Symbol.EASectionEDEsdId); // Extended Attribute ESDID
436 OS.writebe<uint32_t>(Value: Symbol.EASectionOffset); // Extended Attribute Offset
437 OS.writebe<uint32_t>(Value: 0); // Reserved
438 OS.writebe<uint8_t>(Value: Symbol.NameSpace); // Name Space ID
439 OS.writebe<uint8_t>(Value: Symbol.SymbolFlags); // Flags
440 OS.writebe<uint8_t>(Value: Symbol.FillByteValue); // Fill-Byte Value
441 OS.writebe<uint8_t>(Value: 0); // Reserved
442 OS.writebe<uint32_t>(Value: Symbol.ADAEsdId); // ADA ESDID
443 OS.writebe<uint32_t>(Value: Symbol.SortKey); // Sort Priority
444 OS.writebe<uint64_t>(Value: 0); // Reserved
445 for (auto F : Symbol.BehavAttrs.Attr)
446 OS.writebe<uint8_t>(Value: F); // Behavioral Attributes
447 OS.writebe<uint16_t>(Value: NameLength); // Name Length
448 OS.write(Ptr: Name.data(), Size: NameLength); // Name
449}
450
451namespace {
452/// Adapter stream to write a text section.
453class TextStream : public raw_ostream {
454 /// The underlying GOFFOstream.
455 GOFFOstream &OS;
456
457 /// The buffer size is the maximum number of bytes in a TXT section.
458 static constexpr size_t BufferSize = GOFF::MaxDataLength;
459
460 /// Static allocated buffer for the stream, used by the raw_ostream class. The
461 /// buffer is sized to hold the payload of a logical TXT record.
462 char Buffer[BufferSize];
463
464 /// The offset for the next TXT record. This is equal to the number of bytes
465 /// written.
466 size_t Offset;
467
468 /// The Esdid of the GOFF section.
469 const uint32_t EsdId;
470
471 /// The record style.
472 const GOFF::ESDTextStyle RecordStyle;
473
474 /// See raw_ostream::write_impl.
475 void write_impl(const char *Ptr, size_t Size) override;
476
477 uint64_t current_pos() const override { return Offset; }
478
479public:
480 explicit TextStream(GOFFOstream &OS, uint32_t EsdId,
481 GOFF::ESDTextStyle RecordStyle)
482 : OS(OS), Offset(0), EsdId(EsdId), RecordStyle(RecordStyle) {
483 SetBuffer(BufferStart: Buffer, Size: sizeof(Buffer));
484 }
485
486 ~TextStream() override { flush(); }
487};
488} // namespace
489
490void TextStream::write_impl(const char *Ptr, size_t Size) {
491 size_t WrittenLength = 0;
492
493 // We only have signed 32bits of offset.
494 if (Offset + Size > std::numeric_limits<int32_t>::max())
495 report_fatal_error(reason: "TXT section too large");
496
497 while (WrittenLength < Size) {
498 size_t ToWriteLength =
499 std::min(a: Size - WrittenLength, b: size_t(GOFF::MaxDataLength));
500
501 OS.newRecord(Type: GOFF::RT_TXT);
502 OS.writebe<uint8_t>(Value: GOFF::Flags(4, 4, RecordStyle)); // Text Record Style
503 OS.writebe<uint32_t>(Value: EsdId); // Element ESDID
504 OS.writebe<uint32_t>(Value: 0); // Reserved
505 OS.writebe<uint32_t>(Value: static_cast<uint32_t>(Offset)); // Offset
506 OS.writebe<uint32_t>(Value: 0); // Text Field True Length
507 OS.writebe<uint16_t>(Value: 0); // Text Encoding
508 OS.writebe<uint16_t>(Value: ToWriteLength); // Data Length
509 OS.write(Ptr: Ptr + WrittenLength, Size: ToWriteLength); // Data
510
511 WrittenLength += ToWriteLength;
512 Offset += ToWriteLength;
513 }
514}
515
516void GOFFWriter::writeText(const MCSectionGOFF *Section) {
517 // A BSS section contains only zeros, no need to write this.
518 if (Section->isBSS())
519 return;
520
521 TextStream S(OS, Section->getOrdinal(), Section->getTextStyle());
522 Asm.writeSectionData(OS&: S, Section);
523}
524
525namespace {
526// RelocDataItemBuffer provides a static buffer for relocation data items.
527class RelocDataItemBuffer {
528 char Buffer[GOFF::MaxDataLength];
529 char *Ptr;
530
531public:
532 RelocDataItemBuffer() : Ptr(Buffer) {}
533 const char *data() { return Buffer; }
534 size_t size() { return Ptr - Buffer; }
535 void reset() { Ptr = Buffer; }
536 bool fits(size_t S) { return size() + S < GOFF::MaxDataLength; }
537 template <typename T> void writebe(T Val) {
538 assert(fits(sizeof(T)) && "Out-of-bounds write");
539 support::endian::write<T, llvm::endianness::big>(Ptr, Val);
540 Ptr += sizeof(T);
541 }
542};
543} // namespace
544
545void GOFFWriter::writeRelocations() {
546 // Set the IDs in the relocation entries.
547 for (auto &RelocEntry : Relocations) {
548 auto GetRptr = [](const MCSymbolGOFF *Sym) -> uint32_t {
549 if (Sym->isTemporary())
550 return static_cast<MCSectionGOFF &>(Sym->getSection())
551 .getBeginSymbol()
552 ->getIndex();
553 return Sym->getIndex();
554 };
555
556 RelocEntry.PEsdId = RelocEntry.Pptr->getOrdinal();
557 RelocEntry.REsdId = GetRptr(RelocEntry.Rptr);
558 }
559
560 // Sort relocation data items by the P pointer to save space.
561 std::sort(
562 first: Relocations.begin(), last: Relocations.end(),
563 comp: [](const GOFFRelocationEntry &Left, const GOFFRelocationEntry &Right) {
564 return std::tie(args: Left.PEsdId, args: Left.REsdId, args: Left.POffset) <
565 std::tie(args: Right.PEsdId, args: Right.REsdId, args: Right.POffset);
566 });
567
568 // Construct the compressed relocation data items, and write them out.
569 RelocDataItemBuffer Buffer;
570 for (auto I = Relocations.begin(), E = Relocations.end(); I != E;) {
571 Buffer.reset();
572
573 uint32_t PrevResdId = -1;
574 uint32_t PrevPesdId = -1;
575 uint64_t PrevPOffset = -1;
576 for (; I != E; ++I) {
577 const GOFFRelocationEntry &Rel = *I;
578
579 bool SameREsdId = (Rel.REsdId == PrevResdId);
580 bool SamePEsdId = (Rel.PEsdId == PrevPesdId);
581 bool SamePOffset = (Rel.POffset == PrevPOffset);
582 bool EightByteOffset = ((Rel.POffset >> 32) & 0xffffffff);
583
584 // Calculate size of relocation data item, and check if it still fits into
585 // the record.
586 size_t ItemSize = 8; // Smallest size of a relocation data item.
587 if (!SameREsdId)
588 ItemSize += 4;
589 if (!SamePEsdId)
590 ItemSize += 4;
591 if (!SamePOffset)
592 ItemSize += (EightByteOffset ? 8 : 4);
593 if (!Buffer.fits(S: ItemSize))
594 break;
595
596 GOFF::Flags RelocFlags[6];
597 RelocFlags[0].set(BitIndex: 0, Length: 1, NewValue: SameREsdId);
598 RelocFlags[0].set(BitIndex: 1, Length: 1, NewValue: SamePEsdId);
599 RelocFlags[0].set(BitIndex: 2, Length: 1, NewValue: SamePOffset);
600 RelocFlags[0].set(BitIndex: 6, Length: 1, NewValue: EightByteOffset);
601
602 RelocFlags[1].set(BitIndex: 0, Length: 4, NewValue: Rel.ReferenceType);
603 RelocFlags[1].set(BitIndex: 4, Length: 4, NewValue: Rel.ReferentType);
604
605 RelocFlags[2].set(BitIndex: 0, Length: 7, NewValue: Rel.Action);
606 RelocFlags[2].set(BitIndex: 7, Length: 1, NewValue: Rel.FetchStore);
607
608 RelocFlags[4].set(BitIndex: 0, Length: 8, NewValue: Rel.TargetLength);
609
610 for (auto F : RelocFlags)
611 Buffer.writebe<uint8_t>(Val: F);
612 Buffer.writebe<uint16_t>(Val: 0); // Reserved.
613 if (!SameREsdId)
614 Buffer.writebe<uint32_t>(Val: Rel.REsdId);
615 if (!SamePEsdId)
616 Buffer.writebe<uint32_t>(Val: Rel.PEsdId);
617 if (!SamePOffset) {
618 if (EightByteOffset)
619 Buffer.writebe<uint64_t>(Val: Rel.POffset);
620 else
621 Buffer.writebe<uint32_t>(Val: Rel.POffset);
622 }
623
624 PrevResdId = Rel.REsdId;
625 PrevPesdId = Rel.PEsdId;
626 PrevPOffset = Rel.POffset;
627 }
628
629 OS.newRecord(Type: GOFF::RT_RLD);
630 OS.writebe<uint8_t>(Value: 0); // Reserved.
631 OS.writebe<uint16_t>(Value: Buffer.size()); // Length (of the relocation data).
632 OS.write(Ptr: Buffer.data(), Size: Buffer.size()); // Relocation Directory Data Items.
633 }
634}
635
636void GOFFWriter::writeEnd() {
637 uint8_t F = GOFF::END_EPR_None;
638 uint8_t AMODE = 0;
639 uint32_t ESDID = 0;
640
641 // TODO Set Flags/AMODE/ESDID for entry point.
642
643 OS.newRecord(Type: GOFF::RT_END);
644 OS.writebe<uint8_t>(Value: GOFF::Flags(6, 2, F)); // Indicator flags
645 OS.writebe<uint8_t>(Value: AMODE); // AMODE
646 OS.write_zeros(NumZeros: 3); // Reserved
647 // The record count is the number of logical records. In principle, this value
648 // is available as OS.logicalRecords(). However, some tools rely on this field
649 // being zero.
650 OS.writebe<uint32_t>(Value: 0); // Record Count
651 OS.writebe<uint32_t>(Value: ESDID); // ESDID (of entry point)
652}
653
654uint64_t GOFFWriter::writeObject() {
655 writeHeader();
656
657 defineSymbols();
658
659 for (const MCSection &Section : Asm)
660 writeText(Section: static_cast<const MCSectionGOFF *>(&Section));
661
662 writeRelocations();
663
664 writeEnd();
665
666 // Make sure all records are written.
667 OS.finalizeRecord();
668
669 LLVM_DEBUG(dbgs() << "Wrote " << OS.getNumLogicalRecords()
670 << " logical records.");
671
672 return OS.getWrittenSize();
673}
674
675GOFFObjectWriter::GOFFObjectWriter(
676 std::unique_ptr<MCGOFFObjectTargetWriter> MOTW, raw_pwrite_stream &OS)
677 : TargetObjectWriter(std::move(MOTW)), OS(OS) {}
678
679GOFFObjectWriter::~GOFFObjectWriter() = default;
680
681void GOFFObjectWriter::recordRelocation(const MCFragment &F,
682 const MCFixup &Fixup, MCValue Target,
683 uint64_t &FixedValue) {
684 const MCFixupKindInfo &FKI =
685 Asm->getBackend().getFixupKindInfo(Kind: Fixup.getKind());
686 const uint32_t Length = FKI.TargetSize / 8;
687 assert(FKI.TargetSize % 8 == 0 && "Target Size not multiple of 8");
688 const uint64_t FixupOffset = Asm->getFragmentOffset(F) + Fixup.getOffset();
689
690 unsigned RelocType = TargetObjectWriter->getRelocType(Target, Fixup);
691
692 const MCSectionGOFF *PSection = static_cast<MCSectionGOFF *>(F.getParent());
693 const auto &A = *static_cast<const MCSymbolGOFF *>(Target.getAddSym());
694 const MCSymbolGOFF *B = static_cast<const MCSymbolGOFF *>(Target.getSubSym());
695 if (RelocType == MCGOFFObjectTargetWriter::Reloc_Type_RICon) {
696 if (A.isUndefined()) {
697 Asm->reportError(
698 L: Fixup.getLoc(),
699 Msg: Twine("symbol ")
700 .concat(Suffix: A.getExternalName())
701 .concat(Suffix: " must be defined for a relative immediate relocation"));
702 return;
703 }
704 if (&A.getSection() != PSection) {
705 MCSectionGOFF &GOFFSection = static_cast<MCSectionGOFF &>(A.getSection());
706 Asm->reportError(L: Fixup.getLoc(),
707 Msg: Twine("relative immediate relocation section mismatch: ")
708 .concat(Suffix: GOFFSection.getExternalName())
709 .concat(Suffix: " of symbol ")
710 .concat(Suffix: A.getExternalName())
711 .concat(Suffix: " <-> ")
712 .concat(Suffix: PSection->getExternalName()));
713 return;
714 }
715 if (B) {
716 Asm->reportError(
717 L: Fixup.getLoc(),
718 Msg: Twine("subtractive symbol ")
719 .concat(Suffix: B->getExternalName())
720 .concat(Suffix: " not supported for a relative immediate relocation"));
721 return;
722 }
723 FixedValue = Asm->getSymbolOffset(S: A) - FixupOffset + Target.getConstant();
724 return;
725 }
726 FixedValue = Target.getConstant();
727
728 // The symbol only has a section-relative offset if it is a temporary symbol.
729 FixedValue += A.isTemporary() ? Asm->getSymbolOffset(S: A) : 0;
730 A.setUsedInReloc();
731 if (B) {
732 FixedValue -= B->isTemporary() ? Asm->getSymbolOffset(S: *B) : 0;
733 B->setUsedInReloc();
734 }
735
736 // UseQCon causes class offsets versus absolute addresses to be used. This
737 // is analogous to using QCONs in older OBJ object file format.
738 bool UseQCon = RelocType == MCGOFFObjectTargetWriter::Reloc_Type_QCon;
739
740 GOFF::RLDFetchStore FetchStore =
741 (RelocType == MCGOFFObjectTargetWriter::Reloc_Type_RCon ||
742 RelocType == MCGOFFObjectTargetWriter::Reloc_Type_VCon)
743 ? GOFF::RLDFetchStore::RLD_FS_Store
744 : GOFF::RLDFetchStore::RLD_FS_Fetch;
745 assert((FetchStore == GOFF::RLDFetchStore::RLD_FS_Fetch || B == nullptr) &&
746 "No dependent relocations expected");
747
748 enum GOFF::RLDReferenceType ReferenceType = GOFF::RLD_RT_RAddress;
749 enum GOFF::RLDReferentType ReferentType = GOFF::RLD_RO_Label;
750 if (UseQCon) {
751 ReferenceType = GOFF::RLD_RT_ROffset;
752 ReferentType = GOFF::RLD_RO_Class;
753 }
754 if (RelocType == MCGOFFObjectTargetWriter::Reloc_Type_RCon)
755 ReferenceType = GOFF::RLD_RT_RTypeConstant;
756
757 auto DumpReloc = [&PSection, &ReferenceType, &FixupOffset,
758 &FixedValue](const char *N, const MCSymbolGOFF *Sym) {
759 const char *Con;
760 switch (ReferenceType) {
761 case GOFF::RLDReferenceType::RLD_RT_RAddress:
762 Con = "ACon";
763 break;
764 case GOFF::RLDReferenceType::RLD_RT_ROffset:
765 Con = "QCon";
766 break;
767 case GOFF::RLDReferenceType::RLD_RT_RTypeConstant:
768 Con = "VCon";
769 break;
770 default:
771 Con = "(unknown)";
772 }
773 dbgs() << "Reloc " << N << ": " << Con
774 << " Rptr: " << Sym->getExternalName()
775 << " Pptr: " << PSection->getExternalName()
776 << " Offset: " << FixupOffset << " Fixed Imm: " << FixedValue
777 << "\n";
778 };
779 (void)DumpReloc;
780
781 // Save relocation data for later writing.
782 LLVM_DEBUG(DumpReloc("A", &A));
783 Relocations.emplace_back(args&: PSection, args: &A, args&: ReferenceType, args&: ReferentType,
784 args: GOFF::RLD_ACT_Add, args&: FetchStore, args: FixupOffset, args: Length);
785 if (B) {
786 LLVM_DEBUG(DumpReloc("B", B));
787 Relocations.emplace_back(
788 args&: PSection, args&: B, args&: ReferenceType, args&: ReferentType, args: GOFF::RLD_ACT_Subtract,
789 args: GOFF::RLDFetchStore::RLD_FS_Fetch, args: FixupOffset, args: Length);
790 }
791}
792
793uint64_t GOFFObjectWriter::writeObject() {
794 uint64_t Size = GOFFWriter(OS, *Asm, RootSD, Relocations).writeObject();
795 return Size;
796}
797
798std::unique_ptr<MCObjectWriter>
799llvm::createGOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW,
800 raw_pwrite_stream &OS) {
801 return std::make_unique<GOFFObjectWriter>(args: std::move(MOTW), args&: OS);
802}
803