| 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 | |