1//===- yaml2coff - Convert YAML to a COFF 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 COFF component of yaml2obj.
11///
12//===----------------------------------------------------------------------===//
13
14#include "llvm/ADT/StringExtras.h"
15#include "llvm/ADT/StringMap.h"
16#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h"
17#include "llvm/ObjectYAML/ContiguousBlobAccumulator.h"
18#include "llvm/ObjectYAML/ObjectYAML.h"
19#include "llvm/ObjectYAML/yaml2obj.h"
20#include "llvm/Support/BinaryStreamWriter.h"
21#include "llvm/Support/Endian.h"
22#include "llvm/Support/SourceMgr.h"
23#include "llvm/Support/WithColor.h"
24#include "llvm/Support/raw_ostream.h"
25#include <optional>
26#include <vector>
27
28using namespace llvm;
29using llvm::yaml::ContiguousBlobAccumulator;
30
31namespace {
32
33constexpr auto LittleEndian = llvm::endianness::little;
34
35/// This parses a yaml stream that represents a COFF object file.
36/// See docs/yaml2obj for the yaml scheema.
37struct COFFParser {
38 COFFParser(COFFYAML::Object &Obj, yaml::ErrorHandler EH)
39 : Obj(Obj), ErrHandler(EH) {
40 // A COFF string table always starts with a 4 byte size field. Offsets into
41 // it include this size, so allocate it now.
42 StringTable.append(n: 4, c: char(0));
43 }
44
45 bool useBigObj() const {
46 return static_cast<int32_t>(Obj.Sections.size()) >
47 COFF::MaxNumberOfSections16;
48 }
49
50 bool isPE() const { return Obj.OptionalHeader.has_value(); }
51 bool is64Bit() const { return COFF::is64Bit(Machine: Obj.Header.Machine); }
52
53 uint32_t getFileAlignment() const {
54 return Obj.OptionalHeader->Header.FileAlignment;
55 }
56
57 unsigned getSymbolSize() const {
58 return useBigObj() ? COFF::Symbol32Size : COFF::Symbol16Size;
59 }
60
61 bool parseSections() {
62 for (COFFYAML::Section &Sec : Obj.Sections) {
63 // If the name is less than 8 bytes, store it in place, otherwise
64 // store it in the string table.
65 StringRef Name = Sec.Name;
66
67 if (Name.size() <= COFF::NameSize) {
68 llvm::copy(Range&: Name, Out: Sec.Header.Name);
69 } else {
70 // Add string to the string table and format the index for output.
71 unsigned Index = getStringIndex(Str: Name);
72 std::string str = utostr(X: Index);
73 if (str.size() > 7) {
74 ErrHandler("string table got too large");
75 return false;
76 }
77 Sec.Header.Name[0] = '/';
78 llvm::copy(Range&: str, Out: Sec.Header.Name + 1);
79 }
80
81 if (Sec.Alignment) {
82 if (Sec.Alignment > 8192) {
83 ErrHandler("section alignment is too large");
84 return false;
85 }
86 if (!isPowerOf2_32(Value: Sec.Alignment)) {
87 ErrHandler("section alignment is not a power of 2");
88 return false;
89 }
90 Sec.Header.Characteristics |= (Log2_32(Value: Sec.Alignment) + 1) << 20;
91 }
92 }
93 return true;
94 }
95
96 bool parseSymbols() {
97 for (COFFYAML::Symbol &Sym : Obj.Symbols) {
98 // If the name is less than 8 bytes, store it in place, otherwise
99 // store it in the string table.
100 StringRef Name = Sym.Name;
101 if (Name.size() <= COFF::NameSize) {
102 llvm::copy(Range&: Name, Out: Sym.Header.Name);
103 } else {
104 // Add string to the string table and format the index for output.
105 unsigned Index = getStringIndex(Str: Name);
106 *reinterpret_cast<support::aligned_ulittle32_t *>(Sym.Header.Name + 4) =
107 Index;
108 }
109
110 Sym.Header.Type = Sym.SimpleType;
111 Sym.Header.Type |= Sym.ComplexType << COFF::SCT_COMPLEX_TYPE_SHIFT;
112 }
113 return true;
114 }
115
116 bool parse() {
117 if (!parseSections())
118 return false;
119 if (!parseSymbols())
120 return false;
121 return true;
122 }
123
124 unsigned getStringIndex(StringRef Str) {
125 auto [It, Inserted] = StringTableMap.try_emplace(Key: Str, Args: StringTable.size());
126 if (Inserted) {
127 StringTable.append(first: Str.begin(), last: Str.end());
128 StringTable.push_back(c: 0);
129 }
130 return It->second;
131 }
132
133 COFFYAML::Object &Obj;
134
135 codeview::StringsAndChecksums StringsAndChecksums;
136 BumpPtrAllocator Allocator;
137 StringMap<unsigned> StringTableMap;
138 std::string StringTable;
139 uint32_t SectionTableStart;
140 uint32_t SectionTableSize;
141
142 yaml::ErrorHandler ErrHandler;
143};
144
145enum { DOSStubSize = 128 };
146
147} // end anonymous namespace
148
149static yaml::BinaryRef
150toDebugS(ArrayRef<CodeViewYAML::YAMLDebugSubsection> Subsections,
151 const codeview::StringsAndChecksums &SC, BumpPtrAllocator &Allocator) {
152 using namespace codeview;
153 ExitOnError Err("Error occurred writing .debug$S section");
154 auto CVSS =
155 Err(CodeViewYAML::toCodeViewSubsectionList(Allocator, Subsections, SC));
156
157 std::vector<DebugSubsectionRecordBuilder> Builders;
158 uint32_t Size = sizeof(uint32_t);
159 for (auto &SS : CVSS) {
160 DebugSubsectionRecordBuilder B(SS);
161 Size += B.calculateSerializedLength();
162 Builders.push_back(x: std::move(B));
163 }
164 uint8_t *Buffer = Allocator.Allocate<uint8_t>(Num: Size);
165 MutableArrayRef<uint8_t> Output(Buffer, Size);
166 BinaryStreamWriter Writer(Output, llvm::endianness::little);
167
168 Err(Writer.writeInteger<uint32_t>(Value: COFF::DEBUG_SECTION_MAGIC));
169 for (const auto &B : Builders) {
170 Err(B.commit(Writer, Container: CodeViewContainer::ObjectFile));
171 }
172 return {Output};
173}
174
175// Write the content of a section and fill in the header fields locating it.
176// Returns whether the section has any content.
177static bool writeSectionContent(COFFParser &CP, COFFYAML::Section &S,
178 ContiguousBlobAccumulator &CBA) {
179 if (S.SectionData.binary_size() == 0) {
180 if (S.Name == ".debug$S") {
181 assert(CP.StringsAndChecksums.hasStrings() &&
182 "Object file does not have debug string table!");
183 S.SectionData = toDebugS(Subsections: S.DebugS, SC: CP.StringsAndChecksums, Allocator&: CP.Allocator);
184 } else if (S.Name == ".debug$T") {
185 S.SectionData = CodeViewYAML::toDebugT(S.DebugT, Alloc&: CP.Allocator, SectionName: S.Name);
186 } else if (S.Name == ".debug$P") {
187 S.SectionData = CodeViewYAML::toDebugT(S.DebugP, Alloc&: CP.Allocator, SectionName: S.Name);
188 } else if (S.Name == ".debug$H" && S.DebugH) {
189 S.SectionData = CodeViewYAML::toDebugH(DebugH: *S.DebugH, Alloc&: CP.Allocator);
190 }
191 }
192
193 bool HasContent = S.SectionData.binary_size() != 0;
194 for (const auto &E : S.StructuredData)
195 HasContent |= E.size() != 0;
196 // Every BB address map entry encodes at least a version and a feature field.
197 if (S.BBAddrMapEntries && !S.BBAddrMapEntries->empty())
198 HasContent = true;
199
200 // Warn as ELF BBAddrMap does.
201 const std::vector<BBAddrMapYAML::PGOAnalysisMapEntry> *PGOAnalyses = nullptr;
202 if (S.PGOAnalyses) {
203 if (!S.BBAddrMapEntries)
204 WithColor::warning() << "PGOAnalyses should not exist in "
205 ".llvm_bb_addr_map when Entries does not exist";
206 else if (S.BBAddrMapEntries->size() != S.PGOAnalyses->size())
207 WithColor::warning() << "PGOAnalyses must be the same length as Entries "
208 "in .llvm_bb_addr_map";
209 else
210 PGOAnalyses = &S.PGOAnalyses.value();
211 }
212
213 if (!HasContent) {
214 // Leave SizeOfRawData unaltered. For .bss sections in object files, it
215 // carries the section size.
216 S.Header.PointerToRawData = 0;
217 return false;
218 }
219
220 CBA.padToAlignment(Align: CP.isPE() ? CP.getFileAlignment() : 4);
221 S.Header.PointerToRawData = CBA.getOffset();
222 for (const auto &E : S.StructuredData)
223 E.writeAsBinary(CBA);
224 CBA.writeAsBinary(Bin: S.SectionData);
225 if (S.BBAddrMapEntries)
226 BBAddrMapYAML::encodePayload(Entries: *S.BBAddrMapEntries, PGOAnalyses, CBA,
227 Endian: LittleEndian, AddressSize: CP.is64Bit() ? 8 : 4);
228 if (CP.isPE())
229 CBA.padToAlignment(Align: CP.getFileAlignment());
230 S.Header.SizeOfRawData = CBA.getOffset() - S.Header.PointerToRawData;
231 return true;
232}
233
234template <typename T>
235static uint32_t initializeOptionalHeader(COFFParser &CP, uint16_t Magic,
236 T Header) {
237 memset(Header, 0, sizeof(*Header));
238 Header->Magic = Magic;
239 Header->SectionAlignment = CP.Obj.OptionalHeader->Header.SectionAlignment;
240 Header->FileAlignment = CP.Obj.OptionalHeader->Header.FileAlignment;
241 uint32_t SizeOfCode = 0, SizeOfInitializedData = 0,
242 SizeOfUninitializedData = 0;
243 uint32_t SizeOfHeaders = alignTo(CP.SectionTableStart + CP.SectionTableSize,
244 Header->FileAlignment);
245 uint32_t SizeOfImage = alignTo(SizeOfHeaders, Header->SectionAlignment);
246 uint32_t BaseOfData = 0;
247 for (const COFFYAML::Section &S : CP.Obj.Sections) {
248 if (S.Header.Characteristics & COFF::IMAGE_SCN_CNT_CODE)
249 SizeOfCode += S.Header.SizeOfRawData;
250 if (S.Header.Characteristics & COFF::IMAGE_SCN_CNT_INITIALIZED_DATA)
251 SizeOfInitializedData += S.Header.SizeOfRawData;
252 if (S.Header.Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA)
253 SizeOfUninitializedData += S.Header.SizeOfRawData;
254 if (S.Name == ".text")
255 Header->BaseOfCode = S.Header.VirtualAddress; // RVA
256 else if (S.Name == ".data")
257 BaseOfData = S.Header.VirtualAddress; // RVA
258 if (S.Header.VirtualAddress)
259 SizeOfImage += alignTo(S.Header.VirtualSize, Header->SectionAlignment);
260 }
261 Header->SizeOfCode = SizeOfCode;
262 Header->SizeOfInitializedData = SizeOfInitializedData;
263 Header->SizeOfUninitializedData = SizeOfUninitializedData;
264 Header->AddressOfEntryPoint =
265 CP.Obj.OptionalHeader->Header.AddressOfEntryPoint; // RVA
266 Header->ImageBase = CP.Obj.OptionalHeader->Header.ImageBase;
267 Header->MajorOperatingSystemVersion =
268 CP.Obj.OptionalHeader->Header.MajorOperatingSystemVersion;
269 Header->MinorOperatingSystemVersion =
270 CP.Obj.OptionalHeader->Header.MinorOperatingSystemVersion;
271 Header->MajorImageVersion = CP.Obj.OptionalHeader->Header.MajorImageVersion;
272 Header->MinorImageVersion = CP.Obj.OptionalHeader->Header.MinorImageVersion;
273 Header->MajorSubsystemVersion =
274 CP.Obj.OptionalHeader->Header.MajorSubsystemVersion;
275 Header->MinorSubsystemVersion =
276 CP.Obj.OptionalHeader->Header.MinorSubsystemVersion;
277 Header->SizeOfImage = SizeOfImage;
278 Header->SizeOfHeaders = SizeOfHeaders;
279 Header->Subsystem = CP.Obj.OptionalHeader->Header.Subsystem;
280 Header->DLLCharacteristics = CP.Obj.OptionalHeader->Header.DLLCharacteristics;
281 Header->SizeOfStackReserve = CP.Obj.OptionalHeader->Header.SizeOfStackReserve;
282 Header->SizeOfStackCommit = CP.Obj.OptionalHeader->Header.SizeOfStackCommit;
283 Header->SizeOfHeapReserve = CP.Obj.OptionalHeader->Header.SizeOfHeapReserve;
284 Header->SizeOfHeapCommit = CP.Obj.OptionalHeader->Header.SizeOfHeapCommit;
285 Header->NumberOfRvaAndSize = CP.Obj.OptionalHeader->Header.NumberOfRvaAndSize;
286 return BaseOfData;
287}
288
289static bool writeCOFF(COFFParser &CP, ContiguousBlobAccumulator &CBA) {
290 // Calculate number of symbols.
291 CP.Obj.Header.NumberOfSymbols = 0;
292 for (COFFYAML::Symbol &Sym : CP.Obj.Symbols) {
293 uint32_t NumberOfAuxSymbols = 0;
294 if (Sym.FunctionDefinition)
295 NumberOfAuxSymbols += 1;
296 if (Sym.bfAndefSymbol)
297 NumberOfAuxSymbols += 1;
298 if (Sym.WeakExternal)
299 NumberOfAuxSymbols += 1;
300 if (!Sym.File.empty())
301 NumberOfAuxSymbols +=
302 (Sym.File.size() + CP.getSymbolSize() - 1) / CP.getSymbolSize();
303 if (Sym.SectionDefinition)
304 NumberOfAuxSymbols += 1;
305 if (Sym.CLRToken)
306 NumberOfAuxSymbols += 1;
307 Sym.Header.NumberOfAuxSymbols = NumberOfAuxSymbols;
308 CP.Obj.Header.NumberOfSymbols += 1 + NumberOfAuxSymbols;
309 }
310
311 CP.Obj.Header.NumberOfSections = CP.Obj.Sections.size();
312
313 unsigned PEHeaderSize = CP.is64Bit() ? sizeof(object::pe32plus_header)
314 : sizeof(object::pe32_header);
315 if (CP.isPE())
316 CP.Obj.Header.SizeOfOptionalHeader =
317 PEHeaderSize + sizeof(object::data_directory) *
318 CP.Obj.OptionalHeader->Header.NumberOfRvaAndSize;
319
320 // Save field offsets for writing back their final values.
321 uint64_t PointerToSymbolTableOffset;
322
323 if (CP.isPE()) {
324 // PE files start with a DOS stub.
325 object::dos_header DH;
326 memset(s: &DH, c: 0, n: sizeof(DH));
327
328 // DOS EXEs start with "MZ" magic.
329 DH.Magic[0] = 'M';
330 DH.Magic[1] = 'Z';
331 // Initializing the AddressOfRelocationTable is strictly optional but
332 // mollifies certain tools which expect it to have a value greater than
333 // 0x40.
334 DH.AddressOfRelocationTable = sizeof(DH);
335 // This is the address of the PE signature.
336 DH.AddressOfNewExeHeader = DOSStubSize;
337
338 // Write out our DOS stub.
339 CBA.write(Ptr: reinterpret_cast<const char *>(&DH), Size: sizeof(DH));
340 // Write padding until we reach the position of where our PE signature
341 // should live.
342 CBA.writeZeros(Num: DOSStubSize - sizeof(DH));
343 // Write out the PE signature.
344 CBA.write(Ptr: COFF::PEMagic, Size: sizeof(COFF::PEMagic));
345 }
346 if (CP.useBigObj()) {
347 CBA.write(Val: static_cast<uint16_t>(COFF::IMAGE_FILE_MACHINE_UNKNOWN),
348 E: LittleEndian);
349 CBA.write(Val: static_cast<uint16_t>(0xffff), E: LittleEndian);
350 CBA.write(Val: static_cast<uint16_t>(COFF::BigObjHeader::MinBigObjectVersion),
351 E: LittleEndian);
352 CBA.write(Val: CP.Obj.Header.Machine, E: LittleEndian);
353 CBA.write(Val: CP.Obj.Header.TimeDateStamp, E: LittleEndian);
354 CBA.write(Ptr: COFF::BigObjMagic, Size: sizeof(COFF::BigObjMagic));
355 CBA.writeZeros(Num: 4 * sizeof(uint32_t));
356 CBA.write(Val: CP.Obj.Header.NumberOfSections, E: LittleEndian);
357 PointerToSymbolTableOffset = CBA.getOffset();
358 // The final symbol table offset is written after the section data.
359 CBA.writeZeros(Num: sizeof(CP.Obj.Header.PointerToSymbolTable));
360 CBA.write(Val: CP.Obj.Header.NumberOfSymbols, E: LittleEndian);
361 } else {
362 CBA.write(Val: CP.Obj.Header.Machine, E: LittleEndian);
363 CBA.write(Val: static_cast<int16_t>(CP.Obj.Header.NumberOfSections),
364 E: LittleEndian);
365 CBA.write(Val: CP.Obj.Header.TimeDateStamp, E: LittleEndian);
366 PointerToSymbolTableOffset = CBA.getOffset();
367 // The final symbol table offset is written after the section data.
368 CBA.writeZeros(Num: sizeof(CP.Obj.Header.PointerToSymbolTable));
369 CBA.write(Val: CP.Obj.Header.NumberOfSymbols, E: LittleEndian);
370 CBA.write(Val: CP.Obj.Header.SizeOfOptionalHeader, E: LittleEndian);
371 CBA.write(Val: CP.Obj.Header.Characteristics, E: LittleEndian);
372 }
373
374 // The optional header, if present, immediately follows the COFF file header.
375 uint64_t OptionalHeaderOffset = CBA.getOffset();
376 if (CP.isPE()) {
377 // Reserve space for the PE header, whose fields depend on the final section
378 // layout. The data directories that follow it are already final.
379 CBA.writeZeros(Num: PEHeaderSize);
380 for (uint32_t I = 0; I < CP.Obj.OptionalHeader->Header.NumberOfRvaAndSize;
381 ++I) {
382 const std::optional<COFF::DataDirectory> *DataDirectories =
383 CP.Obj.OptionalHeader->DataDirectories;
384 uint32_t NumDataDir = std::size(CP.Obj.OptionalHeader->DataDirectories);
385 if (I >= NumDataDir || !DataDirectories[I]) {
386 CBA.writeZeros(Num: 2 * sizeof(uint32_t));
387 } else {
388 CBA.write(Val: DataDirectories[I]->RelativeVirtualAddress, E: LittleEndian);
389 CBA.write(Val: DataDirectories[I]->Size, E: LittleEndian);
390 }
391 }
392 }
393
394 CP.SectionTableStart = CBA.getOffset();
395 CP.SectionTableSize = COFF::SectionSize * CP.Obj.Sections.size();
396 // Reserve space for the section table. Section offsets and sizes are filled
397 // in later.
398 CBA.writeZeros(Num: CP.SectionTableSize);
399
400 unsigned CurSymbol = 0;
401 StringMap<unsigned> SymbolTableIndexMap;
402 for (const COFFYAML::Symbol &Sym : CP.Obj.Symbols) {
403 SymbolTableIndexMap[Sym.Name] = CurSymbol;
404 CurSymbol += 1 + Sym.Header.NumberOfAuxSymbols;
405 }
406
407 // Collect the CodeView strings and checksums shared by all .debug$S sections.
408 for (COFFYAML::Section &S : CP.Obj.Sections) {
409 // We support specifying exactly one of SectionData or Subsections. So if
410 // there is already some SectionData, then we don't need to do any of this.
411 if (S.Name == ".debug$S" && S.SectionData.binary_size() == 0) {
412 CodeViewYAML::initializeStringsAndChecksums(Sections: S.DebugS,
413 SC&: CP.StringsAndChecksums);
414 if (CP.StringsAndChecksums.hasChecksums() &&
415 CP.StringsAndChecksums.hasStrings())
416 break;
417 }
418 }
419
420 // Output section data.
421 for (COFFYAML::Section &S : CP.Obj.Sections) {
422 bool HasContent = writeSectionContent(CP, S, CBA);
423 if (!HasContent || S.Relocations.empty())
424 continue;
425
426 S.Header.PointerToRelocations = CBA.getOffset();
427 if (S.Header.Characteristics & COFF::IMAGE_SCN_LNK_NRELOC_OVFL) {
428 S.Header.NumberOfRelocations = 0xffff;
429 CBA.write<uint32_t>(/*VirtualAddress=*/Val: S.Relocations.size() + 1,
430 E: LittleEndian);
431 CBA.write<uint32_t>(/*SymbolTableIndex=*/Val: 0, E: LittleEndian);
432 CBA.write<uint16_t>(/*Type=*/Val: 0, E: LittleEndian);
433 } else {
434 S.Header.NumberOfRelocations = S.Relocations.size();
435 }
436
437 for (const COFFYAML::Relocation &R : S.Relocations) {
438 uint32_t SymbolTableIndex;
439 if (R.SymbolTableIndex) {
440 if (!R.SymbolName.empty())
441 WithColor::error()
442 << "Both SymbolName and SymbolTableIndex specified\n";
443 SymbolTableIndex = *R.SymbolTableIndex;
444 } else {
445 SymbolTableIndex = SymbolTableIndexMap[R.SymbolName];
446 }
447 CBA.write(Val: R.VirtualAddress, E: LittleEndian);
448 CBA.write(Val: SymbolTableIndex, E: LittleEndian);
449 CBA.write(Val: R.Type, E: LittleEndian);
450 }
451 }
452
453 // Fill in the optional header now that the section layout is final.
454 if (CP.isPE()) {
455 if (CP.is64Bit()) {
456 object::pe32plus_header PEH;
457 initializeOptionalHeader(CP, Magic: COFF::PE32Header::PE32_PLUS, Header: &PEH);
458 CBA.updateDataAt(Pos: OptionalHeaderOffset, Data: &PEH, Size: sizeof(PEH));
459 } else {
460 object::pe32_header PEH;
461 uint32_t BaseOfData =
462 initializeOptionalHeader(CP, Magic: COFF::PE32Header::PE32, Header: &PEH);
463 PEH.BaseOfData = BaseOfData;
464 CBA.updateDataAt(Pos: OptionalHeaderOffset, Data: &PEH, Size: sizeof(PEH));
465 }
466 }
467
468 // Fill in the section table.
469 static_assert(sizeof(object::coff_section) == COFF::SectionSize,
470 "unexpected COFF section header size");
471 uint64_t SectionHeaderOffset = CP.SectionTableStart;
472 for (const COFFYAML::Section &S : CP.Obj.Sections) {
473 object::coff_section Header{};
474 memcpy(dest: Header.Name, src: S.Header.Name, n: COFF::NameSize);
475 Header.VirtualSize = S.Header.VirtualSize;
476 Header.VirtualAddress = S.Header.VirtualAddress;
477 Header.SizeOfRawData = S.Header.SizeOfRawData;
478 Header.PointerToRawData = S.Header.PointerToRawData;
479 Header.PointerToRelocations = S.Header.PointerToRelocations;
480 Header.PointerToLinenumbers = S.Header.PointerToLineNumbers;
481 Header.NumberOfRelocations = S.Header.NumberOfRelocations;
482 Header.NumberOfLinenumbers = S.Header.NumberOfLineNumbers;
483 Header.Characteristics = S.Header.Characteristics;
484 CBA.updateDataAt(Pos: SectionHeaderOffset, Data: &Header, Size: sizeof(Header));
485 SectionHeaderOffset += sizeof(Header);
486 }
487
488 // Output symbol table.
489 if (CP.Obj.Header.NumberOfSymbols || CP.StringTable.size() > 4)
490 CP.Obj.Header.PointerToSymbolTable = CBA.getOffset();
491 else
492 CP.Obj.Header.PointerToSymbolTable = 0;
493
494 CBA.updateDataAt(Pos: PointerToSymbolTableOffset,
495 Val: CP.Obj.Header.PointerToSymbolTable, E: LittleEndian);
496
497 for (std::vector<COFFYAML::Symbol>::const_iterator i = CP.Obj.Symbols.begin(),
498 e = CP.Obj.Symbols.end();
499 i != e; ++i) {
500 CBA.write(Ptr: i->Header.Name, Size: COFF::NameSize);
501 CBA.write(Val: i->Header.Value, E: LittleEndian);
502 if (CP.useBigObj())
503 CBA.write(Val: i->Header.SectionNumber, E: LittleEndian);
504 else
505 CBA.write(Val: static_cast<int16_t>(i->Header.SectionNumber), E: LittleEndian);
506 CBA.write(Val: i->Header.Type, E: LittleEndian);
507 CBA.write(Val: i->Header.StorageClass, E: LittleEndian);
508 CBA.write(Val: i->Header.NumberOfAuxSymbols, E: LittleEndian);
509
510 if (i->FunctionDefinition) {
511 CBA.write(Val: i->FunctionDefinition->TagIndex, E: LittleEndian);
512 CBA.write(Val: i->FunctionDefinition->TotalSize, E: LittleEndian);
513 CBA.write(Val: i->FunctionDefinition->PointerToLinenumber, E: LittleEndian);
514 CBA.write(Val: i->FunctionDefinition->PointerToNextFunction, E: LittleEndian);
515 CBA.writeZeros(Num: sizeof(i->FunctionDefinition->unused));
516 CBA.writeZeros(Num: CP.getSymbolSize() - COFF::Symbol16Size);
517 }
518 if (i->bfAndefSymbol) {
519 CBA.writeZeros(Num: sizeof(i->bfAndefSymbol->unused1));
520 CBA.write(Val: i->bfAndefSymbol->Linenumber, E: LittleEndian);
521 CBA.writeZeros(Num: sizeof(i->bfAndefSymbol->unused2));
522 CBA.write(Val: i->bfAndefSymbol->PointerToNextFunction, E: LittleEndian);
523 CBA.writeZeros(Num: sizeof(i->bfAndefSymbol->unused3));
524 CBA.writeZeros(Num: CP.getSymbolSize() - COFF::Symbol16Size);
525 }
526 if (i->WeakExternal) {
527 CBA.write(Val: i->WeakExternal->TagIndex, E: LittleEndian);
528 CBA.write(Val: i->WeakExternal->Characteristics, E: LittleEndian);
529 CBA.writeZeros(Num: sizeof(i->WeakExternal->unused));
530 CBA.writeZeros(Num: CP.getSymbolSize() - COFF::Symbol16Size);
531 }
532 if (!i->File.empty()) {
533 unsigned SymbolSize = CP.getSymbolSize();
534 uint32_t NumberOfAuxRecords =
535 (i->File.size() + SymbolSize - 1) / SymbolSize;
536 uint32_t NumberOfAuxBytes = NumberOfAuxRecords * SymbolSize;
537 uint32_t NumZeros = NumberOfAuxBytes - i->File.size();
538 CBA.write(Ptr: i->File.data(), Size: i->File.size());
539 CBA.writeZeros(Num: NumZeros);
540 }
541 if (i->SectionDefinition) {
542 CBA.write(Val: i->SectionDefinition->Length, E: LittleEndian);
543 CBA.write(Val: i->SectionDefinition->NumberOfRelocations, E: LittleEndian);
544 CBA.write(Val: i->SectionDefinition->NumberOfLinenumbers, E: LittleEndian);
545 CBA.write(Val: i->SectionDefinition->CheckSum, E: LittleEndian);
546 CBA.write(Val: static_cast<int16_t>(i->SectionDefinition->Number),
547 E: LittleEndian);
548 CBA.write(Val: i->SectionDefinition->Selection, E: LittleEndian);
549 CBA.writeZeros(Num: sizeof(i->SectionDefinition->unused));
550 CBA.write(Val: static_cast<int16_t>(i->SectionDefinition->Number >> 16),
551 E: LittleEndian);
552 CBA.writeZeros(Num: CP.getSymbolSize() - COFF::Symbol16Size);
553 }
554 if (i->CLRToken) {
555 CBA.write(Val: i->CLRToken->AuxType, E: LittleEndian);
556 CBA.writeZeros(Num: sizeof(i->CLRToken->unused1));
557 CBA.write(Val: i->CLRToken->SymbolTableIndex, E: LittleEndian);
558 CBA.writeZeros(Num: sizeof(i->CLRToken->unused2));
559 CBA.writeZeros(Num: CP.getSymbolSize() - COFF::Symbol16Size);
560 }
561 }
562
563 // Output string table.
564 if (CP.Obj.Header.PointerToSymbolTable) {
565 *reinterpret_cast<support::ulittle32_t *>(CP.StringTable.data()) =
566 CP.StringTable.size();
567 CBA.write(Ptr: CP.StringTable.data(), Size: CP.StringTable.size());
568 }
569 return true;
570}
571
572size_t COFFYAML::SectionDataEntry::size() const {
573 size_t Size = Binary.binary_size();
574 if (UInt32)
575 Size += sizeof(*UInt32);
576 if (LoadConfig32)
577 Size += LoadConfig32->Size;
578 if (LoadConfig64)
579 Size += LoadConfig64->Size;
580 return Size;
581}
582
583template <typename T>
584static void writeLoadConfig(T &S, ContiguousBlobAccumulator &CBA) {
585 CBA.write(Ptr: reinterpret_cast<const char *>(&S),
586 Size: std::min(a: sizeof(S), b: static_cast<size_t>(S.Size)));
587 if (sizeof(S) < S.Size)
588 CBA.writeZeros(Num: S.Size - sizeof(S));
589}
590
591void COFFYAML::SectionDataEntry::writeAsBinary(
592 ContiguousBlobAccumulator &CBA) const {
593 if (UInt32)
594 CBA.write(Val: *UInt32, E: LittleEndian);
595 CBA.writeAsBinary(Bin: Binary);
596 if (LoadConfig32)
597 writeLoadConfig(S: *LoadConfig32, CBA);
598 if (LoadConfig64)
599 writeLoadConfig(S: *LoadConfig64, CBA);
600}
601
602namespace llvm {
603namespace yaml {
604
605bool yaml2coff(llvm::COFFYAML::Object &Doc, raw_ostream &Out,
606 ErrorHandler ErrHandler, uint64_t MaxSize) {
607 COFFParser CP(Doc, ErrHandler);
608 if (!CP.parse()) {
609 ErrHandler("failed to parse YAML file");
610 return false;
611 }
612
613 // Limit the output size to guard against a runaway YAML description.
614 ContiguousBlobAccumulator CBA(/*BaseOffset=*/0, MaxSize);
615 if (!writeCOFF(CP, CBA)) {
616 ErrHandler("failed to write COFF file");
617 return false;
618 }
619 if (Error E = CBA.takeLimitError()) {
620 // Match ELF by reporting a custom error message instead below.
621 consumeError(Err: std::move(E));
622 ErrHandler("the desired output size is greater than permitted. Use the "
623 "--max-size option to change the limit");
624 return false;
625 }
626
627 CBA.writeBlobToStream(Out);
628 return true;
629}
630
631} // namespace yaml
632} // namespace llvm
633