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