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/YAMLTraits.h"
23 #include "llvm/Support/raw_ostream.h"
25 #include "llvm/Support/Format.h"
33 MachOWriter(MachOYAML::Object
&Obj
) : Obj(Obj
), fileStart(0) {
34 is64Bit
= Obj
.Header
.magic
== MachO::MH_MAGIC_64
||
35 Obj
.Header
.magic
== MachO::MH_CIGAM_64
;
36 memset(reinterpret_cast<void *>(&Header
), 0, sizeof(MachO::mach_header_64
));
39 Error
writeMachO(raw_ostream
&OS
);
42 void writeHeader(raw_ostream
&OS
);
43 void writeLoadCommands(raw_ostream
&OS
);
44 Error
writeSectionData(raw_ostream
&OS
);
45 void writeRelocations(raw_ostream
&OS
);
46 void writeLinkEditData(raw_ostream
&OS
);
48 void writeBindOpcodes(raw_ostream
&OS
,
49 std::vector
<MachOYAML::BindOpcode
> &BindOpcodes
);
51 void writeRebaseOpcodes(raw_ostream
&OS
);
52 void writeBasicBindOpcodes(raw_ostream
&OS
);
53 void writeWeakBindOpcodes(raw_ostream
&OS
);
54 void writeLazyBindOpcodes(raw_ostream
&OS
);
55 void writeNameList(raw_ostream
&OS
);
56 void writeStringTable(raw_ostream
&OS
);
57 void writeExportTrie(raw_ostream
&OS
);
58 void writeDynamicSymbolTable(raw_ostream
&OS
);
59 void writeFunctionStarts(raw_ostream
&OS
);
60 void writeChainedFixups(raw_ostream
&OS
);
61 void writeDyldExportsTrie(raw_ostream
&OS
);
62 void writeDataInCode(raw_ostream
&OS
);
64 void dumpExportEntry(raw_ostream
&OS
, MachOYAML::ExportEntry
&Entry
);
65 void ZeroToOffset(raw_ostream
&OS
, size_t offset
);
67 MachOYAML::Object
&Obj
;
70 MachO::mach_header_64 Header
;
72 // Old PPC Object Files didn't have __LINKEDIT segments, the data was just
73 // stuck at the end of the file.
74 bool FoundLinkEditSeg
= false;
77 Error
MachOWriter::writeMachO(raw_ostream
&OS
) {
78 fileStart
= OS
.tell();
80 writeLoadCommands(OS
);
81 if (Error Err
= writeSectionData(OS
))
84 if (!FoundLinkEditSeg
)
85 writeLinkEditData(OS
);
86 return Error::success();
89 void MachOWriter::writeHeader(raw_ostream
&OS
) {
90 Header
.magic
= Obj
.Header
.magic
;
91 Header
.cputype
= Obj
.Header
.cputype
;
92 Header
.cpusubtype
= Obj
.Header
.cpusubtype
;
93 Header
.filetype
= Obj
.Header
.filetype
;
94 Header
.ncmds
= Obj
.Header
.ncmds
;
95 Header
.sizeofcmds
= Obj
.Header
.sizeofcmds
;
96 Header
.flags
= Obj
.Header
.flags
;
97 Header
.reserved
= Obj
.Header
.reserved
;
99 if (Obj
.IsLittleEndian
!= sys::IsLittleEndianHost
)
100 MachO::swapStruct(Header
);
103 is64Bit
? sizeof(MachO::mach_header_64
) : sizeof(MachO::mach_header
);
104 OS
.write((const char *)&Header
, header_size
);
107 template <typename SectionType
>
108 SectionType
constructSection(const MachOYAML::Section
&Sec
) {
110 memcpy(reinterpret_cast<void *>(&TempSec
.sectname
[0]), &Sec
.sectname
[0], 16);
111 memcpy(reinterpret_cast<void *>(&TempSec
.segname
[0]), &Sec
.segname
[0], 16);
112 TempSec
.addr
= Sec
.addr
;
113 TempSec
.size
= Sec
.size
;
114 TempSec
.offset
= Sec
.offset
;
115 TempSec
.align
= Sec
.align
;
116 TempSec
.reloff
= Sec
.reloff
;
117 TempSec
.nreloc
= Sec
.nreloc
;
118 TempSec
.flags
= Sec
.flags
;
119 TempSec
.reserved1
= Sec
.reserved1
;
120 TempSec
.reserved2
= Sec
.reserved2
;
124 template <typename StructType
>
125 size_t writeLoadCommandData(MachOYAML::LoadCommand
&LC
, raw_ostream
&OS
,
126 bool IsLittleEndian
) {
131 size_t writeLoadCommandData
<MachO::segment_command
>(MachOYAML::LoadCommand
&LC
,
133 bool IsLittleEndian
) {
134 size_t BytesWritten
= 0;
135 for (const auto &Sec
: LC
.Sections
) {
136 auto TempSec
= constructSection
<MachO::section
>(Sec
);
137 if (IsLittleEndian
!= sys::IsLittleEndianHost
)
138 MachO::swapStruct(TempSec
);
139 OS
.write(reinterpret_cast<const char *>(&(TempSec
)),
140 sizeof(MachO::section
));
141 BytesWritten
+= sizeof(MachO::section
);
147 size_t writeLoadCommandData
<MachO::segment_command_64
>(
148 MachOYAML::LoadCommand
&LC
, raw_ostream
&OS
, bool IsLittleEndian
) {
149 size_t BytesWritten
= 0;
150 for (const auto &Sec
: LC
.Sections
) {
151 auto TempSec
= constructSection
<MachO::section_64
>(Sec
);
152 TempSec
.reserved3
= Sec
.reserved3
;
153 if (IsLittleEndian
!= sys::IsLittleEndianHost
)
154 MachO::swapStruct(TempSec
);
155 OS
.write(reinterpret_cast<const char *>(&(TempSec
)),
156 sizeof(MachO::section_64
));
157 BytesWritten
+= sizeof(MachO::section_64
);
162 size_t writePayloadString(MachOYAML::LoadCommand
&LC
, raw_ostream
&OS
) {
163 size_t BytesWritten
= 0;
164 if (!LC
.Content
.empty()) {
165 OS
.write(LC
.Content
.c_str(), LC
.Content
.length());
166 BytesWritten
= LC
.Content
.length();
172 size_t writeLoadCommandData
<MachO::dylib_command
>(MachOYAML::LoadCommand
&LC
,
174 bool IsLittleEndian
) {
175 return writePayloadString(LC
, OS
);
179 size_t writeLoadCommandData
<MachO::dylinker_command
>(MachOYAML::LoadCommand
&LC
,
181 bool IsLittleEndian
) {
182 return writePayloadString(LC
, OS
);
186 size_t writeLoadCommandData
<MachO::rpath_command
>(MachOYAML::LoadCommand
&LC
,
188 bool IsLittleEndian
) {
189 return writePayloadString(LC
, OS
);
193 size_t writeLoadCommandData
<MachO::sub_framework_command
>(
194 MachOYAML::LoadCommand
&LC
, raw_ostream
&OS
, bool IsLittleEndian
) {
195 return writePayloadString(LC
, OS
);
199 size_t writeLoadCommandData
<MachO::sub_umbrella_command
>(
200 MachOYAML::LoadCommand
&LC
, raw_ostream
&OS
, bool IsLittleEndian
) {
201 return writePayloadString(LC
, OS
);
205 size_t writeLoadCommandData
<MachO::sub_client_command
>(
206 MachOYAML::LoadCommand
&LC
, raw_ostream
&OS
, bool IsLittleEndian
) {
207 return writePayloadString(LC
, OS
);
211 size_t writeLoadCommandData
<MachO::sub_library_command
>(
212 MachOYAML::LoadCommand
&LC
, raw_ostream
&OS
, bool IsLittleEndian
) {
213 return writePayloadString(LC
, OS
);
217 size_t writeLoadCommandData
<MachO::build_version_command
>(
218 MachOYAML::LoadCommand
&LC
, raw_ostream
&OS
, bool IsLittleEndian
) {
219 size_t BytesWritten
= 0;
220 for (const auto &T
: LC
.Tools
) {
221 struct MachO::build_tool_version tool
= T
;
222 if (IsLittleEndian
!= sys::IsLittleEndianHost
)
223 MachO::swapStruct(tool
);
224 OS
.write(reinterpret_cast<const char *>(&tool
),
225 sizeof(MachO::build_tool_version
));
226 BytesWritten
+= sizeof(MachO::build_tool_version
);
231 void ZeroFillBytes(raw_ostream
&OS
, size_t Size
) {
232 std::vector
<uint8_t> FillData(Size
, 0);
233 OS
.write(reinterpret_cast<char *>(FillData
.data()), Size
);
236 void Fill(raw_ostream
&OS
, size_t Size
, uint32_t Data
) {
237 std::vector
<uint32_t> FillData((Size
/ 4) + 1, Data
);
238 OS
.write(reinterpret_cast<char *>(FillData
.data()), Size
);
241 void MachOWriter::ZeroToOffset(raw_ostream
&OS
, size_t Offset
) {
242 auto currOffset
= OS
.tell() - fileStart
;
243 if (currOffset
< Offset
)
244 ZeroFillBytes(OS
, Offset
- currOffset
);
247 void MachOWriter::writeLoadCommands(raw_ostream
&OS
) {
248 for (auto &LC
: Obj
.LoadCommands
) {
249 size_t BytesWritten
= 0;
250 llvm::MachO::macho_load_command Data
= LC
.Data
;
252 #define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \
253 case MachO::LCName: \
254 if (Obj.IsLittleEndian != sys::IsLittleEndianHost) \
255 MachO::swapStruct(Data.LCStruct##_data); \
256 OS.write(reinterpret_cast<const char *>(&(Data.LCStruct##_data)), \
257 sizeof(MachO::LCStruct)); \
258 BytesWritten = sizeof(MachO::LCStruct); \
260 writeLoadCommandData<MachO::LCStruct>(LC, OS, Obj.IsLittleEndian); \
263 switch (LC
.Data
.load_command_data
.cmd
) {
265 if (Obj
.IsLittleEndian
!= sys::IsLittleEndianHost
)
266 MachO::swapStruct(Data
.load_command_data
);
267 OS
.write(reinterpret_cast<const char *>(&(Data
.load_command_data
)),
268 sizeof(MachO::load_command
));
269 BytesWritten
= sizeof(MachO::load_command
);
271 writeLoadCommandData
<MachO::load_command
>(LC
, OS
, Obj
.IsLittleEndian
);
273 #include "llvm/BinaryFormat/MachO.def"
276 if (LC
.PayloadBytes
.size() > 0) {
277 OS
.write(reinterpret_cast<const char *>(LC
.PayloadBytes
.data()),
278 LC
.PayloadBytes
.size());
279 BytesWritten
+= LC
.PayloadBytes
.size();
282 if (LC
.ZeroPadBytes
> 0) {
283 ZeroFillBytes(OS
, LC
.ZeroPadBytes
);
284 BytesWritten
+= LC
.ZeroPadBytes
;
287 // Fill remaining bytes with 0. This will only get hit in partially
288 // specified test cases.
289 auto BytesRemaining
= LC
.Data
.load_command_data
.cmdsize
- BytesWritten
;
290 if (BytesRemaining
> 0) {
291 ZeroFillBytes(OS
, BytesRemaining
);
296 Error
MachOWriter::writeSectionData(raw_ostream
&OS
) {
297 uint64_t LinkEditOff
= 0;
298 for (auto &LC
: Obj
.LoadCommands
) {
299 switch (LC
.Data
.load_command_data
.cmd
) {
300 case MachO::LC_SEGMENT
:
301 case MachO::LC_SEGMENT_64
:
302 uint64_t segOff
= is64Bit
? LC
.Data
.segment_command_64_data
.fileoff
303 : LC
.Data
.segment_command_data
.fileoff
;
305 strncmp(&LC
.Data
.segment_command_data
.segname
[0], "__LINKEDIT", 16)) {
306 FoundLinkEditSeg
= true;
307 LinkEditOff
= segOff
;
308 if (Obj
.RawLinkEditSegment
)
310 writeLinkEditData(OS
);
312 for (auto &Sec
: LC
.Sections
) {
313 ZeroToOffset(OS
, Sec
.offset
);
314 // Zero Fill any data between the end of the last thing we wrote and the
315 // start of this section.
316 if (OS
.tell() - fileStart
> Sec
.offset
&& Sec
.offset
!= (uint32_t)0)
317 return createStringError(
318 errc::invalid_argument
,
320 "wrote too much data somewhere, section offsets in "
321 "section {0} for segment {1} don't line up: "
322 "[cursor={2:x}], [fileStart={3:x}], [sectionOffset={4:x}]",
323 Sec
.sectname
, Sec
.segname
, OS
.tell(), fileStart
,
326 StringRef
SectName(Sec
.sectname
,
327 strnlen(Sec
.sectname
, sizeof(Sec
.sectname
)));
328 // If the section's content is specified in the 'DWARF' entry, we will
329 // emit it regardless of the section's segname.
330 if (Obj
.DWARF
.getNonEmptySectionNames().count(SectName
.substr(2))) {
332 return createStringError(errc::invalid_argument
,
333 "cannot specify section '" + SectName
+
334 "' contents in the 'DWARF' entry and "
335 "the 'content' at the same time");
336 auto EmitFunc
= DWARFYAML::getDWARFEmitterByName(SectName
.substr(2));
337 if (Error Err
= EmitFunc(OS
, Obj
.DWARF
))
342 // Skip if it's a virtual section.
343 if (MachO::isVirtualSection(Sec
.flags
& MachO::SECTION_TYPE
))
347 yaml::BinaryRef Content
= *Sec
.content
;
348 Content
.writeAsBinary(OS
);
349 ZeroFillBytes(OS
, Sec
.size
- Content
.binary_size());
351 // Fill section data with 0xDEADBEEF.
352 Fill(OS
, Sec
.size
, 0xDEADBEEFu
);
355 uint64_t segSize
= is64Bit
? LC
.Data
.segment_command_64_data
.filesize
356 : LC
.Data
.segment_command_data
.filesize
;
357 ZeroToOffset(OS
, segOff
+ segSize
);
362 if (Obj
.RawLinkEditSegment
) {
363 ZeroToOffset(OS
, LinkEditOff
);
364 if (OS
.tell() - fileStart
> LinkEditOff
|| !LinkEditOff
)
365 return createStringError(errc::invalid_argument
,
366 "section offsets don't line up");
367 Obj
.RawLinkEditSegment
->writeAsBinary(OS
);
369 return Error::success();
372 // The implementation of makeRelocationInfo and makeScatteredRelocationInfo is
373 // consistent with how libObject parses MachO binary files. For the reference
374 // see getStruct, getRelocation, getPlainRelocationPCRel,
375 // getPlainRelocationLength and related methods in MachOObjectFile.cpp
376 static MachO::any_relocation_info
377 makeRelocationInfo(const MachOYAML::Relocation
&R
, bool IsLE
) {
378 assert(!R
.is_scattered
&& "non-scattered relocation expected");
379 MachO::any_relocation_info MRE
;
380 MRE
.r_word0
= R
.address
;
382 MRE
.r_word1
= ((unsigned)R
.symbolnum
<< 0) | ((unsigned)R
.is_pcrel
<< 24) |
383 ((unsigned)R
.length
<< 25) | ((unsigned)R
.is_extern
<< 27) |
384 ((unsigned)R
.type
<< 28);
386 MRE
.r_word1
= ((unsigned)R
.symbolnum
<< 8) | ((unsigned)R
.is_pcrel
<< 7) |
387 ((unsigned)R
.length
<< 5) | ((unsigned)R
.is_extern
<< 4) |
388 ((unsigned)R
.type
<< 0);
392 static MachO::any_relocation_info
393 makeScatteredRelocationInfo(const MachOYAML::Relocation
&R
) {
394 assert(R
.is_scattered
&& "scattered relocation expected");
395 MachO::any_relocation_info MRE
;
396 MRE
.r_word0
= (((unsigned)R
.address
<< 0) | ((unsigned)R
.type
<< 24) |
397 ((unsigned)R
.length
<< 28) | ((unsigned)R
.is_pcrel
<< 30) |
399 MRE
.r_word1
= R
.value
;
403 void MachOWriter::writeRelocations(raw_ostream
&OS
) {
404 for (const MachOYAML::LoadCommand
&LC
: Obj
.LoadCommands
) {
405 switch (LC
.Data
.load_command_data
.cmd
) {
406 case MachO::LC_SEGMENT
:
407 case MachO::LC_SEGMENT_64
:
408 for (const MachOYAML::Section
&Sec
: LC
.Sections
) {
409 if (Sec
.relocations
.empty())
411 ZeroToOffset(OS
, Sec
.reloff
);
412 for (const MachOYAML::Relocation
&R
: Sec
.relocations
) {
413 MachO::any_relocation_info MRE
=
414 R
.is_scattered
? makeScatteredRelocationInfo(R
)
415 : makeRelocationInfo(R
, Obj
.IsLittleEndian
);
416 if (Obj
.IsLittleEndian
!= sys::IsLittleEndianHost
)
417 MachO::swapStruct(MRE
);
418 OS
.write(reinterpret_cast<const char *>(&MRE
),
419 sizeof(MachO::any_relocation_info
));
426 void MachOWriter::writeBindOpcodes(
427 raw_ostream
&OS
, std::vector
<MachOYAML::BindOpcode
> &BindOpcodes
) {
429 for (auto &Opcode
: BindOpcodes
) {
430 uint8_t OpByte
= Opcode
.Opcode
| Opcode
.Imm
;
431 OS
.write(reinterpret_cast<char *>(&OpByte
), 1);
432 for (auto Data
: Opcode
.ULEBExtraData
) {
433 encodeULEB128(Data
, OS
);
435 for (auto Data
: Opcode
.SLEBExtraData
) {
436 encodeSLEB128(Data
, OS
);
438 if (!Opcode
.Symbol
.empty()) {
439 OS
.write(Opcode
.Symbol
.data(), Opcode
.Symbol
.size());
445 void MachOWriter::dumpExportEntry(raw_ostream
&OS
,
446 MachOYAML::ExportEntry
&Entry
) {
447 encodeULEB128(Entry
.TerminalSize
, OS
);
448 if (Entry
.TerminalSize
> 0) {
449 encodeULEB128(Entry
.Flags
, OS
);
450 if (Entry
.Flags
& MachO::EXPORT_SYMBOL_FLAGS_REEXPORT
) {
451 encodeULEB128(Entry
.Other
, OS
);
452 OS
<< Entry
.ImportName
;
455 encodeULEB128(Entry
.Address
, OS
);
456 if (Entry
.Flags
& MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER
)
457 encodeULEB128(Entry
.Other
, OS
);
460 OS
.write(static_cast<uint8_t>(Entry
.Children
.size()));
461 for (auto EE
: Entry
.Children
) {
464 encodeULEB128(EE
.NodeOffset
, OS
);
466 for (auto EE
: Entry
.Children
)
467 dumpExportEntry(OS
, EE
);
470 void MachOWriter::writeExportTrie(raw_ostream
&OS
) {
471 dumpExportEntry(OS
, Obj
.LinkEdit
.ExportTrie
);
474 template <typename NListType
>
475 void writeNListEntry(MachOYAML::NListEntry
&NLE
, raw_ostream
&OS
,
476 bool IsLittleEndian
) {
478 ListEntry
.n_strx
= NLE
.n_strx
;
479 ListEntry
.n_type
= NLE
.n_type
;
480 ListEntry
.n_sect
= NLE
.n_sect
;
481 ListEntry
.n_desc
= NLE
.n_desc
;
482 ListEntry
.n_value
= NLE
.n_value
;
484 if (IsLittleEndian
!= sys::IsLittleEndianHost
)
485 MachO::swapStruct(ListEntry
);
486 OS
.write(reinterpret_cast<const char *>(&ListEntry
), sizeof(NListType
));
489 void MachOWriter::writeLinkEditData(raw_ostream
&OS
) {
490 typedef void (MachOWriter::*writeHandler
)(raw_ostream
&);
491 typedef std::pair
<uint64_t, writeHandler
> writeOperation
;
492 std::vector
<writeOperation
> WriteQueue
;
494 MachO::dyld_info_command
*DyldInfoOnlyCmd
= nullptr;
495 MachO::symtab_command
*SymtabCmd
= nullptr;
496 MachO::dysymtab_command
*DSymtabCmd
= nullptr;
497 MachO::linkedit_data_command
*FunctionStartsCmd
= nullptr;
498 MachO::linkedit_data_command
*ChainedFixupsCmd
= nullptr;
499 MachO::linkedit_data_command
*DyldExportsTrieCmd
= nullptr;
500 MachO::linkedit_data_command
*DataInCodeCmd
= nullptr;
501 for (auto &LC
: Obj
.LoadCommands
) {
502 switch (LC
.Data
.load_command_data
.cmd
) {
503 case MachO::LC_SYMTAB
:
504 SymtabCmd
= &LC
.Data
.symtab_command_data
;
505 WriteQueue
.push_back(
506 std::make_pair(SymtabCmd
->symoff
, &MachOWriter::writeNameList
));
507 WriteQueue
.push_back(
508 std::make_pair(SymtabCmd
->stroff
, &MachOWriter::writeStringTable
));
510 case MachO::LC_DYLD_INFO_ONLY
:
511 DyldInfoOnlyCmd
= &LC
.Data
.dyld_info_command_data
;
512 WriteQueue
.push_back(std::make_pair(DyldInfoOnlyCmd
->rebase_off
,
513 &MachOWriter::writeRebaseOpcodes
));
514 WriteQueue
.push_back(std::make_pair(DyldInfoOnlyCmd
->bind_off
,
515 &MachOWriter::writeBasicBindOpcodes
));
516 WriteQueue
.push_back(std::make_pair(DyldInfoOnlyCmd
->weak_bind_off
,
517 &MachOWriter::writeWeakBindOpcodes
));
518 WriteQueue
.push_back(std::make_pair(DyldInfoOnlyCmd
->lazy_bind_off
,
519 &MachOWriter::writeLazyBindOpcodes
));
520 WriteQueue
.push_back(std::make_pair(DyldInfoOnlyCmd
->export_off
,
521 &MachOWriter::writeExportTrie
));
523 case MachO::LC_DYSYMTAB
:
524 DSymtabCmd
= &LC
.Data
.dysymtab_command_data
;
525 WriteQueue
.push_back(std::make_pair(
526 DSymtabCmd
->indirectsymoff
, &MachOWriter::writeDynamicSymbolTable
));
528 case MachO::LC_FUNCTION_STARTS
:
529 FunctionStartsCmd
= &LC
.Data
.linkedit_data_command_data
;
530 WriteQueue
.push_back(std::make_pair(FunctionStartsCmd
->dataoff
,
531 &MachOWriter::writeFunctionStarts
));
533 case MachO::LC_DYLD_CHAINED_FIXUPS
:
534 ChainedFixupsCmd
= &LC
.Data
.linkedit_data_command_data
;
535 WriteQueue
.push_back(std::make_pair(ChainedFixupsCmd
->dataoff
,
536 &MachOWriter::writeChainedFixups
));
538 case MachO::LC_DYLD_EXPORTS_TRIE
:
539 DyldExportsTrieCmd
= &LC
.Data
.linkedit_data_command_data
;
540 WriteQueue
.push_back(std::make_pair(DyldExportsTrieCmd
->dataoff
,
541 &MachOWriter::writeDyldExportsTrie
));
543 case MachO::LC_DATA_IN_CODE
:
544 DataInCodeCmd
= &LC
.Data
.linkedit_data_command_data
;
545 WriteQueue
.push_back(std::make_pair(DataInCodeCmd
->dataoff
,
546 &MachOWriter::writeDataInCode
));
551 llvm::sort(WriteQueue
, llvm::less_first());
553 for (auto writeOp
: WriteQueue
) {
554 ZeroToOffset(OS
, writeOp
.first
);
555 (this->*writeOp
.second
)(OS
);
559 void MachOWriter::writeRebaseOpcodes(raw_ostream
&OS
) {
560 MachOYAML::LinkEditData
&LinkEdit
= Obj
.LinkEdit
;
562 for (auto Opcode
: LinkEdit
.RebaseOpcodes
) {
563 uint8_t OpByte
= Opcode
.Opcode
| Opcode
.Imm
;
564 OS
.write(reinterpret_cast<char *>(&OpByte
), 1);
565 for (auto Data
: Opcode
.ExtraData
)
566 encodeULEB128(Data
, OS
);
570 void MachOWriter::writeBasicBindOpcodes(raw_ostream
&OS
) {
571 writeBindOpcodes(OS
, Obj
.LinkEdit
.BindOpcodes
);
574 void MachOWriter::writeWeakBindOpcodes(raw_ostream
&OS
) {
575 writeBindOpcodes(OS
, Obj
.LinkEdit
.WeakBindOpcodes
);
578 void MachOWriter::writeLazyBindOpcodes(raw_ostream
&OS
) {
579 writeBindOpcodes(OS
, Obj
.LinkEdit
.LazyBindOpcodes
);
582 void MachOWriter::writeNameList(raw_ostream
&OS
) {
583 for (auto NLE
: Obj
.LinkEdit
.NameList
) {
585 writeNListEntry
<MachO::nlist_64
>(NLE
, OS
, Obj
.IsLittleEndian
);
587 writeNListEntry
<MachO::nlist
>(NLE
, OS
, Obj
.IsLittleEndian
);
591 void MachOWriter::writeStringTable(raw_ostream
&OS
) {
592 for (auto Str
: Obj
.LinkEdit
.StringTable
) {
593 OS
.write(Str
.data(), Str
.size());
598 void MachOWriter::writeDynamicSymbolTable(raw_ostream
&OS
) {
599 for (auto Data
: Obj
.LinkEdit
.IndirectSymbols
)
600 OS
.write(reinterpret_cast<const char *>(&Data
),
601 sizeof(yaml::Hex32::BaseType
));
604 void MachOWriter::writeFunctionStarts(raw_ostream
&OS
) {
606 for (uint64_t NextAddr
: Obj
.LinkEdit
.FunctionStarts
) {
607 uint64_t Delta
= NextAddr
- Addr
;
608 encodeULEB128(Delta
, OS
);
615 void MachOWriter::writeDataInCode(raw_ostream
&OS
) {
616 for (const auto &Entry
: Obj
.LinkEdit
.DataInCode
) {
617 MachO::data_in_code_entry DICE
{Entry
.Offset
, Entry
.Length
, Entry
.Kind
};
618 if (Obj
.IsLittleEndian
!= sys::IsLittleEndianHost
)
619 MachO::swapStruct(DICE
);
620 OS
.write(reinterpret_cast<const char *>(&DICE
),
621 sizeof(MachO::data_in_code_entry
));
625 void MachOWriter::writeChainedFixups(raw_ostream
&OS
) {
626 if (Obj
.LinkEdit
.ChainedFixups
.size() > 0)
627 OS
.write(reinterpret_cast<const char *>(Obj
.LinkEdit
.ChainedFixups
.data()),
628 Obj
.LinkEdit
.ChainedFixups
.size());
631 void MachOWriter::writeDyldExportsTrie(raw_ostream
&OS
) {
632 dumpExportEntry(OS
, Obj
.LinkEdit
.ExportTrie
);
635 class UniversalWriter
{
637 UniversalWriter(yaml::YamlObjectFile
&ObjectFile
)
638 : ObjectFile(ObjectFile
), fileStart(0) {}
640 Error
writeMachO(raw_ostream
&OS
);
643 void writeFatHeader(raw_ostream
&OS
);
644 void writeFatArchs(raw_ostream
&OS
);
646 void ZeroToOffset(raw_ostream
&OS
, size_t offset
);
648 yaml::YamlObjectFile
&ObjectFile
;
652 Error
UniversalWriter::writeMachO(raw_ostream
&OS
) {
653 fileStart
= OS
.tell();
654 if (ObjectFile
.MachO
) {
655 MachOWriter
Writer(*ObjectFile
.MachO
);
656 return Writer
.writeMachO(OS
);
662 auto &FatFile
= *ObjectFile
.FatMachO
;
663 if (FatFile
.FatArchs
.size() < FatFile
.Slices
.size())
664 return createStringError(
665 errc::invalid_argument
,
666 "cannot write 'Slices' if not described in 'FatArches'");
668 for (size_t i
= 0; i
< FatFile
.Slices
.size(); i
++) {
669 ZeroToOffset(OS
, FatFile
.FatArchs
[i
].offset
);
670 MachOWriter
Writer(FatFile
.Slices
[i
]);
671 if (Error Err
= Writer
.writeMachO(OS
))
674 auto SliceEnd
= FatFile
.FatArchs
[i
].offset
+ FatFile
.FatArchs
[i
].size
;
675 ZeroToOffset(OS
, SliceEnd
);
678 return Error::success();
681 void UniversalWriter::writeFatHeader(raw_ostream
&OS
) {
682 auto &FatFile
= *ObjectFile
.FatMachO
;
683 MachO::fat_header header
;
684 header
.magic
= FatFile
.Header
.magic
;
685 header
.nfat_arch
= FatFile
.Header
.nfat_arch
;
686 if (sys::IsLittleEndianHost
)
688 OS
.write(reinterpret_cast<const char *>(&header
), sizeof(MachO::fat_header
));
691 template <typename FatArchType
>
692 FatArchType
constructFatArch(MachOYAML::FatArch
&Arch
) {
694 FatArch
.cputype
= Arch
.cputype
;
695 FatArch
.cpusubtype
= Arch
.cpusubtype
;
696 FatArch
.offset
= Arch
.offset
;
697 FatArch
.size
= Arch
.size
;
698 FatArch
.align
= Arch
.align
;
702 template <typename StructType
>
703 void writeFatArch(MachOYAML::FatArch
&LC
, raw_ostream
&OS
) {}
706 void writeFatArch
<MachO::fat_arch
>(MachOYAML::FatArch
&Arch
, raw_ostream
&OS
) {
707 auto FatArch
= constructFatArch
<MachO::fat_arch
>(Arch
);
708 if (sys::IsLittleEndianHost
)
710 OS
.write(reinterpret_cast<const char *>(&FatArch
), sizeof(MachO::fat_arch
));
714 void writeFatArch
<MachO::fat_arch_64
>(MachOYAML::FatArch
&Arch
,
716 auto FatArch
= constructFatArch
<MachO::fat_arch_64
>(Arch
);
717 FatArch
.reserved
= Arch
.reserved
;
718 if (sys::IsLittleEndianHost
)
720 OS
.write(reinterpret_cast<const char *>(&FatArch
),
721 sizeof(MachO::fat_arch_64
));
724 void UniversalWriter::writeFatArchs(raw_ostream
&OS
) {
725 auto &FatFile
= *ObjectFile
.FatMachO
;
726 bool is64Bit
= FatFile
.Header
.magic
== MachO::FAT_MAGIC_64
;
727 for (auto Arch
: FatFile
.FatArchs
) {
729 writeFatArch
<MachO::fat_arch_64
>(Arch
, OS
);
731 writeFatArch
<MachO::fat_arch
>(Arch
, OS
);
735 void UniversalWriter::ZeroToOffset(raw_ostream
&OS
, size_t Offset
) {
736 auto currOffset
= OS
.tell() - fileStart
;
737 if (currOffset
< Offset
)
738 ZeroFillBytes(OS
, Offset
- currOffset
);
741 } // end anonymous namespace
746 bool yaml2macho(YamlObjectFile
&Doc
, raw_ostream
&Out
, ErrorHandler EH
) {
747 UniversalWriter
Writer(Doc
);
748 if (Error Err
= Writer
.writeMachO(Out
)) {
749 handleAllErrors(std::move(Err
),
750 [&](const ErrorInfoBase
&Err
) { EH(Err
.message()); });