xref: /llvm-project/llvm/lib/ObjectYAML/XCOFFEmitter.cpp (revision 5b7102d1f37eab7a8f17b7bf4124ca76fbdbd66d)
1 //===- yaml2xcoff - Convert YAML to a xcoff object file -------------------===//
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 /// \file
10 /// The xcoff component of yaml2obj.
11 ///
12 //===----------------------------------------------------------------------===//
13 
14 #include "llvm/ADT/DenseMap.h"
15 #include "llvm/BinaryFormat/XCOFF.h"
16 #include "llvm/MC/StringTableBuilder.h"
17 #include "llvm/Object/XCOFFObjectFile.h"
18 #include "llvm/ObjectYAML/ObjectYAML.h"
19 #include "llvm/ObjectYAML/yaml2obj.h"
20 #include "llvm/Support/EndianStream.h"
21 #include "llvm/Support/MemoryBuffer.h"
22 #include "llvm/Support/raw_ostream.h"
23 
24 using namespace llvm;
25 using namespace llvm::object;
26 
27 namespace {
28 
29 constexpr unsigned DefaultSectionAlign = 4;
30 constexpr int16_t MaxSectionIndex = INT16_MAX;
31 constexpr uint32_t MaxRawDataSize = UINT32_MAX;
32 
33 class XCOFFWriter {
34 public:
35   XCOFFWriter(XCOFFYAML::Object &Obj, raw_ostream &OS, yaml::ErrorHandler EH)
36       : Obj(Obj), W(OS, llvm::endianness::big), ErrHandler(EH),
37         StrTblBuilder(StringTableBuilder::XCOFF) {
38     Is64Bit = Obj.Header.Magic == (llvm::yaml::Hex16)XCOFF::XCOFF64;
39   }
40   bool writeXCOFF();
41 
42 private:
43   void reportOverwrite(uint64_t currentOffset, uint64_t specifiedOffset,
44                        const Twine &fieldName);
45   bool nameShouldBeInStringTable(StringRef SymbolName);
46   bool initFileHeader(uint64_t CurrentOffset);
47   void initAuxFileHeader();
48   bool initSectionHeaders(uint64_t &CurrentOffset);
49   bool initRelocations(uint64_t &CurrentOffset);
50   bool initStringTable();
51   bool assignAddressesAndIndices();
52 
53   void writeFileHeader();
54   void writeAuxFileHeader();
55   void writeSectionHeaders();
56   bool writeSectionData();
57   bool writeRelocations();
58   bool writeSymbols();
59   void writeStringTable();
60 
61   bool writeAuxSymbol(const XCOFFYAML::CsectAuxEnt &AuxSym);
62   bool writeAuxSymbol(const XCOFFYAML::FileAuxEnt &AuxSym);
63   bool writeAuxSymbol(const XCOFFYAML::FunctionAuxEnt &AuxSym);
64   bool writeAuxSymbol(const XCOFFYAML::ExcpetionAuxEnt &AuxSym);
65   bool writeAuxSymbol(const XCOFFYAML::BlockAuxEnt &AuxSym);
66   bool writeAuxSymbol(const XCOFFYAML::SectAuxEntForDWARF &AuxSym);
67   bool writeAuxSymbol(const XCOFFYAML::SectAuxEntForStat &AuxSym);
68   bool writeAuxSymbol(const std::unique_ptr<XCOFFYAML::AuxSymbolEnt> &AuxSym);
69 
70   XCOFFYAML::Object &Obj;
71   bool Is64Bit = false;
72   support::endian::Writer W;
73   yaml::ErrorHandler ErrHandler;
74   StringTableBuilder StrTblBuilder;
75   uint64_t StartOffset = 0u;
76   // Map the section name to its corrresponding section index.
77   DenseMap<StringRef, int16_t> SectionIndexMap = {
78       {StringRef("N_DEBUG"), XCOFF::N_DEBUG},
79       {StringRef("N_ABS"), XCOFF::N_ABS},
80       {StringRef("N_UNDEF"), XCOFF::N_UNDEF}};
81   XCOFFYAML::FileHeader InitFileHdr = Obj.Header;
82   XCOFFYAML::AuxiliaryHeader InitAuxFileHdr;
83   std::vector<XCOFFYAML::Section> InitSections = Obj.Sections;
84 };
85 
86 static void writeName(StringRef StrName, support::endian::Writer W) {
87   char Name[XCOFF::NameSize];
88   memset(Name, 0, XCOFF::NameSize);
89   char SrcName[] = "";
90   memcpy(Name, StrName.size() ? StrName.data() : SrcName, StrName.size());
91   ArrayRef<char> NameRef(Name, XCOFF::NameSize);
92   W.write(NameRef);
93 }
94 
95 void XCOFFWriter::reportOverwrite(uint64_t CurrentOffset,
96                                   uint64_t specifiedOffset,
97                                   const Twine &fieldName) {
98   ErrHandler("current file offset (" + Twine(CurrentOffset) +
99              ") is bigger than the specified " + fieldName + " (" +
100              Twine(specifiedOffset) + ") ");
101 }
102 
103 bool XCOFFWriter::nameShouldBeInStringTable(StringRef SymbolName) {
104   // For XCOFF64: The symbol name is always in the string table.
105   return (SymbolName.size() > XCOFF::NameSize) || Is64Bit;
106 }
107 
108 bool XCOFFWriter::initRelocations(uint64_t &CurrentOffset) {
109   for (XCOFFYAML::Section &InitSection : InitSections) {
110     if (!InitSection.Relocations.empty()) {
111       uint64_t RelSize = Is64Bit ? XCOFF::RelocationSerializationSize64
112                                  : XCOFF::RelocationSerializationSize32;
113       uint64_t UsedSize = RelSize * InitSection.Relocations.size();
114 
115       // If NumberOfRelocations was specified, we use it, even if it's
116       // not consistent with the number of provided relocations.
117       if (!InitSection.NumberOfRelocations)
118         InitSection.NumberOfRelocations = InitSection.Relocations.size();
119 
120       // If the YAML file specified an offset to relocations, we use it.
121       if (InitSection.FileOffsetToRelocations) {
122         if (CurrentOffset > InitSection.FileOffsetToRelocations) {
123           reportOverwrite(CurrentOffset, InitSection.FileOffsetToRelocations,
124                           "FileOffsetToRelocations for the " +
125                               InitSection.SectionName + " section");
126           return false;
127         }
128         CurrentOffset = InitSection.FileOffsetToRelocations;
129       } else
130         InitSection.FileOffsetToRelocations = CurrentOffset;
131       CurrentOffset += UsedSize;
132       if (CurrentOffset > MaxRawDataSize) {
133         ErrHandler("maximum object size (" + Twine(MaxRawDataSize) +
134                    ") exceeded when writing relocation data for section " +
135                    Twine(InitSection.SectionName));
136         return false;
137       }
138     }
139   }
140   return true;
141 }
142 
143 bool XCOFFWriter::initSectionHeaders(uint64_t &CurrentOffset) {
144   uint64_t CurrentEndDataAddr = 0;
145   uint64_t CurrentEndTDataAddr = 0;
146   for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) {
147     // Assign indices for sections.
148     if (InitSections[I].SectionName.size() &&
149         !SectionIndexMap[InitSections[I].SectionName]) {
150       // The section index starts from 1.
151       SectionIndexMap[InitSections[I].SectionName] = I + 1;
152       if ((I + 1) > MaxSectionIndex) {
153         ErrHandler("exceeded the maximum permitted section index of " +
154                    Twine(MaxSectionIndex));
155         return false;
156       }
157     }
158 
159     if (!InitSections[I].Size)
160       InitSections[I].Size = InitSections[I].SectionData.binary_size();
161 
162     // Section data addresses (physical/virtual) are related to symbol
163     // addresses and alignments. Furthermore, it is possible to specify the
164     // same starting addresses for the .text, .data, and .tdata sections.
165     // Without examining all the symbols and their addreses and alignments,
166     // it is not possible to compute valid section addresses. The only
167     // condition required by XCOFF is that the .bss section immediately
168     // follows the .data section, and the .tbss section immediately follows
169     // the .tdata section. Therefore, we only assign addresses to the .bss
170     // and .tbss sections if they do not already have non-zero addresses.
171     // (If the YAML file is being used to generate a valid object file, we
172     // expect all section addresses to be specified explicitly.)
173     switch (InitSections[I].Flags) {
174     case XCOFF::STYP_DATA:
175       CurrentEndDataAddr = InitSections[I].Address + InitSections[I].Size;
176       break;
177     case XCOFF::STYP_BSS:
178       if (!InitSections[I].Address)
179         InitSections[I].Address = CurrentEndDataAddr;
180       break;
181     case XCOFF::STYP_TDATA:
182       CurrentEndTDataAddr = InitSections[I].Address + InitSections[I].Size;
183       break;
184     case XCOFF::STYP_TBSS:
185       if (!InitSections[I].Address)
186         InitSections[I].Address = CurrentEndTDataAddr;
187       break;
188     }
189 
190     if (InitSections[I].SectionData.binary_size()) {
191       if (InitSections[I].FileOffsetToData) {
192         // Use the providedFileOffsetToData.
193         if (CurrentOffset > InitSections[I].FileOffsetToData) {
194           reportOverwrite(CurrentOffset, InitSections[I].FileOffsetToData,
195                           "FileOffsetToData for the " +
196                               InitSections[I].SectionName + " section");
197           return false;
198         }
199         CurrentOffset = InitSections[I].FileOffsetToData;
200       } else {
201         CurrentOffset = alignTo(CurrentOffset, DefaultSectionAlign);
202         InitSections[I].FileOffsetToData = CurrentOffset;
203       }
204       CurrentOffset += InitSections[I].SectionData.binary_size();
205       if (CurrentOffset > MaxRawDataSize) {
206         ErrHandler("maximum object size (" + Twine(MaxRawDataSize) +
207                    ") exceeded when writing data for section " + Twine(I + 1) +
208                    " (" + Twine(InitSections[I].SectionName) + ")");
209         return false;
210       }
211     }
212     if (InitSections[I].SectionSubtype) {
213       uint32_t DWARFSubtype =
214           static_cast<uint32_t>(*InitSections[I].SectionSubtype);
215       if (InitSections[I].Flags != XCOFF::STYP_DWARF) {
216         ErrHandler("a DWARFSectionSubtype is only allowed for a DWARF section");
217         return false;
218       }
219       unsigned Mask = Is64Bit ? XCOFFSectionHeader64::SectionFlagsTypeMask
220                               : XCOFFSectionHeader32::SectionFlagsTypeMask;
221       if (DWARFSubtype & Mask) {
222         ErrHandler("the low-order bits of DWARFSectionSubtype must be 0");
223         return false;
224       }
225       InitSections[I].Flags |= DWARFSubtype;
226     }
227   }
228   return initRelocations(CurrentOffset);
229 }
230 
231 bool XCOFFWriter::initStringTable() {
232   if (Obj.StrTbl.RawContent) {
233     size_t RawSize = Obj.StrTbl.RawContent->binary_size();
234     if (Obj.StrTbl.Strings || Obj.StrTbl.Length) {
235       ErrHandler(
236           "can't specify Strings or Length when RawContent is specified");
237       return false;
238     }
239     if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize < RawSize) {
240       ErrHandler("specified ContentSize (" + Twine(*Obj.StrTbl.ContentSize) +
241                  ") is less than the RawContent data size (" + Twine(RawSize) +
242                  ")");
243       return false;
244     }
245     return true;
246   }
247   if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize <= 3) {
248     ErrHandler("ContentSize shouldn't be less than 4 without RawContent");
249     return false;
250   }
251 
252   // Build the string table.
253   StrTblBuilder.clear();
254 
255   if (Obj.StrTbl.Strings) {
256     // Add all specified strings to the string table.
257     for (StringRef StringEnt : *Obj.StrTbl.Strings)
258       StrTblBuilder.add(StringEnt);
259 
260     size_t StrTblIdx = 0;
261     size_t NumOfStrings = Obj.StrTbl.Strings->size();
262     for (XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
263       if (nameShouldBeInStringTable(YamlSym.SymbolName)) {
264         if (StrTblIdx < NumOfStrings) {
265           // Overwrite the symbol name with the specified string.
266           YamlSym.SymbolName = (*Obj.StrTbl.Strings)[StrTblIdx];
267           ++StrTblIdx;
268         } else
269           // Names that are not overwritten are still stored in the string
270           // table.
271           StrTblBuilder.add(YamlSym.SymbolName);
272       }
273     }
274   } else {
275     for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
276       if (nameShouldBeInStringTable(YamlSym.SymbolName))
277         StrTblBuilder.add(YamlSym.SymbolName);
278     }
279   }
280 
281   // Check if the file name in the File Auxiliary Entry should be added to the
282   // string table.
283   for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
284     for (const std::unique_ptr<XCOFFYAML::AuxSymbolEnt> &AuxSym :
285          YamlSym.AuxEntries) {
286       if (auto AS = dyn_cast<XCOFFYAML::FileAuxEnt>(AuxSym.get()))
287         if (nameShouldBeInStringTable(AS->FileNameOrString.value_or("")))
288           StrTblBuilder.add(AS->FileNameOrString.value_or(""));
289     }
290   }
291 
292   StrTblBuilder.finalize();
293 
294   size_t StrTblSize = StrTblBuilder.getSize();
295   if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize < StrTblSize) {
296     ErrHandler("specified ContentSize (" + Twine(*Obj.StrTbl.ContentSize) +
297                ") is less than the size of the data that would otherwise be "
298                "written (" +
299                Twine(StrTblSize) + ")");
300     return false;
301   }
302 
303   return true;
304 }
305 
306 bool XCOFFWriter::initFileHeader(uint64_t CurrentOffset) {
307   // The default format of the object file is XCOFF32.
308   InitFileHdr.Magic = XCOFF::XCOFF32;
309   InitFileHdr.NumberOfSections = Obj.Sections.size();
310   InitFileHdr.NumberOfSymTableEntries = Obj.Symbols.size();
311 
312   for (XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
313     uint32_t AuxCount = YamlSym.AuxEntries.size();
314     if (YamlSym.NumberOfAuxEntries && *YamlSym.NumberOfAuxEntries < AuxCount) {
315       ErrHandler("specified NumberOfAuxEntries " +
316                  Twine(static_cast<uint32_t>(*YamlSym.NumberOfAuxEntries)) +
317                  " is less than the actual number "
318                  "of auxiliary entries " +
319                  Twine(AuxCount));
320       return false;
321     }
322     YamlSym.NumberOfAuxEntries = YamlSym.NumberOfAuxEntries.value_or(AuxCount);
323     // Add the number of auxiliary symbols to the total number.
324     InitFileHdr.NumberOfSymTableEntries += *YamlSym.NumberOfAuxEntries;
325   }
326 
327   // Calculate SymbolTableOffset for the file header.
328   if (InitFileHdr.NumberOfSymTableEntries) {
329     if (Obj.Header.SymbolTableOffset) {
330       if (CurrentOffset > Obj.Header.SymbolTableOffset) {
331         reportOverwrite(CurrentOffset, Obj.Header.SymbolTableOffset,
332                         "SymbolTableOffset");
333         return false;
334       }
335       CurrentOffset = Obj.Header.SymbolTableOffset;
336     }
337     InitFileHdr.SymbolTableOffset = CurrentOffset;
338     CurrentOffset +=
339         InitFileHdr.NumberOfSymTableEntries * XCOFF::SymbolTableEntrySize;
340     if (CurrentOffset > MaxRawDataSize) {
341       ErrHandler("maximum object size of " + Twine(MaxRawDataSize) +
342                  " exceeded when writing symbols");
343       return false;
344     }
345   }
346   // TODO: Calculate FileOffsetToLineNumbers when line number supported.
347   return true;
348 }
349 
350 void XCOFFWriter::initAuxFileHeader() {
351   if (Obj.AuxHeader)
352     InitAuxFileHdr = *Obj.AuxHeader;
353   // In general, an object file might contain multiple sections of a given type,
354   // but in a loadable module, there must be exactly one .text, .data, .bss, and
355   // .loader section. A loadable object might also have one .tdata section and
356   // one .tbss section.
357   // Set these section-related values if not set explicitly. We assume that the
358   // input YAML matches the format of the loadable object, but if multiple input
359   // sections still have the same type, the first section with that type
360   // prevails.
361   for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) {
362     switch (InitSections[I].Flags) {
363     case XCOFF::STYP_TEXT:
364       if (!InitAuxFileHdr.TextSize)
365         InitAuxFileHdr.TextSize = InitSections[I].Size;
366       if (!InitAuxFileHdr.TextStartAddr)
367         InitAuxFileHdr.TextStartAddr = InitSections[I].Address;
368       if (!InitAuxFileHdr.SecNumOfText)
369         InitAuxFileHdr.SecNumOfText = I + 1;
370       break;
371     case XCOFF::STYP_DATA:
372       if (!InitAuxFileHdr.InitDataSize)
373         InitAuxFileHdr.InitDataSize = InitSections[I].Size;
374       if (!InitAuxFileHdr.DataStartAddr)
375         InitAuxFileHdr.DataStartAddr = InitSections[I].Address;
376       if (!InitAuxFileHdr.SecNumOfData)
377         InitAuxFileHdr.SecNumOfData = I + 1;
378       break;
379     case XCOFF::STYP_BSS:
380       if (!InitAuxFileHdr.BssDataSize)
381         InitAuxFileHdr.BssDataSize = InitSections[I].Size;
382       if (!InitAuxFileHdr.SecNumOfBSS)
383         InitAuxFileHdr.SecNumOfBSS = I + 1;
384       break;
385     case XCOFF::STYP_TDATA:
386       if (!InitAuxFileHdr.SecNumOfTData)
387         InitAuxFileHdr.SecNumOfTData = I + 1;
388       break;
389     case XCOFF::STYP_TBSS:
390       if (!InitAuxFileHdr.SecNumOfTBSS)
391         InitAuxFileHdr.SecNumOfTBSS = I + 1;
392       break;
393     case XCOFF::STYP_LOADER:
394       if (!InitAuxFileHdr.SecNumOfLoader)
395         InitAuxFileHdr.SecNumOfLoader = I + 1;
396       break;
397     default:
398       break;
399     }
400   }
401 }
402 
403 bool XCOFFWriter::assignAddressesAndIndices() {
404   uint64_t FileHdrSize =
405       Is64Bit ? XCOFF::FileHeaderSize64 : XCOFF::FileHeaderSize32;
406 
407   // If AuxHeaderSize is specified in the YAML file, we construct
408   // an auxiliary header.
409   uint64_t AuxFileHdrSize = 0;
410 
411   if (Obj.Header.AuxHeaderSize)
412     AuxFileHdrSize = Obj.Header.AuxHeaderSize;
413   else if (Obj.AuxHeader)
414     AuxFileHdrSize =
415         (Is64Bit ? XCOFF::AuxFileHeaderSize64 : XCOFF::AuxFileHeaderSize32);
416   uint64_t SecHdrSize =
417       Is64Bit ? XCOFF::SectionHeaderSize64 : XCOFF::SectionHeaderSize32;
418   uint64_t CurrentOffset =
419       FileHdrSize + AuxFileHdrSize + InitSections.size() * SecHdrSize;
420 
421   // Calculate section header info.
422   if (!initSectionHeaders(CurrentOffset))
423     return false;
424 
425   // Calculate file header info.
426   if (!initFileHeader(CurrentOffset))
427     return false;
428   InitFileHdr.AuxHeaderSize = AuxFileHdrSize;
429 
430   // Initialize the auxiliary file header.
431   if (AuxFileHdrSize)
432     initAuxFileHeader();
433 
434   // Initialize the string table.
435   return initStringTable();
436 }
437 
438 void XCOFFWriter::writeFileHeader() {
439   W.write<uint16_t>(Obj.Header.Magic ? Obj.Header.Magic : InitFileHdr.Magic);
440   W.write<uint16_t>(Obj.Header.NumberOfSections ? Obj.Header.NumberOfSections
441                                                 : InitFileHdr.NumberOfSections);
442   W.write<int32_t>(Obj.Header.TimeStamp);
443   if (Is64Bit) {
444     W.write<uint64_t>(InitFileHdr.SymbolTableOffset);
445     W.write<uint16_t>(InitFileHdr.AuxHeaderSize);
446     W.write<uint16_t>(Obj.Header.Flags);
447     W.write<int32_t>(Obj.Header.NumberOfSymTableEntries
448                          ? Obj.Header.NumberOfSymTableEntries
449                          : InitFileHdr.NumberOfSymTableEntries);
450   } else {
451     W.write<uint32_t>(InitFileHdr.SymbolTableOffset);
452     W.write<int32_t>(Obj.Header.NumberOfSymTableEntries
453                          ? Obj.Header.NumberOfSymTableEntries
454                          : InitFileHdr.NumberOfSymTableEntries);
455     W.write<uint16_t>(InitFileHdr.AuxHeaderSize);
456     W.write<uint16_t>(Obj.Header.Flags);
457   }
458 }
459 
460 void XCOFFWriter::writeAuxFileHeader() {
461   W.write<uint16_t>(InitAuxFileHdr.Magic.value_or(yaml::Hex16(1)));
462   W.write<uint16_t>(InitAuxFileHdr.Version.value_or(yaml::Hex16(1)));
463   if (Is64Bit) {
464     W.OS.write_zeros(4); // Reserved for debugger.
465     W.write<uint64_t>(InitAuxFileHdr.TextStartAddr.value_or(yaml::Hex64(0)));
466     W.write<uint64_t>(InitAuxFileHdr.DataStartAddr.value_or(yaml::Hex64(0)));
467     W.write<uint64_t>(InitAuxFileHdr.TOCAnchorAddr.value_or(yaml::Hex64(0)));
468   } else {
469     W.write<uint32_t>(InitAuxFileHdr.TextSize.value_or(yaml::Hex64(0)));
470     W.write<uint32_t>(InitAuxFileHdr.InitDataSize.value_or(yaml::Hex64(0)));
471     W.write<uint32_t>(InitAuxFileHdr.BssDataSize.value_or(yaml::Hex64(0)));
472     W.write<uint32_t>(InitAuxFileHdr.EntryPointAddr.value_or(yaml::Hex64(0)));
473     W.write<uint32_t>(InitAuxFileHdr.TextStartAddr.value_or(yaml::Hex64(0)));
474     W.write<uint32_t>(InitAuxFileHdr.DataStartAddr.value_or(yaml::Hex64(0)));
475     // A short 32-bit auxiliary header ends here.
476     if (InitFileHdr.AuxHeaderSize == XCOFF::AuxFileHeaderSizeShort)
477       return;
478     W.write<uint32_t>(InitAuxFileHdr.TOCAnchorAddr.value_or(yaml::Hex64(0)));
479   }
480   W.write<uint16_t>(InitAuxFileHdr.SecNumOfEntryPoint.value_or(0));
481   W.write<uint16_t>(InitAuxFileHdr.SecNumOfText.value_or(0));
482   W.write<uint16_t>(InitAuxFileHdr.SecNumOfData.value_or(0));
483   W.write<uint16_t>(InitAuxFileHdr.SecNumOfTOC.value_or(0));
484   W.write<uint16_t>(InitAuxFileHdr.SecNumOfLoader.value_or(0));
485   W.write<uint16_t>(InitAuxFileHdr.SecNumOfBSS.value_or(0));
486   W.write<uint16_t>(InitAuxFileHdr.MaxAlignOfText.value_or(yaml::Hex16(0)));
487   W.write<uint16_t>(InitAuxFileHdr.MaxAlignOfData.value_or(yaml::Hex16(0)));
488   W.write<uint16_t>(InitAuxFileHdr.ModuleType.value_or(yaml::Hex16(0)));
489   W.write<uint8_t>(InitAuxFileHdr.CpuFlag.value_or(yaml::Hex8(0)));
490   W.write<uint8_t>(0); // Reserved for CPU type.
491   if (Is64Bit) {
492     W.write<uint8_t>(InitAuxFileHdr.TextPageSize.value_or(yaml::Hex8(0)));
493     W.write<uint8_t>(InitAuxFileHdr.DataPageSize.value_or(yaml::Hex8(0)));
494     W.write<uint8_t>(InitAuxFileHdr.StackPageSize.value_or(yaml::Hex8(0)));
495     W.write<uint8_t>(
496         InitAuxFileHdr.FlagAndTDataAlignment.value_or(yaml::Hex8(0x80)));
497     W.write<uint64_t>(InitAuxFileHdr.TextSize.value_or(yaml::Hex64(0)));
498     W.write<uint64_t>(InitAuxFileHdr.InitDataSize.value_or(yaml::Hex64(0)));
499     W.write<uint64_t>(InitAuxFileHdr.BssDataSize.value_or(yaml::Hex64(0)));
500     W.write<uint64_t>(InitAuxFileHdr.EntryPointAddr.value_or(yaml::Hex64(0)));
501     W.write<uint64_t>(InitAuxFileHdr.MaxStackSize.value_or(yaml::Hex64(0)));
502     W.write<uint64_t>(InitAuxFileHdr.MaxDataSize.value_or(yaml::Hex64(0)));
503   } else {
504     W.write<uint32_t>(InitAuxFileHdr.MaxStackSize.value_or(yaml::Hex64(0)));
505     W.write<uint32_t>(InitAuxFileHdr.MaxDataSize.value_or(yaml::Hex64(0)));
506     W.OS.write_zeros(4); // Reserved for debugger.
507     W.write<uint8_t>(InitAuxFileHdr.TextPageSize.value_or(yaml::Hex8(0)));
508     W.write<uint8_t>(InitAuxFileHdr.DataPageSize.value_or(yaml::Hex8(0)));
509     W.write<uint8_t>(InitAuxFileHdr.StackPageSize.value_or(yaml::Hex8(0)));
510     W.write<uint8_t>(
511         InitAuxFileHdr.FlagAndTDataAlignment.value_or(yaml::Hex8(0)));
512   }
513   W.write<uint16_t>(InitAuxFileHdr.SecNumOfTData.value_or(0));
514   W.write<uint16_t>(InitAuxFileHdr.SecNumOfTBSS.value_or(0));
515   if (Is64Bit) {
516     W.write<uint16_t>(
517         InitAuxFileHdr.Flag.value_or(yaml::Hex16(XCOFF::SHR_SYMTAB)));
518     if (InitFileHdr.AuxHeaderSize > XCOFF::AuxFileHeaderSize64)
519       W.OS.write_zeros(InitFileHdr.AuxHeaderSize - XCOFF::AuxFileHeaderSize64);
520   } else {
521     if (InitFileHdr.AuxHeaderSize > XCOFF::AuxFileHeaderSize32)
522       W.OS.write_zeros(InitFileHdr.AuxHeaderSize - XCOFF::AuxFileHeaderSize32);
523   }
524 }
525 
526 void XCOFFWriter::writeSectionHeaders() {
527   for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
528     XCOFFYAML::Section DerivedSec = InitSections[I];
529     writeName(DerivedSec.SectionName, W);
530     if (Is64Bit) {
531       // Virtual address is the same as physical address.
532       W.write<uint64_t>(DerivedSec.Address); // Physical address
533       W.write<uint64_t>(DerivedSec.Address); // Virtual address
534       W.write<uint64_t>(DerivedSec.Size);
535       W.write<uint64_t>(DerivedSec.FileOffsetToData);
536       W.write<uint64_t>(DerivedSec.FileOffsetToRelocations);
537       W.write<uint64_t>(DerivedSec.FileOffsetToLineNumbers);
538       W.write<uint32_t>(DerivedSec.NumberOfRelocations);
539       W.write<uint32_t>(DerivedSec.NumberOfLineNumbers);
540       W.write<int32_t>(DerivedSec.Flags);
541       W.OS.write_zeros(4);
542     } else {
543       // Virtual address is the same as physical address.
544       W.write<uint32_t>(DerivedSec.Address); // Physical address
545       W.write<uint32_t>(DerivedSec.Address); // Virtual address
546       W.write<uint32_t>(DerivedSec.Size);
547       W.write<uint32_t>(DerivedSec.FileOffsetToData);
548       W.write<uint32_t>(DerivedSec.FileOffsetToRelocations);
549       W.write<uint32_t>(DerivedSec.FileOffsetToLineNumbers);
550       W.write<uint16_t>(DerivedSec.NumberOfRelocations);
551       W.write<uint16_t>(DerivedSec.NumberOfLineNumbers);
552       W.write<int32_t>(DerivedSec.Flags);
553     }
554   }
555 }
556 
557 bool XCOFFWriter::writeSectionData() {
558   for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
559     XCOFFYAML::Section YamlSec = Obj.Sections[I];
560     if (YamlSec.SectionData.binary_size()) {
561       // Fill the padding size with zeros.
562       int64_t PaddingSize = (uint64_t)InitSections[I].FileOffsetToData -
563                             (W.OS.tell() - StartOffset);
564       if (PaddingSize < 0) {
565         ErrHandler("redundant data was written before section data");
566         return false;
567       }
568       W.OS.write_zeros(PaddingSize);
569       YamlSec.SectionData.writeAsBinary(W.OS);
570     }
571   }
572   return true;
573 }
574 
575 bool XCOFFWriter::writeRelocations() {
576   for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
577     XCOFFYAML::Section YamlSec = Obj.Sections[I];
578     if (!YamlSec.Relocations.empty()) {
579       int64_t PaddingSize =
580           InitSections[I].FileOffsetToRelocations - (W.OS.tell() - StartOffset);
581       if (PaddingSize < 0) {
582         ErrHandler("redundant data was written before relocations");
583         return false;
584       }
585       W.OS.write_zeros(PaddingSize);
586       for (const XCOFFYAML::Relocation &YamlRel : YamlSec.Relocations) {
587         if (Is64Bit)
588           W.write<uint64_t>(YamlRel.VirtualAddress);
589         else
590           W.write<uint32_t>(YamlRel.VirtualAddress);
591         W.write<uint32_t>(YamlRel.SymbolIndex);
592         W.write<uint8_t>(YamlRel.Info);
593         W.write<uint8_t>(YamlRel.Type);
594       }
595     }
596   }
597   return true;
598 }
599 
600 bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::CsectAuxEnt &AuxSym) {
601   uint8_t SymAlignAndType = 0;
602   if (AuxSym.SymbolAlignmentAndType) {
603     if (AuxSym.SymbolType || AuxSym.SymbolAlignment) {
604       ErrHandler("cannot specify SymbolType or SymbolAlignment if "
605                  "SymbolAlignmentAndType is specified");
606       return false;
607     }
608     SymAlignAndType = *AuxSym.SymbolAlignmentAndType;
609   } else {
610     if (AuxSym.SymbolType) {
611       uint8_t SymbolType = *AuxSym.SymbolType;
612       if (SymbolType & ~XCOFFCsectAuxRef::SymbolTypeMask) {
613         ErrHandler("symbol type must be less than " +
614                    Twine(1 + XCOFFCsectAuxRef::SymbolTypeMask));
615         return false;
616       }
617       SymAlignAndType = SymbolType;
618     }
619     if (AuxSym.SymbolAlignment) {
620       const uint8_t ShiftedSymbolAlignmentMask =
621           XCOFFCsectAuxRef::SymbolAlignmentMask >>
622           XCOFFCsectAuxRef::SymbolAlignmentBitOffset;
623 
624       if (*AuxSym.SymbolAlignment & ~ShiftedSymbolAlignmentMask) {
625         ErrHandler("symbol alignment must be less than " +
626                    Twine(1 + ShiftedSymbolAlignmentMask));
627         return false;
628       }
629       SymAlignAndType |= (*AuxSym.SymbolAlignment
630                           << XCOFFCsectAuxRef::SymbolAlignmentBitOffset);
631     }
632   }
633   if (Is64Bit) {
634     W.write<uint32_t>(AuxSym.SectionOrLengthLo.value_or(0));
635     W.write<uint32_t>(AuxSym.ParameterHashIndex.value_or(0));
636     W.write<uint16_t>(AuxSym.TypeChkSectNum.value_or(0));
637     W.write<uint8_t>(SymAlignAndType);
638     W.write<uint8_t>(AuxSym.StorageMappingClass.value_or(XCOFF::XMC_PR));
639     W.write<uint32_t>(AuxSym.SectionOrLengthHi.value_or(0));
640     W.write<uint8_t>(0);
641     W.write<uint8_t>(XCOFF::AUX_CSECT);
642   } else {
643     W.write<uint32_t>(AuxSym.SectionOrLength.value_or(0));
644     W.write<uint32_t>(AuxSym.ParameterHashIndex.value_or(0));
645     W.write<uint16_t>(AuxSym.TypeChkSectNum.value_or(0));
646     W.write<uint8_t>(SymAlignAndType);
647     W.write<uint8_t>(AuxSym.StorageMappingClass.value_or(XCOFF::XMC_PR));
648     W.write<uint32_t>(AuxSym.StabInfoIndex.value_or(0));
649     W.write<uint16_t>(AuxSym.StabSectNum.value_or(0));
650   }
651   return true;
652 }
653 
654 bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::ExcpetionAuxEnt &AuxSym) {
655   assert(Is64Bit && "can't write the exception auxiliary symbol for XCOFF32");
656   W.write<uint64_t>(AuxSym.OffsetToExceptionTbl.value_or(0));
657   W.write<uint32_t>(AuxSym.SizeOfFunction.value_or(0));
658   W.write<uint32_t>(AuxSym.SymIdxOfNextBeyond.value_or(0));
659   W.write<uint8_t>(0);
660   W.write<uint8_t>(XCOFF::AUX_EXCEPT);
661   return true;
662 }
663 
664 bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::FunctionAuxEnt &AuxSym) {
665   if (Is64Bit) {
666     W.write<uint64_t>(AuxSym.PtrToLineNum.value_or(0));
667     W.write<uint32_t>(AuxSym.SizeOfFunction.value_or(0));
668     W.write<uint32_t>(AuxSym.SymIdxOfNextBeyond.value_or(0));
669     W.write<uint8_t>(0);
670     W.write<uint8_t>(XCOFF::AUX_FCN);
671   } else {
672     W.write<uint32_t>(AuxSym.OffsetToExceptionTbl.value_or(0));
673     W.write<uint32_t>(AuxSym.SizeOfFunction.value_or(0));
674     W.write<uint32_t>(AuxSym.PtrToLineNum.value_or(0));
675     W.write<uint32_t>(AuxSym.SymIdxOfNextBeyond.value_or(0));
676     W.OS.write_zeros(2);
677   }
678   return true;
679 }
680 
681 bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::FileAuxEnt &AuxSym) {
682   StringRef FileName = AuxSym.FileNameOrString.value_or("");
683   if (nameShouldBeInStringTable(FileName)) {
684     W.write<int32_t>(0);
685     W.write<uint32_t>(StrTblBuilder.getOffset(FileName));
686   } else {
687     writeName(FileName, W);
688   }
689   W.OS.write_zeros(XCOFF::FileNamePadSize);
690   W.write<uint8_t>(AuxSym.FileStringType.value_or(XCOFF::XFT_FN));
691   if (Is64Bit) {
692     W.OS.write_zeros(2);
693     W.write<uint8_t>(XCOFF::AUX_FILE);
694   } else {
695     W.OS.write_zeros(3);
696   }
697   return true;
698 }
699 
700 bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::BlockAuxEnt &AuxSym) {
701   if (Is64Bit) {
702     W.write<uint32_t>(AuxSym.LineNum.value_or(0));
703     W.OS.write_zeros(13);
704     W.write<uint8_t>(XCOFF::AUX_SYM);
705   } else {
706     W.OS.write_zeros(2);
707     W.write<uint16_t>(AuxSym.LineNumHi.value_or(0));
708     W.write<uint16_t>(AuxSym.LineNumLo.value_or(0));
709     W.OS.write_zeros(12);
710   }
711   return true;
712 }
713 
714 bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::SectAuxEntForDWARF &AuxSym) {
715   if (Is64Bit) {
716     W.write<uint64_t>(AuxSym.LengthOfSectionPortion.value_or(0));
717     W.write<uint64_t>(AuxSym.NumberOfRelocEnt.value_or(0));
718     W.write<uint8_t>(0);
719     W.write<uint8_t>(XCOFF::AUX_SECT);
720   } else {
721     W.write<uint32_t>(AuxSym.LengthOfSectionPortion.value_or(0));
722     W.OS.write_zeros(4);
723     W.write<uint32_t>(AuxSym.NumberOfRelocEnt.value_or(0));
724     W.OS.write_zeros(6);
725   }
726   return true;
727 }
728 
729 bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::SectAuxEntForStat &AuxSym) {
730   assert(!Is64Bit && "can't write the stat auxiliary symbol for XCOFF64");
731   W.write<uint32_t>(AuxSym.SectionLength.value_or(0));
732   W.write<uint16_t>(AuxSym.NumberOfRelocEnt.value_or(0));
733   W.write<uint16_t>(AuxSym.NumberOfLineNum.value_or(0));
734   W.OS.write_zeros(10);
735   return true;
736 }
737 
738 bool XCOFFWriter::writeAuxSymbol(
739     const std::unique_ptr<XCOFFYAML::AuxSymbolEnt> &AuxSym) {
740   if (auto AS = dyn_cast<XCOFFYAML::CsectAuxEnt>(AuxSym.get()))
741     return writeAuxSymbol(*AS);
742   else if (auto AS = dyn_cast<XCOFFYAML::FunctionAuxEnt>(AuxSym.get()))
743     return writeAuxSymbol(*AS);
744   else if (auto AS = dyn_cast<XCOFFYAML::ExcpetionAuxEnt>(AuxSym.get()))
745     return writeAuxSymbol(*AS);
746   else if (auto AS = dyn_cast<XCOFFYAML::FileAuxEnt>(AuxSym.get()))
747     return writeAuxSymbol(*AS);
748   else if (auto AS = dyn_cast<XCOFFYAML::BlockAuxEnt>(AuxSym.get()))
749     return writeAuxSymbol(*AS);
750   else if (auto AS = dyn_cast<XCOFFYAML::SectAuxEntForDWARF>(AuxSym.get()))
751     return writeAuxSymbol(*AS);
752   else if (auto AS = dyn_cast<XCOFFYAML::SectAuxEntForStat>(AuxSym.get()))
753     return writeAuxSymbol(*AS);
754   llvm_unreachable("unknown auxiliary symbol type");
755   return false;
756 }
757 
758 bool XCOFFWriter::writeSymbols() {
759   int64_t PaddingSize =
760       InitFileHdr.SymbolTableOffset - (W.OS.tell() - StartOffset);
761   if (PaddingSize < 0) {
762     ErrHandler("redundant data was written before symbols");
763     return false;
764   }
765   W.OS.write_zeros(PaddingSize);
766   for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
767     if (Is64Bit) {
768       W.write<uint64_t>(YamlSym.Value);
769       W.write<uint32_t>(StrTblBuilder.getOffset(YamlSym.SymbolName));
770     } else {
771       if (nameShouldBeInStringTable(YamlSym.SymbolName)) {
772         // For XCOFF32: A value of 0 indicates that the symbol name is in the
773         // string table.
774         W.write<int32_t>(0);
775         W.write<uint32_t>(StrTblBuilder.getOffset(YamlSym.SymbolName));
776       } else {
777         writeName(YamlSym.SymbolName, W);
778       }
779       W.write<uint32_t>(YamlSym.Value);
780     }
781     if (YamlSym.SectionName) {
782       if (!SectionIndexMap.count(*YamlSym.SectionName)) {
783         ErrHandler("the SectionName " + *YamlSym.SectionName +
784                    " specified in the symbol does not exist");
785         return false;
786       }
787       if (YamlSym.SectionIndex &&
788           SectionIndexMap[*YamlSym.SectionName] != *YamlSym.SectionIndex) {
789         ErrHandler("the SectionName " + *YamlSym.SectionName +
790                    " and the SectionIndex (" + Twine(*YamlSym.SectionIndex) +
791                    ") refer to different sections");
792         return false;
793       }
794       W.write<int16_t>(SectionIndexMap[*YamlSym.SectionName]);
795     } else {
796       W.write<int16_t>(YamlSym.SectionIndex.value_or(0));
797     }
798     W.write<uint16_t>(YamlSym.Type);
799     W.write<uint8_t>(YamlSym.StorageClass);
800 
801     uint8_t NumOfAuxSym = YamlSym.NumberOfAuxEntries.value_or(0);
802     W.write<uint8_t>(NumOfAuxSym);
803 
804     if (!NumOfAuxSym && !YamlSym.AuxEntries.size())
805       continue;
806 
807     // Now write auxiliary entries.
808     if (!YamlSym.AuxEntries.size()) {
809       W.OS.write_zeros(XCOFF::SymbolTableEntrySize * NumOfAuxSym);
810     } else {
811       for (const std::unique_ptr<XCOFFYAML::AuxSymbolEnt> &AuxSym :
812            YamlSym.AuxEntries) {
813         if (!writeAuxSymbol(AuxSym))
814           return false;
815       }
816       // Pad with zeros.
817       if (NumOfAuxSym > YamlSym.AuxEntries.size())
818         W.OS.write_zeros(XCOFF::SymbolTableEntrySize *
819                          (NumOfAuxSym - YamlSym.AuxEntries.size()));
820     }
821   }
822   return true;
823 }
824 
825 void XCOFFWriter::writeStringTable() {
826   if (Obj.StrTbl.RawContent) {
827     Obj.StrTbl.RawContent->writeAsBinary(W.OS);
828     if (Obj.StrTbl.ContentSize) {
829       assert(*Obj.StrTbl.ContentSize >= Obj.StrTbl.RawContent->binary_size() &&
830              "Specified ContentSize is less than the RawContent size.");
831       W.OS.write_zeros(*Obj.StrTbl.ContentSize -
832                        Obj.StrTbl.RawContent->binary_size());
833     }
834     return;
835   }
836 
837   size_t StrTblBuilderSize = StrTblBuilder.getSize();
838   // If neither Length nor ContentSize is specified, write the StrTblBuilder
839   // directly, which contains the auto-generated Length value.
840   if (!Obj.StrTbl.Length && !Obj.StrTbl.ContentSize) {
841     if (StrTblBuilderSize <= 4)
842       return;
843     StrTblBuilder.write(W.OS);
844     return;
845   }
846 
847   // Serialize the string table's content to a temporary buffer.
848   std::unique_ptr<WritableMemoryBuffer> Buf =
849       WritableMemoryBuffer::getNewMemBuffer(StrTblBuilderSize);
850   uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart());
851   StrTblBuilder.write(Ptr);
852   // Replace the first 4 bytes, which contain the auto-generated Length value,
853   // with the specified value.
854   memset(Ptr, 0, 4);
855   support::endian::write32be(Ptr, Obj.StrTbl.Length ? *Obj.StrTbl.Length
856                                                     : *Obj.StrTbl.ContentSize);
857   // Copy the buffer content to the actual output stream.
858   W.OS.write(Buf->getBufferStart(), Buf->getBufferSize());
859   // Add zeros as padding after strings.
860   if (Obj.StrTbl.ContentSize) {
861     assert(*Obj.StrTbl.ContentSize >= StrTblBuilderSize &&
862            "Specified ContentSize is less than the StringTableBuilder size.");
863     W.OS.write_zeros(*Obj.StrTbl.ContentSize - StrTblBuilderSize);
864   }
865 }
866 
867 bool XCOFFWriter::writeXCOFF() {
868   if (!assignAddressesAndIndices())
869     return false;
870   StartOffset = W.OS.tell();
871   writeFileHeader();
872   if (InitFileHdr.AuxHeaderSize)
873     writeAuxFileHeader();
874   if (!Obj.Sections.empty()) {
875     writeSectionHeaders();
876     if (!writeSectionData())
877       return false;
878     if (!writeRelocations())
879       return false;
880   }
881   if (!Obj.Symbols.empty() && !writeSymbols())
882     return false;
883   writeStringTable();
884   return true;
885 }
886 
887 } // end anonymous namespace
888 
889 namespace llvm {
890 namespace yaml {
891 
892 bool yaml2xcoff(XCOFFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH) {
893   XCOFFWriter Writer(Doc, Out, EH);
894   return Writer.writeXCOFF();
895 }
896 
897 } // namespace yaml
898 } // namespace llvm
899