xref: /freebsd/contrib/llvm-project/llvm/lib/ObjectYAML/XCOFFEmitter.cpp (revision 5e801ac66d24704442eba426ed13c3effb8a34e7)
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   void writeFileHeader();
51   void writeAuxFileHeader();
52   void writeSectionHeader();
53   bool writeSectionData();
54   bool writeRelocations();
55   bool writeSymbols();
56   void writeStringTable();
57 
58   XCOFFYAML::Object &Obj;
59   bool Is64Bit = false;
60   support::endian::Writer W;
61   yaml::ErrorHandler ErrHandler;
62   StringTableBuilder StrTblBuilder;
63   uint64_t StartOffset;
64   // Map the section name to its corrresponding section index.
65   DenseMap<StringRef, int16_t> SectionIndexMap = {
66       {StringRef("N_DEBUG"), XCOFF::N_DEBUG},
67       {StringRef("N_ABS"), XCOFF::N_ABS},
68       {StringRef("N_UNDEF"), XCOFF::N_UNDEF}};
69   XCOFFYAML::FileHeader InitFileHdr = Obj.Header;
70   XCOFFYAML::AuxiliaryHeader InitAuxFileHdr;
71   std::vector<XCOFFYAML::Section> InitSections = Obj.Sections;
72 };
73 
74 static void writeName(StringRef StrName, support::endian::Writer W) {
75   char Name[XCOFF::NameSize];
76   memset(Name, 0, XCOFF::NameSize);
77   char SrcName[] = "";
78   memcpy(Name, StrName.size() ? StrName.data() : SrcName, StrName.size());
79   ArrayRef<char> NameRef(Name, XCOFF::NameSize);
80   W.write(NameRef);
81 }
82 
83 bool XCOFFWriter::nameShouldBeInStringTable(StringRef SymbolName) {
84   // For XCOFF64: The symbol name is always in the string table.
85   return (SymbolName.size() > XCOFF::NameSize) || Is64Bit;
86 }
87 
88 bool XCOFFWriter::initRelocations(uint64_t &CurrentOffset) {
89   for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) {
90     if (!InitSections[I].Relocations.empty()) {
91       InitSections[I].NumberOfRelocations = InitSections[I].Relocations.size();
92       InitSections[I].FileOffsetToRelocations = CurrentOffset;
93       uint64_t RelSize = Is64Bit ? XCOFF::RelocationSerializationSize64
94                                  : XCOFF::RelocationSerializationSize32;
95       CurrentOffset += InitSections[I].NumberOfRelocations * RelSize;
96       if (CurrentOffset > MaxRawDataSize) {
97         ErrHandler("maximum object size of" + Twine(MaxRawDataSize) +
98                    "exceeded when writing relocation data");
99         return false;
100       }
101     }
102   }
103   return true;
104 }
105 
106 bool XCOFFWriter::initSectionHeader(uint64_t &CurrentOffset) {
107   uint64_t CurrentSecAddr = 0;
108   for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) {
109     if (CurrentOffset > MaxRawDataSize) {
110       ErrHandler("maximum object size of" + Twine(MaxRawDataSize) +
111                  "exceeded when writing section data");
112       return false;
113     }
114 
115     // Assign indices for sections.
116     if (InitSections[I].SectionName.size() &&
117         !SectionIndexMap[InitSections[I].SectionName]) {
118       // The section index starts from 1.
119       SectionIndexMap[InitSections[I].SectionName] = I + 1;
120       if ((I + 1) > MaxSectionIndex) {
121         ErrHandler("exceeded the maximum permitted section index of " +
122                    Twine(MaxSectionIndex));
123         return false;
124       }
125     }
126 
127     // Calculate the physical/virtual address. This field should contain 0 for
128     // all sections except the text, data and bss sections.
129     if (InitSections[I].Flags != XCOFF::STYP_TEXT &&
130         InitSections[I].Flags != XCOFF::STYP_DATA &&
131         InitSections[I].Flags != XCOFF::STYP_BSS)
132       InitSections[I].Address = 0;
133     else
134       InitSections[I].Address = CurrentSecAddr;
135 
136     // Calculate the FileOffsetToData and data size for sections.
137     if (InitSections[I].SectionData.binary_size()) {
138       InitSections[I].FileOffsetToData = CurrentOffset;
139       CurrentOffset += InitSections[I].SectionData.binary_size();
140       // Ensure the offset is aligned to DefaultSectionAlign.
141       CurrentOffset = alignTo(CurrentOffset, DefaultSectionAlign);
142       InitSections[I].Size = CurrentOffset - InitSections[I].FileOffsetToData;
143       CurrentSecAddr += InitSections[I].Size;
144     }
145   }
146   return initRelocations(CurrentOffset);
147 }
148 
149 bool XCOFFWriter::initStringTable() {
150   if (Obj.StrTbl.RawContent) {
151     size_t RawSize = Obj.StrTbl.RawContent->binary_size();
152     if (Obj.StrTbl.Strings || Obj.StrTbl.Length) {
153       ErrHandler(
154           "can't specify Strings or Length when RawContent is specified");
155       return false;
156     }
157     if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize < RawSize) {
158       ErrHandler("specified ContentSize (" + Twine(*Obj.StrTbl.ContentSize) +
159                  ") is less than the RawContent data size (" + Twine(RawSize) +
160                  ")");
161       return false;
162     }
163     return true;
164   }
165   if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize <= 3) {
166     ErrHandler("ContentSize shouldn't be less than 4 without RawContent");
167     return false;
168   }
169 
170   // Build the string table.
171   StrTblBuilder.clear();
172 
173   if (Obj.StrTbl.Strings) {
174     // All specified strings should be added to the string table.
175     for (StringRef StringEnt : *Obj.StrTbl.Strings)
176       StrTblBuilder.add(StringEnt);
177 
178     size_t StrTblIdx = 0;
179     size_t NumOfStrings = Obj.StrTbl.Strings->size();
180     for (XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
181       if (nameShouldBeInStringTable(YamlSym.SymbolName)) {
182         if (StrTblIdx < NumOfStrings) {
183           // Overwrite the symbol name with the specified string.
184           YamlSym.SymbolName = (*Obj.StrTbl.Strings)[StrTblIdx];
185           ++StrTblIdx;
186         } else
187           // Names that are not overwritten are still stored in the string
188           // table.
189           StrTblBuilder.add(YamlSym.SymbolName);
190       }
191     }
192   } else {
193     for (XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
194       if (nameShouldBeInStringTable(YamlSym.SymbolName))
195         StrTblBuilder.add(YamlSym.SymbolName);
196     }
197   }
198 
199   StrTblBuilder.finalize();
200 
201   size_t StrTblSize = StrTblBuilder.getSize();
202   if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize < StrTblSize) {
203     ErrHandler("specified ContentSize (" + Twine(*Obj.StrTbl.ContentSize) +
204                ") is less than the size of the data that would otherwise be "
205                "written (" +
206                Twine(StrTblSize) + ")");
207     return false;
208   }
209 
210   return true;
211 }
212 
213 bool XCOFFWriter::initFileHeader(uint64_t CurrentOffset) {
214   // The default format of the object file is XCOFF32.
215   InitFileHdr.Magic = XCOFF::XCOFF32;
216   InitFileHdr.NumberOfSections = Obj.Sections.size();
217   InitFileHdr.NumberOfSymTableEntries = Obj.Symbols.size();
218 
219   for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols)
220     // Add the number of auxiliary symbols to the total number.
221     InitFileHdr.NumberOfSymTableEntries += YamlSym.NumberOfAuxEntries;
222 
223   // Calculate SymbolTableOffset for the file header.
224   if (InitFileHdr.NumberOfSymTableEntries) {
225     InitFileHdr.SymbolTableOffset = CurrentOffset;
226     CurrentOffset +=
227         InitFileHdr.NumberOfSymTableEntries * XCOFF::SymbolTableEntrySize;
228     if (CurrentOffset > MaxRawDataSize) {
229       ErrHandler("maximum object size of" + Twine(MaxRawDataSize) +
230                  "exceeded when writing symbols");
231       return false;
232     }
233   }
234   // TODO: Calculate FileOffsetToLineNumbers when line number supported.
235   return true;
236 }
237 
238 void XCOFFWriter::initAuxFileHeader() {
239   InitAuxFileHdr = *Obj.AuxHeader;
240   // In general, an object file might contain multiple sections of a given type,
241   // but in a loadable module, there must be exactly one .text, .data, .bss, and
242   // .loader section. A loadable object might also have one .tdata section and
243   // one .tbss section.
244   // Set these section-related values if not set explicitly. We assume that the
245   // input YAML matches the format of the loadable object, but if multiple input
246   // sections still have the same type, the first section with that type
247   // prevails.
248   for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) {
249     switch (InitSections[I].Flags) {
250     case XCOFF::STYP_TEXT:
251       if (!InitAuxFileHdr.TextSize)
252         InitAuxFileHdr.TextSize = InitSections[I].Size;
253       if (!InitAuxFileHdr.TextStartAddr)
254         InitAuxFileHdr.TextStartAddr = InitSections[I].Address;
255       if (!InitAuxFileHdr.SecNumOfText)
256         InitAuxFileHdr.SecNumOfText = I + 1;
257       break;
258     case XCOFF::STYP_DATA:
259       if (!InitAuxFileHdr.InitDataSize)
260         InitAuxFileHdr.InitDataSize = InitSections[I].Size;
261       if (!InitAuxFileHdr.DataStartAddr)
262         InitAuxFileHdr.DataStartAddr = InitSections[I].Address;
263       if (!InitAuxFileHdr.SecNumOfData)
264         InitAuxFileHdr.SecNumOfData = I + 1;
265       break;
266     case XCOFF::STYP_BSS:
267       if (!InitAuxFileHdr.BssDataSize)
268         InitAuxFileHdr.BssDataSize = InitSections[I].Size;
269       if (!InitAuxFileHdr.SecNumOfBSS)
270         InitAuxFileHdr.SecNumOfBSS = I + 1;
271       break;
272     case XCOFF::STYP_TDATA:
273       if (!InitAuxFileHdr.SecNumOfTData)
274         InitAuxFileHdr.SecNumOfTData = I + 1;
275       break;
276     case XCOFF::STYP_TBSS:
277       if (!InitAuxFileHdr.SecNumOfTBSS)
278         InitAuxFileHdr.SecNumOfTBSS = I + 1;
279       break;
280     case XCOFF::STYP_LOADER:
281       if (!InitAuxFileHdr.SecNumOfLoader)
282         InitAuxFileHdr.SecNumOfLoader = I + 1;
283       break;
284     default:
285       break;
286     }
287   }
288 }
289 
290 bool XCOFFWriter::assignAddressesAndIndices() {
291   uint64_t FileHdrSize =
292       Is64Bit ? XCOFF::FileHeaderSize64 : XCOFF::FileHeaderSize32;
293   uint64_t AuxFileHdrSize = 0;
294   if (Obj.AuxHeader)
295     AuxFileHdrSize = Obj.Header.AuxHeaderSize
296                          ? Obj.Header.AuxHeaderSize
297                          : (Is64Bit ? XCOFF::AuxFileHeaderSize64
298                                     : XCOFF::AuxFileHeaderSize32);
299   uint64_t SecHdrSize =
300       Is64Bit ? XCOFF::SectionHeaderSize64 : XCOFF::SectionHeaderSize32;
301   uint64_t CurrentOffset =
302       FileHdrSize + AuxFileHdrSize + InitSections.size() * SecHdrSize;
303 
304   // Calculate section header info.
305   if (!initSectionHeader(CurrentOffset))
306     return false;
307   InitFileHdr.AuxHeaderSize = AuxFileHdrSize;
308 
309   // Calculate file header info.
310   if (!initFileHeader(CurrentOffset))
311     return false;
312 
313   // Initialize the auxiliary file header.
314   if (Obj.AuxHeader)
315     initAuxFileHeader();
316 
317   // Initialize the string table.
318   return initStringTable();
319 }
320 
321 void XCOFFWriter::writeFileHeader() {
322   W.write<uint16_t>(Obj.Header.Magic ? Obj.Header.Magic : InitFileHdr.Magic);
323   W.write<uint16_t>(Obj.Header.NumberOfSections ? Obj.Header.NumberOfSections
324                                                 : InitFileHdr.NumberOfSections);
325   W.write<int32_t>(Obj.Header.TimeStamp);
326   if (Is64Bit) {
327     W.write<uint64_t>(Obj.Header.SymbolTableOffset
328                           ? Obj.Header.SymbolTableOffset
329                           : InitFileHdr.SymbolTableOffset);
330     W.write<uint16_t>(InitFileHdr.AuxHeaderSize);
331     W.write<uint16_t>(Obj.Header.Flags);
332     W.write<int32_t>(Obj.Header.NumberOfSymTableEntries
333                          ? Obj.Header.NumberOfSymTableEntries
334                          : InitFileHdr.NumberOfSymTableEntries);
335   } else {
336     W.write<uint32_t>(Obj.Header.SymbolTableOffset
337                           ? Obj.Header.SymbolTableOffset
338                           : InitFileHdr.SymbolTableOffset);
339     W.write<int32_t>(Obj.Header.NumberOfSymTableEntries
340                          ? Obj.Header.NumberOfSymTableEntries
341                          : InitFileHdr.NumberOfSymTableEntries);
342     W.write<uint16_t>(InitFileHdr.AuxHeaderSize);
343     W.write<uint16_t>(Obj.Header.Flags);
344   }
345 }
346 
347 void XCOFFWriter::writeAuxFileHeader() {
348   W.write<uint16_t>(InitAuxFileHdr.Magic.getValueOr(yaml::Hex16(1)));
349   W.write<uint16_t>(InitAuxFileHdr.Version.getValueOr(yaml::Hex16(1)));
350   if (Is64Bit) {
351     W.OS.write_zeros(4); // Reserved for debugger.
352     W.write<uint64_t>(InitAuxFileHdr.TextStartAddr.getValueOr(yaml::Hex64(0)));
353     W.write<uint64_t>(InitAuxFileHdr.DataStartAddr.getValueOr(yaml::Hex64(0)));
354     W.write<uint64_t>(InitAuxFileHdr.TOCAnchorAddr.getValueOr(yaml::Hex64(0)));
355   } else {
356     W.write<uint32_t>(InitAuxFileHdr.TextSize.getValueOr(yaml::Hex64(0)));
357     W.write<uint32_t>(InitAuxFileHdr.InitDataSize.getValueOr(yaml::Hex64(0)));
358     W.write<uint32_t>(InitAuxFileHdr.BssDataSize.getValueOr(yaml::Hex64(0)));
359     W.write<uint32_t>(InitAuxFileHdr.EntryPointAddr.getValueOr(yaml::Hex64(0)));
360     W.write<uint32_t>(InitAuxFileHdr.TextStartAddr.getValueOr(yaml::Hex64(0)));
361     W.write<uint32_t>(InitAuxFileHdr.DataStartAddr.getValueOr(yaml::Hex64(0)));
362     W.write<uint32_t>(InitAuxFileHdr.TOCAnchorAddr.getValueOr(yaml::Hex64(0)));
363   }
364   W.write<uint16_t>(InitAuxFileHdr.SecNumOfEntryPoint.getValueOr(0));
365   W.write<uint16_t>(InitAuxFileHdr.SecNumOfText.getValueOr(0));
366   W.write<uint16_t>(InitAuxFileHdr.SecNumOfData.getValueOr(0));
367   W.write<uint16_t>(InitAuxFileHdr.SecNumOfTOC.getValueOr(0));
368   W.write<uint16_t>(InitAuxFileHdr.SecNumOfLoader.getValueOr(0));
369   W.write<uint16_t>(InitAuxFileHdr.SecNumOfBSS.getValueOr(0));
370   W.write<uint16_t>(InitAuxFileHdr.MaxAlignOfText.getValueOr(yaml::Hex16(0)));
371   W.write<uint16_t>(InitAuxFileHdr.MaxAlignOfData.getValueOr(yaml::Hex16(0)));
372   W.write<uint16_t>(InitAuxFileHdr.ModuleType.getValueOr(yaml::Hex16(0)));
373   W.write<uint8_t>(InitAuxFileHdr.CpuFlag.getValueOr(yaml::Hex8(0)));
374   W.write<uint8_t>(0); // Reserved for CPU type.
375   if (Is64Bit) {
376     W.write<uint8_t>(InitAuxFileHdr.TextPageSize.getValueOr(yaml::Hex8(0)));
377     W.write<uint8_t>(InitAuxFileHdr.DataPageSize.getValueOr(yaml::Hex8(0)));
378     W.write<uint8_t>(InitAuxFileHdr.StackPageSize.getValueOr(yaml::Hex8(0)));
379     W.write<uint8_t>(
380         InitAuxFileHdr.FlagAndTDataAlignment.getValueOr(yaml::Hex8(0x80)));
381     W.write<uint64_t>(InitAuxFileHdr.TextSize.getValueOr(yaml::Hex64(0)));
382     W.write<uint64_t>(InitAuxFileHdr.InitDataSize.getValueOr(yaml::Hex64(0)));
383     W.write<uint64_t>(InitAuxFileHdr.BssDataSize.getValueOr(yaml::Hex64(0)));
384     W.write<uint64_t>(InitAuxFileHdr.EntryPointAddr.getValueOr(yaml::Hex64(0)));
385     W.write<uint64_t>(InitAuxFileHdr.MaxStackSize.getValueOr(yaml::Hex64(0)));
386     W.write<uint64_t>(InitAuxFileHdr.MaxDataSize.getValueOr(yaml::Hex64(0)));
387   } else {
388     W.write<uint32_t>(InitAuxFileHdr.MaxStackSize.getValueOr(yaml::Hex64(0)));
389     W.write<uint32_t>(InitAuxFileHdr.MaxDataSize.getValueOr(yaml::Hex64(0)));
390     W.OS.write_zeros(4); // Reserved for debugger.
391     W.write<uint8_t>(InitAuxFileHdr.TextPageSize.getValueOr(yaml::Hex8(0)));
392     W.write<uint8_t>(InitAuxFileHdr.DataPageSize.getValueOr(yaml::Hex8(0)));
393     W.write<uint8_t>(InitAuxFileHdr.StackPageSize.getValueOr(yaml::Hex8(0)));
394     W.write<uint8_t>(
395         InitAuxFileHdr.FlagAndTDataAlignment.getValueOr(yaml::Hex8(0)));
396   }
397   W.write<uint16_t>(InitAuxFileHdr.SecNumOfTData.getValueOr(0));
398   W.write<uint16_t>(InitAuxFileHdr.SecNumOfTBSS.getValueOr(0));
399   if (Is64Bit) {
400     W.write<uint16_t>(InitAuxFileHdr.Flag.getValueOr(yaml::Hex16(XCOFF::SHR_SYMTAB)));
401     if (InitFileHdr.AuxHeaderSize > XCOFF::AuxFileHeaderSize64)
402       W.OS.write_zeros(InitFileHdr.AuxHeaderSize - XCOFF::AuxFileHeaderSize64);
403   } else if (InitFileHdr.AuxHeaderSize > XCOFF::AuxFileHeaderSize32) {
404     W.OS.write_zeros(InitFileHdr.AuxHeaderSize - XCOFF::AuxFileHeaderSize32);
405   }
406 }
407 
408 void XCOFFWriter::writeSectionHeader() {
409   for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
410     XCOFFYAML::Section YamlSec = Obj.Sections[I];
411     XCOFFYAML::Section DerivedSec = InitSections[I];
412     writeName(YamlSec.SectionName, W);
413     // Virtual address is the same as physical address.
414     uint64_t SectionAddress =
415         YamlSec.Address ? YamlSec.Address : DerivedSec.Address;
416     if (Is64Bit) {
417       W.write<uint64_t>(SectionAddress); // Physical address
418       W.write<uint64_t>(SectionAddress); // Virtual address
419       W.write<uint64_t>(YamlSec.Size ? YamlSec.Size : DerivedSec.Size);
420       W.write<uint64_t>(YamlSec.FileOffsetToData ? YamlSec.FileOffsetToData
421                                                  : DerivedSec.FileOffsetToData);
422       W.write<uint64_t>(YamlSec.FileOffsetToRelocations
423                             ? YamlSec.FileOffsetToRelocations
424                             : DerivedSec.FileOffsetToRelocations);
425       W.write<uint64_t>(YamlSec.FileOffsetToLineNumbers);
426       W.write<uint32_t>(YamlSec.NumberOfRelocations
427                             ? YamlSec.NumberOfRelocations
428                             : DerivedSec.NumberOfRelocations);
429       W.write<uint32_t>(YamlSec.NumberOfLineNumbers);
430       W.write<int32_t>(YamlSec.Flags);
431       W.OS.write_zeros(4);
432     } else {
433       W.write<uint32_t>(SectionAddress); // Physical address
434       W.write<uint32_t>(SectionAddress); // Virtual address
435       W.write<uint32_t>(YamlSec.Size ? YamlSec.Size : DerivedSec.Size);
436       W.write<uint32_t>(YamlSec.FileOffsetToData ? YamlSec.FileOffsetToData
437                                                  : DerivedSec.FileOffsetToData);
438       W.write<uint32_t>(YamlSec.FileOffsetToRelocations
439                             ? YamlSec.FileOffsetToRelocations
440                             : DerivedSec.FileOffsetToRelocations);
441       W.write<uint32_t>(YamlSec.FileOffsetToLineNumbers);
442       W.write<uint16_t>(YamlSec.NumberOfRelocations
443                             ? YamlSec.NumberOfRelocations
444                             : DerivedSec.NumberOfRelocations);
445       W.write<uint16_t>(YamlSec.NumberOfLineNumbers);
446       W.write<int32_t>(YamlSec.Flags);
447     }
448   }
449 }
450 
451 bool XCOFFWriter::writeSectionData() {
452   for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
453     XCOFFYAML::Section YamlSec = Obj.Sections[I];
454     if (YamlSec.SectionData.binary_size()) {
455       // Fill the padding size with zeros.
456       int64_t PaddingSize =
457           InitSections[I].FileOffsetToData - (W.OS.tell() - StartOffset);
458       if (PaddingSize < 0) {
459         ErrHandler("redundant data was written before section data");
460         return false;
461       }
462       W.OS.write_zeros(PaddingSize);
463       YamlSec.SectionData.writeAsBinary(W.OS);
464     }
465   }
466   return true;
467 }
468 
469 bool XCOFFWriter::writeRelocations() {
470   for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
471     XCOFFYAML::Section YamlSec = Obj.Sections[I];
472     if (!YamlSec.Relocations.empty()) {
473       int64_t PaddingSize =
474           InitSections[I].FileOffsetToRelocations - (W.OS.tell() - StartOffset);
475       if (PaddingSize < 0) {
476         ErrHandler("redundant data was written before relocations");
477         return false;
478       }
479       W.OS.write_zeros(PaddingSize);
480       for (const XCOFFYAML::Relocation &YamlRel : YamlSec.Relocations) {
481         if (Is64Bit)
482           W.write<uint64_t>(YamlRel.VirtualAddress);
483         else
484           W.write<uint32_t>(YamlRel.VirtualAddress);
485         W.write<uint32_t>(YamlRel.SymbolIndex);
486         W.write<uint8_t>(YamlRel.Info);
487         W.write<uint8_t>(YamlRel.Type);
488       }
489     }
490   }
491   return true;
492 }
493 
494 bool XCOFFWriter::writeSymbols() {
495   int64_t PaddingSize =
496       (uint64_t)InitFileHdr.SymbolTableOffset - (W.OS.tell() - StartOffset);
497   if (PaddingSize < 0) {
498     ErrHandler("redundant data was written before symbols");
499     return false;
500   }
501   W.OS.write_zeros(PaddingSize);
502   for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
503     if (Is64Bit) {
504       W.write<uint64_t>(YamlSym.Value);
505       W.write<uint32_t>(StrTblBuilder.getOffset(YamlSym.SymbolName));
506     } else {
507       if (nameShouldBeInStringTable(YamlSym.SymbolName)) {
508         // For XCOFF32: A value of 0 indicates that the symbol name is in the
509         // string table.
510         W.write<int32_t>(0);
511         W.write<uint32_t>(StrTblBuilder.getOffset(YamlSym.SymbolName));
512       } else {
513         writeName(YamlSym.SymbolName, W);
514       }
515       W.write<uint32_t>(YamlSym.Value);
516     }
517     if (YamlSym.SectionName) {
518       if (!SectionIndexMap.count(*YamlSym.SectionName)) {
519         ErrHandler("the SectionName " + *YamlSym.SectionName +
520                    " specified in the symbol does not exist");
521         return false;
522       }
523       if (YamlSym.SectionIndex &&
524           SectionIndexMap[*YamlSym.SectionName] != *YamlSym.SectionIndex) {
525         ErrHandler("the SectionName " + *YamlSym.SectionName +
526                    " and the SectionIndex (" + Twine(*YamlSym.SectionIndex) +
527                    ") refer to different sections");
528         return false;
529       }
530       W.write<int16_t>(SectionIndexMap[*YamlSym.SectionName]);
531     } else {
532       W.write<int16_t>(YamlSym.SectionIndex ? *YamlSym.SectionIndex : 0);
533     }
534     W.write<uint16_t>(YamlSym.Type);
535     W.write<uint8_t>(YamlSym.StorageClass);
536     W.write<uint8_t>(YamlSym.NumberOfAuxEntries);
537 
538     // Now output the auxiliary entry.
539     for (uint8_t I = 0, E = YamlSym.NumberOfAuxEntries; I < E; ++I) {
540       // TODO: Auxiliary entry is not supported yet.
541       // The auxiliary entries for a symbol follow its symbol table entry. The
542       // length of each auxiliary entry is the same as a symbol table entry (18
543       // bytes). The format and quantity of auxiliary entries depend on the
544       // storage class (n_sclass) and type (n_type) of the symbol table entry.
545       W.OS.write_zeros(XCOFF::SymbolTableEntrySize);
546     }
547   }
548   return true;
549 }
550 
551 void XCOFFWriter::writeStringTable() {
552   if (Obj.StrTbl.RawContent) {
553     Obj.StrTbl.RawContent->writeAsBinary(W.OS);
554     if (Obj.StrTbl.ContentSize) {
555       assert(*Obj.StrTbl.ContentSize >= Obj.StrTbl.RawContent->binary_size() &&
556              "Specified ContentSize is less than the RawContent size.");
557       W.OS.write_zeros(*Obj.StrTbl.ContentSize -
558                        Obj.StrTbl.RawContent->binary_size());
559     }
560     return;
561   }
562 
563   size_t StrTblBuilderSize = StrTblBuilder.getSize();
564   // If neither Length nor ContentSize is specified, write the StrTblBuilder
565   // directly, which contains the auto-generated Length value.
566   if (!Obj.StrTbl.Length && !Obj.StrTbl.ContentSize) {
567     if (StrTblBuilderSize <= 4)
568       return;
569     StrTblBuilder.write(W.OS);
570     return;
571   }
572 
573   // Serialize the string table's content to a temporary buffer.
574   std::unique_ptr<WritableMemoryBuffer> Buf =
575       WritableMemoryBuffer::getNewMemBuffer(StrTblBuilderSize);
576   uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart());
577   StrTblBuilder.write(Ptr);
578   // Replace the first 4 bytes, which contain the auto-generated Length value,
579   // with the specified value.
580   memset(Ptr, 0, 4);
581   support::endian::write32be(Ptr, Obj.StrTbl.Length ? *Obj.StrTbl.Length
582                                                     : *Obj.StrTbl.ContentSize);
583   // Copy the buffer content to the actual output stream.
584   W.OS.write(Buf->getBufferStart(), Buf->getBufferSize());
585   // Add zeros as padding after strings.
586   if (Obj.StrTbl.ContentSize) {
587     assert(*Obj.StrTbl.ContentSize >= StrTblBuilderSize &&
588            "Specified ContentSize is less than the StringTableBuilder size.");
589     W.OS.write_zeros(*Obj.StrTbl.ContentSize - StrTblBuilderSize);
590   }
591 }
592 
593 bool XCOFFWriter::writeXCOFF() {
594   if (!assignAddressesAndIndices())
595     return false;
596   StartOffset = W.OS.tell();
597   writeFileHeader();
598   if (Obj.AuxHeader)
599     writeAuxFileHeader();
600   if (!Obj.Sections.empty()) {
601     writeSectionHeader();
602     if (!writeSectionData())
603       return false;
604     if (!writeRelocations())
605       return false;
606   }
607   if (!Obj.Symbols.empty() && !writeSymbols())
608     return false;
609   writeStringTable();
610   return true;
611 }
612 
613 } // end anonymous namespace
614 
615 namespace llvm {
616 namespace yaml {
617 
618 bool yaml2xcoff(XCOFFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH) {
619   XCOFFWriter Writer(Doc, Out, EH);
620   return Writer.writeXCOFF();
621 }
622 
623 } // namespace yaml
624 } // namespace llvm
625