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/raw_ostream.h"
22 #include "llvm/Support/LEB128.h"
23 
24 using namespace llvm;
25 
26 namespace {
27 
28 constexpr unsigned DefaultSectionAlign = 4;
29 constexpr int16_t MaxSectionIndex = INT16_MAX;
30 constexpr uint32_t MaxRawDataSize = UINT32_MAX;
31 
32 class XCOFFWriter {
33 public:
34   XCOFFWriter(XCOFFYAML::Object &Obj, raw_ostream &OS, yaml::ErrorHandler EH)
35       : Obj(Obj), W(OS, support::big), ErrHandler(EH),
36         Strings(StringTableBuilder::XCOFF) {
37     Is64Bit = Obj.Header.Magic == (llvm::yaml::Hex16)XCOFF::XCOFF64;
38   }
39   bool writeXCOFF();
40 
41 private:
42   bool nameShouldBeInStringTable(StringRef SymbolName);
43   bool initFileHeader(uint64_t CurrentOffset);
44   bool initSectionHeader(uint64_t &CurrentOffset);
45   bool initRelocations(uint64_t &CurrentOffset);
46   bool assignAddressesAndIndices();
47   void writeFileHeader();
48   void writeSectionHeader();
49   bool writeSectionData();
50   bool writeRelocations();
51   bool writeSymbols();
52 
53   XCOFFYAML::Object &Obj;
54   bool Is64Bit = false;
55   support::endian::Writer W;
56   yaml::ErrorHandler ErrHandler;
57   StringTableBuilder Strings;
58   uint64_t StartOffset;
59   // Map the section name to its corrresponding section index.
60   DenseMap<StringRef, int16_t> SectionIndexMap = {
61       {StringRef("N_DEBUG"), XCOFF::N_DEBUG},
62       {StringRef("N_ABS"), XCOFF::N_ABS},
63       {StringRef("N_UNDEF"), XCOFF::N_UNDEF}};
64   XCOFFYAML::FileHeader InitFileHdr = Obj.Header;
65   std::vector<XCOFFYAML::Section> InitSections = Obj.Sections;
66 };
67 
68 static void writeName(StringRef StrName, support::endian::Writer W) {
69   char Name[XCOFF::NameSize];
70   memset(Name, 0, XCOFF::NameSize);
71   char SrcName[] = "";
72   memcpy(Name, StrName.size() ? StrName.data() : SrcName, StrName.size());
73   ArrayRef<char> NameRef(Name, XCOFF::NameSize);
74   W.write(NameRef);
75 }
76 
77 bool XCOFFWriter::nameShouldBeInStringTable(StringRef SymbolName) {
78   // For XCOFF64: The symbol name is always in the string table.
79   return (SymbolName.size() > XCOFF::NameSize) || Is64Bit;
80 }
81 
82 bool XCOFFWriter::initRelocations(uint64_t &CurrentOffset) {
83   for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) {
84     if (!InitSections[I].Relocations.empty()) {
85       InitSections[I].NumberOfRelocations = InitSections[I].Relocations.size();
86       InitSections[I].FileOffsetToRelocations = CurrentOffset;
87       uint64_t RelSize = Is64Bit ? XCOFF::RelocationSerializationSize64
88                                  : XCOFF::RelocationSerializationSize32;
89       CurrentOffset += InitSections[I].NumberOfRelocations * RelSize;
90       if (CurrentOffset > MaxRawDataSize) {
91         ErrHandler("maximum object size of" + Twine(MaxRawDataSize) +
92                    "exceeded when writing relocation data");
93         return false;
94       }
95     }
96   }
97   return true;
98 }
99 
100 bool XCOFFWriter::initSectionHeader(uint64_t &CurrentOffset) {
101   uint64_t CurrentSecAddr = 0;
102   for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) {
103     if (CurrentOffset > MaxRawDataSize) {
104       ErrHandler("maximum object size of" + Twine(MaxRawDataSize) +
105                  "exceeded when writing section data");
106       return false;
107     }
108 
109     // Assign indices for sections.
110     if (InitSections[I].SectionName.size() &&
111         !SectionIndexMap[InitSections[I].SectionName]) {
112       // The section index starts from 1.
113       SectionIndexMap[InitSections[I].SectionName] = I + 1;
114       if ((I + 1) > MaxSectionIndex) {
115         ErrHandler("exceeded the maximum permitted section index of " +
116                    Twine(MaxSectionIndex));
117         return false;
118       }
119     }
120 
121     // Calculate the physical/virtual address. This field should contain 0 for
122     // all sections except the text, data and bss sections.
123     if (InitSections[I].Flags != XCOFF::STYP_TEXT &&
124         InitSections[I].Flags != XCOFF::STYP_DATA &&
125         InitSections[I].Flags != XCOFF::STYP_BSS)
126       InitSections[I].Address = 0;
127     else
128       InitSections[I].Address = CurrentSecAddr;
129 
130     // Calculate the FileOffsetToData and data size for sections.
131     if (InitSections[I].SectionData.binary_size()) {
132       InitSections[I].FileOffsetToData = CurrentOffset;
133       CurrentOffset += InitSections[I].SectionData.binary_size();
134       // Ensure the offset is aligned to DefaultSectionAlign.
135       CurrentOffset = alignTo(CurrentOffset, DefaultSectionAlign);
136       InitSections[I].Size = CurrentOffset - InitSections[I].FileOffsetToData;
137       CurrentSecAddr += InitSections[I].Size;
138     }
139   }
140   return initRelocations(CurrentOffset);
141 }
142 
143 bool XCOFFWriter::initFileHeader(uint64_t CurrentOffset) {
144   // The default format of the object file is XCOFF32.
145   InitFileHdr.Magic = XCOFF::XCOFF32;
146   InitFileHdr.NumberOfSections = Obj.Sections.size();
147   InitFileHdr.NumberOfSymTableEntries = Obj.Symbols.size();
148 
149   for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
150     // Add the number of auxiliary symbols to the total number.
151     InitFileHdr.NumberOfSymTableEntries += YamlSym.NumberOfAuxEntries;
152     if (nameShouldBeInStringTable(YamlSym.SymbolName))
153       Strings.add(YamlSym.SymbolName);
154   }
155   // Finalize the string table.
156   Strings.finalize();
157 
158   // Calculate SymbolTableOffset for the file header.
159   if (InitFileHdr.NumberOfSymTableEntries) {
160     InitFileHdr.SymbolTableOffset = CurrentOffset;
161     CurrentOffset +=
162         InitFileHdr.NumberOfSymTableEntries * XCOFF::SymbolTableEntrySize;
163     if (CurrentOffset > MaxRawDataSize) {
164       ErrHandler("maximum object size of" + Twine(MaxRawDataSize) +
165                  "exceeded when writing symbols");
166       return false;
167     }
168   }
169   // TODO: Calculate FileOffsetToLineNumbers when line number supported.
170   return true;
171 }
172 
173 bool XCOFFWriter::assignAddressesAndIndices() {
174   Strings.clear();
175   uint64_t FileHdrSize =
176       Is64Bit ? XCOFF::FileHeaderSize64 : XCOFF::FileHeaderSize32;
177   uint64_t SecHdrSize =
178       Is64Bit ? XCOFF::SectionHeaderSize64 : XCOFF::SectionHeaderSize32;
179   uint64_t CurrentOffset = FileHdrSize /* TODO: + auxiliaryHeaderSize() */ +
180                            InitSections.size() * SecHdrSize;
181 
182   // Calculate section header info.
183   if (!initSectionHeader(CurrentOffset))
184     return false;
185   // Calculate file header info.
186   return initFileHeader(CurrentOffset);
187 }
188 
189 void XCOFFWriter::writeFileHeader() {
190   W.write<uint16_t>(Obj.Header.Magic ? Obj.Header.Magic : InitFileHdr.Magic);
191   W.write<uint16_t>(Obj.Header.NumberOfSections ? Obj.Header.NumberOfSections
192                                                 : InitFileHdr.NumberOfSections);
193   W.write<int32_t>(Obj.Header.TimeStamp);
194   if (Is64Bit) {
195     W.write<uint64_t>(Obj.Header.SymbolTableOffset
196                           ? Obj.Header.SymbolTableOffset
197                           : InitFileHdr.SymbolTableOffset);
198     W.write<uint16_t>(Obj.Header.AuxHeaderSize);
199     W.write<uint16_t>(Obj.Header.Flags);
200     W.write<int32_t>(Obj.Header.NumberOfSymTableEntries
201                          ? Obj.Header.NumberOfSymTableEntries
202                          : InitFileHdr.NumberOfSymTableEntries);
203   } else {
204     W.write<uint32_t>(Obj.Header.SymbolTableOffset
205                           ? Obj.Header.SymbolTableOffset
206                           : InitFileHdr.SymbolTableOffset);
207     W.write<int32_t>(Obj.Header.NumberOfSymTableEntries
208                          ? Obj.Header.NumberOfSymTableEntries
209                          : InitFileHdr.NumberOfSymTableEntries);
210     W.write<uint16_t>(Obj.Header.AuxHeaderSize);
211     W.write<uint16_t>(Obj.Header.Flags);
212   }
213 }
214 
215 void XCOFFWriter::writeSectionHeader() {
216   for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
217     XCOFFYAML::Section YamlSec = Obj.Sections[I];
218     XCOFFYAML::Section DerivedSec = InitSections[I];
219     writeName(YamlSec.SectionName, W);
220     // Virtual address is the same as physical address.
221     uint64_t SectionAddress =
222         YamlSec.Address ? YamlSec.Address : DerivedSec.Address;
223     if (Is64Bit) {
224       W.write<uint64_t>(SectionAddress); // Physical address
225       W.write<uint64_t>(SectionAddress); // Virtual address
226       W.write<uint64_t>(YamlSec.Size ? YamlSec.Size : DerivedSec.Size);
227       W.write<uint64_t>(YamlSec.FileOffsetToData ? YamlSec.FileOffsetToData
228                                                  : DerivedSec.FileOffsetToData);
229       W.write<uint64_t>(YamlSec.FileOffsetToRelocations
230                             ? YamlSec.FileOffsetToRelocations
231                             : DerivedSec.FileOffsetToRelocations);
232       W.write<uint64_t>(YamlSec.FileOffsetToLineNumbers);
233       W.write<uint32_t>(YamlSec.NumberOfRelocations
234                             ? YamlSec.NumberOfRelocations
235                             : DerivedSec.NumberOfRelocations);
236       W.write<uint32_t>(YamlSec.NumberOfLineNumbers);
237       W.write<int32_t>(YamlSec.Flags);
238       W.OS.write_zeros(4);
239     } else {
240       W.write<uint32_t>(SectionAddress); // Physical address
241       W.write<uint32_t>(SectionAddress); // Virtual address
242       W.write<uint32_t>(YamlSec.Size ? YamlSec.Size : DerivedSec.Size);
243       W.write<uint32_t>(YamlSec.FileOffsetToData ? YamlSec.FileOffsetToData
244                                                  : DerivedSec.FileOffsetToData);
245       W.write<uint32_t>(YamlSec.FileOffsetToRelocations
246                             ? YamlSec.FileOffsetToRelocations
247                             : DerivedSec.FileOffsetToRelocations);
248       W.write<uint32_t>(YamlSec.FileOffsetToLineNumbers);
249       W.write<uint16_t>(YamlSec.NumberOfRelocations
250                             ? YamlSec.NumberOfRelocations
251                             : DerivedSec.NumberOfRelocations);
252       W.write<uint16_t>(YamlSec.NumberOfLineNumbers);
253       W.write<int32_t>(YamlSec.Flags);
254     }
255   }
256 }
257 
258 bool XCOFFWriter::writeSectionData() {
259   for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
260     XCOFFYAML::Section YamlSec = Obj.Sections[I];
261     if (YamlSec.SectionData.binary_size()) {
262       // Fill the padding size with zeros.
263       int64_t PaddingSize =
264           InitSections[I].FileOffsetToData - (W.OS.tell() - StartOffset);
265       if (PaddingSize < 0) {
266         ErrHandler("redundant data was written before section data");
267         return false;
268       }
269       W.OS.write_zeros(PaddingSize);
270       YamlSec.SectionData.writeAsBinary(W.OS);
271     }
272   }
273   return true;
274 }
275 
276 bool XCOFFWriter::writeRelocations() {
277   for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
278     XCOFFYAML::Section YamlSec = Obj.Sections[I];
279     if (!YamlSec.Relocations.empty()) {
280       int64_t PaddingSize =
281           InitSections[I].FileOffsetToRelocations - (W.OS.tell() - StartOffset);
282       if (PaddingSize < 0) {
283         ErrHandler("redundant data was written before relocations");
284         return false;
285       }
286       W.OS.write_zeros(PaddingSize);
287       for (const XCOFFYAML::Relocation &YamlRel : YamlSec.Relocations) {
288         if (Is64Bit)
289           W.write<uint64_t>(YamlRel.VirtualAddress);
290         else
291           W.write<uint32_t>(YamlRel.VirtualAddress);
292         W.write<uint32_t>(YamlRel.SymbolIndex);
293         W.write<uint8_t>(YamlRel.Info);
294         W.write<uint8_t>(YamlRel.Type);
295       }
296     }
297   }
298   return true;
299 }
300 
301 bool XCOFFWriter::writeSymbols() {
302   int64_t PaddingSize =
303       (uint64_t)InitFileHdr.SymbolTableOffset - (W.OS.tell() - StartOffset);
304   if (PaddingSize < 0) {
305     ErrHandler("redundant data was written before symbols");
306     return false;
307   }
308   W.OS.write_zeros(PaddingSize);
309   for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
310     if (Is64Bit) {
311       W.write<uint64_t>(YamlSym.Value);
312       W.write<uint32_t>(Strings.getOffset(YamlSym.SymbolName));
313     } else {
314       if (nameShouldBeInStringTable(YamlSym.SymbolName)) {
315         // For XCOFF32: A value of 0 indicates that the symbol name is in the
316         // string table.
317         W.write<int32_t>(0);
318         W.write<uint32_t>(Strings.getOffset(YamlSym.SymbolName));
319       } else {
320         writeName(YamlSym.SymbolName, W);
321       }
322       W.write<uint32_t>(YamlSym.Value);
323     }
324     W.write<int16_t>(
325         YamlSym.SectionName.size() ? SectionIndexMap[YamlSym.SectionName] : 0);
326     W.write<uint16_t>(YamlSym.Type);
327     W.write<uint8_t>(YamlSym.StorageClass);
328     W.write<uint8_t>(YamlSym.NumberOfAuxEntries);
329 
330     // Now output the auxiliary entry.
331     for (uint8_t I = 0, E = YamlSym.NumberOfAuxEntries; I < E; ++I) {
332       // TODO: Auxiliary entry is not supported yet.
333       // The auxiliary entries for a symbol follow its symbol table entry. The
334       // length of each auxiliary entry is the same as a symbol table entry (18
335       // bytes). The format and quantity of auxiliary entries depend on the
336       // storage class (n_sclass) and type (n_type) of the symbol table entry.
337       W.OS.write_zeros(XCOFF::SymbolTableEntrySize);
338     }
339   }
340   return true;
341 }
342 
343 bool XCOFFWriter::writeXCOFF() {
344   if (!assignAddressesAndIndices())
345     return false;
346   StartOffset = W.OS.tell();
347   writeFileHeader();
348   if (!Obj.Sections.empty()) {
349     writeSectionHeader();
350     if (!writeSectionData())
351       return false;
352     if (!writeRelocations())
353       return false;
354   }
355   if (!Obj.Symbols.empty() && !writeSymbols())
356     return false;
357   // Write the string table.
358   if (Strings.getSize() > 4)
359     Strings.write(W.OS);
360   return true;
361 }
362 
363 } // end anonymous namespace
364 
365 namespace llvm {
366 namespace yaml {
367 
368 bool yaml2xcoff(XCOFFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH) {
369   XCOFFWriter Writer(Doc, Out, EH);
370   return Writer.writeXCOFF();
371 }
372 
373 } // namespace yaml
374 } // namespace llvm
375