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   return SymbolName.size() > XCOFF::NameSize;
79 }
80 
81 bool XCOFFWriter::initRelocations(uint64_t &CurrentOffset) {
82   for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) {
83     if (!InitSections[I].Relocations.empty()) {
84       InitSections[I].NumberOfRelocations = InitSections[I].Relocations.size();
85       InitSections[I].FileOffsetToRelocations = CurrentOffset;
86       CurrentOffset += InitSections[I].NumberOfRelocations *
87                        XCOFF::RelocationSerializationSize32;
88       if (CurrentOffset > MaxRawDataSize) {
89         ErrHandler("maximum object size of" + Twine(MaxRawDataSize) +
90                    "exceeded when writing relocation data");
91         return false;
92       }
93     }
94   }
95   return true;
96 }
97 
98 bool XCOFFWriter::initSectionHeader(uint64_t &CurrentOffset) {
99   uint64_t CurrentSecAddr = 0;
100   for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) {
101     if (CurrentOffset > MaxRawDataSize) {
102       ErrHandler("maximum object size of" + Twine(MaxRawDataSize) +
103                  "exceeded when writing section data");
104       return false;
105     }
106 
107     // Assign indices for sections.
108     if (InitSections[I].SectionName.size() &&
109         !SectionIndexMap[InitSections[I].SectionName]) {
110       // The section index starts from 1.
111       SectionIndexMap[InitSections[I].SectionName] = I + 1;
112       if ((I + 1) > MaxSectionIndex) {
113         ErrHandler("exceeded the maximum permitted section index of " +
114                    Twine(MaxSectionIndex));
115         return false;
116       }
117     }
118 
119     // Calculate the physical/virtual address. This field should contain 0 for
120     // all sections except the text, data and bss sections.
121     if (InitSections[I].Flags != XCOFF::STYP_TEXT &&
122         InitSections[I].Flags != XCOFF::STYP_DATA &&
123         InitSections[I].Flags != XCOFF::STYP_BSS)
124       InitSections[I].Address = 0;
125     else
126       InitSections[I].Address = CurrentSecAddr;
127 
128     // Calculate the FileOffsetToData and data size for sections.
129     if (InitSections[I].SectionData.binary_size()) {
130       InitSections[I].FileOffsetToData = CurrentOffset;
131       CurrentOffset += InitSections[I].SectionData.binary_size();
132       // Ensure the offset is aligned to DefaultSectionAlign.
133       CurrentOffset = alignTo(CurrentOffset, DefaultSectionAlign);
134       InitSections[I].Size = CurrentOffset - InitSections[I].FileOffsetToData;
135       CurrentSecAddr += InitSections[I].Size;
136     }
137   }
138   return initRelocations(CurrentOffset);
139 }
140 
141 bool XCOFFWriter::initFileHeader(uint64_t CurrentOffset) {
142   // The default format of the object file is XCOFF32.
143   InitFileHdr.Magic = XCOFF::XCOFF32;
144   InitFileHdr.NumberOfSections = Obj.Sections.size();
145   InitFileHdr.NumberOfSymTableEntries = Obj.Symbols.size();
146 
147   for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
148     // Add the number of auxiliary symbols to the total number.
149     InitFileHdr.NumberOfSymTableEntries += YamlSym.NumberOfAuxEntries;
150     if (nameShouldBeInStringTable(YamlSym.SymbolName))
151       Strings.add(YamlSym.SymbolName);
152   }
153   // Finalize the string table.
154   Strings.finalize();
155 
156   // Calculate SymbolTableOffset for the file header.
157   if (InitFileHdr.NumberOfSymTableEntries) {
158     InitFileHdr.SymbolTableOffset = CurrentOffset;
159     CurrentOffset +=
160         InitFileHdr.NumberOfSymTableEntries * XCOFF::SymbolTableEntrySize;
161     if (CurrentOffset > MaxRawDataSize) {
162       ErrHandler("maximum object size of" + Twine(MaxRawDataSize) +
163                  "exceeded when writing symbols");
164       return false;
165     }
166   }
167   // TODO: Calculate FileOffsetToLineNumbers when line number supported.
168   return true;
169 }
170 
171 bool XCOFFWriter::assignAddressesAndIndices() {
172   Strings.clear();
173   uint64_t CurrentOffset =
174       XCOFF::FileHeaderSize32 /* TODO: + auxiliaryHeaderSize() */ +
175       InitSections.size() * XCOFF::SectionHeaderSize32;
176 
177   // Calculate section header info.
178   if (!initSectionHeader(CurrentOffset))
179     return false;
180   // Calculate file header info.
181   return initFileHeader(CurrentOffset);
182 }
183 
184 void XCOFFWriter::writeFileHeader() {
185   W.write<uint16_t>(Obj.Header.Magic ? Obj.Header.Magic : InitFileHdr.Magic);
186   W.write<uint16_t>(Obj.Header.NumberOfSections ? Obj.Header.NumberOfSections
187                                                 : InitFileHdr.NumberOfSections);
188   W.write<int32_t>(Obj.Header.TimeStamp);
189   W.write<uint32_t>(Obj.Header.SymbolTableOffset
190                         ? Obj.Header.SymbolTableOffset
191                         : InitFileHdr.SymbolTableOffset);
192   W.write<int32_t>(Obj.Header.NumberOfSymTableEntries
193                        ? Obj.Header.NumberOfSymTableEntries
194                        : InitFileHdr.NumberOfSymTableEntries);
195   W.write<uint16_t>(Obj.Header.AuxHeaderSize);
196   W.write<uint16_t>(Obj.Header.Flags);
197 }
198 
199 void XCOFFWriter::writeSectionHeader() {
200   for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
201     XCOFFYAML::Section YamlSec = Obj.Sections[I];
202     XCOFFYAML::Section DerivedSec = InitSections[I];
203     writeName(YamlSec.SectionName, W);
204     // Virtual address is the same as physical address.
205     uint32_t SectionAddress =
206         YamlSec.Address ? YamlSec.Address : DerivedSec.Address;
207     W.write<uint32_t>(SectionAddress); // Physical address
208     W.write<uint32_t>(SectionAddress); // Virtual address
209     W.write<uint32_t>(YamlSec.Size ? YamlSec.Size : DerivedSec.Size);
210     W.write<uint32_t>(YamlSec.FileOffsetToData ? YamlSec.FileOffsetToData
211                                                : DerivedSec.FileOffsetToData);
212     W.write<uint32_t>(YamlSec.FileOffsetToRelocations
213                           ? YamlSec.FileOffsetToRelocations
214                           : DerivedSec.FileOffsetToRelocations);
215     W.write<uint32_t>(YamlSec.FileOffsetToLineNumbers);
216     W.write<uint16_t>(YamlSec.NumberOfRelocations
217                           ? YamlSec.NumberOfRelocations
218                           : DerivedSec.NumberOfRelocations);
219     W.write<uint16_t>(YamlSec.NumberOfLineNumbers);
220     W.write<int32_t>(YamlSec.Flags);
221   }
222 }
223 
224 bool XCOFFWriter::writeSectionData() {
225   for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
226     XCOFFYAML::Section YamlSec = Obj.Sections[I];
227     if (YamlSec.SectionData.binary_size()) {
228       // Fill the padding size with zeros.
229       int64_t PaddingSize =
230           InitSections[I].FileOffsetToData - (W.OS.tell() - StartOffset);
231       if (PaddingSize < 0) {
232         ErrHandler("redundant data was written before section data");
233         return false;
234       }
235       if (PaddingSize > 0)
236         W.OS.write_zeros(PaddingSize);
237       YamlSec.SectionData.writeAsBinary(W.OS);
238     }
239   }
240   return true;
241 }
242 
243 bool XCOFFWriter::writeRelocations() {
244   for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
245     XCOFFYAML::Section YamlSec = Obj.Sections[I];
246     if (!YamlSec.Relocations.empty()) {
247       int64_t PaddingSize =
248           InitSections[I].FileOffsetToRelocations - (W.OS.tell() - StartOffset);
249       if (PaddingSize < 0) {
250         ErrHandler("redundant data was written before relocations");
251         return false;
252       }
253       if (PaddingSize > 0)
254         W.OS.write_zeros(PaddingSize);
255       for (const XCOFFYAML::Relocation &YamlRel : YamlSec.Relocations) {
256         W.write<uint32_t>(YamlRel.VirtualAddress);
257         W.write<uint32_t>(YamlRel.SymbolIndex);
258         W.write<uint8_t>(YamlRel.Info);
259         W.write<uint8_t>(YamlRel.Type);
260       }
261     }
262   }
263   return true;
264 }
265 
266 bool XCOFFWriter::writeSymbols() {
267   int64_t PaddingSize =
268       (uint64_t)InitFileHdr.SymbolTableOffset - (W.OS.tell() - StartOffset);
269   if (PaddingSize < 0) {
270     ErrHandler("redundant data was written before symbols");
271     return false;
272   }
273   if (PaddingSize > 0)
274     W.OS.write_zeros(PaddingSize);
275   for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
276     if (nameShouldBeInStringTable(YamlSym.SymbolName)) {
277       // For XCOFF32: A value of 0 indicates that the symbol name is in the
278       // string table.
279       W.write<int32_t>(0);
280       W.write<uint32_t>(Strings.getOffset(YamlSym.SymbolName));
281     } else {
282       writeName(YamlSym.SymbolName, W);
283     }
284     W.write<uint32_t>(YamlSym.Value);
285     W.write<int16_t>(
286         YamlSym.SectionName.size() ? SectionIndexMap[YamlSym.SectionName] : 0);
287     W.write<uint16_t>(YamlSym.Type);
288     W.write<uint8_t>(YamlSym.StorageClass);
289     W.write<uint8_t>(YamlSym.NumberOfAuxEntries);
290 
291     // Now output the auxiliary entry.
292     for (uint8_t I = 0, E = YamlSym.NumberOfAuxEntries; I < E; ++I) {
293       // TODO: Auxiliary entry is not supported yet.
294       // The auxiliary entries for a symbol follow its symbol table entry. The
295       // length of each auxiliary entry is the same as a symbol table entry (18
296       // bytes). The format and quantity of auxiliary entries depend on the
297       // storage class (n_sclass) and type (n_type) of the symbol table entry.
298       W.OS.write_zeros(18);
299     }
300   }
301   return true;
302 }
303 
304 bool XCOFFWriter::writeXCOFF() {
305   if (Is64Bit) {
306     ErrHandler("only XCOFF32 is currently supported");
307     return false;
308   }
309   if (!assignAddressesAndIndices())
310     return false;
311   StartOffset = W.OS.tell();
312   writeFileHeader();
313   if (!Obj.Sections.empty()) {
314     writeSectionHeader();
315     if (!writeSectionData())
316       return false;
317     if (!writeRelocations())
318       return false;
319   }
320   if (!Obj.Symbols.empty() && !writeSymbols())
321     return false;
322   // Write the string table.
323   if (Strings.getSize() > 4)
324     Strings.write(W.OS);
325   return true;
326 }
327 
328 } // end anonymous namespace
329 
330 namespace llvm {
331 namespace yaml {
332 
333 bool yaml2xcoff(XCOFFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH) {
334   XCOFFWriter Writer(Doc, Out, EH);
335   return Writer.writeXCOFF();
336 }
337 
338 } // namespace yaml
339 } // namespace llvm
340