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