| 1 | //===-------------- MachO.cpp - JIT linker function for MachO -------------===// |
| 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 | // MachO jit-link function. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include "llvm/ExecutionEngine/JITLink/MachO.h" |
| 14 | |
| 15 | #include "llvm/BinaryFormat/MachO.h" |
| 16 | #include "llvm/ExecutionEngine/JITLink/MachO_arm64.h" |
| 17 | #include "llvm/ExecutionEngine/JITLink/MachO_x86_64.h" |
| 18 | #include "llvm/Support/Format.h" |
| 19 | |
| 20 | using namespace llvm; |
| 21 | |
| 22 | #define DEBUG_TYPE "jitlink" |
| 23 | |
| 24 | namespace llvm { |
| 25 | namespace jitlink { |
| 26 | |
| 27 | Expected<std::unique_ptr<LinkGraph>> |
| 28 | createLinkGraphFromMachOObject(MemoryBufferRef ObjectBuffer, |
| 29 | std::shared_ptr<orc::SymbolStringPool> SSP) { |
| 30 | StringRef Data = ObjectBuffer.getBuffer(); |
| 31 | if (Data.size() < 4) |
| 32 | return make_error<JITLinkError>(Args: "Truncated MachO buffer \"" + |
| 33 | ObjectBuffer.getBufferIdentifier() + "\"" ); |
| 34 | |
| 35 | uint32_t Magic; |
| 36 | memcpy(dest: &Magic, src: Data.data(), n: sizeof(uint32_t)); |
| 37 | LLVM_DEBUG({ |
| 38 | dbgs() << "jitLink_MachO: magic = " << format("0x%08" PRIx32, Magic) |
| 39 | << ", identifier = \"" << ObjectBuffer.getBufferIdentifier() |
| 40 | << "\"\n" ; |
| 41 | }); |
| 42 | |
| 43 | if (Magic == MachO::MH_MAGIC || Magic == MachO::MH_CIGAM) |
| 44 | return make_error<JITLinkError>(Args: "MachO 32-bit platforms not supported" ); |
| 45 | else if (Magic == MachO::MH_MAGIC_64 || Magic == MachO::MH_CIGAM_64) { |
| 46 | |
| 47 | if (Data.size() < sizeof(MachO::mach_header_64)) |
| 48 | return make_error<JITLinkError>(Args: "Truncated MachO buffer \"" + |
| 49 | ObjectBuffer.getBufferIdentifier() + |
| 50 | "\"" ); |
| 51 | |
| 52 | // Read the CPU type from the header. |
| 53 | uint32_t CPUType; |
| 54 | memcpy(dest: &CPUType, src: Data.data() + 4, n: sizeof(uint32_t)); |
| 55 | if (Magic == MachO::MH_CIGAM_64) |
| 56 | CPUType = llvm::byteswap<uint32_t>(V: CPUType); |
| 57 | |
| 58 | LLVM_DEBUG({ |
| 59 | dbgs() << "jitLink_MachO: cputype = " << format("0x%08" PRIx32, CPUType) |
| 60 | << "\n" ; |
| 61 | }); |
| 62 | |
| 63 | switch (CPUType) { |
| 64 | case MachO::CPU_TYPE_ARM64: |
| 65 | return createLinkGraphFromMachOObject_arm64(ObjectBuffer, SSP: std::move(SSP)); |
| 66 | case MachO::CPU_TYPE_X86_64: |
| 67 | return createLinkGraphFromMachOObject_x86_64(ObjectBuffer, |
| 68 | SSP: std::move(SSP)); |
| 69 | } |
| 70 | return make_error<JITLinkError>(Args: "MachO-64 CPU type not valid" ); |
| 71 | } else |
| 72 | return make_error<JITLinkError>(Args: "Unrecognized MachO magic value" ); |
| 73 | } |
| 74 | |
| 75 | void link_MachO(std::unique_ptr<LinkGraph> G, |
| 76 | std::unique_ptr<JITLinkContext> Ctx) { |
| 77 | |
| 78 | switch (G->getTargetTriple().getArch()) { |
| 79 | case Triple::aarch64: |
| 80 | return link_MachO_arm64(G: std::move(G), Ctx: std::move(Ctx)); |
| 81 | case Triple::x86_64: |
| 82 | return link_MachO_x86_64(G: std::move(G), Ctx: std::move(Ctx)); |
| 83 | default: |
| 84 | Ctx->notifyFailed(Err: make_error<JITLinkError>(Args: "MachO-64 CPU type not valid" )); |
| 85 | return; |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | template <typename MachOHeaderType> |
| 90 | static Expected<Block &> (LinkGraph &G, Section &Sec) { |
| 91 | auto &B = G.createMutableContentBlock(Parent&: Sec, ContentSize: sizeof(MachOHeaderType), |
| 92 | Address: orc::ExecutorAddr(), Alignment: 8, AlignmentOffset: 0, ZeroInitialize: true); |
| 93 | MachOHeaderType Hdr; |
| 94 | Hdr.magic = G.getPointerSize() == 4 ? MachO::MH_MAGIC : MachO::MH_MAGIC_64; |
| 95 | if (auto CPUType = MachO::getCPUType(T: G.getTargetTriple())) |
| 96 | Hdr.cputype = *CPUType; |
| 97 | else |
| 98 | return CPUType.takeError(); |
| 99 | if (auto CPUSubType = MachO::getCPUSubType(T: G.getTargetTriple())) |
| 100 | Hdr.cpusubtype = *CPUSubType; |
| 101 | else |
| 102 | return CPUSubType.takeError(); |
| 103 | Hdr.filetype = MachO::MH_OBJECT; |
| 104 | |
| 105 | if (G.getEndianness() != endianness::native) |
| 106 | MachO::swapStruct(Hdr); |
| 107 | |
| 108 | memcpy(B.getAlreadyMutableContent().data(), &Hdr, sizeof(Hdr)); |
| 109 | |
| 110 | return B; |
| 111 | } |
| 112 | |
| 113 | Expected<Symbol &> (LinkGraph &G) { |
| 114 | StringRef ("__TEXT,__lcl_macho_hdr" ); |
| 115 | Section *Sec = G.findSectionByName(Name: LocalHeaderSectionName); |
| 116 | if (Sec) { |
| 117 | assert(Sec->blocks_size() == 1 && "Unexpected number of blocks" ); |
| 118 | assert(Sec->symbols_size() == 1 && "Unexpected number of symbols" ); |
| 119 | auto &Sym = **Sec->symbols().begin(); |
| 120 | assert(Sym.getOffset() == 0 && "Symbol not at start of header block" ); |
| 121 | return Sym; |
| 122 | } |
| 123 | |
| 124 | // Create the local header section, move all other sections up in the |
| 125 | // section ordering to ensure that it's laid out first. |
| 126 | for (auto &Sec : G.sections()) |
| 127 | Sec.setOrdinal(Sec.getOrdinal() + 1); |
| 128 | |
| 129 | Sec = &G.createSection(Name: LocalHeaderSectionName, Prot: orc::MemProt::Read); |
| 130 | |
| 131 | Sec->setOrdinal(0); |
| 132 | |
| 133 | Block *B = nullptr; |
| 134 | switch (G.getTargetTriple().getArch()) { |
| 135 | case Triple::aarch64: |
| 136 | case Triple::x86_64: |
| 137 | if (auto BOrErr = createLocalHeaderBlock<MachO::mach_header_64>(G, Sec&: *Sec)) |
| 138 | B = &*BOrErr; |
| 139 | else |
| 140 | return BOrErr.takeError(); |
| 141 | break; |
| 142 | default: |
| 143 | return make_error<JITLinkError>(Args: "Cannot create local Mach-O header for " + |
| 144 | G.getName() + ": unsupported triple " + |
| 145 | G.getTargetTriple().str()); |
| 146 | } |
| 147 | |
| 148 | return G.addAnonymousSymbol(Content&: *B, Offset: 0, Size: B->getSize(), IsCallable: false, IsLive: false); |
| 149 | } |
| 150 | |
| 151 | } // end namespace jitlink |
| 152 | } // end namespace llvm |
| 153 | |