1 //===- X86_64.cpp ---------------------------------------------------------===// 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 #include "InputFiles.h" 10 #include "Symbols.h" 11 #include "SyntheticSections.h" 12 #include "Target.h" 13 14 #include "lld/Common/ErrorHandler.h" 15 #include "llvm/BinaryFormat/MachO.h" 16 #include "llvm/Support/Endian.h" 17 18 using namespace llvm::MachO; 19 using namespace llvm::support::endian; 20 using namespace lld; 21 using namespace lld::macho; 22 23 namespace { 24 25 struct X86_64 : TargetInfo { 26 X86_64(); 27 28 uint64_t getEmbeddedAddend(MemoryBufferRef, const section_64 &, 29 const relocation_info) const override; 30 void relocateOne(uint8_t *loc, const Reloc &, uint64_t va, 31 uint64_t pc) const override; 32 33 void writeStub(uint8_t *buf, const macho::Symbol &) const override; 34 void writeStubHelperHeader(uint8_t *buf) const override; 35 void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &, 36 uint64_t entryAddr) const override; 37 38 void relaxGotLoad(uint8_t *loc, uint8_t type) const override; 39 const TargetInfo::RelocAttrs &getRelocAttrs(uint8_t type) const override; 40 uint64_t getPageSize() const override { return 4 * 1024; } 41 }; 42 43 } // namespace 44 45 const TargetInfo::RelocAttrs &X86_64::getRelocAttrs(uint8_t type) const { 46 static const std::array<TargetInfo::RelocAttrs, 10> relocAttrsArray{{ 47 #define B(x) RelocAttrBits::x 48 {"UNSIGNED", B(TLV) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(DYSYM8) | 49 B(BYTE4) | B(BYTE8)}, 50 {"SIGNED", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, 51 {"BRANCH", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, 52 {"GOT_LOAD", B(PCREL) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)}, 53 {"GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(BYTE4)}, 54 {"SUBTRACTOR", B(SUBTRAHEND)}, 55 {"SIGNED_1", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, 56 {"SIGNED_2", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, 57 {"SIGNED_4", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, 58 {"TLV", B(PCREL) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)}, 59 #undef B 60 }}; 61 assert(type < relocAttrsArray.size() && "invalid relocation type"); 62 if (type >= relocAttrsArray.size()) 63 return TargetInfo::invalidRelocAttrs; 64 return relocAttrsArray[type]; 65 } 66 67 uint64_t X86_64::getEmbeddedAddend(MemoryBufferRef mb, const section_64 &sec, 68 relocation_info rel) const { 69 auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart()); 70 const uint8_t *loc = buf + sec.offset + rel.r_address; 71 72 switch (rel.r_length) { 73 case 2: 74 return read32le(loc); 75 case 3: 76 return read64le(loc); 77 default: 78 llvm_unreachable("invalid r_length"); 79 } 80 } 81 82 void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value, 83 uint64_t pc) const { 84 value += r.addend; 85 if (r.pcrel) 86 value -= (pc + 4); 87 switch (r.length) { 88 case 2: 89 write32le(loc, value); 90 break; 91 case 3: 92 write64le(loc, value); 93 break; 94 default: 95 llvm_unreachable("invalid r_length"); 96 } 97 } 98 99 // The following methods emit a number of assembly sequences with RIP-relative 100 // addressing. Note that RIP-relative addressing on X86-64 has the RIP pointing 101 // to the next instruction, not the current instruction, so we always have to 102 // account for the current instruction's size when calculating offsets. 103 // writeRipRelative helps with that. 104 // 105 // bufAddr: The virtual address corresponding to buf[0]. 106 // bufOff: The offset within buf of the next instruction. 107 // destAddr: The destination address that the current instruction references. 108 static void writeRipRelative(uint8_t *buf, uint64_t bufAddr, uint64_t bufOff, 109 uint64_t destAddr) { 110 uint64_t rip = bufAddr + bufOff; 111 // For the instructions we care about, the RIP-relative address is always 112 // stored in the last 4 bytes of the instruction. 113 write32le(buf + bufOff - 4, destAddr - rip); 114 } 115 116 static constexpr uint8_t stub[] = { 117 0xff, 0x25, 0, 0, 0, 0, // jmpq *__la_symbol_ptr(%rip) 118 }; 119 120 void X86_64::writeStub(uint8_t *buf, const macho::Symbol &sym) const { 121 memcpy(buf, stub, 2); // just copy the two nonzero bytes 122 uint64_t stubAddr = in.stubs->addr + sym.stubsIndex * sizeof(stub); 123 writeRipRelative(buf, stubAddr, sizeof(stub), 124 in.lazyPointers->addr + sym.stubsIndex * WordSize); 125 } 126 127 static constexpr uint8_t stubHelperHeader[] = { 128 0x4c, 0x8d, 0x1d, 0, 0, 0, 0, // 0x0: leaq ImageLoaderCache(%rip), %r11 129 0x41, 0x53, // 0x7: pushq %r11 130 0xff, 0x25, 0, 0, 0, 0, // 0x9: jmpq *dyld_stub_binder@GOT(%rip) 131 0x90, // 0xf: nop 132 }; 133 134 void X86_64::writeStubHelperHeader(uint8_t *buf) const { 135 memcpy(buf, stubHelperHeader, sizeof(stubHelperHeader)); 136 writeRipRelative(buf, in.stubHelper->addr, 7, in.imageLoaderCache->getVA()); 137 writeRipRelative(buf, in.stubHelper->addr, 0xf, 138 in.got->addr + 139 in.stubHelper->stubBinder->gotIndex * WordSize); 140 } 141 142 static constexpr uint8_t stubHelperEntry[] = { 143 0x68, 0, 0, 0, 0, // 0x0: pushq <bind offset> 144 0xe9, 0, 0, 0, 0, // 0x5: jmp <__stub_helper> 145 }; 146 147 void X86_64::writeStubHelperEntry(uint8_t *buf, const DylibSymbol &sym, 148 uint64_t entryAddr) const { 149 memcpy(buf, stubHelperEntry, sizeof(stubHelperEntry)); 150 write32le(buf + 1, sym.lazyBindOffset); 151 writeRipRelative(buf, entryAddr, sizeof(stubHelperEntry), 152 in.stubHelper->addr); 153 } 154 155 void X86_64::relaxGotLoad(uint8_t *loc, uint8_t type) const { 156 // Convert MOVQ to LEAQ 157 if (loc[-2] != 0x8b) 158 error(getRelocAttrs(type).name + " reloc requires MOVQ instruction"); 159 loc[-2] = 0x8d; 160 } 161 162 X86_64::X86_64() { 163 cpuType = CPU_TYPE_X86_64; 164 cpuSubtype = CPU_SUBTYPE_X86_64_ALL; 165 166 stubSize = sizeof(stub); 167 stubHelperHeaderSize = sizeof(stubHelperHeader); 168 stubHelperEntrySize = sizeof(stubHelperEntry); 169 } 170 171 TargetInfo *macho::createX86_64TargetInfo() { 172 static X86_64 t; 173 return &t; 174 } 175