1 //===- yaml2macho - Convert YAML to a Mach object file --------------------===//
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
7 //===----------------------------------------------------------------------===//
10 /// The Mach component of yaml2obj.
12 //===----------------------------------------------------------------------===//
14 #include "llvm/BinaryFormat/MachO.h"
15 #include "llvm/ObjectYAML/DWARFEmitter.h"
16 #include "llvm/ObjectYAML/ObjectYAML.h"
17 #include "llvm/ObjectYAML/yaml2obj.h"
18 #include "llvm/Support/Errc.h"
19 #include "llvm/Support/Error.h"
20 #include "llvm/Support/FormatVariadic.h"
21 #include "llvm/Support/LEB128.h"
22 #include "llvm/Support/SystemZ/zOSSupport.h"
23 #include "llvm/Support/YAMLTraits.h"
24 #include "llvm/Support/raw_ostream.h"
26 #include "llvm/Support/Format.h"
34 MachOWriter(MachOYAML::Object
&Obj
) : Obj(Obj
), fileStart(0) {
35 is64Bit
= Obj
.Header
.magic
== MachO::MH_MAGIC_64
||
36 Obj
.Header
.magic
== MachO::MH_CIGAM_64
;
37 memset(reinterpret_cast<void *>(&Header
), 0, sizeof(MachO::mach_header_64
));
40 Error
writeMachO(raw_ostream
&OS
);
43 void writeHeader(raw_ostream
&OS
);
44 void writeLoadCommands(raw_ostream
&OS
);
45 Error
writeSectionData(raw_ostream
&OS
);
46 void writeRelocations(raw_ostream
&OS
);
47 void writeLinkEditData(raw_ostream
&OS
);
49 void writeBindOpcodes(raw_ostream
&OS
,
50 std::vector
<MachOYAML::BindOpcode
> &BindOpcodes
);
52 void writeRebaseOpcodes(raw_ostream
&OS
);
53 void writeBasicBindOpcodes(raw_ostream
&OS
);
54 void writeWeakBindOpcodes(raw_ostream
&OS
);
55 void writeLazyBindOpcodes(raw_ostream
&OS
);
56 void writeNameList(raw_ostream
&OS
);
57 void writeStringTable(raw_ostream
&OS
);
58 void writeExportTrie(raw_ostream
&OS
);
59 void writeDynamicSymbolTable(raw_ostream
&OS
);
60 void writeFunctionStarts(raw_ostream
&OS
);
61 void writeChainedFixups(raw_ostream
&OS
);
62 void writeDyldExportsTrie(raw_ostream
&OS
);
63 void writeDataInCode(raw_ostream
&OS
);
65 void dumpExportEntry(raw_ostream
&OS
, MachOYAML::ExportEntry
&Entry
);
66 void ZeroToOffset(raw_ostream
&OS
, size_t offset
);
68 MachOYAML::Object
&Obj
;
71 MachO::mach_header_64 Header
;
73 // Old PPC Object Files didn't have __LINKEDIT segments, the data was just
74 // stuck at the end of the file.
75 bool FoundLinkEditSeg
= false;
78 Error
MachOWriter::writeMachO(raw_ostream
&OS
) {
79 fileStart
= OS
.tell();
81 writeLoadCommands(OS
);
82 if (Error Err
= writeSectionData(OS
))
85 if (!FoundLinkEditSeg
)
86 writeLinkEditData(OS
);
87 return Error::success();
90 void MachOWriter::writeHeader(raw_ostream
&OS
) {
91 Header
.magic
= Obj
.Header
.magic
;
92 Header
.cputype
= Obj
.Header
.cputype
;
93 Header
.cpusubtype
= Obj
.Header
.cpusubtype
;
94 Header
.filetype
= Obj
.Header
.filetype
;
95 Header
.ncmds
= Obj
.Header
.ncmds
;
96 Header
.sizeofcmds
= Obj
.Header
.sizeofcmds
;
97 Header
.flags
= Obj
.Header
.flags
;
98 Header
.reserved
= Obj
.Header
.reserved
;
100 if (Obj
.IsLittleEndian
!= sys::IsLittleEndianHost
)
101 MachO::swapStruct(Header
);
104 is64Bit
? sizeof(MachO::mach_header_64
) : sizeof(MachO::mach_header
);
105 OS
.write((const char *)&Header
, header_size
);
108 template <typename SectionType
>
109 SectionType
constructSection(const MachOYAML::Section
&Sec
) {
111 memcpy(reinterpret_cast<void *>(&TempSec
.sectname
[0]), &Sec
.sectname
[0], 16);
112 memcpy(reinterpret_cast<void *>(&TempSec
.segname
[0]), &Sec
.segname
[0], 16);
113 TempSec
.addr
= Sec
.addr
;
114 TempSec
.size
= Sec
.size
;
115 TempSec
.offset
= Sec
.offset
;
116 TempSec
.align
= Sec
.align
;
117 TempSec
.reloff
= Sec
.reloff
;
118 TempSec
.nreloc
= Sec
.nreloc
;
119 TempSec
.flags
= Sec
.flags
;
120 TempSec
.reserved1
= Sec
.reserved1
;
121 TempSec
.reserved2
= Sec
.reserved2
;
125 template <typename StructType
>
126 size_t writeLoadCommandData(MachOYAML::LoadCommand
&LC
, raw_ostream
&OS
,
127 bool IsLittleEndian
) {
132 size_t writeLoadCommandData
<MachO::segment_command
>(MachOYAML::LoadCommand
&LC
,
134 bool IsLittleEndian
) {
135 size_t BytesWritten
= 0;
136 for (const auto &Sec
: LC
.Sections
) {
137 auto TempSec
= constructSection
<MachO::section
>(Sec
);
138 if (IsLittleEndian
!= sys::IsLittleEndianHost
)
139 MachO::swapStruct(TempSec
);
140 OS
.write(reinterpret_cast<const char *>(&(TempSec
)),
141 sizeof(MachO::section
));
142 BytesWritten
+= sizeof(MachO::section
);
148 size_t writeLoadCommandData
<MachO::segment_command_64
>(
149 MachOYAML::LoadCommand
&LC
, raw_ostream
&OS
, bool IsLittleEndian
) {
150 size_t BytesWritten
= 0;
151 for (const auto &Sec
: LC
.Sections
) {
152 auto TempSec
= constructSection
<MachO::section_64
>(Sec
);
153 TempSec
.reserved3
= Sec
.reserved3
;
154 if (IsLittleEndian
!= sys::IsLittleEndianHost
)
155 MachO::swapStruct(TempSec
);
156 OS
.write(reinterpret_cast<const char *>(&(TempSec
)),
157 sizeof(MachO::section_64
));
158 BytesWritten
+= sizeof(MachO::section_64
);
163 size_t writePayloadString(MachOYAML::LoadCommand
&LC
, raw_ostream
&OS
) {
164 size_t BytesWritten
= 0;
165 if (!LC
.Content
.empty()) {
166 OS
.write(LC
.Content
.c_str(), LC
.Content
.length());
167 BytesWritten
= LC
.Content
.length();
173 size_t writeLoadCommandData
<MachO::dylib_command
>(MachOYAML::LoadCommand
&LC
,
175 bool IsLittleEndian
) {
176 return writePayloadString(LC
, OS
);
180 size_t writeLoadCommandData
<MachO::dylinker_command
>(MachOYAML::LoadCommand
&LC
,
182 bool IsLittleEndian
) {
183 return writePayloadString(LC
, OS
);
187 size_t writeLoadCommandData
<MachO::rpath_command
>(MachOYAML::LoadCommand
&LC
,
189 bool IsLittleEndian
) {
190 return writePayloadString(LC
, OS
);
194 size_t writeLoadCommandData
<MachO::sub_framework_command
>(
195 MachOYAML::LoadCommand
&LC
, raw_ostream
&OS
, bool IsLittleEndian
) {
196 return writePayloadString(LC
, OS
);
200 size_t writeLoadCommandData
<MachO::sub_umbrella_command
>(
201 MachOYAML::LoadCommand
&LC
, raw_ostream
&OS
, bool IsLittleEndian
) {
202 return writePayloadString(LC
, OS
);
206 size_t writeLoadCommandData
<MachO::sub_client_command
>(
207 MachOYAML::LoadCommand
&LC
, raw_ostream
&OS
, bool IsLittleEndian
) {
208 return writePayloadString(LC
, OS
);
212 size_t writeLoadCommandData
<MachO::sub_library_command
>(
213 MachOYAML::LoadCommand
&LC
, raw_ostream
&OS
, bool IsLittleEndian
) {
214 return writePayloadString(LC
, OS
);
218 size_t writeLoadCommandData
<MachO::build_version_command
>(
219 MachOYAML::LoadCommand
&LC
, raw_ostream
&OS
, bool IsLittleEndian
) {
220 size_t BytesWritten
= 0;
221 for (const auto &T
: LC
.Tools
) {
222 struct MachO::build_tool_version tool
= T
;
223 if (IsLittleEndian
!= sys::IsLittleEndianHost
)
224 MachO::swapStruct(tool
);
225 OS
.write(reinterpret_cast<const char *>(&tool
),
226 sizeof(MachO::build_tool_version
));
227 BytesWritten
+= sizeof(MachO::build_tool_version
);
232 void ZeroFillBytes(raw_ostream
&OS
, size_t Size
) {
233 std::vector
<uint8_t> FillData(Size
, 0);
234 OS
.write(reinterpret_cast<char *>(FillData
.data()), Size
);
237 void Fill(raw_ostream
&OS
, size_t Size
, uint32_t Data
) {
238 std::vector
<uint32_t> FillData((Size
/ 4) + 1, Data
);
239 OS
.write(reinterpret_cast<char *>(FillData
.data()), Size
);
242 void MachOWriter::ZeroToOffset(raw_ostream
&OS
, size_t Offset
) {
243 auto currOffset
= OS
.tell() - fileStart
;
244 if (currOffset
< Offset
)
245 ZeroFillBytes(OS
, Offset
- currOffset
);
248 void MachOWriter::writeLoadCommands(raw_ostream
&OS
) {
249 for (auto &LC
: Obj
.LoadCommands
) {
250 size_t BytesWritten
= 0;
251 llvm::MachO::macho_load_command Data
= LC
.Data
;
253 #define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \
254 case MachO::LCName: \
255 if (Obj.IsLittleEndian != sys::IsLittleEndianHost) \
256 MachO::swapStruct(Data.LCStruct##_data); \
257 OS.write(reinterpret_cast<const char *>(&(Data.LCStruct##_data)), \
258 sizeof(MachO::LCStruct)); \
259 BytesWritten = sizeof(MachO::LCStruct); \
261 writeLoadCommandData<MachO::LCStruct>(LC, OS, Obj.IsLittleEndian); \
264 switch (LC
.Data
.load_command_data
.cmd
) {
266 if (Obj
.IsLittleEndian
!= sys::IsLittleEndianHost
)
267 MachO::swapStruct(Data
.load_command_data
);
268 OS
.write(reinterpret_cast<const char *>(&(Data
.load_command_data
)),
269 sizeof(MachO::load_command
));
270 BytesWritten
= sizeof(MachO::load_command
);
272 writeLoadCommandData
<MachO::load_command
>(LC
, OS
, Obj
.IsLittleEndian
);
274 #include "llvm/BinaryFormat/MachO.def"
277 if (LC
.PayloadBytes
.size() > 0) {
278 OS
.write(reinterpret_cast<const char *>(LC
.PayloadBytes
.data()),
279 LC
.PayloadBytes
.size());
280 BytesWritten
+= LC
.PayloadBytes
.size();
283 if (LC
.ZeroPadBytes
> 0) {
284 ZeroFillBytes(OS
, LC
.ZeroPadBytes
);
285 BytesWritten
+= LC
.ZeroPadBytes
;
288 // Fill remaining bytes with 0. This will only get hit in partially
289 // specified test cases.
290 auto BytesRemaining
= LC
.Data
.load_command_data
.cmdsize
- BytesWritten
;
291 if (BytesRemaining
> 0) {
292 ZeroFillBytes(OS
, BytesRemaining
);
297 Error
MachOWriter::writeSectionData(raw_ostream
&OS
) {
298 uint64_t LinkEditOff
= 0;
299 for (auto &LC
: Obj
.LoadCommands
) {
300 switch (LC
.Data
.load_command_data
.cmd
) {
301 case MachO::LC_SEGMENT
:
302 case MachO::LC_SEGMENT_64
:
303 uint64_t segOff
= is64Bit
? LC
.Data
.segment_command_64_data
.fileoff
304 : LC
.Data
.segment_command_data
.fileoff
;
306 strncmp(&LC
.Data
.segment_command_data
.segname
[0], "__LINKEDIT", 16)) {
307 FoundLinkEditSeg
= true;
308 LinkEditOff
= segOff
;
309 if (Obj
.RawLinkEditSegment
)
311 writeLinkEditData(OS
);
313 for (auto &Sec
: LC
.Sections
) {
314 ZeroToOffset(OS
, Sec
.offset
);
315 // Zero Fill any data between the end of the last thing we wrote and the
316 // start of this section.
317 if (OS
.tell() - fileStart
> Sec
.offset
&& Sec
.offset
!= (uint32_t)0)
318 return createStringError(
319 errc::invalid_argument
,
321 "wrote too much data somewhere, section offsets in "
322 "section {0} for segment {1} don't line up: "
323 "[cursor={2:x}], [fileStart={3:x}], [sectionOffset={4:x}]",
324 Sec
.sectname
, Sec
.segname
, OS
.tell(), fileStart
,
327 StringRef
SectName(Sec
.sectname
,
328 strnlen(Sec
.sectname
, sizeof(Sec
.sectname
)));
329 // If the section's content is specified in the 'DWARF' entry, we will
330 // emit it regardless of the section's segname.
331 if (Obj
.DWARF
.getNonEmptySectionNames().count(SectName
.substr(2))) {
333 return createStringError(errc::invalid_argument
,
334 "cannot specify section '" + SectName
+
335 "' contents in the 'DWARF' entry and "
336 "the 'content' at the same time");
337 auto EmitFunc
= DWARFYAML::getDWARFEmitterByName(SectName
.substr(2));
338 if (Error Err
= EmitFunc(OS
, Obj
.DWARF
))
343 // Skip if it's a virtual section.
344 if (MachO::isVirtualSection(Sec
.flags
& MachO::SECTION_TYPE
))
348 yaml::BinaryRef Content
= *Sec
.content
;
349 Content
.writeAsBinary(OS
);
350 ZeroFillBytes(OS
, Sec
.size
- Content
.binary_size());
352 // Fill section data with 0xDEADBEEF.
353 Fill(OS
, Sec
.size
, 0xDEADBEEFu
);
356 uint64_t segSize
= is64Bit
? LC
.Data
.segment_command_64_data
.filesize
357 : LC
.Data
.segment_command_data
.filesize
;
358 ZeroToOffset(OS
, segOff
+ segSize
);
363 if (Obj
.RawLinkEditSegment
) {
364 ZeroToOffset(OS
, LinkEditOff
);
365 if (OS
.tell() - fileStart
> LinkEditOff
|| !LinkEditOff
)
366 return createStringError(errc::invalid_argument
,
367 "section offsets don't line up");
368 Obj
.RawLinkEditSegment
->writeAsBinary(OS
);
370 return Error::success();
373 // The implementation of makeRelocationInfo and makeScatteredRelocationInfo is
374 // consistent with how libObject parses MachO binary files. For the reference
375 // see getStruct, getRelocation, getPlainRelocationPCRel,
376 // getPlainRelocationLength and related methods in MachOObjectFile.cpp
377 static MachO::any_relocation_info
378 makeRelocationInfo(const MachOYAML::Relocation
&R
, bool IsLE
) {
379 assert(!R
.is_scattered
&& "non-scattered relocation expected");
380 MachO::any_relocation_info MRE
;
381 MRE
.r_word0
= R
.address
;
383 MRE
.r_word1
= ((unsigned)R
.symbolnum
<< 0) | ((unsigned)R
.is_pcrel
<< 24) |
384 ((unsigned)R
.length
<< 25) | ((unsigned)R
.is_extern
<< 27) |
385 ((unsigned)R
.type
<< 28);
387 MRE
.r_word1
= ((unsigned)R
.symbolnum
<< 8) | ((unsigned)R
.is_pcrel
<< 7) |
388 ((unsigned)R
.length
<< 5) | ((unsigned)R
.is_extern
<< 4) |
389 ((unsigned)R
.type
<< 0);
393 static MachO::any_relocation_info
394 makeScatteredRelocationInfo(const MachOYAML::Relocation
&R
) {
395 assert(R
.is_scattered
&& "scattered relocation expected");
396 MachO::any_relocation_info MRE
;
397 MRE
.r_word0
= (((unsigned)R
.address
<< 0) | ((unsigned)R
.type
<< 24) |
398 ((unsigned)R
.length
<< 28) | ((unsigned)R
.is_pcrel
<< 30) |
400 MRE
.r_word1
= R
.value
;
404 void MachOWriter::writeRelocations(raw_ostream
&OS
) {
405 for (const MachOYAML::LoadCommand
&LC
: Obj
.LoadCommands
) {
406 switch (LC
.Data
.load_command_data
.cmd
) {
407 case MachO::LC_SEGMENT
:
408 case MachO::LC_SEGMENT_64
:
409 for (const MachOYAML::Section
&Sec
: LC
.Sections
) {
410 if (Sec
.relocations
.empty())
412 ZeroToOffset(OS
, Sec
.reloff
);
413 for (const MachOYAML::Relocation
&R
: Sec
.relocations
) {
414 MachO::any_relocation_info MRE
=
415 R
.is_scattered
? makeScatteredRelocationInfo(R
)
416 : makeRelocationInfo(R
, Obj
.IsLittleEndian
);
417 if (Obj
.IsLittleEndian
!= sys::IsLittleEndianHost
)
418 MachO::swapStruct(MRE
);
419 OS
.write(reinterpret_cast<const char *>(&MRE
),
420 sizeof(MachO::any_relocation_info
));
427 void MachOWriter::writeBindOpcodes(
428 raw_ostream
&OS
, std::vector
<MachOYAML::BindOpcode
> &BindOpcodes
) {
430 for (const auto &Opcode
: BindOpcodes
) {
431 uint8_t OpByte
= Opcode
.Opcode
| Opcode
.Imm
;
432 OS
.write(reinterpret_cast<char *>(&OpByte
), 1);
433 for (auto Data
: Opcode
.ULEBExtraData
) {
434 encodeULEB128(Data
, OS
);
436 for (auto Data
: Opcode
.SLEBExtraData
) {
437 encodeSLEB128(Data
, OS
);
439 if (!Opcode
.Symbol
.empty()) {
440 OS
.write(Opcode
.Symbol
.data(), Opcode
.Symbol
.size());
446 void MachOWriter::dumpExportEntry(raw_ostream
&OS
,
447 MachOYAML::ExportEntry
&Entry
) {
448 encodeULEB128(Entry
.TerminalSize
, OS
);
449 if (Entry
.TerminalSize
> 0) {
450 encodeULEB128(Entry
.Flags
, OS
);
451 if (Entry
.Flags
& MachO::EXPORT_SYMBOL_FLAGS_REEXPORT
) {
452 encodeULEB128(Entry
.Other
, OS
);
453 OS
<< Entry
.ImportName
;
456 encodeULEB128(Entry
.Address
, OS
);
457 if (Entry
.Flags
& MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER
)
458 encodeULEB128(Entry
.Other
, OS
);
461 OS
.write(static_cast<uint8_t>(Entry
.Children
.size()));
462 for (const auto &EE
: Entry
.Children
) {
465 encodeULEB128(EE
.NodeOffset
, OS
);
467 for (auto EE
: Entry
.Children
)
468 dumpExportEntry(OS
, EE
);
471 void MachOWriter::writeExportTrie(raw_ostream
&OS
) {
472 dumpExportEntry(OS
, Obj
.LinkEdit
.ExportTrie
);
475 template <typename NListType
>
476 void writeNListEntry(MachOYAML::NListEntry
&NLE
, raw_ostream
&OS
,
477 bool IsLittleEndian
) {
479 ListEntry
.n_strx
= NLE
.n_strx
;
480 ListEntry
.n_type
= NLE
.n_type
;
481 ListEntry
.n_sect
= NLE
.n_sect
;
482 ListEntry
.n_desc
= NLE
.n_desc
;
483 ListEntry
.n_value
= NLE
.n_value
;
485 if (IsLittleEndian
!= sys::IsLittleEndianHost
)
486 MachO::swapStruct(ListEntry
);
487 OS
.write(reinterpret_cast<const char *>(&ListEntry
), sizeof(NListType
));
490 void MachOWriter::writeLinkEditData(raw_ostream
&OS
) {
491 typedef void (MachOWriter::*writeHandler
)(raw_ostream
&);
492 typedef std::pair
<uint64_t, writeHandler
> writeOperation
;
493 std::vector
<writeOperation
> WriteQueue
;
495 MachO::dyld_info_command
*DyldInfoOnlyCmd
= nullptr;
496 MachO::symtab_command
*SymtabCmd
= nullptr;
497 MachO::dysymtab_command
*DSymtabCmd
= nullptr;
498 MachO::linkedit_data_command
*FunctionStartsCmd
= nullptr;
499 MachO::linkedit_data_command
*ChainedFixupsCmd
= nullptr;
500 MachO::linkedit_data_command
*DyldExportsTrieCmd
= nullptr;
501 MachO::linkedit_data_command
*DataInCodeCmd
= nullptr;
502 for (auto &LC
: Obj
.LoadCommands
) {
503 switch (LC
.Data
.load_command_data
.cmd
) {
504 case MachO::LC_SYMTAB
:
505 SymtabCmd
= &LC
.Data
.symtab_command_data
;
506 WriteQueue
.push_back(
507 std::make_pair(SymtabCmd
->symoff
, &MachOWriter::writeNameList
));
508 WriteQueue
.push_back(
509 std::make_pair(SymtabCmd
->stroff
, &MachOWriter::writeStringTable
));
511 case MachO::LC_DYLD_INFO_ONLY
:
512 DyldInfoOnlyCmd
= &LC
.Data
.dyld_info_command_data
;
513 WriteQueue
.push_back(std::make_pair(DyldInfoOnlyCmd
->rebase_off
,
514 &MachOWriter::writeRebaseOpcodes
));
515 WriteQueue
.push_back(std::make_pair(DyldInfoOnlyCmd
->bind_off
,
516 &MachOWriter::writeBasicBindOpcodes
));
517 WriteQueue
.push_back(std::make_pair(DyldInfoOnlyCmd
->weak_bind_off
,
518 &MachOWriter::writeWeakBindOpcodes
));
519 WriteQueue
.push_back(std::make_pair(DyldInfoOnlyCmd
->lazy_bind_off
,
520 &MachOWriter::writeLazyBindOpcodes
));
521 WriteQueue
.push_back(std::make_pair(DyldInfoOnlyCmd
->export_off
,
522 &MachOWriter::writeExportTrie
));
524 case MachO::LC_DYSYMTAB
:
525 DSymtabCmd
= &LC
.Data
.dysymtab_command_data
;
526 WriteQueue
.push_back(std::make_pair(
527 DSymtabCmd
->indirectsymoff
, &MachOWriter::writeDynamicSymbolTable
));
529 case MachO::LC_FUNCTION_STARTS
:
530 FunctionStartsCmd
= &LC
.Data
.linkedit_data_command_data
;
531 WriteQueue
.push_back(std::make_pair(FunctionStartsCmd
->dataoff
,
532 &MachOWriter::writeFunctionStarts
));
534 case MachO::LC_DYLD_CHAINED_FIXUPS
:
535 ChainedFixupsCmd
= &LC
.Data
.linkedit_data_command_data
;
536 WriteQueue
.push_back(std::make_pair(ChainedFixupsCmd
->dataoff
,
537 &MachOWriter::writeChainedFixups
));
539 case MachO::LC_DYLD_EXPORTS_TRIE
:
540 DyldExportsTrieCmd
= &LC
.Data
.linkedit_data_command_data
;
541 WriteQueue
.push_back(std::make_pair(DyldExportsTrieCmd
->dataoff
,
542 &MachOWriter::writeDyldExportsTrie
));
544 case MachO::LC_DATA_IN_CODE
:
545 DataInCodeCmd
= &LC
.Data
.linkedit_data_command_data
;
546 WriteQueue
.push_back(std::make_pair(DataInCodeCmd
->dataoff
,
547 &MachOWriter::writeDataInCode
));
552 llvm::sort(WriteQueue
, llvm::less_first());
554 for (auto writeOp
: WriteQueue
) {
555 ZeroToOffset(OS
, writeOp
.first
);
556 (this->*writeOp
.second
)(OS
);
560 void MachOWriter::writeRebaseOpcodes(raw_ostream
&OS
) {
561 MachOYAML::LinkEditData
&LinkEdit
= Obj
.LinkEdit
;
563 for (const auto &Opcode
: LinkEdit
.RebaseOpcodes
) {
564 uint8_t OpByte
= Opcode
.Opcode
| Opcode
.Imm
;
565 OS
.write(reinterpret_cast<char *>(&OpByte
), 1);
566 for (auto Data
: Opcode
.ExtraData
)
567 encodeULEB128(Data
, OS
);
571 void MachOWriter::writeBasicBindOpcodes(raw_ostream
&OS
) {
572 writeBindOpcodes(OS
, Obj
.LinkEdit
.BindOpcodes
);
575 void MachOWriter::writeWeakBindOpcodes(raw_ostream
&OS
) {
576 writeBindOpcodes(OS
, Obj
.LinkEdit
.WeakBindOpcodes
);
579 void MachOWriter::writeLazyBindOpcodes(raw_ostream
&OS
) {
580 writeBindOpcodes(OS
, Obj
.LinkEdit
.LazyBindOpcodes
);
583 void MachOWriter::writeNameList(raw_ostream
&OS
) {
584 for (auto NLE
: Obj
.LinkEdit
.NameList
) {
586 writeNListEntry
<MachO::nlist_64
>(NLE
, OS
, Obj
.IsLittleEndian
);
588 writeNListEntry
<MachO::nlist
>(NLE
, OS
, Obj
.IsLittleEndian
);
592 void MachOWriter::writeStringTable(raw_ostream
&OS
) {
593 for (auto Str
: Obj
.LinkEdit
.StringTable
) {
594 OS
.write(Str
.data(), Str
.size());
599 void MachOWriter::writeDynamicSymbolTable(raw_ostream
&OS
) {
600 for (auto Data
: Obj
.LinkEdit
.IndirectSymbols
)
601 OS
.write(reinterpret_cast<const char *>(&Data
),
602 sizeof(yaml::Hex32::BaseType
));
605 void MachOWriter::writeFunctionStarts(raw_ostream
&OS
) {
607 for (uint64_t NextAddr
: Obj
.LinkEdit
.FunctionStarts
) {
608 uint64_t Delta
= NextAddr
- Addr
;
609 encodeULEB128(Delta
, OS
);
616 void MachOWriter::writeDataInCode(raw_ostream
&OS
) {
617 for (const auto &Entry
: Obj
.LinkEdit
.DataInCode
) {
618 MachO::data_in_code_entry DICE
{Entry
.Offset
, Entry
.Length
, Entry
.Kind
};
619 if (Obj
.IsLittleEndian
!= sys::IsLittleEndianHost
)
620 MachO::swapStruct(DICE
);
621 OS
.write(reinterpret_cast<const char *>(&DICE
),
622 sizeof(MachO::data_in_code_entry
));
626 void MachOWriter::writeChainedFixups(raw_ostream
&OS
) {
627 if (Obj
.LinkEdit
.ChainedFixups
.size() > 0)
628 OS
.write(reinterpret_cast<const char *>(Obj
.LinkEdit
.ChainedFixups
.data()),
629 Obj
.LinkEdit
.ChainedFixups
.size());
632 void MachOWriter::writeDyldExportsTrie(raw_ostream
&OS
) {
633 dumpExportEntry(OS
, Obj
.LinkEdit
.ExportTrie
);
636 class UniversalWriter
{
638 UniversalWriter(yaml::YamlObjectFile
&ObjectFile
)
639 : ObjectFile(ObjectFile
), fileStart(0) {}
641 Error
writeMachO(raw_ostream
&OS
);
644 void writeFatHeader(raw_ostream
&OS
);
645 void writeFatArchs(raw_ostream
&OS
);
647 void ZeroToOffset(raw_ostream
&OS
, size_t offset
);
649 yaml::YamlObjectFile
&ObjectFile
;
653 Error
UniversalWriter::writeMachO(raw_ostream
&OS
) {
654 fileStart
= OS
.tell();
655 if (ObjectFile
.MachO
) {
656 MachOWriter
Writer(*ObjectFile
.MachO
);
657 return Writer
.writeMachO(OS
);
663 auto &FatFile
= *ObjectFile
.FatMachO
;
664 if (FatFile
.FatArchs
.size() < FatFile
.Slices
.size())
665 return createStringError(
666 errc::invalid_argument
,
667 "cannot write 'Slices' if not described in 'FatArches'");
669 for (size_t i
= 0; i
< FatFile
.Slices
.size(); i
++) {
670 ZeroToOffset(OS
, FatFile
.FatArchs
[i
].offset
);
671 MachOWriter
Writer(FatFile
.Slices
[i
]);
672 if (Error Err
= Writer
.writeMachO(OS
))
675 auto SliceEnd
= FatFile
.FatArchs
[i
].offset
+ FatFile
.FatArchs
[i
].size
;
676 ZeroToOffset(OS
, SliceEnd
);
679 return Error::success();
682 void UniversalWriter::writeFatHeader(raw_ostream
&OS
) {
683 auto &FatFile
= *ObjectFile
.FatMachO
;
684 MachO::fat_header header
;
685 header
.magic
= FatFile
.Header
.magic
;
686 header
.nfat_arch
= FatFile
.Header
.nfat_arch
;
687 if (sys::IsLittleEndianHost
)
689 OS
.write(reinterpret_cast<const char *>(&header
), sizeof(MachO::fat_header
));
692 template <typename FatArchType
>
693 FatArchType
constructFatArch(MachOYAML::FatArch
&Arch
) {
695 FatArch
.cputype
= Arch
.cputype
;
696 FatArch
.cpusubtype
= Arch
.cpusubtype
;
697 FatArch
.offset
= Arch
.offset
;
698 FatArch
.size
= Arch
.size
;
699 FatArch
.align
= Arch
.align
;
703 template <typename StructType
>
704 void writeFatArch(MachOYAML::FatArch
&LC
, raw_ostream
&OS
) {}
707 void writeFatArch
<MachO::fat_arch
>(MachOYAML::FatArch
&Arch
, raw_ostream
&OS
) {
708 auto FatArch
= constructFatArch
<MachO::fat_arch
>(Arch
);
709 if (sys::IsLittleEndianHost
)
711 OS
.write(reinterpret_cast<const char *>(&FatArch
), sizeof(MachO::fat_arch
));
715 void writeFatArch
<MachO::fat_arch_64
>(MachOYAML::FatArch
&Arch
,
717 auto FatArch
= constructFatArch
<MachO::fat_arch_64
>(Arch
);
718 FatArch
.reserved
= Arch
.reserved
;
719 if (sys::IsLittleEndianHost
)
721 OS
.write(reinterpret_cast<const char *>(&FatArch
),
722 sizeof(MachO::fat_arch_64
));
725 void UniversalWriter::writeFatArchs(raw_ostream
&OS
) {
726 auto &FatFile
= *ObjectFile
.FatMachO
;
727 bool is64Bit
= FatFile
.Header
.magic
== MachO::FAT_MAGIC_64
;
728 for (auto Arch
: FatFile
.FatArchs
) {
730 writeFatArch
<MachO::fat_arch_64
>(Arch
, OS
);
732 writeFatArch
<MachO::fat_arch
>(Arch
, OS
);
736 void UniversalWriter::ZeroToOffset(raw_ostream
&OS
, size_t Offset
) {
737 auto currOffset
= OS
.tell() - fileStart
;
738 if (currOffset
< Offset
)
739 ZeroFillBytes(OS
, Offset
- currOffset
);
742 } // end anonymous namespace
747 bool yaml2macho(YamlObjectFile
&Doc
, raw_ostream
&Out
, ErrorHandler EH
) {
748 UniversalWriter
Writer(Doc
);
749 if (Error Err
= Writer
.writeMachO(Out
)) {
750 handleAllErrors(std::move(Err
),
751 [&](const ErrorInfoBase
&Err
) { EH(Err
.message()); });