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:
XCOFFWriter(XCOFFYAML::Object & Obj,raw_ostream & OS,yaml::ErrorHandler EH)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
writeName(StringRef StrName,support::endian::Writer W)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
nameShouldBeInStringTable(StringRef SymbolName)77 bool XCOFFWriter::nameShouldBeInStringTable(StringRef SymbolName) {
78 return SymbolName.size() > XCOFF::NameSize;
79 }
80
initRelocations(uint64_t & CurrentOffset)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
initSectionHeader(uint64_t & CurrentOffset)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
initFileHeader(uint64_t CurrentOffset)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
assignAddressesAndIndices()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
writeFileHeader()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
writeSectionHeader()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
writeSectionData()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
writeRelocations()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
writeSymbols()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
writeXCOFF()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
yaml2xcoff(XCOFFYAML::Object & Doc,raw_ostream & Out,ErrorHandler EH)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