1 | //===- yaml2macho - Convert YAML to a Mach 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 Mach component of yaml2obj. |
11 | /// |
12 | //===----------------------------------------------------------------------===// |
13 | |
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/SystemZ/zOSSupport.h" |
24 | #include "llvm/Support/raw_ostream.h" |
25 | |
26 | using namespace llvm; |
27 | |
28 | namespace { |
29 | |
30 | class MachOWriter { |
31 | public: |
32 | MachOWriter(MachOYAML::Object &Obj) : Obj(Obj), fileStart(0) { |
33 | is64Bit = Obj.Header.magic == MachO::MH_MAGIC_64 || |
34 | Obj.Header.magic == MachO::MH_CIGAM_64; |
35 | memset(s: reinterpret_cast<void *>(&Header), c: 0, n: sizeof(MachO::mach_header_64)); |
36 | } |
37 | |
38 | Error writeMachO(raw_ostream &OS); |
39 | |
40 | private: |
41 | void writeHeader(raw_ostream &OS); |
42 | void writeLoadCommands(raw_ostream &OS); |
43 | Error writeSectionData(raw_ostream &OS); |
44 | void writeRelocations(raw_ostream &OS); |
45 | void writeLinkEditData(raw_ostream &OS); |
46 | |
47 | void writeBindOpcodes(raw_ostream &OS, |
48 | std::vector<MachOYAML::BindOpcode> &BindOpcodes); |
49 | // LinkEdit writers |
50 | void writeRebaseOpcodes(raw_ostream &OS); |
51 | void writeBasicBindOpcodes(raw_ostream &OS); |
52 | void writeWeakBindOpcodes(raw_ostream &OS); |
53 | void writeLazyBindOpcodes(raw_ostream &OS); |
54 | void writeNameList(raw_ostream &OS); |
55 | void writeStringTable(raw_ostream &OS); |
56 | void writeExportTrie(raw_ostream &OS); |
57 | void writeDynamicSymbolTable(raw_ostream &OS); |
58 | void writeFunctionStarts(raw_ostream &OS); |
59 | void writeChainedFixups(raw_ostream &OS); |
60 | void writeDyldExportsTrie(raw_ostream &OS); |
61 | void writeDataInCode(raw_ostream &OS); |
62 | |
63 | void dumpExportEntry(raw_ostream &OS, MachOYAML::ExportEntry &Entry); |
64 | void ZeroToOffset(raw_ostream &OS, size_t offset); |
65 | |
66 | MachOYAML::Object &Obj; |
67 | bool is64Bit; |
68 | uint64_t fileStart; |
69 | MachO::mach_header_64 ; |
70 | |
71 | // Old PPC Object Files didn't have __LINKEDIT segments, the data was just |
72 | // stuck at the end of the file. |
73 | bool FoundLinkEditSeg = false; |
74 | }; |
75 | |
76 | Error MachOWriter::writeMachO(raw_ostream &OS) { |
77 | fileStart = OS.tell(); |
78 | writeHeader(OS); |
79 | writeLoadCommands(OS); |
80 | if (Error Err = writeSectionData(OS)) |
81 | return Err; |
82 | writeRelocations(OS); |
83 | if (!FoundLinkEditSeg) |
84 | writeLinkEditData(OS); |
85 | return Error::success(); |
86 | } |
87 | |
88 | void MachOWriter::(raw_ostream &OS) { |
89 | Header.magic = Obj.Header.magic; |
90 | Header.cputype = Obj.Header.cputype; |
91 | Header.cpusubtype = Obj.Header.cpusubtype; |
92 | Header.filetype = Obj.Header.filetype; |
93 | Header.ncmds = Obj.Header.ncmds; |
94 | Header.sizeofcmds = Obj.Header.sizeofcmds; |
95 | Header.flags = Obj.Header.flags; |
96 | Header.reserved = Obj.Header.reserved; |
97 | |
98 | if (Obj.IsLittleEndian != sys::IsLittleEndianHost) |
99 | MachO::swapStruct(H&: Header); |
100 | |
101 | auto = |
102 | is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); |
103 | OS.write(Ptr: (const char *)&Header, Size: header_size); |
104 | } |
105 | |
106 | template <typename SectionType> |
107 | SectionType constructSection(const MachOYAML::Section &Sec) { |
108 | SectionType TempSec; |
109 | memcpy(dest: reinterpret_cast<void *>(&TempSec.sectname[0]), src: &Sec.sectname[0], n: 16); |
110 | memcpy(dest: reinterpret_cast<void *>(&TempSec.segname[0]), src: &Sec.segname[0], n: 16); |
111 | TempSec.addr = Sec.addr; |
112 | TempSec.size = Sec.size; |
113 | TempSec.offset = Sec.offset; |
114 | TempSec.align = Sec.align; |
115 | TempSec.reloff = Sec.reloff; |
116 | TempSec.nreloc = Sec.nreloc; |
117 | TempSec.flags = Sec.flags; |
118 | TempSec.reserved1 = Sec.reserved1; |
119 | TempSec.reserved2 = Sec.reserved2; |
120 | return TempSec; |
121 | } |
122 | |
123 | template <typename StructType> |
124 | size_t writeLoadCommandData(MachOYAML::LoadCommand &LC, raw_ostream &OS, |
125 | bool IsLittleEndian) { |
126 | return 0; |
127 | } |
128 | |
129 | template <> |
130 | size_t writeLoadCommandData<MachO::segment_command>(MachOYAML::LoadCommand &LC, |
131 | raw_ostream &OS, |
132 | bool IsLittleEndian) { |
133 | size_t BytesWritten = 0; |
134 | for (const auto &Sec : LC.Sections) { |
135 | auto TempSec = constructSection<MachO::section>(Sec); |
136 | if (IsLittleEndian != sys::IsLittleEndianHost) |
137 | MachO::swapStruct(sect&: TempSec); |
138 | OS.write(Ptr: reinterpret_cast<const char *>(&(TempSec)), |
139 | Size: sizeof(MachO::section)); |
140 | BytesWritten += sizeof(MachO::section); |
141 | } |
142 | return BytesWritten; |
143 | } |
144 | |
145 | template <> |
146 | size_t writeLoadCommandData<MachO::segment_command_64>( |
147 | MachOYAML::LoadCommand &LC, raw_ostream &OS, bool IsLittleEndian) { |
148 | size_t BytesWritten = 0; |
149 | for (const auto &Sec : LC.Sections) { |
150 | auto TempSec = constructSection<MachO::section_64>(Sec); |
151 | TempSec.reserved3 = Sec.reserved3; |
152 | if (IsLittleEndian != sys::IsLittleEndianHost) |
153 | MachO::swapStruct(sect&: TempSec); |
154 | OS.write(Ptr: reinterpret_cast<const char *>(&(TempSec)), |
155 | Size: sizeof(MachO::section_64)); |
156 | BytesWritten += sizeof(MachO::section_64); |
157 | } |
158 | return BytesWritten; |
159 | } |
160 | |
161 | size_t writePayloadString(MachOYAML::LoadCommand &LC, raw_ostream &OS) { |
162 | size_t BytesWritten = 0; |
163 | if (!LC.Content.empty()) { |
164 | OS.write(Ptr: LC.Content.c_str(), Size: LC.Content.length()); |
165 | BytesWritten = LC.Content.length(); |
166 | } |
167 | return BytesWritten; |
168 | } |
169 | |
170 | template <> |
171 | size_t writeLoadCommandData<MachO::dylib_command>(MachOYAML::LoadCommand &LC, |
172 | raw_ostream &OS, |
173 | bool IsLittleEndian) { |
174 | return writePayloadString(LC, OS); |
175 | } |
176 | |
177 | template <> |
178 | size_t writeLoadCommandData<MachO::dylinker_command>(MachOYAML::LoadCommand &LC, |
179 | raw_ostream &OS, |
180 | bool IsLittleEndian) { |
181 | return writePayloadString(LC, OS); |
182 | } |
183 | |
184 | template <> |
185 | size_t writeLoadCommandData<MachO::rpath_command>(MachOYAML::LoadCommand &LC, |
186 | raw_ostream &OS, |
187 | bool IsLittleEndian) { |
188 | return writePayloadString(LC, OS); |
189 | } |
190 | |
191 | template <> |
192 | size_t writeLoadCommandData<MachO::sub_framework_command>( |
193 | MachOYAML::LoadCommand &LC, raw_ostream &OS, bool IsLittleEndian) { |
194 | return writePayloadString(LC, OS); |
195 | } |
196 | |
197 | template <> |
198 | size_t writeLoadCommandData<MachO::sub_umbrella_command>( |
199 | MachOYAML::LoadCommand &LC, raw_ostream &OS, bool IsLittleEndian) { |
200 | return writePayloadString(LC, OS); |
201 | } |
202 | |
203 | template <> |
204 | size_t writeLoadCommandData<MachO::sub_client_command>( |
205 | MachOYAML::LoadCommand &LC, raw_ostream &OS, bool IsLittleEndian) { |
206 | return writePayloadString(LC, OS); |
207 | } |
208 | |
209 | template <> |
210 | size_t writeLoadCommandData<MachO::sub_library_command>( |
211 | MachOYAML::LoadCommand &LC, raw_ostream &OS, bool IsLittleEndian) { |
212 | return writePayloadString(LC, OS); |
213 | } |
214 | |
215 | template <> |
216 | size_t writeLoadCommandData<MachO::build_version_command>( |
217 | MachOYAML::LoadCommand &LC, raw_ostream &OS, bool IsLittleEndian) { |
218 | size_t BytesWritten = 0; |
219 | for (const auto &T : LC.Tools) { |
220 | struct MachO::build_tool_version tool = T; |
221 | if (IsLittleEndian != sys::IsLittleEndianHost) |
222 | MachO::swapStruct(C&: tool); |
223 | OS.write(Ptr: reinterpret_cast<const char *>(&tool), |
224 | Size: sizeof(MachO::build_tool_version)); |
225 | BytesWritten += sizeof(MachO::build_tool_version); |
226 | } |
227 | return BytesWritten; |
228 | } |
229 | |
230 | void ZeroFillBytes(raw_ostream &OS, size_t Size) { |
231 | std::vector<uint8_t> FillData(Size, 0); |
232 | OS.write(Ptr: reinterpret_cast<char *>(FillData.data()), Size); |
233 | } |
234 | |
235 | void Fill(raw_ostream &OS, size_t Size, uint32_t Data) { |
236 | std::vector<uint32_t> FillData((Size / 4) + 1, Data); |
237 | OS.write(Ptr: reinterpret_cast<char *>(FillData.data()), Size); |
238 | } |
239 | |
240 | void MachOWriter::ZeroToOffset(raw_ostream &OS, size_t Offset) { |
241 | auto currOffset = OS.tell() - fileStart; |
242 | if (currOffset < Offset) |
243 | ZeroFillBytes(OS, Size: Offset - currOffset); |
244 | } |
245 | |
246 | void MachOWriter::writeLoadCommands(raw_ostream &OS) { |
247 | for (auto &LC : Obj.LoadCommands) { |
248 | size_t BytesWritten = 0; |
249 | llvm::MachO::macho_load_command Data = LC.Data; |
250 | |
251 | #define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ |
252 | case MachO::LCName: \ |
253 | if (Obj.IsLittleEndian != sys::IsLittleEndianHost) \ |
254 | MachO::swapStruct(Data.LCStruct##_data); \ |
255 | OS.write(reinterpret_cast<const char *>(&(Data.LCStruct##_data)), \ |
256 | sizeof(MachO::LCStruct)); \ |
257 | BytesWritten = sizeof(MachO::LCStruct); \ |
258 | BytesWritten += \ |
259 | writeLoadCommandData<MachO::LCStruct>(LC, OS, Obj.IsLittleEndian); \ |
260 | break; |
261 | |
262 | switch (LC.Data.load_command_data.cmd) { |
263 | default: |
264 | if (Obj.IsLittleEndian != sys::IsLittleEndianHost) |
265 | MachO::swapStruct(lc&: Data.load_command_data); |
266 | OS.write(Ptr: reinterpret_cast<const char *>(&(Data.load_command_data)), |
267 | Size: sizeof(MachO::load_command)); |
268 | BytesWritten = sizeof(MachO::load_command); |
269 | BytesWritten += |
270 | writeLoadCommandData<MachO::load_command>(LC, OS, IsLittleEndian: Obj.IsLittleEndian); |
271 | break; |
272 | #include "llvm/BinaryFormat/MachO.def" |
273 | } |
274 | |
275 | if (LC.PayloadBytes.size() > 0) { |
276 | OS.write(Ptr: reinterpret_cast<const char *>(LC.PayloadBytes.data()), |
277 | Size: LC.PayloadBytes.size()); |
278 | BytesWritten += LC.PayloadBytes.size(); |
279 | } |
280 | |
281 | if (LC.ZeroPadBytes > 0) { |
282 | ZeroFillBytes(OS, Size: LC.ZeroPadBytes); |
283 | BytesWritten += LC.ZeroPadBytes; |
284 | } |
285 | |
286 | // Fill remaining bytes with 0. This will only get hit in partially |
287 | // specified test cases. |
288 | auto BytesRemaining = LC.Data.load_command_data.cmdsize - BytesWritten; |
289 | if (BytesRemaining > 0) { |
290 | ZeroFillBytes(OS, Size: BytesRemaining); |
291 | } |
292 | } |
293 | } |
294 | |
295 | Error MachOWriter::writeSectionData(raw_ostream &OS) { |
296 | uint64_t LinkEditOff = 0; |
297 | for (auto &LC : Obj.LoadCommands) { |
298 | switch (LC.Data.load_command_data.cmd) { |
299 | case MachO::LC_SEGMENT: |
300 | case MachO::LC_SEGMENT_64: |
301 | uint64_t segOff = is64Bit ? LC.Data.segment_command_64_data.fileoff |
302 | : LC.Data.segment_command_data.fileoff; |
303 | if (0 == |
304 | strncmp(s1: &LC.Data.segment_command_data.segname[0], s2: "__LINKEDIT" , n: 16)) { |
305 | FoundLinkEditSeg = true; |
306 | LinkEditOff = segOff; |
307 | if (Obj.RawLinkEditSegment) |
308 | continue; |
309 | writeLinkEditData(OS); |
310 | } |
311 | for (auto &Sec : LC.Sections) { |
312 | ZeroToOffset(OS, Offset: Sec.offset); |
313 | // Zero Fill any data between the end of the last thing we wrote and the |
314 | // start of this section. |
315 | if (OS.tell() - fileStart > Sec.offset && Sec.offset != (uint32_t)0) |
316 | return createStringError( |
317 | EC: errc::invalid_argument, |
318 | S: llvm::formatv( |
319 | Fmt: "wrote too much data somewhere, section offsets in " |
320 | "section {0} for segment {1} don't line up: " |
321 | "[cursor={2:x}], [fileStart={3:x}], [sectionOffset={4:x}]" , |
322 | Vals&: Sec.sectname, Vals&: Sec.segname, Vals: OS.tell(), Vals&: fileStart, |
323 | Vals&: Sec.offset.value)); |
324 | |
325 | StringRef SectName(Sec.sectname, |
326 | strnlen(string: Sec.sectname, maxlen: sizeof(Sec.sectname))); |
327 | // If the section's content is specified in the 'DWARF' entry, we will |
328 | // emit it regardless of the section's segname. |
329 | if (Obj.DWARF.getNonEmptySectionNames().count(key: SectName.substr(Start: 2))) { |
330 | if (Sec.content) |
331 | return createStringError(EC: errc::invalid_argument, |
332 | S: "cannot specify section '" + SectName + |
333 | "' contents in the 'DWARF' entry and " |
334 | "the 'content' at the same time" ); |
335 | auto EmitFunc = DWARFYAML::getDWARFEmitterByName(SecName: SectName.substr(Start: 2)); |
336 | if (Error Err = EmitFunc(OS, Obj.DWARF)) |
337 | return Err; |
338 | continue; |
339 | } |
340 | |
341 | // Skip if it's a virtual section. |
342 | if (MachO::isVirtualSection(type: Sec.flags & MachO::SECTION_TYPE)) |
343 | continue; |
344 | |
345 | if (Sec.content) { |
346 | yaml::BinaryRef Content = *Sec.content; |
347 | Content.writeAsBinary(OS); |
348 | ZeroFillBytes(OS, Size: Sec.size - Content.binary_size()); |
349 | } else { |
350 | // Fill section data with 0xDEADBEEF. |
351 | Fill(OS, Size: Sec.size, Data: 0xDEADBEEFu); |
352 | } |
353 | } |
354 | uint64_t segSize = is64Bit ? LC.Data.segment_command_64_data.filesize |
355 | : LC.Data.segment_command_data.filesize; |
356 | ZeroToOffset(OS, Offset: segOff + segSize); |
357 | break; |
358 | } |
359 | } |
360 | |
361 | if (Obj.RawLinkEditSegment) { |
362 | ZeroToOffset(OS, Offset: LinkEditOff); |
363 | if (OS.tell() - fileStart > LinkEditOff || !LinkEditOff) |
364 | return createStringError(EC: errc::invalid_argument, |
365 | S: "section offsets don't line up" ); |
366 | Obj.RawLinkEditSegment->writeAsBinary(OS); |
367 | } |
368 | return Error::success(); |
369 | } |
370 | |
371 | // The implementation of makeRelocationInfo and makeScatteredRelocationInfo is |
372 | // consistent with how libObject parses MachO binary files. For the reference |
373 | // see getStruct, getRelocation, getPlainRelocationPCRel, |
374 | // getPlainRelocationLength and related methods in MachOObjectFile.cpp |
375 | static MachO::any_relocation_info |
376 | makeRelocationInfo(const MachOYAML::Relocation &R, bool IsLE) { |
377 | assert(!R.is_scattered && "non-scattered relocation expected" ); |
378 | MachO::any_relocation_info MRE; |
379 | MRE.r_word0 = R.address; |
380 | if (IsLE) |
381 | MRE.r_word1 = ((unsigned)R.symbolnum << 0) | ((unsigned)R.is_pcrel << 24) | |
382 | ((unsigned)R.length << 25) | ((unsigned)R.is_extern << 27) | |
383 | ((unsigned)R.type << 28); |
384 | else |
385 | MRE.r_word1 = ((unsigned)R.symbolnum << 8) | ((unsigned)R.is_pcrel << 7) | |
386 | ((unsigned)R.length << 5) | ((unsigned)R.is_extern << 4) | |
387 | ((unsigned)R.type << 0); |
388 | return MRE; |
389 | } |
390 | |
391 | static MachO::any_relocation_info |
392 | makeScatteredRelocationInfo(const MachOYAML::Relocation &R) { |
393 | assert(R.is_scattered && "scattered relocation expected" ); |
394 | MachO::any_relocation_info MRE; |
395 | MRE.r_word0 = (((unsigned)R.address << 0) | ((unsigned)R.type << 24) | |
396 | ((unsigned)R.length << 28) | ((unsigned)R.is_pcrel << 30) | |
397 | MachO::R_SCATTERED); |
398 | MRE.r_word1 = R.value; |
399 | return MRE; |
400 | } |
401 | |
402 | void MachOWriter::writeRelocations(raw_ostream &OS) { |
403 | for (const MachOYAML::LoadCommand &LC : Obj.LoadCommands) { |
404 | switch (LC.Data.load_command_data.cmd) { |
405 | case MachO::LC_SEGMENT: |
406 | case MachO::LC_SEGMENT_64: |
407 | for (const MachOYAML::Section &Sec : LC.Sections) { |
408 | if (Sec.relocations.empty()) |
409 | continue; |
410 | ZeroToOffset(OS, Offset: Sec.reloff); |
411 | for (const MachOYAML::Relocation &R : Sec.relocations) { |
412 | MachO::any_relocation_info MRE = |
413 | R.is_scattered ? makeScatteredRelocationInfo(R) |
414 | : makeRelocationInfo(R, IsLE: Obj.IsLittleEndian); |
415 | if (Obj.IsLittleEndian != sys::IsLittleEndianHost) |
416 | MachO::swapStruct(reloc&: MRE); |
417 | OS.write(Ptr: reinterpret_cast<const char *>(&MRE), |
418 | Size: sizeof(MachO::any_relocation_info)); |
419 | } |
420 | } |
421 | } |
422 | } |
423 | } |
424 | |
425 | void MachOWriter::writeBindOpcodes( |
426 | raw_ostream &OS, std::vector<MachOYAML::BindOpcode> &BindOpcodes) { |
427 | |
428 | for (const auto &Opcode : BindOpcodes) { |
429 | uint8_t OpByte = Opcode.Opcode | Opcode.Imm; |
430 | OS.write(Ptr: reinterpret_cast<char *>(&OpByte), Size: 1); |
431 | for (auto Data : Opcode.ULEBExtraData) { |
432 | encodeULEB128(Value: Data, OS); |
433 | } |
434 | for (auto Data : Opcode.SLEBExtraData) { |
435 | encodeSLEB128(Value: Data, OS); |
436 | } |
437 | if (!Opcode.Symbol.empty()) { |
438 | OS.write(Ptr: Opcode.Symbol.data(), Size: Opcode.Symbol.size()); |
439 | OS.write(C: '\0'); |
440 | } |
441 | } |
442 | } |
443 | |
444 | void MachOWriter::dumpExportEntry(raw_ostream &OS, |
445 | MachOYAML::ExportEntry &Entry) { |
446 | encodeULEB128(Value: Entry.TerminalSize, OS); |
447 | if (Entry.TerminalSize > 0) { |
448 | encodeULEB128(Value: Entry.Flags, OS); |
449 | if (Entry.Flags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT) { |
450 | encodeULEB128(Value: Entry.Other, OS); |
451 | OS << Entry.ImportName; |
452 | OS.write(C: '\0'); |
453 | } else { |
454 | encodeULEB128(Value: Entry.Address, OS); |
455 | if (Entry.Flags & MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) |
456 | encodeULEB128(Value: Entry.Other, OS); |
457 | } |
458 | } |
459 | OS.write(C: static_cast<uint8_t>(Entry.Children.size())); |
460 | for (const auto &EE : Entry.Children) { |
461 | OS << EE.Name; |
462 | OS.write(C: '\0'); |
463 | encodeULEB128(Value: EE.NodeOffset, OS); |
464 | } |
465 | for (auto EE : Entry.Children) |
466 | dumpExportEntry(OS, Entry&: EE); |
467 | } |
468 | |
469 | void MachOWriter::writeExportTrie(raw_ostream &OS) { |
470 | dumpExportEntry(OS, Entry&: Obj.LinkEdit.ExportTrie); |
471 | } |
472 | |
473 | template <typename NListType> |
474 | void writeNListEntry(MachOYAML::NListEntry &NLE, raw_ostream &OS, |
475 | bool IsLittleEndian) { |
476 | NListType ListEntry; |
477 | ListEntry.n_strx = NLE.n_strx; |
478 | ListEntry.n_type = NLE.n_type; |
479 | ListEntry.n_sect = NLE.n_sect; |
480 | ListEntry.n_desc = NLE.n_desc; |
481 | ListEntry.n_value = NLE.n_value; |
482 | |
483 | if (IsLittleEndian != sys::IsLittleEndianHost) |
484 | MachO::swapStruct(ListEntry); |
485 | OS.write(Ptr: reinterpret_cast<const char *>(&ListEntry), Size: sizeof(NListType)); |
486 | } |
487 | |
488 | void MachOWriter::writeLinkEditData(raw_ostream &OS) { |
489 | typedef void (MachOWriter::*writeHandler)(raw_ostream &); |
490 | typedef std::pair<uint64_t, writeHandler> writeOperation; |
491 | std::vector<writeOperation> WriteQueue; |
492 | |
493 | MachO::dyld_info_command *DyldInfoOnlyCmd = nullptr; |
494 | MachO::symtab_command *SymtabCmd = nullptr; |
495 | MachO::dysymtab_command *DSymtabCmd = nullptr; |
496 | MachO::linkedit_data_command *FunctionStartsCmd = nullptr; |
497 | MachO::linkedit_data_command *ChainedFixupsCmd = nullptr; |
498 | MachO::linkedit_data_command *DyldExportsTrieCmd = nullptr; |
499 | MachO::linkedit_data_command *DataInCodeCmd = nullptr; |
500 | for (auto &LC : Obj.LoadCommands) { |
501 | switch (LC.Data.load_command_data.cmd) { |
502 | case MachO::LC_SYMTAB: |
503 | SymtabCmd = &LC.Data.symtab_command_data; |
504 | WriteQueue.push_back( |
505 | x: std::make_pair(x&: SymtabCmd->symoff, y: &MachOWriter::writeNameList)); |
506 | WriteQueue.push_back( |
507 | x: std::make_pair(x&: SymtabCmd->stroff, y: &MachOWriter::writeStringTable)); |
508 | break; |
509 | case MachO::LC_DYLD_INFO_ONLY: |
510 | DyldInfoOnlyCmd = &LC.Data.dyld_info_command_data; |
511 | WriteQueue.push_back(x: std::make_pair(x&: DyldInfoOnlyCmd->rebase_off, |
512 | y: &MachOWriter::writeRebaseOpcodes)); |
513 | WriteQueue.push_back(x: std::make_pair(x&: DyldInfoOnlyCmd->bind_off, |
514 | y: &MachOWriter::writeBasicBindOpcodes)); |
515 | WriteQueue.push_back(x: std::make_pair(x&: DyldInfoOnlyCmd->weak_bind_off, |
516 | y: &MachOWriter::writeWeakBindOpcodes)); |
517 | WriteQueue.push_back(x: std::make_pair(x&: DyldInfoOnlyCmd->lazy_bind_off, |
518 | y: &MachOWriter::writeLazyBindOpcodes)); |
519 | WriteQueue.push_back(x: std::make_pair(x&: DyldInfoOnlyCmd->export_off, |
520 | y: &MachOWriter::writeExportTrie)); |
521 | break; |
522 | case MachO::LC_DYSYMTAB: |
523 | DSymtabCmd = &LC.Data.dysymtab_command_data; |
524 | WriteQueue.push_back(x: std::make_pair( |
525 | x&: DSymtabCmd->indirectsymoff, y: &MachOWriter::writeDynamicSymbolTable)); |
526 | break; |
527 | case MachO::LC_FUNCTION_STARTS: |
528 | FunctionStartsCmd = &LC.Data.linkedit_data_command_data; |
529 | WriteQueue.push_back(x: std::make_pair(x&: FunctionStartsCmd->dataoff, |
530 | y: &MachOWriter::writeFunctionStarts)); |
531 | break; |
532 | case MachO::LC_DYLD_CHAINED_FIXUPS: |
533 | ChainedFixupsCmd = &LC.Data.linkedit_data_command_data; |
534 | WriteQueue.push_back(x: std::make_pair(x&: ChainedFixupsCmd->dataoff, |
535 | y: &MachOWriter::writeChainedFixups)); |
536 | break; |
537 | case MachO::LC_DYLD_EXPORTS_TRIE: |
538 | DyldExportsTrieCmd = &LC.Data.linkedit_data_command_data; |
539 | WriteQueue.push_back(x: std::make_pair(x&: DyldExportsTrieCmd->dataoff, |
540 | y: &MachOWriter::writeDyldExportsTrie)); |
541 | break; |
542 | case MachO::LC_DATA_IN_CODE: |
543 | DataInCodeCmd = &LC.Data.linkedit_data_command_data; |
544 | WriteQueue.push_back(x: std::make_pair(x&: DataInCodeCmd->dataoff, |
545 | y: &MachOWriter::writeDataInCode)); |
546 | break; |
547 | } |
548 | } |
549 | |
550 | llvm::sort(C&: WriteQueue, Comp: llvm::less_first()); |
551 | |
552 | for (auto writeOp : WriteQueue) { |
553 | ZeroToOffset(OS, Offset: writeOp.first); |
554 | (this->*writeOp.second)(OS); |
555 | } |
556 | } |
557 | |
558 | void MachOWriter::writeRebaseOpcodes(raw_ostream &OS) { |
559 | MachOYAML::LinkEditData &LinkEdit = Obj.LinkEdit; |
560 | |
561 | for (const auto &Opcode : LinkEdit.RebaseOpcodes) { |
562 | uint8_t OpByte = Opcode.Opcode | Opcode.Imm; |
563 | OS.write(Ptr: reinterpret_cast<char *>(&OpByte), Size: 1); |
564 | for (auto Data : Opcode.ExtraData) |
565 | encodeULEB128(Value: Data, OS); |
566 | } |
567 | } |
568 | |
569 | void MachOWriter::writeBasicBindOpcodes(raw_ostream &OS) { |
570 | writeBindOpcodes(OS, BindOpcodes&: Obj.LinkEdit.BindOpcodes); |
571 | } |
572 | |
573 | void MachOWriter::writeWeakBindOpcodes(raw_ostream &OS) { |
574 | writeBindOpcodes(OS, BindOpcodes&: Obj.LinkEdit.WeakBindOpcodes); |
575 | } |
576 | |
577 | void MachOWriter::writeLazyBindOpcodes(raw_ostream &OS) { |
578 | writeBindOpcodes(OS, BindOpcodes&: Obj.LinkEdit.LazyBindOpcodes); |
579 | } |
580 | |
581 | void MachOWriter::writeNameList(raw_ostream &OS) { |
582 | for (auto NLE : Obj.LinkEdit.NameList) { |
583 | if (is64Bit) |
584 | writeNListEntry<MachO::nlist_64>(NLE, OS, IsLittleEndian: Obj.IsLittleEndian); |
585 | else |
586 | writeNListEntry<MachO::nlist>(NLE, OS, IsLittleEndian: Obj.IsLittleEndian); |
587 | } |
588 | } |
589 | |
590 | void MachOWriter::writeStringTable(raw_ostream &OS) { |
591 | for (auto Str : Obj.LinkEdit.StringTable) { |
592 | OS.write(Ptr: Str.data(), Size: Str.size()); |
593 | OS.write(C: '\0'); |
594 | } |
595 | } |
596 | |
597 | void MachOWriter::writeDynamicSymbolTable(raw_ostream &OS) { |
598 | for (auto Data : Obj.LinkEdit.IndirectSymbols) |
599 | OS.write(Ptr: reinterpret_cast<const char *>(&Data), |
600 | Size: sizeof(yaml::Hex32::BaseType)); |
601 | } |
602 | |
603 | void MachOWriter::writeFunctionStarts(raw_ostream &OS) { |
604 | uint64_t Addr = 0; |
605 | for (uint64_t NextAddr : Obj.LinkEdit.FunctionStarts) { |
606 | uint64_t Delta = NextAddr - Addr; |
607 | encodeULEB128(Value: Delta, OS); |
608 | Addr = NextAddr; |
609 | } |
610 | |
611 | OS.write(C: '\0'); |
612 | } |
613 | |
614 | void MachOWriter::writeDataInCode(raw_ostream &OS) { |
615 | for (const auto &Entry : Obj.LinkEdit.DataInCode) { |
616 | MachO::data_in_code_entry DICE{.offset: Entry.Offset, .length: Entry.Length, .kind: Entry.Kind}; |
617 | if (Obj.IsLittleEndian != sys::IsLittleEndianHost) |
618 | MachO::swapStruct(C&: DICE); |
619 | OS.write(Ptr: reinterpret_cast<const char *>(&DICE), |
620 | Size: sizeof(MachO::data_in_code_entry)); |
621 | } |
622 | } |
623 | |
624 | void MachOWriter::writeChainedFixups(raw_ostream &OS) { |
625 | if (Obj.LinkEdit.ChainedFixups.size() > 0) |
626 | OS.write(Ptr: reinterpret_cast<const char *>(Obj.LinkEdit.ChainedFixups.data()), |
627 | Size: Obj.LinkEdit.ChainedFixups.size()); |
628 | } |
629 | |
630 | void MachOWriter::writeDyldExportsTrie(raw_ostream &OS) { |
631 | dumpExportEntry(OS, Entry&: Obj.LinkEdit.ExportTrie); |
632 | } |
633 | |
634 | class UniversalWriter { |
635 | public: |
636 | UniversalWriter(yaml::YamlObjectFile &ObjectFile) |
637 | : ObjectFile(ObjectFile), fileStart(0) {} |
638 | |
639 | Error writeMachO(raw_ostream &OS); |
640 | |
641 | private: |
642 | void writeFatHeader(raw_ostream &OS); |
643 | void writeFatArchs(raw_ostream &OS); |
644 | |
645 | void ZeroToOffset(raw_ostream &OS, size_t offset); |
646 | |
647 | yaml::YamlObjectFile &ObjectFile; |
648 | uint64_t fileStart; |
649 | }; |
650 | |
651 | Error UniversalWriter::writeMachO(raw_ostream &OS) { |
652 | fileStart = OS.tell(); |
653 | if (ObjectFile.MachO) { |
654 | MachOWriter Writer(*ObjectFile.MachO); |
655 | return Writer.writeMachO(OS); |
656 | } |
657 | |
658 | writeFatHeader(OS); |
659 | writeFatArchs(OS); |
660 | |
661 | auto &FatFile = *ObjectFile.FatMachO; |
662 | if (FatFile.FatArchs.size() < FatFile.Slices.size()) |
663 | return createStringError( |
664 | EC: errc::invalid_argument, |
665 | S: "cannot write 'Slices' if not described in 'FatArches'" ); |
666 | |
667 | for (size_t i = 0; i < FatFile.Slices.size(); i++) { |
668 | ZeroToOffset(OS, offset: FatFile.FatArchs[i].offset); |
669 | MachOWriter Writer(FatFile.Slices[i]); |
670 | if (Error Err = Writer.writeMachO(OS)) |
671 | return Err; |
672 | |
673 | auto SliceEnd = FatFile.FatArchs[i].offset + FatFile.FatArchs[i].size; |
674 | ZeroToOffset(OS, offset: SliceEnd); |
675 | } |
676 | |
677 | return Error::success(); |
678 | } |
679 | |
680 | void UniversalWriter::(raw_ostream &OS) { |
681 | auto &FatFile = *ObjectFile.FatMachO; |
682 | MachO::fat_header ; |
683 | header.magic = FatFile.Header.magic; |
684 | header.nfat_arch = FatFile.Header.nfat_arch; |
685 | if (sys::IsLittleEndianHost) |
686 | swapStruct(mh&: header); |
687 | OS.write(Ptr: reinterpret_cast<const char *>(&header), Size: sizeof(MachO::fat_header)); |
688 | } |
689 | |
690 | template <typename FatArchType> |
691 | FatArchType constructFatArch(MachOYAML::FatArch &Arch) { |
692 | FatArchType FatArch; |
693 | FatArch.cputype = Arch.cputype; |
694 | FatArch.cpusubtype = Arch.cpusubtype; |
695 | FatArch.offset = Arch.offset; |
696 | FatArch.size = Arch.size; |
697 | FatArch.align = Arch.align; |
698 | return FatArch; |
699 | } |
700 | |
701 | template <typename StructType> |
702 | void writeFatArch(MachOYAML::FatArch &LC, raw_ostream &OS) {} |
703 | |
704 | template <> |
705 | void writeFatArch<MachO::fat_arch>(MachOYAML::FatArch &Arch, raw_ostream &OS) { |
706 | auto FatArch = constructFatArch<MachO::fat_arch>(Arch); |
707 | if (sys::IsLittleEndianHost) |
708 | swapStruct(mh&: FatArch); |
709 | OS.write(Ptr: reinterpret_cast<const char *>(&FatArch), Size: sizeof(MachO::fat_arch)); |
710 | } |
711 | |
712 | template <> |
713 | void writeFatArch<MachO::fat_arch_64>(MachOYAML::FatArch &Arch, |
714 | raw_ostream &OS) { |
715 | auto FatArch = constructFatArch<MachO::fat_arch_64>(Arch); |
716 | FatArch.reserved = Arch.reserved; |
717 | if (sys::IsLittleEndianHost) |
718 | swapStruct(mh&: FatArch); |
719 | OS.write(Ptr: reinterpret_cast<const char *>(&FatArch), |
720 | Size: sizeof(MachO::fat_arch_64)); |
721 | } |
722 | |
723 | void UniversalWriter::writeFatArchs(raw_ostream &OS) { |
724 | auto &FatFile = *ObjectFile.FatMachO; |
725 | bool is64Bit = FatFile.Header.magic == MachO::FAT_MAGIC_64; |
726 | for (auto Arch : FatFile.FatArchs) { |
727 | if (is64Bit) |
728 | writeFatArch<MachO::fat_arch_64>(Arch, OS); |
729 | else |
730 | writeFatArch<MachO::fat_arch>(Arch, OS); |
731 | } |
732 | } |
733 | |
734 | void UniversalWriter::ZeroToOffset(raw_ostream &OS, size_t Offset) { |
735 | auto currOffset = OS.tell() - fileStart; |
736 | if (currOffset < Offset) |
737 | ZeroFillBytes(OS, Size: Offset - currOffset); |
738 | } |
739 | |
740 | } // end anonymous namespace |
741 | |
742 | namespace llvm { |
743 | namespace yaml { |
744 | |
745 | bool yaml2macho(YamlObjectFile &Doc, raw_ostream &Out, ErrorHandler EH) { |
746 | UniversalWriter Writer(Doc); |
747 | if (Error Err = Writer.writeMachO(OS&: Out)) { |
748 | handleAllErrors(E: std::move(Err), |
749 | Handlers: [&](const ErrorInfoBase &Err) { EH(Err.message()); }); |
750 | return false; |
751 | } |
752 | return true; |
753 | } |
754 | |
755 | } // namespace yaml |
756 | } // namespace llvm |
757 | |