brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.3 KiB · 9d312a2 Raw
228 lines · cpp
1//===----- x86_64.cpp - Generic JITLink x86-64 edge kinds, utilities ------===//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// Generic utilities for graphs representing x86-64 objects.10//11//===----------------------------------------------------------------------===//12 13#include "llvm/ExecutionEngine/JITLink/x86_64.h"14 15#define DEBUG_TYPE "jitlink"16 17namespace llvm {18namespace jitlink {19namespace x86_64 {20 21const char *getEdgeKindName(Edge::Kind K) {22  switch (K) {23  case Pointer64:24    return "Pointer64";25  case Pointer32:26    return "Pointer32";27  case Pointer32Signed:28    return "Pointer32Signed";29  case Pointer16:30    return "Pointer16";31  case Pointer8:32    return "Pointer8";33  case Delta64:34    return "Delta64";35  case Delta32:36    return "Delta32";37  case Delta16:38    return "Delta16";39  case Delta8:40    return "Delta8";41  case NegDelta64:42    return "NegDelta64";43  case NegDelta32:44    return "NegDelta32";45  case Size64:46    return "Size64";47  case Size32:48    return "Size32";49  case Delta64FromGOT:50    return "Delta64FromGOT";51  case PCRel32:52    return "PCRel32";53  case BranchPCRel32:54    return "BranchPCRel32";55  case BranchPCRel32ToPtrJumpStub:56    return "BranchPCRel32ToPtrJumpStub";57  case BranchPCRel32ToPtrJumpStubBypassable:58    return "BranchPCRel32ToPtrJumpStubBypassable";59  case RequestGOTAndTransformToDelta32:60    return "RequestGOTAndTransformToDelta32";61  case RequestGOTAndTransformToDelta64:62    return "RequestGOTAndTransformToDelta64";63  case RequestGOTAndTransformToDelta64FromGOT:64    return "RequestGOTAndTransformToDelta64FromGOT";65  case PCRel32GOTLoadREXRelaxable:66    return "PCRel32GOTLoadREXRelaxable";67  case RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable:68    return "RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable";69  case PCRel32GOTLoadRelaxable:70    return "PCRel32GOTLoadRelaxable";71  case RequestGOTAndTransformToPCRel32GOTLoadRelaxable:72    return "RequestGOTAndTransformToPCRel32GOTLoadRelaxable";73  case PCRel32TLVPLoadREXRelaxable:74    return "PCRel32TLVPLoadREXRelaxable";75  case RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable:76    return "RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable";77  default:78    return getGenericEdgeKindName(K);79  }80}81 82const char NullPointerContent[PointerSize] = {0x00, 0x00, 0x00, 0x00,83                                              0x00, 0x00, 0x00, 0x00};84 85const char PointerJumpStubContent[6] = {86    static_cast<char>(0xFFu), 0x25, 0x00, 0x00, 0x00, 0x00};87 88const char ReentryTrampolineContent[5] = {89  static_cast<char>(0xe8), 0x00, 0x00, 0x00, 0x0090};91 92void GOTTableManager::registerExistingEntries() {93  for (auto *EntrySym : GOTSection->symbols()) {94    assert(EntrySym->getBlock().edges_size() == 1 &&95           "GOT block edge count != 1");96    registerPreExistingEntry(EntrySym->getBlock().edges().begin()->getTarget(),97                             *EntrySym);98  }99}100 101void PLTTableManager::registerExistingEntries() {102  for (auto *EntrySym : StubsSection->symbols()) {103    assert(EntrySym->getBlock().edges_size() == 1 &&104           "PLT block edge count != 1");105    auto &GOTSym = EntrySym->getBlock().edges().begin()->getTarget();106    assert(GOTSym.getBlock().edges_size() == 1 && "GOT block edge count != 1");107    registerPreExistingEntry(GOTSym.getBlock().edges().begin()->getTarget(),108                             *EntrySym);109  }110}111 112Error optimizeGOTAndStubAccesses(LinkGraph &G) {113  LLVM_DEBUG(dbgs() << "Optimizing GOT entries and stubs:\n");114 115  for (auto *B : G.blocks())116    for (auto &E : B->edges()) {117      if (E.getKind() == x86_64::PCRel32GOTLoadRelaxable ||118          E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable) {119#ifndef NDEBUG120        bool REXPrefix = E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable;121        assert(E.getOffset() >= (REXPrefix ? 3u : 2u) &&122               "GOT edge occurs too early in block");123#endif124        auto *FixupData = reinterpret_cast<uint8_t *>(125                              const_cast<char *>(B->getContent().data())) +126                          E.getOffset();127        const uint8_t Op = FixupData[-2];128        const uint8_t ModRM = FixupData[-1];129 130        auto &GOTEntryBlock = E.getTarget().getBlock();131        assert(GOTEntryBlock.getSize() == G.getPointerSize() &&132               "GOT entry block should be pointer sized");133        assert(GOTEntryBlock.edges_size() == 1 &&134               "GOT entry should only have one outgoing edge");135        auto &GOTTarget = GOTEntryBlock.edges().begin()->getTarget();136        orc::ExecutorAddr TargetAddr = GOTTarget.getAddress();137        orc::ExecutorAddr EdgeAddr = B->getFixupAddress(E);138        int64_t Displacement = TargetAddr - EdgeAddr + 4;139        bool TargetInRangeForImmU32 = isUInt<32>(TargetAddr.getValue());140        bool DisplacementInRangeForImmS32 = isInt<32>(Displacement);141 142        // If both of the Target and displacement is out of range, then143        // there isn't optimization chance.144        if (!(TargetInRangeForImmU32 || DisplacementInRangeForImmS32))145          continue;146 147        // Transform "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg".148        if (Op == 0x8b && DisplacementInRangeForImmS32) {149          FixupData[-2] = 0x8d;150          E.setKind(x86_64::Delta32);151          E.setTarget(GOTTarget);152          E.setAddend(E.getAddend() - 4);153          LLVM_DEBUG({154            dbgs() << "  Replaced GOT load wih LEA:\n    ";155            printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind()));156            dbgs() << "\n";157          });158          continue;159        }160 161        // Transform call/jmp instructions162        if (Op == 0xff && TargetInRangeForImmU32) {163          if (ModRM == 0x15) {164            // ABI says we can convert "call *foo@GOTPCREL(%rip)" to "nop; call165            // foo" But lld convert it to "addr32 call foo, because that makes166            // result expression to be a single instruction.167            FixupData[-2] = 0x67;168            FixupData[-1] = 0xe8;169            LLVM_DEBUG({170              dbgs() << "  replaced call instruction's memory operand wih imm "171                        "operand:\n    ";172              printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind()));173              dbgs() << "\n";174            });175          } else {176            // Transform "jmp *foo@GOTPCREL(%rip)" to "jmp foo; nop"177            assert(ModRM == 0x25 && "Invalid ModRm for call/jmp instructions");178            FixupData[-2] = 0xe9;179            FixupData[3] = 0x90;180            E.setOffset(E.getOffset() - 1);181            LLVM_DEBUG({182              dbgs() << "  replaced jmp instruction's memory operand wih imm "183                        "operand:\n    ";184              printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind()));185              dbgs() << "\n";186            });187          }188          E.setKind(x86_64::Pointer32);189          E.setTarget(GOTTarget);190          continue;191        }192      } else if (E.getKind() == x86_64::BranchPCRel32ToPtrJumpStubBypassable) {193        auto &StubBlock = E.getTarget().getBlock();194        assert(StubBlock.getSize() == sizeof(PointerJumpStubContent) &&195               "Stub block should be stub sized");196        assert(StubBlock.edges_size() == 1 &&197               "Stub block should only have one outgoing edge");198 199        auto &GOTBlock = StubBlock.edges().begin()->getTarget().getBlock();200        assert(GOTBlock.getSize() == G.getPointerSize() &&201               "GOT block should be pointer sized");202        assert(GOTBlock.edges_size() == 1 &&203               "GOT block should only have one outgoing edge");204 205        auto &GOTTarget = GOTBlock.edges().begin()->getTarget();206        orc::ExecutorAddr EdgeAddr = B->getAddress() + E.getOffset();207        orc::ExecutorAddr TargetAddr = GOTTarget.getAddress();208 209        int64_t Displacement = TargetAddr - EdgeAddr + 4;210        if (isInt<32>(Displacement)) {211          E.setKind(x86_64::BranchPCRel32);212          E.setTarget(GOTTarget);213          LLVM_DEBUG({214            dbgs() << "  Replaced stub branch with direct branch:\n    ";215            printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind()));216            dbgs() << "\n";217          });218        }219      }220    }221 222  return Error::success();223}224 225} // end namespace x86_64226} // end namespace jitlink227} // end namespace llvm228