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
25using namespace llvm;
26using namespace llvm::object;
27
28namespace {
29
30constexpr unsigned DefaultSectionAlign = 4;
31constexpr int16_t MaxSectionIndex = INT16_MAX;
32constexpr uint32_t MaxRawDataSize = UINT32_MAX;
33
34class XCOFFWriter {
35public:
36 XCOFFWriter(XCOFFYAML::Object &Obj, raw_ostream &OS, yaml::ErrorHandler EH)
37 : Obj(Obj), W(OS, llvm::endianness::big), ErrHandler(EH),
38 StrTblBuilder(StringTableBuilder::XCOFF) {
39 Is64Bit = Obj.Header.Magic == (llvm::yaml::Hex16)XCOFF::XCOFF64;
40 }
41 bool writeXCOFF();
42
43private:
44 void reportOverwrite(uint64_t currentOffset, uint64_t specifiedOffset,
45 const Twine &fieldName);
46 bool nameShouldBeInStringTable(StringRef SymbolName);
47 bool initFileHeader(uint64_t CurrentOffset);
48 void initAuxFileHeader();
49 bool initSectionHeaders(uint64_t &CurrentOffset);
50 bool initRelocations(uint64_t &CurrentOffset);
51 bool initStringTable();
52 bool assignAddressesAndIndices();
53
54 void writeFileHeader();
55 void writeAuxFileHeader();
56 void writeSectionHeaders();
57 bool writeSectionData();
58 bool writeRelocations();
59 bool writeSymbols();
60 void writeStringTable();
61
62 bool writeAuxSymbol(const XCOFFYAML::CsectAuxEnt &AuxSym);
63 bool writeAuxSymbol(const XCOFFYAML::FileAuxEnt &AuxSym);
64 bool writeAuxSymbol(const XCOFFYAML::FunctionAuxEnt &AuxSym);
65 bool writeAuxSymbol(const XCOFFYAML::ExcpetionAuxEnt &AuxSym);
66 bool writeAuxSymbol(const XCOFFYAML::BlockAuxEnt &AuxSym);
67 bool writeAuxSymbol(const XCOFFYAML::SectAuxEntForDWARF &AuxSym);
68 bool writeAuxSymbol(const XCOFFYAML::SectAuxEntForStat &AuxSym);
69 bool writeAuxSymbol(const std::unique_ptr<XCOFFYAML::AuxSymbolEnt> &AuxSym);
70
71 XCOFFYAML::Object &Obj;
72 bool Is64Bit = false;
73 support::endian::Writer W;
74 yaml::ErrorHandler ErrHandler;
75 StringTableBuilder StrTblBuilder;
76 uint64_t StartOffset = 0u;
77 // Map the section name to its corrresponding section index.
78 DenseMap<StringRef, int16_t> SectionIndexMap = {
79 {StringRef("N_DEBUG"), XCOFF::N_DEBUG},
80 {StringRef("N_ABS"), XCOFF::N_ABS},
81 {StringRef("N_UNDEF"), XCOFF::N_UNDEF}};
82 XCOFFYAML::FileHeader InitFileHdr = Obj.Header;
83 XCOFFYAML::AuxiliaryHeader InitAuxFileHdr;
84 std::vector<XCOFFYAML::Section> InitSections = Obj.Sections;
85};
86
87static void writeName(StringRef StrName, support::endian::Writer W) {
88 char Name[XCOFF::NameSize];
89 memset(s: Name, c: 0, n: XCOFF::NameSize);
90 char SrcName[] = "";
91 memcpy(dest: Name, src: StrName.size() ? StrName.data() : SrcName, n: StrName.size());
92 ArrayRef<char> NameRef(Name, XCOFF::NameSize);
93 W.write(Val: NameRef);
94}
95
96void XCOFFWriter::reportOverwrite(uint64_t CurrentOffset,
97 uint64_t specifiedOffset,
98 const Twine &fieldName) {
99 ErrHandler("current file offset (" + Twine(CurrentOffset) +
100 ") is bigger than the specified " + fieldName + " (" +
101 Twine(specifiedOffset) + ") ");
102}
103
104bool XCOFFWriter::nameShouldBeInStringTable(StringRef SymbolName) {
105 // For XCOFF64: The symbol name is always in the string table.
106 return (SymbolName.size() > XCOFF::NameSize) || Is64Bit;
107}
108
109bool XCOFFWriter::initRelocations(uint64_t &CurrentOffset) {
110 for (XCOFFYAML::Section &InitSection : InitSections) {
111 if (!InitSection.Relocations.empty()) {
112 uint64_t RelSize = Is64Bit ? XCOFF::RelocationSerializationSize64
113 : XCOFF::RelocationSerializationSize32;
114 uint64_t UsedSize = RelSize * InitSection.Relocations.size();
115
116 // If NumberOfRelocations was specified, we use it, even if it's
117 // not consistent with the number of provided relocations.
118 if (!InitSection.NumberOfRelocations)
119 InitSection.NumberOfRelocations = InitSection.Relocations.size();
120
121 // If the YAML file specified an offset to relocations, we use it.
122 if (InitSection.FileOffsetToRelocations) {
123 if (CurrentOffset > InitSection.FileOffsetToRelocations) {
124 reportOverwrite(CurrentOffset, specifiedOffset: InitSection.FileOffsetToRelocations,
125 fieldName: "FileOffsetToRelocations for the " +
126 InitSection.SectionName + " section");
127 return false;
128 }
129 CurrentOffset = InitSection.FileOffsetToRelocations;
130 } else
131 InitSection.FileOffsetToRelocations = CurrentOffset;
132 CurrentOffset += UsedSize;
133 if (CurrentOffset > MaxRawDataSize) {
134 ErrHandler("maximum object size (" + Twine(MaxRawDataSize) +
135 ") exceeded when writing relocation data for section " +
136 Twine(InitSection.SectionName));
137 return false;
138 }
139 }
140 }
141 return true;
142}
143
144bool XCOFFWriter::initSectionHeaders(uint64_t &CurrentOffset) {
145 uint64_t CurrentEndDataAddr = 0;
146 uint64_t CurrentEndTDataAddr = 0;
147 for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) {
148 // Assign indices for sections.
149 if (InitSections[I].SectionName.size() &&
150 !SectionIndexMap[InitSections[I].SectionName]) {
151 // The section index starts from 1.
152 SectionIndexMap[InitSections[I].SectionName] = I + 1;
153 if ((I + 1) > MaxSectionIndex) {
154 ErrHandler("exceeded the maximum permitted section index of " +
155 Twine(MaxSectionIndex));
156 return false;
157 }
158 }
159
160 if (!InitSections[I].Size)
161 InitSections[I].Size = InitSections[I].SectionData.binary_size();
162
163 // Section data addresses (physical/virtual) are related to symbol
164 // addresses and alignments. Furthermore, it is possible to specify the
165 // same starting addresses for the .text, .data, and .tdata sections.
166 // Without examining all the symbols and their addreses and alignments,
167 // it is not possible to compute valid section addresses. The only
168 // condition required by XCOFF is that the .bss section immediately
169 // follows the .data section, and the .tbss section immediately follows
170 // the .tdata section. Therefore, we only assign addresses to the .bss
171 // and .tbss sections if they do not already have non-zero addresses.
172 // (If the YAML file is being used to generate a valid object file, we
173 // expect all section addresses to be specified explicitly.)
174 switch (InitSections[I].Flags) {
175 case XCOFF::STYP_DATA:
176 CurrentEndDataAddr = InitSections[I].Address + InitSections[I].Size;
177 break;
178 case XCOFF::STYP_BSS:
179 if (!InitSections[I].Address)
180 InitSections[I].Address = CurrentEndDataAddr;
181 break;
182 case XCOFF::STYP_TDATA:
183 CurrentEndTDataAddr = InitSections[I].Address + InitSections[I].Size;
184 break;
185 case XCOFF::STYP_TBSS:
186 if (!InitSections[I].Address)
187 InitSections[I].Address = CurrentEndTDataAddr;
188 break;
189 }
190
191 if (InitSections[I].SectionData.binary_size()) {
192 if (InitSections[I].FileOffsetToData) {
193 // Use the providedFileOffsetToData.
194 if (CurrentOffset > InitSections[I].FileOffsetToData) {
195 reportOverwrite(CurrentOffset, specifiedOffset: InitSections[I].FileOffsetToData,
196 fieldName: "FileOffsetToData for the " +
197 InitSections[I].SectionName + " section");
198 return false;
199 }
200 CurrentOffset = InitSections[I].FileOffsetToData;
201 } else {
202 CurrentOffset = alignTo(Value: CurrentOffset, Align: DefaultSectionAlign);
203 InitSections[I].FileOffsetToData = CurrentOffset;
204 }
205 CurrentOffset += InitSections[I].SectionData.binary_size();
206 if (CurrentOffset > MaxRawDataSize) {
207 ErrHandler("maximum object size (" + Twine(MaxRawDataSize) +
208 ") exceeded when writing data for section " + Twine(I + 1) +
209 " (" + Twine(InitSections[I].SectionName) + ")");
210 return false;
211 }
212 }
213 if (InitSections[I].SectionSubtype) {
214 uint32_t DWARFSubtype =
215 static_cast<uint32_t>(*InitSections[I].SectionSubtype);
216 if (InitSections[I].Flags != XCOFF::STYP_DWARF) {
217 ErrHandler("a DWARFSectionSubtype is only allowed for a DWARF section");
218 return false;
219 }
220 unsigned Mask = Is64Bit ? XCOFFSectionHeader64::SectionFlagsTypeMask
221 : XCOFFSectionHeader32::SectionFlagsTypeMask;
222 if (DWARFSubtype & Mask) {
223 ErrHandler("the low-order bits of DWARFSectionSubtype must be 0");
224 return false;
225 }
226 InitSections[I].Flags |= DWARFSubtype;
227 }
228 }
229 return initRelocations(CurrentOffset);
230}
231
232bool XCOFFWriter::initStringTable() {
233 if (Obj.StrTbl.RawContent) {
234 size_t RawSize = Obj.StrTbl.RawContent->binary_size();
235 if (Obj.StrTbl.Strings || Obj.StrTbl.Length) {
236 ErrHandler(
237 "can't specify Strings or Length when RawContent is specified");
238 return false;
239 }
240 if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize < RawSize) {
241 ErrHandler("specified ContentSize (" + Twine(*Obj.StrTbl.ContentSize) +
242 ") is less than the RawContent data size (" + Twine(RawSize) +
243 ")");
244 return false;
245 }
246 return true;
247 }
248 if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize <= 3) {
249 ErrHandler("ContentSize shouldn't be less than 4 without RawContent");
250 return false;
251 }
252
253 // Build the string table.
254 StrTblBuilder.clear();
255
256 if (Obj.StrTbl.Strings) {
257 // Add all specified strings to the string table.
258 for (StringRef StringEnt : *Obj.StrTbl.Strings)
259 StrTblBuilder.add(S: StringEnt);
260
261 size_t StrTblIdx = 0;
262 size_t NumOfStrings = Obj.StrTbl.Strings->size();
263 for (XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
264 if (nameShouldBeInStringTable(SymbolName: YamlSym.SymbolName)) {
265 if (StrTblIdx < NumOfStrings) {
266 // Overwrite the symbol name with the specified string.
267 YamlSym.SymbolName = (*Obj.StrTbl.Strings)[StrTblIdx];
268 ++StrTblIdx;
269 } else
270 // Names that are not overwritten are still stored in the string
271 // table.
272 StrTblBuilder.add(S: YamlSym.SymbolName);
273 }
274 }
275 } else {
276 for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
277 if (nameShouldBeInStringTable(SymbolName: YamlSym.SymbolName))
278 StrTblBuilder.add(S: YamlSym.SymbolName);
279 }
280 }
281
282 // Check if the file name in the File Auxiliary Entry should be added to the
283 // string table.
284 for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
285 for (const std::unique_ptr<XCOFFYAML::AuxSymbolEnt> &AuxSym :
286 YamlSym.AuxEntries) {
287 if (auto AS = dyn_cast<XCOFFYAML::FileAuxEnt>(Val: AuxSym.get()))
288 if (nameShouldBeInStringTable(SymbolName: AS->FileNameOrString.value_or(u: "")))
289 StrTblBuilder.add(S: AS->FileNameOrString.value_or(u: ""));
290 }
291 }
292
293 StrTblBuilder.finalize();
294
295 size_t StrTblSize = StrTblBuilder.getSize();
296 if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize < StrTblSize) {
297 ErrHandler("specified ContentSize (" + Twine(*Obj.StrTbl.ContentSize) +
298 ") is less than the size of the data that would otherwise be "
299 "written (" +
300 Twine(StrTblSize) + ")");
301 return false;
302 }
303
304 return true;
305}
306
307bool XCOFFWriter::initFileHeader(uint64_t CurrentOffset) {
308 // The default format of the object file is XCOFF32.
309 InitFileHdr.Magic = XCOFF::XCOFF32;
310 InitFileHdr.NumberOfSections = Obj.Sections.size();
311 InitFileHdr.NumberOfSymTableEntries = Obj.Symbols.size();
312
313 for (XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
314 uint32_t AuxCount = YamlSym.AuxEntries.size();
315 if (YamlSym.NumberOfAuxEntries && *YamlSym.NumberOfAuxEntries < AuxCount) {
316 ErrHandler("specified NumberOfAuxEntries " +
317 Twine(static_cast<uint32_t>(*YamlSym.NumberOfAuxEntries)) +
318 " is less than the actual number "
319 "of auxiliary entries " +
320 Twine(AuxCount));
321 return false;
322 }
323 YamlSym.NumberOfAuxEntries = YamlSym.NumberOfAuxEntries.value_or(u&: AuxCount);
324 // Add the number of auxiliary symbols to the total number.
325 InitFileHdr.NumberOfSymTableEntries += *YamlSym.NumberOfAuxEntries;
326 }
327
328 // Calculate SymbolTableOffset for the file header.
329 if (InitFileHdr.NumberOfSymTableEntries) {
330 if (Obj.Header.SymbolTableOffset) {
331 if (CurrentOffset > Obj.Header.SymbolTableOffset) {
332 reportOverwrite(CurrentOffset, specifiedOffset: Obj.Header.SymbolTableOffset,
333 fieldName: "SymbolTableOffset");
334 return false;
335 }
336 CurrentOffset = Obj.Header.SymbolTableOffset;
337 }
338 InitFileHdr.SymbolTableOffset = CurrentOffset;
339 CurrentOffset +=
340 InitFileHdr.NumberOfSymTableEntries * XCOFF::SymbolTableEntrySize;
341 if (CurrentOffset > MaxRawDataSize) {
342 ErrHandler("maximum object size of " + Twine(MaxRawDataSize) +
343 " exceeded when writing symbols");
344 return false;
345 }
346 }
347 // TODO: Calculate FileOffsetToLineNumbers when line number supported.
348 return true;
349}
350
351void XCOFFWriter::initAuxFileHeader() {
352 if (Obj.AuxHeader)
353 InitAuxFileHdr = *Obj.AuxHeader;
354 // In general, an object file might contain multiple sections of a given type,
355 // but in a loadable module, there must be exactly one .text, .data, .bss, and
356 // .loader section. A loadable object might also have one .tdata section and
357 // one .tbss section.
358 // Set these section-related values if not set explicitly. We assume that the
359 // input YAML matches the format of the loadable object, but if multiple input
360 // sections still have the same type, the first section with that type
361 // prevails.
362 for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) {
363 switch (InitSections[I].Flags) {
364 case XCOFF::STYP_TEXT:
365 if (!InitAuxFileHdr.TextSize)
366 InitAuxFileHdr.TextSize = InitSections[I].Size;
367 if (!InitAuxFileHdr.TextStartAddr)
368 InitAuxFileHdr.TextStartAddr = InitSections[I].Address;
369 if (!InitAuxFileHdr.SecNumOfText)
370 InitAuxFileHdr.SecNumOfText = I + 1;
371 break;
372 case XCOFF::STYP_DATA:
373 if (!InitAuxFileHdr.InitDataSize)
374 InitAuxFileHdr.InitDataSize = InitSections[I].Size;
375 if (!InitAuxFileHdr.DataStartAddr)
376 InitAuxFileHdr.DataStartAddr = InitSections[I].Address;
377 if (!InitAuxFileHdr.SecNumOfData)
378 InitAuxFileHdr.SecNumOfData = I + 1;
379 break;
380 case XCOFF::STYP_BSS:
381 if (!InitAuxFileHdr.BssDataSize)
382 InitAuxFileHdr.BssDataSize = InitSections[I].Size;
383 if (!InitAuxFileHdr.SecNumOfBSS)
384 InitAuxFileHdr.SecNumOfBSS = I + 1;
385 break;
386 case XCOFF::STYP_TDATA:
387 if (!InitAuxFileHdr.SecNumOfTData)
388 InitAuxFileHdr.SecNumOfTData = I + 1;
389 break;
390 case XCOFF::STYP_TBSS:
391 if (!InitAuxFileHdr.SecNumOfTBSS)
392 InitAuxFileHdr.SecNumOfTBSS = I + 1;
393 break;
394 case XCOFF::STYP_LOADER:
395 if (!InitAuxFileHdr.SecNumOfLoader)
396 InitAuxFileHdr.SecNumOfLoader = I + 1;
397 break;
398 default:
399 break;
400 }
401 }
402}
403
404bool XCOFFWriter::assignAddressesAndIndices() {
405 uint64_t FileHdrSize =
406 Is64Bit ? XCOFF::FileHeaderSize64 : XCOFF::FileHeaderSize32;
407
408 // If AuxHeaderSize is specified in the YAML file, we construct
409 // an auxiliary header.
410 uint64_t AuxFileHdrSize = 0;
411
412 if (Obj.Header.AuxHeaderSize)
413 AuxFileHdrSize = Obj.Header.AuxHeaderSize;
414 else if (Obj.AuxHeader)
415 AuxFileHdrSize =
416 (Is64Bit ? XCOFF::AuxFileHeaderSize64 : XCOFF::AuxFileHeaderSize32);
417 uint64_t SecHdrSize =
418 Is64Bit ? XCOFF::SectionHeaderSize64 : XCOFF::SectionHeaderSize32;
419 uint64_t CurrentOffset =
420 FileHdrSize + AuxFileHdrSize + InitSections.size() * SecHdrSize;
421
422 // Calculate section header info.
423 if (!initSectionHeaders(CurrentOffset))
424 return false;
425
426 // Calculate file header info.
427 if (!initFileHeader(CurrentOffset))
428 return false;
429 InitFileHdr.AuxHeaderSize = AuxFileHdrSize;
430
431 // Initialize the auxiliary file header.
432 if (AuxFileHdrSize)
433 initAuxFileHeader();
434
435 // Initialize the string table.
436 return initStringTable();
437}
438
439void XCOFFWriter::writeFileHeader() {
440 W.write<uint16_t>(Val: Obj.Header.Magic ? Obj.Header.Magic : InitFileHdr.Magic);
441 W.write<uint16_t>(Val: Obj.Header.NumberOfSections ? Obj.Header.NumberOfSections
442 : InitFileHdr.NumberOfSections);
443 W.write<int32_t>(Val: Obj.Header.TimeStamp);
444 if (Is64Bit) {
445 W.write<uint64_t>(Val: InitFileHdr.SymbolTableOffset);
446 W.write<uint16_t>(Val: InitFileHdr.AuxHeaderSize);
447 W.write<uint16_t>(Val: Obj.Header.Flags);
448 W.write<int32_t>(Val: Obj.Header.NumberOfSymTableEntries
449 ? Obj.Header.NumberOfSymTableEntries
450 : InitFileHdr.NumberOfSymTableEntries);
451 } else {
452 W.write<uint32_t>(Val: InitFileHdr.SymbolTableOffset);
453 W.write<int32_t>(Val: Obj.Header.NumberOfSymTableEntries
454 ? Obj.Header.NumberOfSymTableEntries
455 : InitFileHdr.NumberOfSymTableEntries);
456 W.write<uint16_t>(Val: InitFileHdr.AuxHeaderSize);
457 W.write<uint16_t>(Val: Obj.Header.Flags);
458 }
459}
460
461void XCOFFWriter::writeAuxFileHeader() {
462 W.write<uint16_t>(Val: InitAuxFileHdr.Magic.value_or(u: yaml::Hex16(1)));
463 W.write<uint16_t>(Val: InitAuxFileHdr.Version.value_or(u: yaml::Hex16(1)));
464 if (Is64Bit) {
465 W.OS.write_zeros(NumZeros: 4); // Reserved for debugger.
466 W.write<uint64_t>(Val: InitAuxFileHdr.TextStartAddr.value_or(u: yaml::Hex64(0)));
467 W.write<uint64_t>(Val: InitAuxFileHdr.DataStartAddr.value_or(u: yaml::Hex64(0)));
468 W.write<uint64_t>(Val: InitAuxFileHdr.TOCAnchorAddr.value_or(u: yaml::Hex64(0)));
469 } else {
470 W.write<uint32_t>(Val: InitAuxFileHdr.TextSize.value_or(u: yaml::Hex64(0)));
471 W.write<uint32_t>(Val: InitAuxFileHdr.InitDataSize.value_or(u: yaml::Hex64(0)));
472 W.write<uint32_t>(Val: InitAuxFileHdr.BssDataSize.value_or(u: yaml::Hex64(0)));
473 W.write<uint32_t>(Val: InitAuxFileHdr.EntryPointAddr.value_or(u: yaml::Hex64(0)));
474 W.write<uint32_t>(Val: InitAuxFileHdr.TextStartAddr.value_or(u: yaml::Hex64(0)));
475 W.write<uint32_t>(Val: InitAuxFileHdr.DataStartAddr.value_or(u: yaml::Hex64(0)));
476 // A short 32-bit auxiliary header ends here.
477 if (InitFileHdr.AuxHeaderSize == XCOFF::AuxFileHeaderSizeShort)
478 return;
479 W.write<uint32_t>(Val: InitAuxFileHdr.TOCAnchorAddr.value_or(u: yaml::Hex64(0)));
480 }
481 W.write<uint16_t>(Val: InitAuxFileHdr.SecNumOfEntryPoint.value_or(u: 0));
482 W.write<uint16_t>(Val: InitAuxFileHdr.SecNumOfText.value_or(u: 0));
483 W.write<uint16_t>(Val: InitAuxFileHdr.SecNumOfData.value_or(u: 0));
484 W.write<uint16_t>(Val: InitAuxFileHdr.SecNumOfTOC.value_or(u: 0));
485 W.write<uint16_t>(Val: InitAuxFileHdr.SecNumOfLoader.value_or(u: 0));
486 W.write<uint16_t>(Val: InitAuxFileHdr.SecNumOfBSS.value_or(u: 0));
487 W.write<uint16_t>(Val: InitAuxFileHdr.MaxAlignOfText.value_or(u: yaml::Hex16(0)));
488 W.write<uint16_t>(Val: InitAuxFileHdr.MaxAlignOfData.value_or(u: yaml::Hex16(0)));
489 W.write<uint16_t>(Val: InitAuxFileHdr.ModuleType.value_or(u: yaml::Hex16(0)));
490 W.write<uint8_t>(Val: InitAuxFileHdr.CpuFlag.value_or(u: yaml::Hex8(0)));
491 W.write<uint8_t>(Val: 0); // Reserved for CPU type.
492 if (Is64Bit) {
493 W.write<uint8_t>(Val: InitAuxFileHdr.TextPageSize.value_or(u: yaml::Hex8(0)));
494 W.write<uint8_t>(Val: InitAuxFileHdr.DataPageSize.value_or(u: yaml::Hex8(0)));
495 W.write<uint8_t>(Val: InitAuxFileHdr.StackPageSize.value_or(u: yaml::Hex8(0)));
496 W.write<uint8_t>(
497 Val: InitAuxFileHdr.FlagAndTDataAlignment.value_or(u: yaml::Hex8(0x80)));
498 W.write<uint64_t>(Val: InitAuxFileHdr.TextSize.value_or(u: yaml::Hex64(0)));
499 W.write<uint64_t>(Val: InitAuxFileHdr.InitDataSize.value_or(u: yaml::Hex64(0)));
500 W.write<uint64_t>(Val: InitAuxFileHdr.BssDataSize.value_or(u: yaml::Hex64(0)));
501 W.write<uint64_t>(Val: InitAuxFileHdr.EntryPointAddr.value_or(u: yaml::Hex64(0)));
502 W.write<uint64_t>(Val: InitAuxFileHdr.MaxStackSize.value_or(u: yaml::Hex64(0)));
503 W.write<uint64_t>(Val: InitAuxFileHdr.MaxDataSize.value_or(u: yaml::Hex64(0)));
504 } else {
505 W.write<uint32_t>(Val: InitAuxFileHdr.MaxStackSize.value_or(u: yaml::Hex64(0)));
506 W.write<uint32_t>(Val: InitAuxFileHdr.MaxDataSize.value_or(u: yaml::Hex64(0)));
507 W.OS.write_zeros(NumZeros: 4); // Reserved for debugger.
508 W.write<uint8_t>(Val: InitAuxFileHdr.TextPageSize.value_or(u: yaml::Hex8(0)));
509 W.write<uint8_t>(Val: InitAuxFileHdr.DataPageSize.value_or(u: yaml::Hex8(0)));
510 W.write<uint8_t>(Val: InitAuxFileHdr.StackPageSize.value_or(u: yaml::Hex8(0)));
511 W.write<uint8_t>(
512 Val: InitAuxFileHdr.FlagAndTDataAlignment.value_or(u: yaml::Hex8(0)));
513 }
514 W.write<uint16_t>(Val: InitAuxFileHdr.SecNumOfTData.value_or(u: 0));
515 W.write<uint16_t>(Val: InitAuxFileHdr.SecNumOfTBSS.value_or(u: 0));
516 if (Is64Bit) {
517 W.write<uint16_t>(
518 Val: InitAuxFileHdr.Flag.value_or(u: yaml::Hex16(XCOFF::SHR_SYMTAB)));
519 if (InitFileHdr.AuxHeaderSize > XCOFF::AuxFileHeaderSize64)
520 W.OS.write_zeros(NumZeros: InitFileHdr.AuxHeaderSize - XCOFF::AuxFileHeaderSize64);
521 } else {
522 if (InitFileHdr.AuxHeaderSize > XCOFF::AuxFileHeaderSize32)
523 W.OS.write_zeros(NumZeros: InitFileHdr.AuxHeaderSize - XCOFF::AuxFileHeaderSize32);
524 }
525}
526
527void XCOFFWriter::writeSectionHeaders() {
528 for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
529 XCOFFYAML::Section DerivedSec = InitSections[I];
530 writeName(StrName: DerivedSec.SectionName, W);
531 if (Is64Bit) {
532 // Virtual address is the same as physical address.
533 W.write<uint64_t>(Val: DerivedSec.Address); // Physical address
534 W.write<uint64_t>(Val: DerivedSec.Address); // Virtual address
535 W.write<uint64_t>(Val: DerivedSec.Size);
536 W.write<uint64_t>(Val: DerivedSec.FileOffsetToData);
537 W.write<uint64_t>(Val: DerivedSec.FileOffsetToRelocations);
538 W.write<uint64_t>(Val: DerivedSec.FileOffsetToLineNumbers);
539 W.write<uint32_t>(Val: DerivedSec.NumberOfRelocations);
540 W.write<uint32_t>(Val: DerivedSec.NumberOfLineNumbers);
541 W.write<int32_t>(Val: DerivedSec.Flags);
542 W.OS.write_zeros(NumZeros: 4);
543 } else {
544 // Virtual address is the same as physical address.
545 W.write<uint32_t>(Val: DerivedSec.Address); // Physical address
546 W.write<uint32_t>(Val: DerivedSec.Address); // Virtual address
547 W.write<uint32_t>(Val: DerivedSec.Size);
548 W.write<uint32_t>(Val: DerivedSec.FileOffsetToData);
549 W.write<uint32_t>(Val: DerivedSec.FileOffsetToRelocations);
550 W.write<uint32_t>(Val: DerivedSec.FileOffsetToLineNumbers);
551 W.write<uint16_t>(Val: DerivedSec.NumberOfRelocations);
552 W.write<uint16_t>(Val: DerivedSec.NumberOfLineNumbers);
553 W.write<int32_t>(Val: DerivedSec.Flags);
554 }
555 }
556}
557
558bool XCOFFWriter::writeSectionData() {
559 for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
560 XCOFFYAML::Section YamlSec = Obj.Sections[I];
561 if (YamlSec.SectionData.binary_size()) {
562 // Fill the padding size with zeros.
563 int64_t PaddingSize = (uint64_t)InitSections[I].FileOffsetToData -
564 (W.OS.tell() - StartOffset);
565 if (PaddingSize < 0) {
566 ErrHandler("redundant data was written before section data");
567 return false;
568 }
569 W.OS.write_zeros(NumZeros: PaddingSize);
570 YamlSec.SectionData.writeAsBinary(OS&: W.OS);
571 }
572 }
573 return true;
574}
575
576bool XCOFFWriter::writeRelocations() {
577 for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
578 XCOFFYAML::Section YamlSec = Obj.Sections[I];
579 if (!YamlSec.Relocations.empty()) {
580 int64_t PaddingSize =
581 InitSections[I].FileOffsetToRelocations - (W.OS.tell() - StartOffset);
582 if (PaddingSize < 0) {
583 ErrHandler("redundant data was written before relocations");
584 return false;
585 }
586 W.OS.write_zeros(NumZeros: PaddingSize);
587 for (const XCOFFYAML::Relocation &YamlRel : YamlSec.Relocations) {
588 if (Is64Bit)
589 W.write<uint64_t>(Val: YamlRel.VirtualAddress);
590 else
591 W.write<uint32_t>(Val: YamlRel.VirtualAddress);
592 W.write<uint32_t>(Val: YamlRel.SymbolIndex);
593 W.write<uint8_t>(Val: YamlRel.Info);
594 W.write<uint8_t>(Val: YamlRel.Type);
595 }
596 }
597 }
598 return true;
599}
600
601bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::CsectAuxEnt &AuxSym) {
602 uint8_t SymAlignAndType = 0;
603 if (AuxSym.SymbolAlignmentAndType) {
604 if (AuxSym.SymbolType || AuxSym.SymbolAlignment) {
605 ErrHandler("cannot specify SymbolType or SymbolAlignment if "
606 "SymbolAlignmentAndType is specified");
607 return false;
608 }
609 SymAlignAndType = *AuxSym.SymbolAlignmentAndType;
610 } else {
611 if (AuxSym.SymbolType) {
612 uint8_t SymbolType = *AuxSym.SymbolType;
613 if (SymbolType & ~XCOFFCsectAuxRef::SymbolTypeMask) {
614 ErrHandler("symbol type must be less than " +
615 Twine(1 + XCOFFCsectAuxRef::SymbolTypeMask));
616 return false;
617 }
618 SymAlignAndType = SymbolType;
619 }
620 if (AuxSym.SymbolAlignment) {
621 const uint8_t ShiftedSymbolAlignmentMask =
622 XCOFFCsectAuxRef::SymbolAlignmentMask >>
623 XCOFFCsectAuxRef::SymbolAlignmentBitOffset;
624
625 if (*AuxSym.SymbolAlignment & ~ShiftedSymbolAlignmentMask) {
626 ErrHandler("symbol alignment must be less than " +
627 Twine(1 + ShiftedSymbolAlignmentMask));
628 return false;
629 }
630 SymAlignAndType |= (*AuxSym.SymbolAlignment
631 << XCOFFCsectAuxRef::SymbolAlignmentBitOffset);
632 }
633 }
634 if (Is64Bit) {
635 W.write<uint32_t>(Val: AuxSym.SectionOrLengthLo.value_or(u: 0));
636 W.write<uint32_t>(Val: AuxSym.ParameterHashIndex.value_or(u: 0));
637 W.write<uint16_t>(Val: AuxSym.TypeChkSectNum.value_or(u: 0));
638 W.write<uint8_t>(Val: SymAlignAndType);
639 W.write<uint8_t>(Val: AuxSym.StorageMappingClass.value_or(u: XCOFF::XMC_PR));
640 W.write<uint32_t>(Val: AuxSym.SectionOrLengthHi.value_or(u: 0));
641 W.write<uint8_t>(Val: 0);
642 W.write<uint8_t>(Val: XCOFF::AUX_CSECT);
643 } else {
644 W.write<uint32_t>(Val: AuxSym.SectionOrLength.value_or(u: 0));
645 W.write<uint32_t>(Val: AuxSym.ParameterHashIndex.value_or(u: 0));
646 W.write<uint16_t>(Val: AuxSym.TypeChkSectNum.value_or(u: 0));
647 W.write<uint8_t>(Val: SymAlignAndType);
648 W.write<uint8_t>(Val: AuxSym.StorageMappingClass.value_or(u: XCOFF::XMC_PR));
649 W.write<uint32_t>(Val: AuxSym.StabInfoIndex.value_or(u: 0));
650 W.write<uint16_t>(Val: AuxSym.StabSectNum.value_or(u: 0));
651 }
652 return true;
653}
654
655bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::ExcpetionAuxEnt &AuxSym) {
656 assert(Is64Bit && "can't write the exception auxiliary symbol for XCOFF32");
657 W.write<uint64_t>(Val: AuxSym.OffsetToExceptionTbl.value_or(u: 0));
658 W.write<uint32_t>(Val: AuxSym.SizeOfFunction.value_or(u: 0));
659 W.write<uint32_t>(Val: AuxSym.SymIdxOfNextBeyond.value_or(u: 0));
660 W.write<uint8_t>(Val: 0);
661 W.write<uint8_t>(Val: XCOFF::AUX_EXCEPT);
662 return true;
663}
664
665bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::FunctionAuxEnt &AuxSym) {
666 if (Is64Bit) {
667 W.write<uint64_t>(Val: AuxSym.PtrToLineNum.value_or(u: 0));
668 W.write<uint32_t>(Val: AuxSym.SizeOfFunction.value_or(u: 0));
669 W.write<uint32_t>(Val: AuxSym.SymIdxOfNextBeyond.value_or(u: 0));
670 W.write<uint8_t>(Val: 0);
671 W.write<uint8_t>(Val: XCOFF::AUX_FCN);
672 } else {
673 W.write<uint32_t>(Val: AuxSym.OffsetToExceptionTbl.value_or(u: 0));
674 W.write<uint32_t>(Val: AuxSym.SizeOfFunction.value_or(u: 0));
675 W.write<uint32_t>(Val: AuxSym.PtrToLineNum.value_or(u: 0));
676 W.write<uint32_t>(Val: AuxSym.SymIdxOfNextBeyond.value_or(u: 0));
677 W.OS.write_zeros(NumZeros: 2);
678 }
679 return true;
680}
681
682bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::FileAuxEnt &AuxSym) {
683 StringRef FileName = AuxSym.FileNameOrString.value_or(u: "");
684 if (nameShouldBeInStringTable(SymbolName: FileName)) {
685 W.write<int32_t>(Val: 0);
686 W.write<uint32_t>(Val: StrTblBuilder.getOffset(S: FileName));
687 } else {
688 writeName(StrName: FileName, W);
689 }
690 W.OS.write_zeros(NumZeros: XCOFF::FileNamePadSize);
691 W.write<uint8_t>(Val: AuxSym.FileStringType.value_or(u: XCOFF::XFT_FN));
692 if (Is64Bit) {
693 W.OS.write_zeros(NumZeros: 2);
694 W.write<uint8_t>(Val: XCOFF::AUX_FILE);
695 } else {
696 W.OS.write_zeros(NumZeros: 3);
697 }
698 return true;
699}
700
701bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::BlockAuxEnt &AuxSym) {
702 if (Is64Bit) {
703 W.write<uint32_t>(Val: AuxSym.LineNum.value_or(u: 0));
704 W.OS.write_zeros(NumZeros: 13);
705 W.write<uint8_t>(Val: XCOFF::AUX_SYM);
706 } else {
707 W.OS.write_zeros(NumZeros: 2);
708 W.write<uint16_t>(Val: AuxSym.LineNumHi.value_or(u: 0));
709 W.write<uint16_t>(Val: AuxSym.LineNumLo.value_or(u: 0));
710 W.OS.write_zeros(NumZeros: 12);
711 }
712 return true;
713}
714
715bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::SectAuxEntForDWARF &AuxSym) {
716 if (Is64Bit) {
717 W.write<uint64_t>(Val: AuxSym.LengthOfSectionPortion.value_or(u: 0));
718 W.write<uint64_t>(Val: AuxSym.NumberOfRelocEnt.value_or(u: 0));
719 W.write<uint8_t>(Val: 0);
720 W.write<uint8_t>(Val: XCOFF::AUX_SECT);
721 } else {
722 W.write<uint32_t>(Val: AuxSym.LengthOfSectionPortion.value_or(u: 0));
723 W.OS.write_zeros(NumZeros: 4);
724 W.write<uint32_t>(Val: AuxSym.NumberOfRelocEnt.value_or(u: 0));
725 W.OS.write_zeros(NumZeros: 6);
726 }
727 return true;
728}
729
730bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::SectAuxEntForStat &AuxSym) {
731 assert(!Is64Bit && "can't write the stat auxiliary symbol for XCOFF64");
732 W.write<uint32_t>(Val: AuxSym.SectionLength.value_or(u: 0));
733 W.write<uint16_t>(Val: AuxSym.NumberOfRelocEnt.value_or(u: 0));
734 W.write<uint16_t>(Val: AuxSym.NumberOfLineNum.value_or(u: 0));
735 W.OS.write_zeros(NumZeros: 10);
736 return true;
737}
738
739bool XCOFFWriter::writeAuxSymbol(
740 const std::unique_ptr<XCOFFYAML::AuxSymbolEnt> &AuxSym) {
741 if (auto AS = dyn_cast<XCOFFYAML::CsectAuxEnt>(Val: AuxSym.get()))
742 return writeAuxSymbol(AuxSym: *AS);
743 else if (auto AS = dyn_cast<XCOFFYAML::FunctionAuxEnt>(Val: AuxSym.get()))
744 return writeAuxSymbol(AuxSym: *AS);
745 else if (auto AS = dyn_cast<XCOFFYAML::ExcpetionAuxEnt>(Val: AuxSym.get()))
746 return writeAuxSymbol(AuxSym: *AS);
747 else if (auto AS = dyn_cast<XCOFFYAML::FileAuxEnt>(Val: AuxSym.get()))
748 return writeAuxSymbol(AuxSym: *AS);
749 else if (auto AS = dyn_cast<XCOFFYAML::BlockAuxEnt>(Val: AuxSym.get()))
750 return writeAuxSymbol(AuxSym: *AS);
751 else if (auto AS = dyn_cast<XCOFFYAML::SectAuxEntForDWARF>(Val: AuxSym.get()))
752 return writeAuxSymbol(AuxSym: *AS);
753 else if (auto AS = dyn_cast<XCOFFYAML::SectAuxEntForStat>(Val: AuxSym.get()))
754 return writeAuxSymbol(AuxSym: *AS);
755 llvm_unreachable("unknown auxiliary symbol type");
756 return false;
757}
758
759bool XCOFFWriter::writeSymbols() {
760 int64_t PaddingSize =
761 InitFileHdr.SymbolTableOffset - (W.OS.tell() - StartOffset);
762 if (PaddingSize < 0) {
763 ErrHandler("redundant data was written before symbols");
764 return false;
765 }
766 W.OS.write_zeros(NumZeros: PaddingSize);
767 for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
768 if (Is64Bit) {
769 W.write<uint64_t>(Val: YamlSym.Value);
770 W.write<uint32_t>(Val: StrTblBuilder.getOffset(S: YamlSym.SymbolName));
771 } else {
772 if (nameShouldBeInStringTable(SymbolName: YamlSym.SymbolName)) {
773 // For XCOFF32: A value of 0 indicates that the symbol name is in the
774 // string table.
775 W.write<int32_t>(Val: 0);
776 W.write<uint32_t>(Val: StrTblBuilder.getOffset(S: YamlSym.SymbolName));
777 } else {
778 writeName(StrName: YamlSym.SymbolName, W);
779 }
780 W.write<uint32_t>(Val: YamlSym.Value);
781 }
782 if (YamlSym.SectionName) {
783 if (!SectionIndexMap.count(Val: *YamlSym.SectionName)) {
784 ErrHandler("the SectionName " + *YamlSym.SectionName +
785 " specified in the symbol does not exist");
786 return false;
787 }
788 if (YamlSym.SectionIndex &&
789 SectionIndexMap[*YamlSym.SectionName] != *YamlSym.SectionIndex) {
790 ErrHandler("the SectionName " + *YamlSym.SectionName +
791 " and the SectionIndex (" + Twine(*YamlSym.SectionIndex) +
792 ") refer to different sections");
793 return false;
794 }
795 W.write<int16_t>(Val: SectionIndexMap[*YamlSym.SectionName]);
796 } else {
797 W.write<int16_t>(Val: YamlSym.SectionIndex ? *YamlSym.SectionIndex : 0);
798 }
799 W.write<uint16_t>(Val: YamlSym.Type);
800 W.write<uint8_t>(Val: YamlSym.StorageClass);
801
802 uint8_t NumOfAuxSym = YamlSym.NumberOfAuxEntries.value_or(u: 0);
803 W.write<uint8_t>(Val: NumOfAuxSym);
804
805 if (!NumOfAuxSym && !YamlSym.AuxEntries.size())
806 continue;
807
808 // Now write auxiliary entries.
809 if (!YamlSym.AuxEntries.size()) {
810 W.OS.write_zeros(NumZeros: XCOFF::SymbolTableEntrySize * NumOfAuxSym);
811 } else {
812 for (const std::unique_ptr<XCOFFYAML::AuxSymbolEnt> &AuxSym :
813 YamlSym.AuxEntries) {
814 if (!writeAuxSymbol(AuxSym))
815 return false;
816 }
817 // Pad with zeros.
818 if (NumOfAuxSym > YamlSym.AuxEntries.size())
819 W.OS.write_zeros(NumZeros: XCOFF::SymbolTableEntrySize *
820 (NumOfAuxSym - YamlSym.AuxEntries.size()));
821 }
822 }
823 return true;
824}
825
826void XCOFFWriter::writeStringTable() {
827 if (Obj.StrTbl.RawContent) {
828 Obj.StrTbl.RawContent->writeAsBinary(OS&: W.OS);
829 if (Obj.StrTbl.ContentSize) {
830 assert(*Obj.StrTbl.ContentSize >= Obj.StrTbl.RawContent->binary_size() &&
831 "Specified ContentSize is less than the RawContent size.");
832 W.OS.write_zeros(NumZeros: *Obj.StrTbl.ContentSize -
833 Obj.StrTbl.RawContent->binary_size());
834 }
835 return;
836 }
837
838 size_t StrTblBuilderSize = StrTblBuilder.getSize();
839 // If neither Length nor ContentSize is specified, write the StrTblBuilder
840 // directly, which contains the auto-generated Length value.
841 if (!Obj.StrTbl.Length && !Obj.StrTbl.ContentSize) {
842 if (StrTblBuilderSize <= 4)
843 return;
844 StrTblBuilder.write(OS&: W.OS);
845 return;
846 }
847
848 // Serialize the string table's content to a temporary buffer.
849 std::unique_ptr<WritableMemoryBuffer> Buf =
850 WritableMemoryBuffer::getNewMemBuffer(Size: StrTblBuilderSize);
851 uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart());
852 StrTblBuilder.write(Buf: Ptr);
853 // Replace the first 4 bytes, which contain the auto-generated Length value,
854 // with the specified value.
855 memset(s: Ptr, c: 0, n: 4);
856 support::endian::write32be(P: Ptr, V: Obj.StrTbl.Length ? *Obj.StrTbl.Length
857 : *Obj.StrTbl.ContentSize);
858 // Copy the buffer content to the actual output stream.
859 W.OS.write(Ptr: Buf->getBufferStart(), Size: Buf->getBufferSize());
860 // Add zeros as padding after strings.
861 if (Obj.StrTbl.ContentSize) {
862 assert(*Obj.StrTbl.ContentSize >= StrTblBuilderSize &&
863 "Specified ContentSize is less than the StringTableBuilder size.");
864 W.OS.write_zeros(NumZeros: *Obj.StrTbl.ContentSize - StrTblBuilderSize);
865 }
866}
867
868bool XCOFFWriter::writeXCOFF() {
869 if (!assignAddressesAndIndices())
870 return false;
871 StartOffset = W.OS.tell();
872 writeFileHeader();
873 if (InitFileHdr.AuxHeaderSize)
874 writeAuxFileHeader();
875 if (!Obj.Sections.empty()) {
876 writeSectionHeaders();
877 if (!writeSectionData())
878 return false;
879 if (!writeRelocations())
880 return false;
881 }
882 if (!Obj.Symbols.empty() && !writeSymbols())
883 return false;
884 writeStringTable();
885 return true;
886}
887
888} // end anonymous namespace
889
890namespace llvm {
891namespace yaml {
892
893bool yaml2xcoff(XCOFFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH) {
894 XCOFFWriter Writer(Doc, Out, EH);
895 return Writer.writeXCOFF();
896}
897
898} // namespace yaml
899} // namespace llvm
900