Path: blob/main/contrib/llvm-project/lld/MachO/Arch/X86_64.cpp
34889 views
//===- X86_64.cpp ---------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "InputFiles.h"9#include "Symbols.h"10#include "SyntheticSections.h"11#include "Target.h"1213#include "lld/Common/ErrorHandler.h"14#include "mach-o/compact_unwind_encoding.h"15#include "llvm/BinaryFormat/MachO.h"16#include "llvm/Support/Endian.h"1718using namespace llvm::MachO;19using namespace llvm::support::endian;20using namespace lld;21using namespace lld::macho;2223namespace {2425struct X86_64 : TargetInfo {26X86_64();2728int64_t getEmbeddedAddend(MemoryBufferRef, uint64_t offset,29const relocation_info) const override;30void relocateOne(uint8_t *loc, const Reloc &, uint64_t va,31uint64_t relocVA) const override;3233void writeStub(uint8_t *buf, const Symbol &,34uint64_t pointerVA) const override;35void writeStubHelperHeader(uint8_t *buf) const override;36void writeStubHelperEntry(uint8_t *buf, const Symbol &,37uint64_t entryAddr) const override;3839void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,40uint64_t &stubOffset, uint64_t selrefVA,41Symbol *objcMsgSend) const override;4243void relaxGotLoad(uint8_t *loc, uint8_t type) const override;44uint64_t getPageSize() const override { return 4 * 1024; }4546void handleDtraceReloc(const Symbol *sym, const Reloc &r,47uint8_t *loc) const override;48};49} // namespace5051static constexpr std::array<RelocAttrs, 10> relocAttrsArray{{52#define B(x) RelocAttrBits::x53{"UNSIGNED",54B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4) | B(BYTE8)},55{"SIGNED", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},56{"BRANCH", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},57{"GOT_LOAD", B(PCREL) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)},58{"GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)},59{"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)},60{"SIGNED_1", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},61{"SIGNED_2", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},62{"SIGNED_4", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},63{"TLV", B(PCREL) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)},64#undef B65}};6667static int pcrelOffset(uint8_t type) {68switch (type) {69case X86_64_RELOC_SIGNED_1:70return 1;71case X86_64_RELOC_SIGNED_2:72return 2;73case X86_64_RELOC_SIGNED_4:74return 4;75default:76return 0;77}78}7980int64_t X86_64::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,81relocation_info rel) const {82auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());83const uint8_t *loc = buf + offset + rel.r_address;8485switch (rel.r_length) {86case 2:87return static_cast<int32_t>(read32le(loc)) + pcrelOffset(rel.r_type);88case 3:89return read64le(loc) + pcrelOffset(rel.r_type);90default:91llvm_unreachable("invalid r_length");92}93}9495void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,96uint64_t relocVA) const {97if (r.pcrel) {98uint64_t pc = relocVA + 4 + pcrelOffset(r.type);99value -= pc;100}101102switch (r.length) {103case 2:104if (r.type == X86_64_RELOC_UNSIGNED)105checkUInt(loc, r, value, 32);106else107checkInt(loc, r, value, 32);108write32le(loc, value);109break;110case 3:111write64le(loc, value);112break;113default:114llvm_unreachable("invalid r_length");115}116}117118// The following methods emit a number of assembly sequences with RIP-relative119// addressing. Note that RIP-relative addressing on X86-64 has the RIP pointing120// to the next instruction, not the current instruction, so we always have to121// account for the current instruction's size when calculating offsets.122// writeRipRelative helps with that.123//124// bufAddr: The virtual address corresponding to buf[0].125// bufOff: The offset within buf of the next instruction.126// destAddr: The destination address that the current instruction references.127static void writeRipRelative(SymbolDiagnostic d, uint8_t *buf, uint64_t bufAddr,128uint64_t bufOff, uint64_t destAddr) {129uint64_t rip = bufAddr + bufOff;130checkInt(buf, d, destAddr - rip, 32);131// For the instructions we care about, the RIP-relative address is always132// stored in the last 4 bytes of the instruction.133write32le(buf + bufOff - 4, destAddr - rip);134}135136static constexpr uint8_t stub[] = {1370xff, 0x25, 0, 0, 0, 0, // jmpq *__la_symbol_ptr(%rip)138};139140void X86_64::writeStub(uint8_t *buf, const Symbol &sym,141uint64_t pointerVA) const {142memcpy(buf, stub, 2); // just copy the two nonzero bytes143uint64_t stubAddr = in.stubs->addr + sym.stubsIndex * sizeof(stub);144writeRipRelative({&sym, "stub"}, buf, stubAddr, sizeof(stub), pointerVA);145}146147static constexpr uint8_t stubHelperHeader[] = {1480x4c, 0x8d, 0x1d, 0, 0, 0, 0, // 0x0: leaq ImageLoaderCache(%rip), %r111490x41, 0x53, // 0x7: pushq %r111500xff, 0x25, 0, 0, 0, 0, // 0x9: jmpq *dyld_stub_binder@GOT(%rip)1510x90, // 0xf: nop152};153154void X86_64::writeStubHelperHeader(uint8_t *buf) const {155memcpy(buf, stubHelperHeader, sizeof(stubHelperHeader));156SymbolDiagnostic d = {nullptr, "stub helper header"};157writeRipRelative(d, buf, in.stubHelper->addr, 7,158in.imageLoaderCache->getVA());159writeRipRelative(d, buf, in.stubHelper->addr, 0xf,160in.got->addr +161in.stubHelper->stubBinder->gotIndex * LP64::wordSize);162}163164static constexpr uint8_t stubHelperEntry[] = {1650x68, 0, 0, 0, 0, // 0x0: pushq <bind offset>1660xe9, 0, 0, 0, 0, // 0x5: jmp <__stub_helper>167};168169void X86_64::writeStubHelperEntry(uint8_t *buf, const Symbol &sym,170uint64_t entryAddr) const {171memcpy(buf, stubHelperEntry, sizeof(stubHelperEntry));172write32le(buf + 1, sym.lazyBindOffset);173writeRipRelative({&sym, "stub helper"}, buf, entryAddr,174sizeof(stubHelperEntry), in.stubHelper->addr);175}176177static constexpr uint8_t objcStubsFastCode[] = {1780x48, 0x8b, 0x35, 0, 0, 0, 0, // 0x0: movq selrefs@selector(%rip), %rsi1790xff, 0x25, 0, 0, 0, 0, // 0x7: jmpq *_objc_msgSend@GOT(%rip)180};181182void X86_64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,183uint64_t &stubOffset, uint64_t selrefVA,184Symbol *objcMsgSend) const {185uint64_t objcMsgSendAddr = in.got->addr;186uint64_t objcMsgSendIndex = objcMsgSend->gotIndex;187188memcpy(buf, objcStubsFastCode, sizeof(objcStubsFastCode));189SymbolDiagnostic d = {sym, sym->getName()};190uint64_t stubAddr = stubsAddr + stubOffset;191writeRipRelative(d, buf, stubAddr, 7, selrefVA);192writeRipRelative(d, buf, stubAddr, 0xd,193objcMsgSendAddr + objcMsgSendIndex * LP64::wordSize);194stubOffset += target->objcStubsFastSize;195}196197void X86_64::relaxGotLoad(uint8_t *loc, uint8_t type) const {198// Convert MOVQ to LEAQ199if (loc[-2] != 0x8b)200error(getRelocAttrs(type).name + " reloc requires MOVQ instruction");201loc[-2] = 0x8d;202}203204X86_64::X86_64() : TargetInfo(LP64()) {205cpuType = CPU_TYPE_X86_64;206cpuSubtype = CPU_SUBTYPE_X86_64_ALL;207208modeDwarfEncoding = UNWIND_X86_MODE_DWARF;209subtractorRelocType = X86_64_RELOC_SUBTRACTOR;210unsignedRelocType = X86_64_RELOC_UNSIGNED;211212stubSize = sizeof(stub);213stubHelperHeaderSize = sizeof(stubHelperHeader);214stubHelperEntrySize = sizeof(stubHelperEntry);215216objcStubsFastSize = sizeof(objcStubsFastCode);217objcStubsFastAlignment = 1;218219relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()};220}221222TargetInfo *macho::createX86_64TargetInfo() {223static X86_64 t;224return &t;225}226227void X86_64::handleDtraceReloc(const Symbol *sym, const Reloc &r,228uint8_t *loc) const {229assert(r.type == X86_64_RELOC_BRANCH);230231if (config->outputType == MH_OBJECT)232return;233234if (sym->getName().starts_with("___dtrace_probe")) {235// change call site to a NOP236loc[-1] = 0x90;237write32le(loc, 0x00401F0F);238} else if (sym->getName().starts_with("___dtrace_isenabled")) {239// change call site to a clear eax240loc[-1] = 0x33;241write32le(loc, 0x909090C0);242} else {243error("Unrecognized dtrace symbol prefix: " + toString(*sym));244}245}246247248