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/LEB128.h"
19 #include "llvm/Support/YAMLTraits.h"
20 #include "llvm/Support/raw_ostream.h"
22 #include "llvm/Support/Format.h"
30 MachOWriter(MachOYAML::Object
&Obj
) : Obj(Obj
), is64Bit(true), fileStart(0) {
31 is64Bit
= Obj
.Header
.magic
== MachO::MH_MAGIC_64
||
32 Obj
.Header
.magic
== MachO::MH_CIGAM_64
;
33 memset(reinterpret_cast<void *>(&Header
), 0, sizeof(MachO::mach_header_64
));
36 void writeMachO(raw_ostream
&OS
);
39 void writeHeader(raw_ostream
&OS
);
40 void writeLoadCommands(raw_ostream
&OS
);
41 void writeSectionData(raw_ostream
&OS
);
42 void writeLinkEditData(raw_ostream
&OS
);
44 void writeBindOpcodes(raw_ostream
&OS
,
45 std::vector
<MachOYAML::BindOpcode
> &BindOpcodes
);
47 void writeRebaseOpcodes(raw_ostream
&OS
);
48 void writeBasicBindOpcodes(raw_ostream
&OS
);
49 void writeWeakBindOpcodes(raw_ostream
&OS
);
50 void writeLazyBindOpcodes(raw_ostream
&OS
);
51 void writeNameList(raw_ostream
&OS
);
52 void writeStringTable(raw_ostream
&OS
);
53 void writeExportTrie(raw_ostream
&OS
);
55 void dumpExportEntry(raw_ostream
&OS
, MachOYAML::ExportEntry
&Entry
);
56 void ZeroToOffset(raw_ostream
&OS
, size_t offset
);
58 MachOYAML::Object
&Obj
;
62 MachO::mach_header_64 Header
;
65 void MachOWriter::writeMachO(raw_ostream
&OS
) {
66 fileStart
= OS
.tell();
68 writeLoadCommands(OS
);
72 void MachOWriter::writeHeader(raw_ostream
&OS
) {
73 Header
.magic
= Obj
.Header
.magic
;
74 Header
.cputype
= Obj
.Header
.cputype
;
75 Header
.cpusubtype
= Obj
.Header
.cpusubtype
;
76 Header
.filetype
= Obj
.Header
.filetype
;
77 Header
.ncmds
= Obj
.Header
.ncmds
;
78 Header
.sizeofcmds
= Obj
.Header
.sizeofcmds
;
79 Header
.flags
= Obj
.Header
.flags
;
80 Header
.reserved
= Obj
.Header
.reserved
;
82 if (Obj
.IsLittleEndian
!= sys::IsLittleEndianHost
)
83 MachO::swapStruct(Header
);
86 is64Bit
? sizeof(MachO::mach_header_64
) : sizeof(MachO::mach_header
);
87 OS
.write((const char *)&Header
, header_size
);
90 template <typename SectionType
>
91 SectionType
constructSection(MachOYAML::Section Sec
) {
93 memcpy(reinterpret_cast<void *>(&TempSec
.sectname
[0]), &Sec
.sectname
[0], 16);
94 memcpy(reinterpret_cast<void *>(&TempSec
.segname
[0]), &Sec
.segname
[0], 16);
95 TempSec
.addr
= Sec
.addr
;
96 TempSec
.size
= Sec
.size
;
97 TempSec
.offset
= Sec
.offset
;
98 TempSec
.align
= Sec
.align
;
99 TempSec
.reloff
= Sec
.reloff
;
100 TempSec
.nreloc
= Sec
.nreloc
;
101 TempSec
.flags
= Sec
.flags
;
102 TempSec
.reserved1
= Sec
.reserved1
;
103 TempSec
.reserved2
= Sec
.reserved2
;
107 template <typename StructType
>
108 size_t writeLoadCommandData(MachOYAML::LoadCommand
&LC
, raw_ostream
&OS
,
109 bool IsLittleEndian
) {
114 size_t writeLoadCommandData
<MachO::segment_command
>(MachOYAML::LoadCommand
&LC
,
116 bool IsLittleEndian
) {
117 size_t BytesWritten
= 0;
118 for (const auto &Sec
: LC
.Sections
) {
119 auto TempSec
= constructSection
<MachO::section
>(Sec
);
120 if (IsLittleEndian
!= sys::IsLittleEndianHost
)
121 MachO::swapStruct(TempSec
);
122 OS
.write(reinterpret_cast<const char *>(&(TempSec
)),
123 sizeof(MachO::section
));
124 BytesWritten
+= sizeof(MachO::section
);
130 size_t writeLoadCommandData
<MachO::segment_command_64
>(
131 MachOYAML::LoadCommand
&LC
, raw_ostream
&OS
, bool IsLittleEndian
) {
132 size_t BytesWritten
= 0;
133 for (const auto &Sec
: LC
.Sections
) {
134 auto TempSec
= constructSection
<MachO::section_64
>(Sec
);
135 TempSec
.reserved3
= Sec
.reserved3
;
136 if (IsLittleEndian
!= sys::IsLittleEndianHost
)
137 MachO::swapStruct(TempSec
);
138 OS
.write(reinterpret_cast<const char *>(&(TempSec
)),
139 sizeof(MachO::section_64
));
140 BytesWritten
+= sizeof(MachO::section_64
);
145 size_t writePayloadString(MachOYAML::LoadCommand
&LC
, raw_ostream
&OS
) {
146 size_t BytesWritten
= 0;
147 if (!LC
.PayloadString
.empty()) {
148 OS
.write(LC
.PayloadString
.c_str(), LC
.PayloadString
.length());
149 BytesWritten
= LC
.PayloadString
.length();
155 size_t writeLoadCommandData
<MachO::dylib_command
>(MachOYAML::LoadCommand
&LC
,
157 bool IsLittleEndian
) {
158 return writePayloadString(LC
, OS
);
162 size_t writeLoadCommandData
<MachO::dylinker_command
>(MachOYAML::LoadCommand
&LC
,
164 bool IsLittleEndian
) {
165 return writePayloadString(LC
, OS
);
169 size_t writeLoadCommandData
<MachO::rpath_command
>(MachOYAML::LoadCommand
&LC
,
171 bool IsLittleEndian
) {
172 return writePayloadString(LC
, OS
);
176 size_t writeLoadCommandData
<MachO::build_version_command
>(
177 MachOYAML::LoadCommand
&LC
, raw_ostream
&OS
, bool IsLittleEndian
) {
178 size_t BytesWritten
= 0;
179 for (const auto &T
: LC
.Tools
) {
180 struct MachO::build_tool_version tool
= T
;
181 if (IsLittleEndian
!= sys::IsLittleEndianHost
)
182 MachO::swapStruct(tool
);
183 OS
.write(reinterpret_cast<const char *>(&tool
),
184 sizeof(MachO::build_tool_version
));
185 BytesWritten
+= sizeof(MachO::build_tool_version
);
190 void ZeroFillBytes(raw_ostream
&OS
, size_t Size
) {
191 std::vector
<uint8_t> FillData
;
192 FillData
.insert(FillData
.begin(), Size
, 0);
193 OS
.write(reinterpret_cast<char *>(FillData
.data()), Size
);
196 void Fill(raw_ostream
&OS
, size_t Size
, uint32_t Data
) {
197 std::vector
<uint32_t> FillData
;
198 FillData
.insert(FillData
.begin(), (Size
/ 4) + 1, Data
);
199 OS
.write(reinterpret_cast<char *>(FillData
.data()), Size
);
202 void MachOWriter::ZeroToOffset(raw_ostream
&OS
, size_t Offset
) {
203 auto currOffset
= OS
.tell() - fileStart
;
204 if (currOffset
< Offset
)
205 ZeroFillBytes(OS
, Offset
- currOffset
);
208 void MachOWriter::writeLoadCommands(raw_ostream
&OS
) {
209 for (auto &LC
: Obj
.LoadCommands
) {
210 size_t BytesWritten
= 0;
211 llvm::MachO::macho_load_command Data
= LC
.Data
;
213 #define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \
214 case MachO::LCName: \
215 if (Obj.IsLittleEndian != sys::IsLittleEndianHost) \
216 MachO::swapStruct(Data.LCStruct##_data); \
217 OS.write(reinterpret_cast<const char *>(&(Data.LCStruct##_data)), \
218 sizeof(MachO::LCStruct)); \
219 BytesWritten = sizeof(MachO::LCStruct); \
221 writeLoadCommandData<MachO::LCStruct>(LC, OS, Obj.IsLittleEndian); \
224 switch (LC
.Data
.load_command_data
.cmd
) {
226 if (Obj
.IsLittleEndian
!= sys::IsLittleEndianHost
)
227 MachO::swapStruct(Data
.load_command_data
);
228 OS
.write(reinterpret_cast<const char *>(&(Data
.load_command_data
)),
229 sizeof(MachO::load_command
));
230 BytesWritten
= sizeof(MachO::load_command
);
232 writeLoadCommandData
<MachO::load_command
>(LC
, OS
, Obj
.IsLittleEndian
);
234 #include "llvm/BinaryFormat/MachO.def"
237 if (LC
.PayloadBytes
.size() > 0) {
238 OS
.write(reinterpret_cast<const char *>(LC
.PayloadBytes
.data()),
239 LC
.PayloadBytes
.size());
240 BytesWritten
+= LC
.PayloadBytes
.size();
243 if (LC
.ZeroPadBytes
> 0) {
244 ZeroFillBytes(OS
, LC
.ZeroPadBytes
);
245 BytesWritten
+= LC
.ZeroPadBytes
;
248 // Fill remaining bytes with 0. This will only get hit in partially
249 // specified test cases.
250 auto BytesRemaining
= LC
.Data
.load_command_data
.cmdsize
- BytesWritten
;
251 if (BytesRemaining
> 0) {
252 ZeroFillBytes(OS
, BytesRemaining
);
257 void MachOWriter::writeSectionData(raw_ostream
&OS
) {
258 bool FoundLinkEditSeg
= false;
259 for (auto &LC
: Obj
.LoadCommands
) {
260 switch (LC
.Data
.load_command_data
.cmd
) {
261 case MachO::LC_SEGMENT
:
262 case MachO::LC_SEGMENT_64
:
263 uint64_t segOff
= is64Bit
? LC
.Data
.segment_command_64_data
.fileoff
264 : LC
.Data
.segment_command_data
.fileoff
;
266 strncmp(&LC
.Data
.segment_command_data
.segname
[0], "__LINKEDIT", 16)) {
267 FoundLinkEditSeg
= true;
268 writeLinkEditData(OS
);
270 for (auto &Sec
: LC
.Sections
) {
271 ZeroToOffset(OS
, Sec
.offset
);
272 // Zero Fill any data between the end of the last thing we wrote and the
273 // start of this section.
274 assert((OS
.tell() - fileStart
<= Sec
.offset
||
275 Sec
.offset
== (uint32_t)0) &&
276 "Wrote too much data somewhere, section offsets don't line up.");
277 if (0 == strncmp(&Sec
.segname
[0], "__DWARF", 16)) {
278 if (0 == strncmp(&Sec
.sectname
[0], "__debug_str", 16)) {
279 DWARFYAML::EmitDebugStr(OS
, Obj
.DWARF
);
280 } else if (0 == strncmp(&Sec
.sectname
[0], "__debug_abbrev", 16)) {
281 DWARFYAML::EmitDebugAbbrev(OS
, Obj
.DWARF
);
282 } else if (0 == strncmp(&Sec
.sectname
[0], "__debug_aranges", 16)) {
283 DWARFYAML::EmitDebugAranges(OS
, Obj
.DWARF
);
284 } else if (0 == strncmp(&Sec
.sectname
[0], "__debug_pubnames", 16)) {
285 DWARFYAML::EmitPubSection(OS
, Obj
.DWARF
.PubNames
,
287 } else if (0 == strncmp(&Sec
.sectname
[0], "__debug_pubtypes", 16)) {
288 DWARFYAML::EmitPubSection(OS
, Obj
.DWARF
.PubTypes
,
290 } else if (0 == strncmp(&Sec
.sectname
[0], "__debug_info", 16)) {
291 DWARFYAML::EmitDebugInfo(OS
, Obj
.DWARF
);
292 } else if (0 == strncmp(&Sec
.sectname
[0], "__debug_line", 16)) {
293 DWARFYAML::EmitDebugLine(OS
, Obj
.DWARF
);
299 // Skip if it's a virtual section.
300 if (MachO::isVirtualSection(Sec
.flags
& MachO::SECTION_TYPE
))
304 yaml::BinaryRef Content
= *Sec
.content
;
305 Content
.writeAsBinary(OS
);
306 ZeroFillBytes(OS
, Sec
.size
- Content
.binary_size());
308 // Fill section data with 0xDEADBEEF.
309 Fill(OS
, Sec
.size
, 0xDEADBEEFu
);
312 uint64_t segSize
= is64Bit
? LC
.Data
.segment_command_64_data
.filesize
313 : LC
.Data
.segment_command_data
.filesize
;
314 ZeroToOffset(OS
, segOff
+ segSize
);
318 // Old PPC Object Files didn't have __LINKEDIT segments, the data was just
319 // stuck at the end of the file.
320 if (!FoundLinkEditSeg
)
321 writeLinkEditData(OS
);
324 void MachOWriter::writeBindOpcodes(
325 raw_ostream
&OS
, std::vector
<MachOYAML::BindOpcode
> &BindOpcodes
) {
327 for (auto Opcode
: BindOpcodes
) {
328 uint8_t OpByte
= Opcode
.Opcode
| Opcode
.Imm
;
329 OS
.write(reinterpret_cast<char *>(&OpByte
), 1);
330 for (auto Data
: Opcode
.ULEBExtraData
) {
331 encodeULEB128(Data
, OS
);
333 for (auto Data
: Opcode
.SLEBExtraData
) {
334 encodeSLEB128(Data
, OS
);
336 if (!Opcode
.Symbol
.empty()) {
337 OS
.write(Opcode
.Symbol
.data(), Opcode
.Symbol
.size());
343 void MachOWriter::dumpExportEntry(raw_ostream
&OS
,
344 MachOYAML::ExportEntry
&Entry
) {
345 encodeSLEB128(Entry
.TerminalSize
, OS
);
346 if (Entry
.TerminalSize
> 0) {
347 encodeSLEB128(Entry
.Flags
, OS
);
348 if (Entry
.Flags
& MachO::EXPORT_SYMBOL_FLAGS_REEXPORT
) {
349 encodeSLEB128(Entry
.Other
, OS
);
350 OS
<< Entry
.ImportName
;
353 encodeSLEB128(Entry
.Address
, OS
);
354 if (Entry
.Flags
& MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER
)
355 encodeSLEB128(Entry
.Other
, OS
);
358 OS
.write(static_cast<uint8_t>(Entry
.Children
.size()));
359 for (auto EE
: Entry
.Children
) {
362 encodeSLEB128(EE
.NodeOffset
, OS
);
364 for (auto EE
: Entry
.Children
)
365 dumpExportEntry(OS
, EE
);
368 void MachOWriter::writeExportTrie(raw_ostream
&OS
) {
369 dumpExportEntry(OS
, Obj
.LinkEdit
.ExportTrie
);
372 template <typename NListType
>
373 void writeNListEntry(MachOYAML::NListEntry
&NLE
, raw_ostream
&OS
,
374 bool IsLittleEndian
) {
376 ListEntry
.n_strx
= NLE
.n_strx
;
377 ListEntry
.n_type
= NLE
.n_type
;
378 ListEntry
.n_sect
= NLE
.n_sect
;
379 ListEntry
.n_desc
= NLE
.n_desc
;
380 ListEntry
.n_value
= NLE
.n_value
;
382 if (IsLittleEndian
!= sys::IsLittleEndianHost
)
383 MachO::swapStruct(ListEntry
);
384 OS
.write(reinterpret_cast<const char *>(&ListEntry
), sizeof(NListType
));
387 void MachOWriter::writeLinkEditData(raw_ostream
&OS
) {
388 typedef void (MachOWriter::*writeHandler
)(raw_ostream
&);
389 typedef std::pair
<uint64_t, writeHandler
> writeOperation
;
390 std::vector
<writeOperation
> WriteQueue
;
392 MachO::dyld_info_command
*DyldInfoOnlyCmd
= 0;
393 MachO::symtab_command
*SymtabCmd
= 0;
394 for (auto &LC
: Obj
.LoadCommands
) {
395 switch (LC
.Data
.load_command_data
.cmd
) {
396 case MachO::LC_SYMTAB
:
397 SymtabCmd
= &LC
.Data
.symtab_command_data
;
398 WriteQueue
.push_back(
399 std::make_pair(SymtabCmd
->symoff
, &MachOWriter::writeNameList
));
400 WriteQueue
.push_back(
401 std::make_pair(SymtabCmd
->stroff
, &MachOWriter::writeStringTable
));
403 case MachO::LC_DYLD_INFO_ONLY
:
404 DyldInfoOnlyCmd
= &LC
.Data
.dyld_info_command_data
;
405 WriteQueue
.push_back(std::make_pair(DyldInfoOnlyCmd
->rebase_off
,
406 &MachOWriter::writeRebaseOpcodes
));
407 WriteQueue
.push_back(std::make_pair(DyldInfoOnlyCmd
->bind_off
,
408 &MachOWriter::writeBasicBindOpcodes
));
409 WriteQueue
.push_back(std::make_pair(DyldInfoOnlyCmd
->weak_bind_off
,
410 &MachOWriter::writeWeakBindOpcodes
));
411 WriteQueue
.push_back(std::make_pair(DyldInfoOnlyCmd
->lazy_bind_off
,
412 &MachOWriter::writeLazyBindOpcodes
));
413 WriteQueue
.push_back(std::make_pair(DyldInfoOnlyCmd
->export_off
,
414 &MachOWriter::writeExportTrie
));
419 llvm::sort(WriteQueue
, [](const writeOperation
&a
, const writeOperation
&b
) {
420 return a
.first
< b
.first
;
423 for (auto writeOp
: WriteQueue
) {
424 ZeroToOffset(OS
, writeOp
.first
);
425 (this->*writeOp
.second
)(OS
);
429 void MachOWriter::writeRebaseOpcodes(raw_ostream
&OS
) {
430 MachOYAML::LinkEditData
&LinkEdit
= Obj
.LinkEdit
;
432 for (auto Opcode
: LinkEdit
.RebaseOpcodes
) {
433 uint8_t OpByte
= Opcode
.Opcode
| Opcode
.Imm
;
434 OS
.write(reinterpret_cast<char *>(&OpByte
), 1);
435 for (auto Data
: Opcode
.ExtraData
)
436 encodeULEB128(Data
, OS
);
440 void MachOWriter::writeBasicBindOpcodes(raw_ostream
&OS
) {
441 writeBindOpcodes(OS
, Obj
.LinkEdit
.BindOpcodes
);
444 void MachOWriter::writeWeakBindOpcodes(raw_ostream
&OS
) {
445 writeBindOpcodes(OS
, Obj
.LinkEdit
.WeakBindOpcodes
);
448 void MachOWriter::writeLazyBindOpcodes(raw_ostream
&OS
) {
449 writeBindOpcodes(OS
, Obj
.LinkEdit
.LazyBindOpcodes
);
452 void MachOWriter::writeNameList(raw_ostream
&OS
) {
453 for (auto NLE
: Obj
.LinkEdit
.NameList
) {
455 writeNListEntry
<MachO::nlist_64
>(NLE
, OS
, Obj
.IsLittleEndian
);
457 writeNListEntry
<MachO::nlist
>(NLE
, OS
, Obj
.IsLittleEndian
);
461 void MachOWriter::writeStringTable(raw_ostream
&OS
) {
462 for (auto Str
: Obj
.LinkEdit
.StringTable
) {
463 OS
.write(Str
.data(), Str
.size());
468 class UniversalWriter
{
470 UniversalWriter(yaml::YamlObjectFile
&ObjectFile
)
471 : ObjectFile(ObjectFile
), fileStart(0) {}
473 void writeMachO(raw_ostream
&OS
);
476 void writeFatHeader(raw_ostream
&OS
);
477 void writeFatArchs(raw_ostream
&OS
);
479 void ZeroToOffset(raw_ostream
&OS
, size_t offset
);
481 yaml::YamlObjectFile
&ObjectFile
;
485 void UniversalWriter::writeMachO(raw_ostream
&OS
) {
486 fileStart
= OS
.tell();
487 if (ObjectFile
.MachO
) {
488 MachOWriter
Writer(*ObjectFile
.MachO
);
489 Writer
.writeMachO(OS
);
496 auto &FatFile
= *ObjectFile
.FatMachO
;
497 assert(FatFile
.FatArchs
.size() == FatFile
.Slices
.size());
498 for (size_t i
= 0; i
< FatFile
.Slices
.size(); i
++) {
499 ZeroToOffset(OS
, FatFile
.FatArchs
[i
].offset
);
500 MachOWriter
Writer(FatFile
.Slices
[i
]);
501 Writer
.writeMachO(OS
);
503 auto SliceEnd
= FatFile
.FatArchs
[i
].offset
+ FatFile
.FatArchs
[i
].size
;
504 ZeroToOffset(OS
, SliceEnd
);
508 void UniversalWriter::writeFatHeader(raw_ostream
&OS
) {
509 auto &FatFile
= *ObjectFile
.FatMachO
;
510 MachO::fat_header header
;
511 header
.magic
= FatFile
.Header
.magic
;
512 header
.nfat_arch
= FatFile
.Header
.nfat_arch
;
513 if (sys::IsLittleEndianHost
)
515 OS
.write(reinterpret_cast<const char *>(&header
), sizeof(MachO::fat_header
));
518 template <typename FatArchType
>
519 FatArchType
constructFatArch(MachOYAML::FatArch
&Arch
) {
521 FatArch
.cputype
= Arch
.cputype
;
522 FatArch
.cpusubtype
= Arch
.cpusubtype
;
523 FatArch
.offset
= Arch
.offset
;
524 FatArch
.size
= Arch
.size
;
525 FatArch
.align
= Arch
.align
;
529 template <typename StructType
>
530 void writeFatArch(MachOYAML::FatArch
&LC
, raw_ostream
&OS
) {}
533 void writeFatArch
<MachO::fat_arch
>(MachOYAML::FatArch
&Arch
, raw_ostream
&OS
) {
534 auto FatArch
= constructFatArch
<MachO::fat_arch
>(Arch
);
535 if (sys::IsLittleEndianHost
)
537 OS
.write(reinterpret_cast<const char *>(&FatArch
), sizeof(MachO::fat_arch
));
541 void writeFatArch
<MachO::fat_arch_64
>(MachOYAML::FatArch
&Arch
,
543 auto FatArch
= constructFatArch
<MachO::fat_arch_64
>(Arch
);
544 FatArch
.reserved
= Arch
.reserved
;
545 if (sys::IsLittleEndianHost
)
547 OS
.write(reinterpret_cast<const char *>(&FatArch
),
548 sizeof(MachO::fat_arch_64
));
551 void UniversalWriter::writeFatArchs(raw_ostream
&OS
) {
552 auto &FatFile
= *ObjectFile
.FatMachO
;
553 bool is64Bit
= FatFile
.Header
.magic
== MachO::FAT_MAGIC_64
;
554 for (auto Arch
: FatFile
.FatArchs
) {
556 writeFatArch
<MachO::fat_arch_64
>(Arch
, OS
);
558 writeFatArch
<MachO::fat_arch
>(Arch
, OS
);
562 void UniversalWriter::ZeroToOffset(raw_ostream
&OS
, size_t Offset
) {
563 auto currOffset
= OS
.tell() - fileStart
;
564 if (currOffset
< Offset
)
565 ZeroFillBytes(OS
, Offset
- currOffset
);
568 } // end anonymous namespace
573 bool yaml2macho(YamlObjectFile
&Doc
, raw_ostream
&Out
, ErrorHandler
/*EH*/) {
574 UniversalWriter
Writer(Doc
);
575 Writer
.writeMachO(Out
);