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