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   bool initSectionHeader(uint64_t &CurrentOffset);
46   bool initRelocations(uint64_t &CurrentOffset);
47   bool initStringTable();
48   bool assignAddressesAndIndices();
49   void writeFileHeader();
50   void writeSectionHeader();
51   bool writeSectionData();
52   bool writeRelocations();
53   bool writeSymbols();
54   void writeStringTable();
55 
56   XCOFFYAML::Object &Obj;
57   bool Is64Bit = false;
58   support::endian::Writer W;
59   yaml::ErrorHandler ErrHandler;
60   StringTableBuilder StrTblBuilder;
61   uint64_t StartOffset;
62   // Map the section name to its corrresponding section index.
63   DenseMap<StringRef, int16_t> SectionIndexMap = {
64       {StringRef("N_DEBUG"), XCOFF::N_DEBUG},
65       {StringRef("N_ABS"), XCOFF::N_ABS},
66       {StringRef("N_UNDEF"), XCOFF::N_UNDEF}};
67   XCOFFYAML::FileHeader InitFileHdr = Obj.Header;
68   std::vector<XCOFFYAML::Section> InitSections = Obj.Sections;
69 };
70 
71 static void writeName(StringRef StrName, support::endian::Writer W) {
72   char Name[XCOFF::NameSize];
73   memset(Name, 0, XCOFF::NameSize);
74   char SrcName[] = "";
75   memcpy(Name, StrName.size() ? StrName.data() : SrcName, StrName.size());
76   ArrayRef<char> NameRef(Name, XCOFF::NameSize);
77   W.write(NameRef);
78 }
79 
80 bool XCOFFWriter::nameShouldBeInStringTable(StringRef SymbolName) {
81   // For XCOFF64: The symbol name is always in the string table.
82   return (SymbolName.size() > XCOFF::NameSize) || Is64Bit;
83 }
84 
85 bool XCOFFWriter::initRelocations(uint64_t &CurrentOffset) {
86   for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) {
87     if (!InitSections[I].Relocations.empty()) {
88       InitSections[I].NumberOfRelocations = InitSections[I].Relocations.size();
89       InitSections[I].FileOffsetToRelocations = CurrentOffset;
90       uint64_t RelSize = Is64Bit ? XCOFF::RelocationSerializationSize64
91                                  : XCOFF::RelocationSerializationSize32;
92       CurrentOffset += InitSections[I].NumberOfRelocations * RelSize;
93       if (CurrentOffset > MaxRawDataSize) {
94         ErrHandler("maximum object size of" + Twine(MaxRawDataSize) +
95                    "exceeded when writing relocation data");
96         return false;
97       }
98     }
99   }
100   return true;
101 }
102 
103 bool XCOFFWriter::initSectionHeader(uint64_t &CurrentOffset) {
104   uint64_t CurrentSecAddr = 0;
105   for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) {
106     if (CurrentOffset > MaxRawDataSize) {
107       ErrHandler("maximum object size of" + Twine(MaxRawDataSize) +
108                  "exceeded when writing section data");
109       return false;
110     }
111 
112     // Assign indices for sections.
113     if (InitSections[I].SectionName.size() &&
114         !SectionIndexMap[InitSections[I].SectionName]) {
115       // The section index starts from 1.
116       SectionIndexMap[InitSections[I].SectionName] = I + 1;
117       if ((I + 1) > MaxSectionIndex) {
118         ErrHandler("exceeded the maximum permitted section index of " +
119                    Twine(MaxSectionIndex));
120         return false;
121       }
122     }
123 
124     // Calculate the physical/virtual address. This field should contain 0 for
125     // all sections except the text, data and bss sections.
126     if (InitSections[I].Flags != XCOFF::STYP_TEXT &&
127         InitSections[I].Flags != XCOFF::STYP_DATA &&
128         InitSections[I].Flags != XCOFF::STYP_BSS)
129       InitSections[I].Address = 0;
130     else
131       InitSections[I].Address = CurrentSecAddr;
132 
133     // Calculate the FileOffsetToData and data size for sections.
134     if (InitSections[I].SectionData.binary_size()) {
135       InitSections[I].FileOffsetToData = CurrentOffset;
136       CurrentOffset += InitSections[I].SectionData.binary_size();
137       // Ensure the offset is aligned to DefaultSectionAlign.
138       CurrentOffset = alignTo(CurrentOffset, DefaultSectionAlign);
139       InitSections[I].Size = CurrentOffset - InitSections[I].FileOffsetToData;
140       CurrentSecAddr += InitSections[I].Size;
141     }
142   }
143   return initRelocations(CurrentOffset);
144 }
145 
146 bool XCOFFWriter::initStringTable() {
147   if (Obj.StrTbl.RawContent) {
148     size_t RawSize = Obj.StrTbl.RawContent->binary_size();
149     if (Obj.StrTbl.Strings || Obj.StrTbl.Length) {
150       ErrHandler(
151           "can't specify Strings or Length when RawContent is specified");
152       return false;
153     }
154     if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize < RawSize) {
155       ErrHandler("specified ContentSize (" + Twine(*Obj.StrTbl.ContentSize) +
156                  ") is less than the RawContent data size (" + Twine(RawSize) +
157                  ")");
158       return false;
159     }
160     return true;
161   }
162   if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize <= 3) {
163     ErrHandler("ContentSize shouldn't be less than 4 without RawContent");
164     return false;
165   }
166 
167   // Build the string table.
168   StrTblBuilder.clear();
169 
170   if (Obj.StrTbl.Strings) {
171     // All specified strings should be added to the string table.
172     for (StringRef StringEnt : *Obj.StrTbl.Strings)
173       StrTblBuilder.add(StringEnt);
174 
175     size_t StrTblIdx = 0;
176     size_t NumOfStrings = Obj.StrTbl.Strings->size();
177     for (XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
178       if (nameShouldBeInStringTable(YamlSym.SymbolName)) {
179         if (StrTblIdx < NumOfStrings) {
180           // Overwrite the symbol name with the specified string.
181           YamlSym.SymbolName = (*Obj.StrTbl.Strings)[StrTblIdx];
182           ++StrTblIdx;
183         } else
184           // Names that are not overwritten are still stored in the string
185           // table.
186           StrTblBuilder.add(YamlSym.SymbolName);
187       }
188     }
189   } else {
190     for (XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
191       if (nameShouldBeInStringTable(YamlSym.SymbolName))
192         StrTblBuilder.add(YamlSym.SymbolName);
193     }
194   }
195 
196   StrTblBuilder.finalize();
197 
198   size_t StrTblSize = StrTblBuilder.getSize();
199   if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize < StrTblSize) {
200     ErrHandler("specified ContentSize (" + Twine(*Obj.StrTbl.ContentSize) +
201                ") is less than the size of the data that would otherwise be "
202                "written (" +
203                Twine(StrTblSize) + ")");
204     return false;
205   }
206 
207   return true;
208 }
209 
210 bool XCOFFWriter::initFileHeader(uint64_t CurrentOffset) {
211   // The default format of the object file is XCOFF32.
212   InitFileHdr.Magic = XCOFF::XCOFF32;
213   InitFileHdr.NumberOfSections = Obj.Sections.size();
214   InitFileHdr.NumberOfSymTableEntries = Obj.Symbols.size();
215 
216   for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols)
217     // Add the number of auxiliary symbols to the total number.
218     InitFileHdr.NumberOfSymTableEntries += YamlSym.NumberOfAuxEntries;
219 
220   // Calculate SymbolTableOffset for the file header.
221   if (InitFileHdr.NumberOfSymTableEntries) {
222     InitFileHdr.SymbolTableOffset = CurrentOffset;
223     CurrentOffset +=
224         InitFileHdr.NumberOfSymTableEntries * XCOFF::SymbolTableEntrySize;
225     if (CurrentOffset > MaxRawDataSize) {
226       ErrHandler("maximum object size of" + Twine(MaxRawDataSize) +
227                  "exceeded when writing symbols");
228       return false;
229     }
230   }
231   // TODO: Calculate FileOffsetToLineNumbers when line number supported.
232   return true;
233 }
234 
235 bool XCOFFWriter::assignAddressesAndIndices() {
236   uint64_t FileHdrSize =
237       Is64Bit ? XCOFF::FileHeaderSize64 : XCOFF::FileHeaderSize32;
238   uint64_t SecHdrSize =
239       Is64Bit ? XCOFF::SectionHeaderSize64 : XCOFF::SectionHeaderSize32;
240   uint64_t CurrentOffset = FileHdrSize /* TODO: + auxiliaryHeaderSize() */ +
241                            InitSections.size() * SecHdrSize;
242 
243   // Calculate section header info.
244   if (!initSectionHeader(CurrentOffset))
245     return false;
246 
247   // Calculate file header info.
248   if (!initFileHeader(CurrentOffset))
249     return false;
250 
251   // Initialize the string table.
252   return initStringTable();
253 }
254 
255 void XCOFFWriter::writeFileHeader() {
256   W.write<uint16_t>(Obj.Header.Magic ? Obj.Header.Magic : InitFileHdr.Magic);
257   W.write<uint16_t>(Obj.Header.NumberOfSections ? Obj.Header.NumberOfSections
258                                                 : InitFileHdr.NumberOfSections);
259   W.write<int32_t>(Obj.Header.TimeStamp);
260   if (Is64Bit) {
261     W.write<uint64_t>(Obj.Header.SymbolTableOffset
262                           ? Obj.Header.SymbolTableOffset
263                           : InitFileHdr.SymbolTableOffset);
264     W.write<uint16_t>(Obj.Header.AuxHeaderSize);
265     W.write<uint16_t>(Obj.Header.Flags);
266     W.write<int32_t>(Obj.Header.NumberOfSymTableEntries
267                          ? Obj.Header.NumberOfSymTableEntries
268                          : InitFileHdr.NumberOfSymTableEntries);
269   } else {
270     W.write<uint32_t>(Obj.Header.SymbolTableOffset
271                           ? Obj.Header.SymbolTableOffset
272                           : InitFileHdr.SymbolTableOffset);
273     W.write<int32_t>(Obj.Header.NumberOfSymTableEntries
274                          ? Obj.Header.NumberOfSymTableEntries
275                          : InitFileHdr.NumberOfSymTableEntries);
276     W.write<uint16_t>(Obj.Header.AuxHeaderSize);
277     W.write<uint16_t>(Obj.Header.Flags);
278   }
279 }
280 
281 void XCOFFWriter::writeSectionHeader() {
282   for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
283     XCOFFYAML::Section YamlSec = Obj.Sections[I];
284     XCOFFYAML::Section DerivedSec = InitSections[I];
285     writeName(YamlSec.SectionName, W);
286     // Virtual address is the same as physical address.
287     uint64_t SectionAddress =
288         YamlSec.Address ? YamlSec.Address : DerivedSec.Address;
289     if (Is64Bit) {
290       W.write<uint64_t>(SectionAddress); // Physical address
291       W.write<uint64_t>(SectionAddress); // Virtual address
292       W.write<uint64_t>(YamlSec.Size ? YamlSec.Size : DerivedSec.Size);
293       W.write<uint64_t>(YamlSec.FileOffsetToData ? YamlSec.FileOffsetToData
294                                                  : DerivedSec.FileOffsetToData);
295       W.write<uint64_t>(YamlSec.FileOffsetToRelocations
296                             ? YamlSec.FileOffsetToRelocations
297                             : DerivedSec.FileOffsetToRelocations);
298       W.write<uint64_t>(YamlSec.FileOffsetToLineNumbers);
299       W.write<uint32_t>(YamlSec.NumberOfRelocations
300                             ? YamlSec.NumberOfRelocations
301                             : DerivedSec.NumberOfRelocations);
302       W.write<uint32_t>(YamlSec.NumberOfLineNumbers);
303       W.write<int32_t>(YamlSec.Flags);
304       W.OS.write_zeros(4);
305     } else {
306       W.write<uint32_t>(SectionAddress); // Physical address
307       W.write<uint32_t>(SectionAddress); // Virtual address
308       W.write<uint32_t>(YamlSec.Size ? YamlSec.Size : DerivedSec.Size);
309       W.write<uint32_t>(YamlSec.FileOffsetToData ? YamlSec.FileOffsetToData
310                                                  : DerivedSec.FileOffsetToData);
311       W.write<uint32_t>(YamlSec.FileOffsetToRelocations
312                             ? YamlSec.FileOffsetToRelocations
313                             : DerivedSec.FileOffsetToRelocations);
314       W.write<uint32_t>(YamlSec.FileOffsetToLineNumbers);
315       W.write<uint16_t>(YamlSec.NumberOfRelocations
316                             ? YamlSec.NumberOfRelocations
317                             : DerivedSec.NumberOfRelocations);
318       W.write<uint16_t>(YamlSec.NumberOfLineNumbers);
319       W.write<int32_t>(YamlSec.Flags);
320     }
321   }
322 }
323 
324 bool XCOFFWriter::writeSectionData() {
325   for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
326     XCOFFYAML::Section YamlSec = Obj.Sections[I];
327     if (YamlSec.SectionData.binary_size()) {
328       // Fill the padding size with zeros.
329       int64_t PaddingSize =
330           InitSections[I].FileOffsetToData - (W.OS.tell() - StartOffset);
331       if (PaddingSize < 0) {
332         ErrHandler("redundant data was written before section data");
333         return false;
334       }
335       W.OS.write_zeros(PaddingSize);
336       YamlSec.SectionData.writeAsBinary(W.OS);
337     }
338   }
339   return true;
340 }
341 
342 bool XCOFFWriter::writeRelocations() {
343   for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
344     XCOFFYAML::Section YamlSec = Obj.Sections[I];
345     if (!YamlSec.Relocations.empty()) {
346       int64_t PaddingSize =
347           InitSections[I].FileOffsetToRelocations - (W.OS.tell() - StartOffset);
348       if (PaddingSize < 0) {
349         ErrHandler("redundant data was written before relocations");
350         return false;
351       }
352       W.OS.write_zeros(PaddingSize);
353       for (const XCOFFYAML::Relocation &YamlRel : YamlSec.Relocations) {
354         if (Is64Bit)
355           W.write<uint64_t>(YamlRel.VirtualAddress);
356         else
357           W.write<uint32_t>(YamlRel.VirtualAddress);
358         W.write<uint32_t>(YamlRel.SymbolIndex);
359         W.write<uint8_t>(YamlRel.Info);
360         W.write<uint8_t>(YamlRel.Type);
361       }
362     }
363   }
364   return true;
365 }
366 
367 bool XCOFFWriter::writeSymbols() {
368   int64_t PaddingSize =
369       (uint64_t)InitFileHdr.SymbolTableOffset - (W.OS.tell() - StartOffset);
370   if (PaddingSize < 0) {
371     ErrHandler("redundant data was written before symbols");
372     return false;
373   }
374   W.OS.write_zeros(PaddingSize);
375   for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
376     if (Is64Bit) {
377       W.write<uint64_t>(YamlSym.Value);
378       W.write<uint32_t>(StrTblBuilder.getOffset(YamlSym.SymbolName));
379     } else {
380       if (nameShouldBeInStringTable(YamlSym.SymbolName)) {
381         // For XCOFF32: A value of 0 indicates that the symbol name is in the
382         // string table.
383         W.write<int32_t>(0);
384         W.write<uint32_t>(StrTblBuilder.getOffset(YamlSym.SymbolName));
385       } else {
386         writeName(YamlSym.SymbolName, W);
387       }
388       W.write<uint32_t>(YamlSym.Value);
389     }
390     W.write<int16_t>(
391         YamlSym.SectionName.size() ? SectionIndexMap[YamlSym.SectionName] : 0);
392     W.write<uint16_t>(YamlSym.Type);
393     W.write<uint8_t>(YamlSym.StorageClass);
394     W.write<uint8_t>(YamlSym.NumberOfAuxEntries);
395 
396     // Now output the auxiliary entry.
397     for (uint8_t I = 0, E = YamlSym.NumberOfAuxEntries; I < E; ++I) {
398       // TODO: Auxiliary entry is not supported yet.
399       // The auxiliary entries for a symbol follow its symbol table entry. The
400       // length of each auxiliary entry is the same as a symbol table entry (18
401       // bytes). The format and quantity of auxiliary entries depend on the
402       // storage class (n_sclass) and type (n_type) of the symbol table entry.
403       W.OS.write_zeros(XCOFF::SymbolTableEntrySize);
404     }
405   }
406   return true;
407 }
408 
409 void XCOFFWriter::writeStringTable() {
410   if (Obj.StrTbl.RawContent) {
411     Obj.StrTbl.RawContent->writeAsBinary(W.OS);
412     if (Obj.StrTbl.ContentSize) {
413       assert(*Obj.StrTbl.ContentSize >= Obj.StrTbl.RawContent->binary_size() &&
414              "Specified ContentSize is less than the RawContent size.");
415       W.OS.write_zeros(*Obj.StrTbl.ContentSize -
416                        Obj.StrTbl.RawContent->binary_size());
417     }
418     return;
419   }
420 
421   size_t StrTblBuilderSize = StrTblBuilder.getSize();
422   // If neither Length nor ContentSize is specified, write the StrTblBuilder
423   // directly, which contains the auto-generated Length value.
424   if (!Obj.StrTbl.Length && !Obj.StrTbl.ContentSize) {
425     if (StrTblBuilderSize <= 4)
426       return;
427     StrTblBuilder.write(W.OS);
428     return;
429   }
430 
431   // Serialize the string table's content to a temporary buffer.
432   std::unique_ptr<WritableMemoryBuffer> Buf =
433       WritableMemoryBuffer::getNewMemBuffer(StrTblBuilderSize);
434   uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart());
435   StrTblBuilder.write(Ptr);
436   // Replace the first 4 bytes, which contain the auto-generated Length value,
437   // with the specified value.
438   memset(Ptr, 0, 4);
439   support::endian::write32be(Ptr, Obj.StrTbl.Length ? *Obj.StrTbl.Length
440                                                     : *Obj.StrTbl.ContentSize);
441   // Copy the buffer content to the actual output stream.
442   W.OS.write(Buf->getBufferStart(), Buf->getBufferSize());
443   // Add zeros as padding after strings.
444   if (Obj.StrTbl.ContentSize) {
445     assert(*Obj.StrTbl.ContentSize >= StrTblBuilderSize &&
446            "Specified ContentSize is less than the StringTableBuilder size.");
447     W.OS.write_zeros(*Obj.StrTbl.ContentSize - StrTblBuilderSize);
448   }
449 }
450 
451 bool XCOFFWriter::writeXCOFF() {
452   if (!assignAddressesAndIndices())
453     return false;
454   StartOffset = W.OS.tell();
455   writeFileHeader();
456   if (!Obj.Sections.empty()) {
457     writeSectionHeader();
458     if (!writeSectionData())
459       return false;
460     if (!writeRelocations())
461       return false;
462   }
463   if (!Obj.Symbols.empty() && !writeSymbols())
464     return false;
465   writeStringTable();
466   return true;
467 }
468 
469 } // end anonymous namespace
470 
471 namespace llvm {
472 namespace yaml {
473 
474 bool yaml2xcoff(XCOFFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH) {
475   XCOFFWriter Writer(Doc, Out, EH);
476   return Writer.writeXCOFF();
477 }
478 
479 } // namespace yaml
480 } // namespace llvm
481