Path: blob/main/contrib/llvm-project/llvm/lib/ExecutionEngine/JITLink/x86_64.cpp
35271 views
//===----- x86_64.cpp - Generic JITLink x86-64 edge kinds, utilities ------===//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//===----------------------------------------------------------------------===//7//8// Generic utilities for graphs representing x86-64 objects.9//10//===----------------------------------------------------------------------===//1112#include "llvm/ExecutionEngine/JITLink/x86_64.h"1314#define DEBUG_TYPE "jitlink"1516namespace llvm {17namespace jitlink {18namespace x86_64 {1920const char *getEdgeKindName(Edge::Kind K) {21switch (K) {22case Pointer64:23return "Pointer64";24case Pointer32:25return "Pointer32";26case Pointer32Signed:27return "Pointer32Signed";28case Pointer16:29return "Pointer16";30case Pointer8:31return "Pointer8";32case Delta64:33return "Delta64";34case Delta32:35return "Delta32";36case Delta8:37return "Delta8";38case NegDelta64:39return "NegDelta64";40case NegDelta32:41return "NegDelta32";42case Delta64FromGOT:43return "Delta64FromGOT";44case PCRel32:45return "PCRel32";46case BranchPCRel32:47return "BranchPCRel32";48case BranchPCRel32ToPtrJumpStub:49return "BranchPCRel32ToPtrJumpStub";50case BranchPCRel32ToPtrJumpStubBypassable:51return "BranchPCRel32ToPtrJumpStubBypassable";52case RequestGOTAndTransformToDelta32:53return "RequestGOTAndTransformToDelta32";54case RequestGOTAndTransformToDelta64:55return "RequestGOTAndTransformToDelta64";56case RequestGOTAndTransformToDelta64FromGOT:57return "RequestGOTAndTransformToDelta64FromGOT";58case PCRel32GOTLoadREXRelaxable:59return "PCRel32GOTLoadREXRelaxable";60case RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable:61return "RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable";62case PCRel32GOTLoadRelaxable:63return "PCRel32GOTLoadRelaxable";64case RequestGOTAndTransformToPCRel32GOTLoadRelaxable:65return "RequestGOTAndTransformToPCRel32GOTLoadRelaxable";66case PCRel32TLVPLoadREXRelaxable:67return "PCRel32TLVPLoadREXRelaxable";68case RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable:69return "RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable";70default:71return getGenericEdgeKindName(static_cast<Edge::Kind>(K));72}73}7475const char NullPointerContent[PointerSize] = {0x00, 0x00, 0x00, 0x00,760x00, 0x00, 0x00, 0x00};7778const char PointerJumpStubContent[6] = {79static_cast<char>(0xFFu), 0x25, 0x00, 0x00, 0x00, 0x00};8081Error optimizeGOTAndStubAccesses(LinkGraph &G) {82LLVM_DEBUG(dbgs() << "Optimizing GOT entries and stubs:\n");8384for (auto *B : G.blocks())85for (auto &E : B->edges()) {86if (E.getKind() == x86_64::PCRel32GOTLoadRelaxable ||87E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable) {88#ifndef NDEBUG89bool REXPrefix = E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable;90assert(E.getOffset() >= (REXPrefix ? 3u : 2u) &&91"GOT edge occurs too early in block");92#endif93auto *FixupData = reinterpret_cast<uint8_t *>(94const_cast<char *>(B->getContent().data())) +95E.getOffset();96const uint8_t Op = FixupData[-2];97const uint8_t ModRM = FixupData[-1];9899auto &GOTEntryBlock = E.getTarget().getBlock();100assert(GOTEntryBlock.getSize() == G.getPointerSize() &&101"GOT entry block should be pointer sized");102assert(GOTEntryBlock.edges_size() == 1 &&103"GOT entry should only have one outgoing edge");104auto &GOTTarget = GOTEntryBlock.edges().begin()->getTarget();105orc::ExecutorAddr TargetAddr = GOTTarget.getAddress();106orc::ExecutorAddr EdgeAddr = B->getFixupAddress(E);107int64_t Displacement = TargetAddr - EdgeAddr + 4;108bool TargetInRangeForImmU32 = isUInt<32>(TargetAddr.getValue());109bool DisplacementInRangeForImmS32 = isInt<32>(Displacement);110111// If both of the Target and displacement is out of range, then112// there isn't optimization chance.113if (!(TargetInRangeForImmU32 || DisplacementInRangeForImmS32))114continue;115116// Transform "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg".117if (Op == 0x8b && DisplacementInRangeForImmS32) {118FixupData[-2] = 0x8d;119E.setKind(x86_64::Delta32);120E.setTarget(GOTTarget);121E.setAddend(E.getAddend() - 4);122LLVM_DEBUG({123dbgs() << " Replaced GOT load wih LEA:\n ";124printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind()));125dbgs() << "\n";126});127continue;128}129130// Transform call/jmp instructions131if (Op == 0xff && TargetInRangeForImmU32) {132if (ModRM == 0x15) {133// ABI says we can convert "call *foo@GOTPCREL(%rip)" to "nop; call134// foo" But lld convert it to "addr32 call foo, because that makes135// result expression to be a single instruction.136FixupData[-2] = 0x67;137FixupData[-1] = 0xe8;138LLVM_DEBUG({139dbgs() << " replaced call instruction's memory operand wih imm "140"operand:\n ";141printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind()));142dbgs() << "\n";143});144} else {145// Transform "jmp *foo@GOTPCREL(%rip)" to "jmp foo; nop"146assert(ModRM == 0x25 && "Invalid ModRm for call/jmp instructions");147FixupData[-2] = 0xe9;148FixupData[3] = 0x90;149E.setOffset(E.getOffset() - 1);150LLVM_DEBUG({151dbgs() << " replaced jmp instruction's memory operand wih imm "152"operand:\n ";153printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind()));154dbgs() << "\n";155});156}157E.setKind(x86_64::Pointer32);158E.setTarget(GOTTarget);159continue;160}161} else if (E.getKind() == x86_64::BranchPCRel32ToPtrJumpStubBypassable) {162auto &StubBlock = E.getTarget().getBlock();163assert(StubBlock.getSize() == sizeof(PointerJumpStubContent) &&164"Stub block should be stub sized");165assert(StubBlock.edges_size() == 1 &&166"Stub block should only have one outgoing edge");167168auto &GOTBlock = StubBlock.edges().begin()->getTarget().getBlock();169assert(GOTBlock.getSize() == G.getPointerSize() &&170"GOT block should be pointer sized");171assert(GOTBlock.edges_size() == 1 &&172"GOT block should only have one outgoing edge");173174auto &GOTTarget = GOTBlock.edges().begin()->getTarget();175orc::ExecutorAddr EdgeAddr = B->getAddress() + E.getOffset();176orc::ExecutorAddr TargetAddr = GOTTarget.getAddress();177178int64_t Displacement = TargetAddr - EdgeAddr + 4;179if (isInt<32>(Displacement)) {180E.setKind(x86_64::BranchPCRel32);181E.setTarget(GOTTarget);182LLVM_DEBUG({183dbgs() << " Replaced stub branch with direct branch:\n ";184printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind()));185dbgs() << "\n";186});187}188}189}190191return Error::success();192}193194} // end namespace x86_64195} // end namespace jitlink196} // end namespace llvm197198199