text: //===--- Relocation.cpp  - Interface for object file relocations ----------===//
  //
  //                     The LLVM Compiler Infrastructure
  //
  // This file is distributed under the University of Illinois Open Source
  // License. See LICENSE.TXT for details.
  //
  //===----------------------------------------------------------------------===//
  //
  //===----------------------------------------------------------------------===//
  
  #include "Relocation.h"
  #include "llvm/MC/MCContext.h"
  #include "llvm/MC/MCStreamer.h"
  
  using namespace llvm;
  using namespace bolt;
  
  Triple::ArchType Relocation::Arch;
  
  namespace {
  
  bool isSupportedX86(uint64_t Type) {
    switch (Type) {
    default:
      return false;
    case ELF::R_X86_64_8:
    case ELF::R_X86_64_16:
    case ELF::R_X86_64_32:
    case ELF::R_X86_64_32S:
    case ELF::R_X86_64_64:
    case ELF::R_X86_64_PC8:
    case ELF::R_X86_64_PC32:
    case ELF::R_X86_64_PC64:
    case ELF::R_X86_64_PLT32:
    case ELF::R_X86_64_GOTPCREL:
    case ELF::R_X86_64_GOTTPOFF:
    case ELF::R_X86_64_TPOFF32:
    case ELF::R_X86_64_GOTPCRELX:
    case ELF::R_X86_64_REX_GOTPCRELX:
      return true;
    }
  }
  
  bool isSupportedAArch64(uint64_t Type) {
    switch (Type) {
    default:
      return false;
    case ELF::R_AARCH64_CALL26:
    case ELF::R_AARCH64_ADR_PREL_PG_HI21:
    case ELF::R_AARCH64_LDST64_ABS_LO12_NC:
    case ELF::R_AARCH64_ADD_ABS_LO12_NC:
    case ELF::R_AARCH64_LDST128_ABS_LO12_NC:
    case ELF::R_AARCH64_LDST32_ABS_LO12_NC:
    case ELF::R_AARCH64_LDST16_ABS_LO12_NC:
    case ELF::R_AARCH64_LDST8_ABS_LO12_NC:
    case ELF::R_AARCH64_ADR_GOT_PAGE:
    case ELF::R_AARCH64_TLSDESC_ADR_PAGE21:
    case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
    case ELF::R_AARCH64_TLSLE_ADD_TPREL_HI12:
    case ELF::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
    case ELF::R_AARCH64_LD64_GOT_LO12_NC:
    case ELF::R_AARCH64_TLSDESC_LD64_LO12:
    case ELF::R_AARCH64_TLSDESC_ADD_LO12:
    case ELF::R_AARCH64_TLSDESC_CALL:
    case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
    case ELF::R_AARCH64_JUMP26:
    case ELF::R_AARCH64_PREL32:
    case ELF::R_AARCH64_ABS64:
      return true;
    }
  }
  
  size_t getSizeForTypeX86(uint64_t Type) {
    switch (Type) {
    default:
      llvm_unreachable("unsupported relocation type");
    case ELF::R_X86_64_8:
    case ELF::R_X86_64_PC8:
      return 1;
    case ELF::R_X86_64_16:
      return 2;
    case ELF::R_X86_64_PLT32:
    case ELF::R_X86_64_PC32:
    case ELF::R_X86_64_32S:
    case ELF::R_X86_64_32:
    case ELF::R_X86_64_GOTPCREL:
    case ELF::R_X86_64_GOTTPOFF:
    case ELF::R_X86_64_TPOFF32:
    case ELF::R_X86_64_GOTPCRELX:
    case ELF::R_X86_64_REX_GOTPCRELX:
      return 4;
    case ELF::R_X86_64_PC64:
    case ELF::R_X86_64_64:
      return 8;
    }
  }
  
  size_t getSizeForTypeAArch64(uint64_t Type) {
    switch (Type) {
    default:
      dbgs() << "Reloc num: " << Type << "\n";
      llvm_unreachable("unsupported relocation type");
    case ELF::R_AARCH64_CALL26:
    case ELF::R_AARCH64_ADR_PREL_PG_HI21:
    case ELF::R_AARCH64_LDST64_ABS_LO12_NC:
    case ELF::R_AARCH64_ADD_ABS_LO12_NC:
    case ELF::R_AARCH64_LDST128_ABS_LO12_NC:
    case ELF::R_AARCH64_LDST32_ABS_LO12_NC:
    case ELF::R_AARCH64_LDST16_ABS_LO12_NC:
    case ELF::R_AARCH64_LDST8_ABS_LO12_NC:
    case ELF::R_AARCH64_ADR_GOT_PAGE:
    case ELF::R_AARCH64_TLSDESC_ADR_PAGE21:
    case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
    case ELF::R_AARCH64_TLSLE_ADD_TPREL_HI12:
    case ELF::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
    case ELF::R_AARCH64_LD64_GOT_LO12_NC:
    case ELF::R_AARCH64_TLSDESC_LD64_LO12:
    case ELF::R_AARCH64_TLSDESC_ADD_LO12:
    case ELF::R_AARCH64_TLSDESC_CALL:
    case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
    case ELF::R_AARCH64_JUMP26:
    case ELF::R_AARCH64_PREL32:
      return 4;
    case ELF::R_AARCH64_ABS64:
      return 8;
    }
  }
  
  uint64_t extractValueX86(uint64_t Type, uint64_t Contents, uint64_t PC) {
    if (Type == ELF::R_X86_64_32S)
      return SignExtend64<32>(Contents & 0xffffffff);
    return Contents;
  }
  
  uint64_t extractValueAArch64(uint64_t Type, uint64_t Contents, uint64_t PC) {
    switch (Type) {
    default:
      llvm_unreachable("unsupported relocation type");
    case ELF::R_AARCH64_ABS64:
      return Contents;
    case ELF::R_AARCH64_PREL32:
      return static_cast<int64_t>(PC) + SignExtend64<32>(Contents & 0xffffffff);
    case ELF::R_AARCH64_TLSDESC_CALL:
    case ELF::R_AARCH64_JUMP26:
    case ELF::R_AARCH64_CALL26:
      // Immediate goes in bits 25:0 of B and BL.
      Contents &= ~0xfffffffffc000000ULL;
      return static_cast<int64_t>(PC) + SignExtend64<28>(Contents << 2);
    case ELF::R_AARCH64_ADR_GOT_PAGE:
    case ELF::R_AARCH64_TLSDESC_ADR_PAGE21:
    case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
    case ELF::R_AARCH64_ADR_PREL_PG_HI21: {
      // Bits 32:12 of Symbol address goes in bits 30:29 + 23:5 of ADRP
      // instruction
      Contents &= ~0xffffffff9f00001fUll;
      auto LowBits = (Contents >> 29) & 0x3;
      auto HighBits = (Contents >> 5) & 0x7ffff;
      Contents = LowBits | (HighBits << 2);
      Contents = static_cast<int64_t>(PC) + SignExtend64<32>(Contents << 12);
      Contents &= ~0xfffUll;
      return Contents;
    }
    case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
    case ELF::R_AARCH64_TLSDESC_LD64_LO12:
    case ELF::R_AARCH64_LD64_GOT_LO12_NC:
    case ELF::R_AARCH64_LDST64_ABS_LO12_NC: {
      // Immediate goes in bits 21:10 of LD/ST instruction, taken
      // from bits 11:3 of Symbol address
      Contents &= ~0xffffffffffc003ffU;
      return Contents >> (10 - 3);
    }
    case ELF::R_AARCH64_TLSLE_ADD_TPREL_HI12:
    case ELF::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
    case ELF::R_AARCH64_TLSDESC_ADD_LO12:
    case ELF::R_AARCH64_ADD_ABS_LO12_NC: {
      // Immediate goes in bits 21:10 of ADD instruction
      Contents &= ~0xffffffffffc003ffU;
      return Contents >> (10 - 0);
    }
    case ELF::R_AARCH64_LDST128_ABS_LO12_NC: {
      // Immediate goes in bits 21:10 of ADD instruction, taken
      // from bits 11:4 of Symbol address
      Contents &= ~0xffffffffffc003ffU;
      return Contents >> (10 - 4);
    }
    case ELF::R_AARCH64_LDST32_ABS_LO12_NC: {
      // Immediate goes in bits 21:10 of ADD instruction, taken
      // from bits 11:2 of Symbol address
      Contents &= ~0xffffffffffc003ffU;
      return Contents >> (10 - 2);
    }
    case ELF::R_AARCH64_LDST16_ABS_LO12_NC: {
      // Immediate goes in bits 21:10 of ADD instruction, taken
      // from bits 11:1 of Symbol address
      Contents &= ~0xffffffffffc003ffU;
      return Contents >> (10 - 1);
    }
    case ELF::R_AARCH64_LDST8_ABS_LO12_NC: {
      // Immediate goes in bits 21:10 of ADD instruction, taken
      // from bits 11:0 of Symbol address
      Contents &= ~0xffffffffffc003ffU;
      return Contents >> (10 - 0);
    }
    }
  }
  
  bool isGOTX86(uint64_t Type) {
    switch (Type) {
    default:
      return false;
    case ELF::R_X86_64_GOT32:
    case ELF::R_X86_64_GOTPCREL:
    case ELF::R_X86_64_GOTTPOFF:
    case ELF::R_X86_64_GOTOFF64:
    case ELF::R_X86_64_GOTPC32:
    case ELF::R_X86_64_GOT64:
    case ELF::R_X86_64_GOTPCREL64:
    case ELF::R_X86_64_GOTPC64:
    case ELF::R_X86_64_GOTPLT64:
    case ELF::R_X86_64_GOTPC32_TLSDESC:
    case ELF::R_X86_64_GOTPCRELX:
    case ELF::R_X86_64_REX_GOTPCRELX:
      return true;
    }
  }
  
  bool isGOTAArch64(uint64_t Type) {
    switch (Type) {
    default:
      return false;
    case ELF::R_AARCH64_ADR_GOT_PAGE:
    case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
    case ELF::R_AARCH64_LD64_GOT_LO12_NC:
    case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
    case ELF::R_AARCH64_TLSLE_ADD_TPREL_HI12:
    case ELF::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
    case ELF::R_AARCH64_TLSDESC_ADR_PAGE21:
    case ELF::R_AARCH64_TLSDESC_LD64_LO12:
    case ELF::R_AARCH64_TLSDESC_ADD_LO12:
    case ELF::R_AARCH64_TLSDESC_CALL:
      return true;
    }
  }
  
  bool isTLSX86(uint64_t Type) {
    switch (Type) {
    default:
      return false;
    case ELF::R_X86_64_TPOFF32:
    case ELF::R_X86_64_TPOFF64:
    case ELF::R_X86_64_GOTTPOFF:
      return true;
    }
  }
  
  bool isTLSAArch64(uint64_t Type) {
    switch (Type) {
    default:
      return false;
    case ELF::R_AARCH64_TLSDESC_ADR_PAGE21:
    case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
    case ELF::R_AARCH64_TLSLE_ADD_TPREL_HI12:
    case ELF::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
    case ELF::R_AARCH64_TLSDESC_LD64_LO12:
    case ELF::R_AARCH64_TLSDESC_ADD_LO12:
    case ELF::R_AARCH64_TLSDESC_CALL:
    case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
      return true;
    }
  }
  
  bool isPCRelativeX86(uint64_t Type) {
    switch (Type) {
    default:
      llvm_unreachable("Unknown relocation type");
    case ELF::R_X86_64_64:
    case ELF::R_X86_64_32:
    case ELF::R_X86_64_32S:
    case ELF::R_X86_64_16:
    case ELF::R_X86_64_8:
    case ELF::R_X86_64_TPOFF32:
      return false;
    case ELF::R_X86_64_PC8:
    case ELF::R_X86_64_PC32:
    case ELF::R_X86_64_PC64:
    case ELF::R_X86_64_GOTPCREL:
    case ELF::R_X86_64_PLT32:
    case ELF::R_X86_64_GOTTPOFF:
    case ELF::R_X86_64_GOTPCRELX:
    case ELF::R_X86_64_REX_GOTPCRELX:
      return true;
    }
  }
  
  bool isPCRelativeAArch64(uint64_t Type) {
    switch (Type) {
    default:
      llvm_unreachable("Unknown relocation type");
    case ELF::R_AARCH64_ABS64:
    case ELF::R_AARCH64_LDST64_ABS_LO12_NC:
    case ELF::R_AARCH64_ADD_ABS_LO12_NC:
    case ELF::R_AARCH64_LDST128_ABS_LO12_NC:
    case ELF::R_AARCH64_LDST32_ABS_LO12_NC:
    case ELF::R_AARCH64_LDST16_ABS_LO12_NC:
    case ELF::R_AARCH64_LDST8_ABS_LO12_NC:
    case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
    case ELF::R_AARCH64_TLSLE_ADD_TPREL_HI12:
    case ELF::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
    case ELF::R_AARCH64_LD64_GOT_LO12_NC:
    case ELF::R_AARCH64_TLSDESC_LD64_LO12:
    case ELF::R_AARCH64_TLSDESC_ADD_LO12:
      return false;
    case ELF::R_AARCH64_TLSDESC_CALL:
    case ELF::R_AARCH64_CALL26:
    case ELF::R_AARCH64_ADR_PREL_PG_HI21:
    case ELF::R_AARCH64_ADR_GOT_PAGE:
    case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
    case ELF::R_AARCH64_TLSDESC_ADR_PAGE21:
    case ELF::R_AARCH64_JUMP26:
    case ELF::R_AARCH64_PREL32:
      return true;
    }
  }
  
  } // end anonymous namespace
  
  bool Relocation::isSupported(uint64_t Type) {
    if (Arch == Triple::aarch64)
      return isSupportedAArch64(Type);
    return isSupportedX86(Type);
  }
  
  size_t Relocation::getSizeForType(uint64_t Type) {
    if (Arch == Triple::aarch64)
      return getSizeForTypeAArch64(Type);
    return getSizeForTypeX86(Type);
  }
  
  uint64_t Relocation::extractValue(uint64_t Type, uint64_t Contents,
                                    uint64_t PC) {
    if (Arch == Triple::aarch64)
      return extractValueAArch64(Type, Contents, PC);
    return extractValueX86(Type, Contents, PC);
  }
  
  bool Relocation::isGOT(uint64_t Type) {
    if (Arch == Triple::aarch64)
      return isGOTAArch64(Type);
    return isGOTX86(Type);
  }
  
  bool Relocation::isNone(uint64_t Type) {
    if (Arch == Triple::aarch64)
      return Type == ELF::R_AARCH64_NONE;
    return Type == ELF::R_X86_64_NONE;
  }
  
  bool Relocation::isTLS(uint64_t Type) {
    if (Arch == Triple::aarch64)
      return isTLSAArch64(Type);
    return isTLSX86(Type);
  }
  
  bool Relocation::isPCRelative(uint64_t Type) {
    if (Arch == Triple::aarch64)
      return isPCRelativeAArch64(Type);
    return isPCRelativeX86(Type);
  }
  
  uint64_t Relocation::getPC32() {
    if (Arch == Triple::aarch64)
      return ELF::R_AARCH64_PREL32;
    return ELF::R_X86_64_PC32;
  }
  
  uint64_t Relocation::getPC64() {
    if (Arch == Triple::aarch64)
      return ELF::R_AARCH64_PREL64;
    return ELF::R_X86_64_PC64;
  }
  
  size_t Relocation::emit(MCStreamer *Streamer) const {
    const auto Size = getSizeForType(Type);
    auto &Ctx = Streamer->getContext();
    if (isPCRelative(Type)) {
      auto *TempLabel = Ctx.createTempSymbol();
      Streamer->EmitLabel(TempLabel);
      const MCExpr *Value{nullptr};
      if (Symbol) {
        Value = MCSymbolRefExpr::create(Symbol, Ctx);
        if (Addend) {
          Value = MCBinaryExpr::createAdd(Value,
                                          MCConstantExpr::create(Addend, Ctx),
                                          Ctx);
        }
      } else {
        Value = MCConstantExpr::create(Addend, Ctx);
      }
      Value = MCBinaryExpr::createSub(Value,
                                      MCSymbolRefExpr::create(TempLabel, Ctx),
                                      Ctx);
      Streamer->EmitValue(Value, Size);
  
      return Size;
    }
  
    if (Symbol && Addend) {
      auto Value = MCBinaryExpr::createAdd(MCSymbolRefExpr::create(Symbol, Ctx),
                                           MCConstantExpr::create(Addend, Ctx),
                                           Ctx);
      Streamer->EmitValue(Value, Size);
    } else if (Symbol) {
      Streamer->EmitSymbolValue(Symbol, Size);
    } else {
      Streamer->EmitIntValue(Addend, Size);
    }
  
    return Size;
  }
  
  #define ELF_RELOC(name, value) #name,
  
  void Relocation::print(raw_ostream &OS) const {
    static const char *X86RelocNames[] = {
  #include "llvm/BinaryFormat/ELFRelocs/x86_64.def"
    };
    static const char *AArch64RelocNames[] = {
  #include "llvm/BinaryFormat/ELFRelocs/AArch64.def"
    };
    if (Arch == Triple::aarch64)
      OS << AArch64RelocNames[Type];
    else
      OS << X86RelocNames[Type];
    OS << ", 0x" << Twine::utohexstr(Offset);
    if (Symbol) {
      OS << ", " << Symbol->getName();
    }
    if (int64_t(Addend) < 0)
      OS << ", -0x" << Twine::utohexstr(-int64_t(Addend));
    else
      OS << ", 0x" << Twine::utohexstr(Addend);
    OS << ", 0x" << Twine::utohexstr(Value);
  }
  