15ffd83dbSDimitry Andric //===- X86_64.cpp ---------------------------------------------------------===// 25ffd83dbSDimitry Andric // 35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65ffd83dbSDimitry Andric // 75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 85ffd83dbSDimitry Andric 95ffd83dbSDimitry Andric #include "InputFiles.h" 105ffd83dbSDimitry Andric #include "Symbols.h" 115ffd83dbSDimitry Andric #include "SyntheticSections.h" 125ffd83dbSDimitry Andric #include "Target.h" 135ffd83dbSDimitry Andric 145ffd83dbSDimitry Andric #include "lld/Common/ErrorHandler.h" 1581ad6265SDimitry Andric #include "mach-o/compact_unwind_encoding.h" 165ffd83dbSDimitry Andric #include "llvm/BinaryFormat/MachO.h" 175ffd83dbSDimitry Andric #include "llvm/Support/Endian.h" 185ffd83dbSDimitry Andric 195ffd83dbSDimitry Andric using namespace llvm::MachO; 205ffd83dbSDimitry Andric using namespace llvm::support::endian; 215ffd83dbSDimitry Andric using namespace lld; 225ffd83dbSDimitry Andric using namespace lld::macho; 235ffd83dbSDimitry Andric 245ffd83dbSDimitry Andric namespace { 255ffd83dbSDimitry Andric 265ffd83dbSDimitry Andric struct X86_64 : TargetInfo { 275ffd83dbSDimitry Andric X86_64(); 285ffd83dbSDimitry Andric 29fe6060f1SDimitry Andric int64_t getEmbeddedAddend(MemoryBufferRef, uint64_t offset, 30fe6060f1SDimitry Andric const relocation_info) const override; 31fe6060f1SDimitry Andric void relocateOne(uint8_t *loc, const Reloc &, uint64_t va, 32fe6060f1SDimitry Andric uint64_t relocVA) const override; 335ffd83dbSDimitry Andric 34bdd1243dSDimitry Andric void writeStub(uint8_t *buf, const Symbol &, 35bdd1243dSDimitry Andric uint64_t pointerVA) const override; 365ffd83dbSDimitry Andric void writeStubHelperHeader(uint8_t *buf) const override; 3781ad6265SDimitry Andric void writeStubHelperEntry(uint8_t *buf, const Symbol &, 385ffd83dbSDimitry Andric uint64_t entryAddr) const override; 395ffd83dbSDimitry Andric 40bdd1243dSDimitry Andric void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr, 41*0fca6ea1SDimitry Andric uint64_t &stubOffset, uint64_t selrefVA, 427a6dacacSDimitry Andric Symbol *objcMsgSend) const override; 43bdd1243dSDimitry Andric 44fe6060f1SDimitry Andric void relaxGotLoad(uint8_t *loc, uint8_t type) const override; 45fe6060f1SDimitry Andric uint64_t getPageSize() const override { return 4 * 1024; } 46753f127fSDimitry Andric 47753f127fSDimitry Andric void handleDtraceReloc(const Symbol *sym, const Reloc &r, 48753f127fSDimitry Andric uint8_t *loc) const override; 495ffd83dbSDimitry Andric }; 505ffd83dbSDimitry Andric } // namespace 515ffd83dbSDimitry Andric 52fcaf7f86SDimitry Andric static constexpr std::array<RelocAttrs, 10> relocAttrsArray{{ 53fe6060f1SDimitry Andric #define B(x) RelocAttrBits::x 54fe6060f1SDimitry Andric {"UNSIGNED", 55fe6060f1SDimitry Andric B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4) | B(BYTE8)}, 56fe6060f1SDimitry Andric {"SIGNED", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, 57fe6060f1SDimitry Andric {"BRANCH", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, 58fe6060f1SDimitry Andric {"GOT_LOAD", B(PCREL) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)}, 59fe6060f1SDimitry Andric {"GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)}, 60fe6060f1SDimitry Andric {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)}, 61fe6060f1SDimitry Andric {"SIGNED_1", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, 62fe6060f1SDimitry Andric {"SIGNED_2", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, 63fe6060f1SDimitry Andric {"SIGNED_4", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, 64fe6060f1SDimitry Andric {"TLV", B(PCREL) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)}, 65fe6060f1SDimitry Andric #undef B 66fe6060f1SDimitry Andric }}; 675ffd83dbSDimitry Andric 68fe6060f1SDimitry Andric static int pcrelOffset(uint8_t type) { 69fe6060f1SDimitry Andric switch (type) { 705ffd83dbSDimitry Andric case X86_64_RELOC_SIGNED_1: 71fe6060f1SDimitry Andric return 1; 725ffd83dbSDimitry Andric case X86_64_RELOC_SIGNED_2: 73fe6060f1SDimitry Andric return 2; 745ffd83dbSDimitry Andric case X86_64_RELOC_SIGNED_4: 75fe6060f1SDimitry Andric return 4; 765ffd83dbSDimitry Andric default: 775ffd83dbSDimitry Andric return 0; 785ffd83dbSDimitry Andric } 79fe6060f1SDimitry Andric } 80fe6060f1SDimitry Andric 81fe6060f1SDimitry Andric int64_t X86_64::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset, 82fe6060f1SDimitry Andric relocation_info rel) const { 83fe6060f1SDimitry Andric auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart()); 84fe6060f1SDimitry Andric const uint8_t *loc = buf + offset + rel.r_address; 855ffd83dbSDimitry Andric 865ffd83dbSDimitry Andric switch (rel.r_length) { 875ffd83dbSDimitry Andric case 2: 88fe6060f1SDimitry Andric return static_cast<int32_t>(read32le(loc)) + pcrelOffset(rel.r_type); 895ffd83dbSDimitry Andric case 3: 90fe6060f1SDimitry Andric return read64le(loc) + pcrelOffset(rel.r_type); 915ffd83dbSDimitry Andric default: 925ffd83dbSDimitry Andric llvm_unreachable("invalid r_length"); 935ffd83dbSDimitry Andric } 945ffd83dbSDimitry Andric } 955ffd83dbSDimitry Andric 96fe6060f1SDimitry Andric void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value, 97fe6060f1SDimitry Andric uint64_t relocVA) const { 98fe6060f1SDimitry Andric if (r.pcrel) { 99fe6060f1SDimitry Andric uint64_t pc = relocVA + 4 + pcrelOffset(r.type); 100fe6060f1SDimitry Andric value -= pc; 1015ffd83dbSDimitry Andric } 1025ffd83dbSDimitry Andric 1035ffd83dbSDimitry Andric switch (r.length) { 1045ffd83dbSDimitry Andric case 2: 105fe6060f1SDimitry Andric if (r.type == X86_64_RELOC_UNSIGNED) 10681ad6265SDimitry Andric checkUInt(loc, r, value, 32); 107fe6060f1SDimitry Andric else 10881ad6265SDimitry Andric checkInt(loc, r, value, 32); 109fe6060f1SDimitry Andric write32le(loc, value); 1105ffd83dbSDimitry Andric break; 1115ffd83dbSDimitry Andric case 3: 112fe6060f1SDimitry Andric write64le(loc, value); 1135ffd83dbSDimitry Andric break; 1145ffd83dbSDimitry Andric default: 1155ffd83dbSDimitry Andric llvm_unreachable("invalid r_length"); 1165ffd83dbSDimitry Andric } 1175ffd83dbSDimitry Andric } 1185ffd83dbSDimitry Andric 1195ffd83dbSDimitry Andric // The following methods emit a number of assembly sequences with RIP-relative 1205ffd83dbSDimitry Andric // addressing. Note that RIP-relative addressing on X86-64 has the RIP pointing 1215ffd83dbSDimitry Andric // to the next instruction, not the current instruction, so we always have to 1225ffd83dbSDimitry Andric // account for the current instruction's size when calculating offsets. 1235ffd83dbSDimitry Andric // writeRipRelative helps with that. 1245ffd83dbSDimitry Andric // 1255ffd83dbSDimitry Andric // bufAddr: The virtual address corresponding to buf[0]. 1265ffd83dbSDimitry Andric // bufOff: The offset within buf of the next instruction. 1275ffd83dbSDimitry Andric // destAddr: The destination address that the current instruction references. 128fe6060f1SDimitry Andric static void writeRipRelative(SymbolDiagnostic d, uint8_t *buf, uint64_t bufAddr, 129fe6060f1SDimitry Andric uint64_t bufOff, uint64_t destAddr) { 1305ffd83dbSDimitry Andric uint64_t rip = bufAddr + bufOff; 13181ad6265SDimitry Andric checkInt(buf, d, destAddr - rip, 32); 1325ffd83dbSDimitry Andric // For the instructions we care about, the RIP-relative address is always 1335ffd83dbSDimitry Andric // stored in the last 4 bytes of the instruction. 1345ffd83dbSDimitry Andric write32le(buf + bufOff - 4, destAddr - rip); 1355ffd83dbSDimitry Andric } 1365ffd83dbSDimitry Andric 1375ffd83dbSDimitry Andric static constexpr uint8_t stub[] = { 1385ffd83dbSDimitry Andric 0xff, 0x25, 0, 0, 0, 0, // jmpq *__la_symbol_ptr(%rip) 1395ffd83dbSDimitry Andric }; 1405ffd83dbSDimitry Andric 141bdd1243dSDimitry Andric void X86_64::writeStub(uint8_t *buf, const Symbol &sym, 142bdd1243dSDimitry Andric uint64_t pointerVA) const { 1435ffd83dbSDimitry Andric memcpy(buf, stub, 2); // just copy the two nonzero bytes 1445ffd83dbSDimitry Andric uint64_t stubAddr = in.stubs->addr + sym.stubsIndex * sizeof(stub); 145bdd1243dSDimitry Andric writeRipRelative({&sym, "stub"}, buf, stubAddr, sizeof(stub), pointerVA); 1465ffd83dbSDimitry Andric } 1475ffd83dbSDimitry Andric 1485ffd83dbSDimitry Andric static constexpr uint8_t stubHelperHeader[] = { 1495ffd83dbSDimitry Andric 0x4c, 0x8d, 0x1d, 0, 0, 0, 0, // 0x0: leaq ImageLoaderCache(%rip), %r11 1505ffd83dbSDimitry Andric 0x41, 0x53, // 0x7: pushq %r11 1515ffd83dbSDimitry Andric 0xff, 0x25, 0, 0, 0, 0, // 0x9: jmpq *dyld_stub_binder@GOT(%rip) 1525ffd83dbSDimitry Andric 0x90, // 0xf: nop 1535ffd83dbSDimitry Andric }; 1545ffd83dbSDimitry Andric 155fe6060f1SDimitry Andric void X86_64::writeStubHelperHeader(uint8_t *buf) const { 156fe6060f1SDimitry Andric memcpy(buf, stubHelperHeader, sizeof(stubHelperHeader)); 157fe6060f1SDimitry Andric SymbolDiagnostic d = {nullptr, "stub helper header"}; 158fe6060f1SDimitry Andric writeRipRelative(d, buf, in.stubHelper->addr, 7, 159fe6060f1SDimitry Andric in.imageLoaderCache->getVA()); 160fe6060f1SDimitry Andric writeRipRelative(d, buf, in.stubHelper->addr, 0xf, 161fe6060f1SDimitry Andric in.got->addr + 162fe6060f1SDimitry Andric in.stubHelper->stubBinder->gotIndex * LP64::wordSize); 163fe6060f1SDimitry Andric } 164fe6060f1SDimitry Andric 1655ffd83dbSDimitry Andric static constexpr uint8_t stubHelperEntry[] = { 1665ffd83dbSDimitry Andric 0x68, 0, 0, 0, 0, // 0x0: pushq <bind offset> 1675ffd83dbSDimitry Andric 0xe9, 0, 0, 0, 0, // 0x5: jmp <__stub_helper> 1685ffd83dbSDimitry Andric }; 1695ffd83dbSDimitry Andric 17081ad6265SDimitry Andric void X86_64::writeStubHelperEntry(uint8_t *buf, const Symbol &sym, 1715ffd83dbSDimitry Andric uint64_t entryAddr) const { 1725ffd83dbSDimitry Andric memcpy(buf, stubHelperEntry, sizeof(stubHelperEntry)); 1735ffd83dbSDimitry Andric write32le(buf + 1, sym.lazyBindOffset); 174fe6060f1SDimitry Andric writeRipRelative({&sym, "stub helper"}, buf, entryAddr, 175fe6060f1SDimitry Andric sizeof(stubHelperEntry), in.stubHelper->addr); 1765ffd83dbSDimitry Andric } 1775ffd83dbSDimitry Andric 178bdd1243dSDimitry Andric static constexpr uint8_t objcStubsFastCode[] = { 179bdd1243dSDimitry Andric 0x48, 0x8b, 0x35, 0, 0, 0, 0, // 0x0: movq selrefs@selector(%rip), %rsi 180bdd1243dSDimitry Andric 0xff, 0x25, 0, 0, 0, 0, // 0x7: jmpq *_objc_msgSend@GOT(%rip) 181bdd1243dSDimitry Andric }; 182bdd1243dSDimitry Andric 183bdd1243dSDimitry Andric void X86_64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr, 184*0fca6ea1SDimitry Andric uint64_t &stubOffset, uint64_t selrefVA, 1857a6dacacSDimitry Andric Symbol *objcMsgSend) const { 1867a6dacacSDimitry Andric uint64_t objcMsgSendAddr = in.got->addr; 1877a6dacacSDimitry Andric uint64_t objcMsgSendIndex = objcMsgSend->gotIndex; 1887a6dacacSDimitry Andric 189bdd1243dSDimitry Andric memcpy(buf, objcStubsFastCode, sizeof(objcStubsFastCode)); 190bdd1243dSDimitry Andric SymbolDiagnostic d = {sym, sym->getName()}; 191bdd1243dSDimitry Andric uint64_t stubAddr = stubsAddr + stubOffset; 192*0fca6ea1SDimitry Andric writeRipRelative(d, buf, stubAddr, 7, selrefVA); 193bdd1243dSDimitry Andric writeRipRelative(d, buf, stubAddr, 0xd, 1947a6dacacSDimitry Andric objcMsgSendAddr + objcMsgSendIndex * LP64::wordSize); 1957a6dacacSDimitry Andric stubOffset += target->objcStubsFastSize; 196bdd1243dSDimitry Andric } 197bdd1243dSDimitry Andric 198fe6060f1SDimitry Andric void X86_64::relaxGotLoad(uint8_t *loc, uint8_t type) const { 199fe6060f1SDimitry Andric // Convert MOVQ to LEAQ 200fe6060f1SDimitry Andric if (loc[-2] != 0x8b) 201fe6060f1SDimitry Andric error(getRelocAttrs(type).name + " reloc requires MOVQ instruction"); 202fe6060f1SDimitry Andric loc[-2] = 0x8d; 2035ffd83dbSDimitry Andric } 2045ffd83dbSDimitry Andric 205fe6060f1SDimitry Andric X86_64::X86_64() : TargetInfo(LP64()) { 2065ffd83dbSDimitry Andric cpuType = CPU_TYPE_X86_64; 2075ffd83dbSDimitry Andric cpuSubtype = CPU_SUBTYPE_X86_64_ALL; 2085ffd83dbSDimitry Andric 20981ad6265SDimitry Andric modeDwarfEncoding = UNWIND_X86_MODE_DWARF; 21081ad6265SDimitry Andric subtractorRelocType = X86_64_RELOC_SUBTRACTOR; 21181ad6265SDimitry Andric unsignedRelocType = X86_64_RELOC_UNSIGNED; 21281ad6265SDimitry Andric 2135ffd83dbSDimitry Andric stubSize = sizeof(stub); 2145ffd83dbSDimitry Andric stubHelperHeaderSize = sizeof(stubHelperHeader); 2155ffd83dbSDimitry Andric stubHelperEntrySize = sizeof(stubHelperEntry); 216fcaf7f86SDimitry Andric 217bdd1243dSDimitry Andric objcStubsFastSize = sizeof(objcStubsFastCode); 2187a6dacacSDimitry Andric objcStubsFastAlignment = 1; 219bdd1243dSDimitry Andric 220fcaf7f86SDimitry Andric relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()}; 2215ffd83dbSDimitry Andric } 2225ffd83dbSDimitry Andric 2235ffd83dbSDimitry Andric TargetInfo *macho::createX86_64TargetInfo() { 2245ffd83dbSDimitry Andric static X86_64 t; 2255ffd83dbSDimitry Andric return &t; 2265ffd83dbSDimitry Andric } 227753f127fSDimitry Andric 228753f127fSDimitry Andric void X86_64::handleDtraceReloc(const Symbol *sym, const Reloc &r, 229753f127fSDimitry Andric uint8_t *loc) const { 230753f127fSDimitry Andric assert(r.type == X86_64_RELOC_BRANCH); 231753f127fSDimitry Andric 232753f127fSDimitry Andric if (config->outputType == MH_OBJECT) 233753f127fSDimitry Andric return; 234753f127fSDimitry Andric 23506c3fb27SDimitry Andric if (sym->getName().starts_with("___dtrace_probe")) { 236753f127fSDimitry Andric // change call site to a NOP 237753f127fSDimitry Andric loc[-1] = 0x90; 238753f127fSDimitry Andric write32le(loc, 0x00401F0F); 23906c3fb27SDimitry Andric } else if (sym->getName().starts_with("___dtrace_isenabled")) { 240753f127fSDimitry Andric // change call site to a clear eax 241753f127fSDimitry Andric loc[-1] = 0x33; 242753f127fSDimitry Andric write32le(loc, 0x909090C0); 243753f127fSDimitry Andric } else { 244753f127fSDimitry Andric error("Unrecognized dtrace symbol prefix: " + toString(*sym)); 245753f127fSDimitry Andric } 246753f127fSDimitry Andric } 247