263 lines · cpp
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-exception6//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 "mach-o/compact_unwind_encoding.h"16#include "llvm/BinaryFormat/MachO.h"17#include "llvm/Support/Endian.h"18 19using namespace llvm::MachO;20using namespace llvm::support::endian;21using namespace lld;22using namespace lld::macho;23 24namespace {25 26struct X86_64 : TargetInfo {27 X86_64();28 29 int64_t getEmbeddedAddend(MemoryBufferRef, uint64_t offset,30 const relocation_info) const override;31 void relocateOne(uint8_t *loc, const Reloc &, uint64_t va,32 uint64_t relocVA) const override;33 34 void writeStub(uint8_t *buf, const Symbol &,35 uint64_t pointerVA) const override;36 void writeStubHelperHeader(uint8_t *buf) const override;37 void writeStubHelperEntry(uint8_t *buf, const Symbol &,38 uint64_t entryAddr) const override;39 40 void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,41 uint64_t &stubOffset, uint64_t selrefVA,42 Symbol *objcMsgSend) const override;43 44 void relaxGotLoad(uint8_t *loc, uint8_t type) const override;45 uint64_t getPageSize() const override { return 4 * 1024; }46 47 void handleDtraceReloc(const Symbol *sym, const Reloc &r,48 uint8_t *loc) const override;49};50} // namespace51 52static constexpr std::array<RelocAttrs, 10> relocAttrsArray{{53#define B(x) RelocAttrBits::x54 {"UNSIGNED", B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE1) |55 B(BYTE4) | B(BYTE8)},56 {"SIGNED", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},57 {"BRANCH",58 B(PCREL) | B(EXTERN) | B(LOCAL) | B(BRANCH) | B(BYTE1) | B(BYTE4)},59 {"GOT_LOAD", B(PCREL) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)},60 {"GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)},61 {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)},62 {"SIGNED_1", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},63 {"SIGNED_2", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},64 {"SIGNED_4", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},65 {"TLV", B(PCREL) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)},66#undef B67}};68 69static int pcrelOffset(uint8_t type) {70 switch (type) {71 case X86_64_RELOC_SIGNED_1:72 return 1;73 case X86_64_RELOC_SIGNED_2:74 return 2;75 case X86_64_RELOC_SIGNED_4:76 return 4;77 default:78 return 0;79 }80}81 82int64_t X86_64::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,83 relocation_info rel) const {84 auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());85 const uint8_t *loc = buf + offset + rel.r_address;86 int64_t addend;87 88 switch (rel.r_length) {89 case 0:90 addend = static_cast<int8_t>(*loc);91 break;92 case 2:93 addend = static_cast<int32_t>(read32le(loc));94 break;95 case 3:96 addend = read64le(loc);97 break;98 default:99 llvm_unreachable("invalid r_length");100 }101 102 return addend + pcrelOffset(rel.r_type);103}104 105void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,106 uint64_t relocVA) const {107 if (r.pcrel) {108 uint64_t pc = relocVA + (1ull << r.length) + pcrelOffset(r.type);109 value -= pc;110 }111 112 switch (r.length) {113 case 0:114 if (r.type == X86_64_RELOC_UNSIGNED)115 checkUInt(loc, r, value, 8);116 else117 checkInt(loc, r, value, 8);118 *loc = value;119 break;120 case 2:121 if (r.type == X86_64_RELOC_UNSIGNED)122 checkUInt(loc, r, value, 32);123 else124 checkInt(loc, r, value, 32);125 write32le(loc, value);126 break;127 case 3:128 write64le(loc, value);129 break;130 default:131 llvm_unreachable("invalid r_length");132 }133}134 135// The following methods emit a number of assembly sequences with RIP-relative136// addressing. Note that RIP-relative addressing on X86-64 has the RIP pointing137// to the next instruction, not the current instruction, so we always have to138// account for the current instruction's size when calculating offsets.139// writeRipRelative helps with that.140//141// bufAddr: The virtual address corresponding to buf[0].142// bufOff: The offset within buf of the next instruction.143// destAddr: The destination address that the current instruction references.144static void writeRipRelative(SymbolDiagnostic d, uint8_t *buf, uint64_t bufAddr,145 uint64_t bufOff, uint64_t destAddr) {146 uint64_t rip = bufAddr + bufOff;147 checkInt(buf, d, destAddr - rip, 32);148 // For the instructions we care about, the RIP-relative address is always149 // stored in the last 4 bytes of the instruction.150 write32le(buf + bufOff - 4, destAddr - rip);151}152 153static constexpr uint8_t stub[] = {154 0xff, 0x25, 0, 0, 0, 0, // jmpq *__la_symbol_ptr(%rip)155};156 157void X86_64::writeStub(uint8_t *buf, const Symbol &sym,158 uint64_t pointerVA) const {159 memcpy(buf, stub, 2); // just copy the two nonzero bytes160 uint64_t stubAddr = in.stubs->addr + sym.stubsIndex * sizeof(stub);161 writeRipRelative({&sym, "stub"}, buf, stubAddr, sizeof(stub), pointerVA);162}163 164static constexpr uint8_t stubHelperHeader[] = {165 0x4c, 0x8d, 0x1d, 0, 0, 0, 0, // 0x0: leaq ImageLoaderCache(%rip), %r11166 0x41, 0x53, // 0x7: pushq %r11167 0xff, 0x25, 0, 0, 0, 0, // 0x9: jmpq *dyld_stub_binder@GOT(%rip)168 0x90, // 0xf: nop169};170 171void X86_64::writeStubHelperHeader(uint8_t *buf) const {172 memcpy(buf, stubHelperHeader, sizeof(stubHelperHeader));173 SymbolDiagnostic d = {nullptr, "stub helper header"};174 writeRipRelative(d, buf, in.stubHelper->addr, 7,175 in.imageLoaderCache->getVA());176 writeRipRelative(d, buf, in.stubHelper->addr, 0xf,177 in.got->addr +178 in.stubHelper->stubBinder->gotIndex * LP64::wordSize);179}180 181static constexpr uint8_t stubHelperEntry[] = {182 0x68, 0, 0, 0, 0, // 0x0: pushq <bind offset>183 0xe9, 0, 0, 0, 0, // 0x5: jmp <__stub_helper>184};185 186void X86_64::writeStubHelperEntry(uint8_t *buf, const Symbol &sym,187 uint64_t entryAddr) const {188 memcpy(buf, stubHelperEntry, sizeof(stubHelperEntry));189 write32le(buf + 1, sym.lazyBindOffset);190 writeRipRelative({&sym, "stub helper"}, buf, entryAddr,191 sizeof(stubHelperEntry), in.stubHelper->addr);192}193 194static constexpr uint8_t objcStubsFastCode[] = {195 0x48, 0x8b, 0x35, 0, 0, 0, 0, // 0x0: movq selrefs@selector(%rip), %rsi196 0xff, 0x25, 0, 0, 0, 0, // 0x7: jmpq *_objc_msgSend@GOT(%rip)197};198 199void X86_64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,200 uint64_t &stubOffset, uint64_t selrefVA,201 Symbol *objcMsgSend) const {202 uint64_t objcMsgSendAddr = in.got->addr;203 uint64_t objcMsgSendIndex = objcMsgSend->gotIndex;204 205 memcpy(buf, objcStubsFastCode, sizeof(objcStubsFastCode));206 SymbolDiagnostic d = {sym, sym->getName()};207 uint64_t stubAddr = stubsAddr + stubOffset;208 writeRipRelative(d, buf, stubAddr, 7, selrefVA);209 writeRipRelative(d, buf, stubAddr, 0xd,210 objcMsgSendAddr + objcMsgSendIndex * LP64::wordSize);211 stubOffset += target->objcStubsFastSize;212}213 214void X86_64::relaxGotLoad(uint8_t *loc, uint8_t type) const {215 // Convert MOVQ to LEAQ216 if (loc[-2] != 0x8b)217 error(getRelocAttrs(type).name + " reloc requires MOVQ instruction");218 loc[-2] = 0x8d;219}220 221X86_64::X86_64() : TargetInfo(LP64()) {222 cpuType = CPU_TYPE_X86_64;223 cpuSubtype = CPU_SUBTYPE_X86_64_ALL;224 225 modeDwarfEncoding = UNWIND_X86_MODE_DWARF;226 subtractorRelocType = X86_64_RELOC_SUBTRACTOR;227 unsignedRelocType = X86_64_RELOC_UNSIGNED;228 229 stubSize = sizeof(stub);230 stubHelperHeaderSize = sizeof(stubHelperHeader);231 stubHelperEntrySize = sizeof(stubHelperEntry);232 233 objcStubsFastSize = sizeof(objcStubsFastCode);234 objcStubsFastAlignment = 1;235 236 relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()};237}238 239TargetInfo *macho::createX86_64TargetInfo() {240 static X86_64 t;241 return &t;242}243 244void X86_64::handleDtraceReloc(const Symbol *sym, const Reloc &r,245 uint8_t *loc) const {246 assert(r.type == X86_64_RELOC_BRANCH);247 248 if (config->outputType == MH_OBJECT)249 return;250 251 if (sym->getName().starts_with("___dtrace_probe")) {252 // change call site to a NOP253 loc[-1] = 0x90;254 write32le(loc, 0x00401F0F);255 } else if (sym->getName().starts_with("___dtrace_isenabled")) {256 // change call site to a clear eax257 loc[-1] = 0x33;258 write32le(loc, 0x909090C0);259 } else {260 error("Unrecognized dtrace symbol prefix: " + toString(*sym));261 }262}263