[llvm] [BOLT] Move target-specific relocation logic into target handlers (PR #217926)

via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 22 02:08:03 PDT 2026


https://github.com/maksimra updated https://github.com/llvm/llvm-project/pull/217926

>From 82755fb2b5af3adbf2d6c43cc34640980fd5f481 Mon Sep 17 00:00:00 2001
From: Maksim <rachinskii.mv at phystech.edu>
Date: Thu, 20 Aug 2026 17:10:41 +0300
Subject: [PATCH 1/2] [BOLT] Introduce target-specific relocation handlers

This PR cleans up BOLT's target-specific relocation handling.
Move relocation logic directly into the X86, AArch64, and RISC-V handlers located in their corresponding lib/Target directories.
This removes the remaining target-specific relocation helpers from the generic Relocation.cpp implementation.
This is a behavior-preserving refactoring.

AI tools (codex) were used to assist with implementation.
---
 bolt/include/bolt/Core/BinaryContext.h        |    8 +
 bolt/include/bolt/Core/BinarySection.h        |    5 +-
 bolt/include/bolt/Core/Relocation.h           |  180 ++-
 bolt/lib/Core/BinaryContext.cpp               |   14 +-
 bolt/lib/Core/BinaryEmitter.cpp               |   14 +-
 bolt/lib/Core/BinaryFunction.cpp              |    8 +-
 bolt/lib/Core/BinarySection.cpp               |   57 +-
 bolt/lib/Core/CMakeLists.txt                  |    3 +
 bolt/lib/Core/Relocation.cpp                  | 1028 +----------------
 bolt/lib/Rewrite/LinuxKernelRewriter.cpp      |   10 +-
 bolt/lib/Rewrite/MachORewriteInstance.cpp     |    1 -
 bolt/lib/Rewrite/RSeqRewriter.cpp             |    2 +-
 bolt/lib/Rewrite/RewriteInstance.cpp          |   82 +-
 .../Target/AArch64/AArch64MCSymbolizer.cpp    |    5 +-
 .../AArch64/AArch64RelocationHandler.cpp      |  448 +++++++
 bolt/lib/Target/AArch64/CMakeLists.txt        |    3 +
 bolt/lib/Target/RISCV/CMakeLists.txt          |    3 +
 bolt/lib/Target/RISCV/RISCVMCSymbolizer.cpp   |   24 +-
 .../Target/RISCV/RISCVRelocationHandler.cpp   |  348 ++++++
 bolt/lib/Target/X86/CMakeLists.txt            |    3 +
 bolt/lib/Target/X86/X86MCSymbolizer.cpp       |   19 +-
 bolt/lib/Target/X86/X86RelocationHandler.cpp  |  224 ++++
 bolt/unittests/Core/BinaryContext.cpp         |   19 +-
 bolt/unittests/Core/CMakeLists.txt            |    1 +
 bolt/unittests/Core/MCPlusBuilder.cpp         |    1 -
 bolt/unittests/Core/MemoryMaps.cpp            |    1 -
 bolt/unittests/Core/Relocation.cpp            |   75 ++
 bolt/unittests/Passes/LivenessAnalysis.cpp    |    1 -
 bolt/unittests/Passes/PointerAuthCFIFixup.cpp |    1 -
 bolt/unittests/Profile/DataAggregator.cpp     |    1 -
 bolt/unittests/Profile/PerfScripts.cpp        |    1 -
 bolt/unittests/Profile/PerfSpeEvents.cpp      |    1 -
 32 files changed, 1387 insertions(+), 1204 deletions(-)
 create mode 100644 bolt/lib/Target/AArch64/AArch64RelocationHandler.cpp
 create mode 100644 bolt/lib/Target/RISCV/RISCVRelocationHandler.cpp
 create mode 100644 bolt/lib/Target/X86/X86RelocationHandler.cpp
 create mode 100644 bolt/unittests/Core/Relocation.cpp

diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h
index bee2b46e78cd1..38aaa128f96a4 100644
--- a/bolt/include/bolt/Core/BinaryContext.h
+++ b/bolt/include/bolt/Core/BinaryContext.h
@@ -730,6 +730,10 @@ class BinaryContext {
 
   std::unique_ptr<Triple> TheTriple;
 
+private:
+  std::unique_ptr<RelocationHandler> RelocHandler;
+
+public:
   std::shared_ptr<orc::SymbolStringPool> SSP;
 
   const Target *TheTarget;
@@ -987,6 +991,10 @@ class BinaryContext {
 
   bool isMachO() const { return TheTriple->isOSBinFormatMachO(); }
 
+  const RelocationHandler &getRelocationHandler() const {
+    return *RelocHandler;
+  }
+
   bool isAArch64() const {
     return TheTriple->getArch() == llvm::Triple::aarch64;
   }
diff --git a/bolt/include/bolt/Core/BinarySection.h b/bolt/include/bolt/Core/BinarySection.h
index 3e3ad8217ea71..ae907fea091f2 100644
--- a/bolt/include/bolt/Core/BinarySection.h
+++ b/bolt/include/bolt/Core/BinarySection.h
@@ -373,10 +373,7 @@ class BinarySection {
                                     JmpRelocationIndex});
   }
 
-  void addDynamicRelocation(const Relocation &Reloc) {
-    assert(Reloc.Offset < getSize() && "offset not within section bounds");
-    DynamicRelocations.emplace(Reloc);
-  }
+  void addDynamicRelocation(const Relocation &Reloc);
 
   /// Add relocation against the original contents of this section.
   void addPendingRelocation(const Relocation &Rel) {
diff --git a/bolt/include/bolt/Core/Relocation.h b/bolt/include/bolt/Core/Relocation.h
index 02d18ff97cf98..897bb0758a007 100644
--- a/bolt/include/bolt/Core/Relocation.h
+++ b/bolt/include/bolt/Core/Relocation.h
@@ -17,6 +17,7 @@
 #include "llvm/MC/MCExpr.h"
 #include "llvm/MC/MCStreamer.h"
 #include "llvm/TargetParser/Triple.h"
+#include <memory>
 
 namespace llvm {
 class MCSymbol;
@@ -34,6 +35,79 @@ enum { R_X86_64_converted_reloc_bit = 0x80 };
 
 namespace bolt {
 
+/// Target-specific relocation operations. One handler is owned by each
+/// BinaryContext, while Relocation remains a lightweight value type.
+class RelocationHandler {
+public:
+  virtual ~RelocationHandler() = default;
+
+  /// Check if \p Type is a supported relocation type.
+  virtual bool isSupported(uint32_t Type) const = 0;
+
+  /// Return size in bytes of the given relocation \p Type.
+  virtual size_t getSizeForType(uint32_t Type) const = 0;
+
+  /// Skip relocations that we don't want to handle in BOLT
+  virtual bool skipRelocationType(uint32_t Type) const = 0;
+
+  /// Adjust value depending on relocation type (make it PC relative or not).
+  virtual uint64_t encodeValue(uint32_t Type, uint64_t Value,
+                               uint64_t PC) const = 0;
+
+  /// Return true if there are enough bits to encode the relocation value.
+  virtual bool canEncodeValue(uint32_t Type, uint64_t Value,
+                              uint64_t PC) const = 0;
+
+  /// Extract current relocated value from binary contents. This is used for
+  /// RISC architectures where values are encoded in specific bits depending
+  /// on the relocation value. For X86, we limit to sign extending the value
+  /// if necessary.
+  virtual uint64_t extractValue(uint32_t Type, uint64_t Contents,
+                                uint64_t PC) const = 0;
+
+  /// Return true if relocation type implies the creation of a GOT entry
+  virtual bool isGOT(uint32_t Type) const = 0;
+
+  /// Return true if relocation type is NONE
+  bool isNone(uint32_t Type) const { return Type == getNone(); }
+
+  /// Return true if relocation type is RELATIVE
+  virtual bool isRelative(uint32_t Type) const = 0;
+
+  /// Return true if relocation type is IRELATIVE
+  virtual bool isIRelative(uint32_t Type) const = 0;
+
+  /// Return true if relocation type is for thread local storage.
+  virtual bool isTLS(uint32_t Type) const = 0;
+
+  /// Return true of relocation type is for referencing a specific instruction
+  /// (as opposed to a function, basic block, etc).
+  virtual bool isInstructionReference(uint32_t Type) const { return false; }
+
+  /// Return code for a NONE relocation
+  virtual uint32_t getNone() const = 0;
+
+  /// Return code for a PC-relative 4-byte relocation
+  virtual uint32_t getPC32() const = 0;
+
+  /// Return code for a PC-relative 8-byte relocation
+  virtual uint32_t getPC64() const = 0;
+
+  /// Return true if relocation type is PC-relative. Return false otherwise.
+  virtual bool isPCRelative(uint32_t Type) const = 0;
+
+  /// Return code for a ABS 8-byte relocation
+  virtual uint32_t getAbs64() const = 0;
+
+  /// Return code for a RELATIVE relocation
+  virtual uint32_t getRelative() const = 0;
+  virtual MCBinaryExpr::Opcode getComposeOpcodeFor(uint32_t Type) const;
+  virtual void printType(raw_ostream &OS, uint32_t Type) const = 0;
+};
+
+std::unique_ptr<RelocationHandler>
+createRelocationHandler(Triple::ArchType Arch);
+
 /// Relocation class.
 class Relocation {
 public:
@@ -44,17 +118,12 @@ class Relocation {
              uint32_t JmpRelocationIndex = NoJmpRelocationIndex)
       : Offset(Offset), Symbol(Symbol), Addend(Addend), Value(Value),
         Type(Type), JmpRelocationIndex(JmpRelocationIndex), Optional(false),
-        IsRELR(IsRELR) {
-    assert((isRelative() || !isRELR()) &&
-           "Only relative relocations can be relr.");
-  }
+        IsRELR(IsRELR) {}
 
   Relocation()
       : Offset(0), Symbol(0), Addend(0), Value(0), Type(0),
         JmpRelocationIndex(NoJmpRelocationIndex), Optional(0), IsRELR(0) {}
 
-  static Triple::ArchType Arch; /// set by BinaryContext ctor.
-
   /// The offset of this relocation in the object it is contained in.
   uint64_t Offset;
 
@@ -96,9 +165,6 @@ class Relocation {
     return JmpRelocationIndex;
   }
 
-  /// Return size in bytes of the given relocation \p Type.
-  static size_t getSizeForType(uint32_t Type);
-
   void setOptional() { Optional = true; }
 
   bool isOptional() { return Optional; }
@@ -106,91 +172,24 @@ class Relocation {
   bool isRELR() const { return IsRELR; }
 
   /// Return size of this relocation.
-  size_t getSize() const { return getSizeForType(Type); }
-
-  /// Skip relocations that we don't want to handle in BOLT
-  static bool skipRelocationType(uint32_t Type);
-
-  /// Adjust value depending on relocation type (make it PC relative or not).
-  static uint64_t encodeValue(uint32_t Type, uint64_t Value, uint64_t PC);
-
-  /// Return true if there are enough bits to encode the relocation value.
-  static bool canEncodeValue(uint32_t Type, uint64_t Value, uint64_t PC);
-
-  /// Extract current relocated value from binary contents. This is used for
-  /// RISC architectures where values are encoded in specific bits depending
-  /// on the relocation value. For X86, we limit to sign extending the value
-  /// if necessary.
-  static uint64_t extractValue(uint32_t Type, uint64_t Contents, uint64_t PC);
-
-  /// Return true if relocation type is PC-relative. Return false otherwise.
-  static bool isPCRelative(uint32_t Type);
-
-  /// Check if \p Type is a supported relocation type.
-  static bool isSupported(uint32_t Type);
-
-  /// Return true if relocation type implies the creation of a GOT entry
-  static bool isGOT(uint32_t Type);
-
-  /// Special relocation type that allows the linker to modify the instruction.
-  static bool isX86GOTPCRELX(uint32_t Type);
-  static bool isX86GOTPC64(uint32_t Type);
-
-  /// Return true if relocation type is NONE
-  static bool isNone(uint32_t Type);
-
-  /// Return true if relocation type is RELATIVE
-  static bool isRelative(uint32_t Type);
-
-  /// Return true if relocation type is IRELATIVE
-  static bool isIRelative(uint32_t Type);
-
-  /// Return true if relocation type is for thread local storage.
-  static bool isTLS(uint32_t Type);
-
-  /// Return true of relocation type is for referencing a specific instruction
-  /// (as opposed to a function, basic block, etc).
-  static bool isInstructionReference(uint32_t Type);
-
   /// Return the relocation type of \p Rel from llvm::object. It checks for
   /// overflows as BOLT uses 32 bits for the type.
   static uint32_t getType(const object::RelocationRef &Rel);
 
-  /// Return code for a NONE relocation
-  static uint32_t getNone();
-
-  /// Return code for a PC-relative 4-byte relocation
-  static uint32_t getPC32();
-
-  /// Return code for a PC-relative 8-byte relocation
-  static uint32_t getPC64();
-
-  /// Return code for a ABS 8-byte relocation
-  static uint32_t getAbs64();
-
-  /// Return code for a RELATIVE relocation
-  static uint32_t getRelative();
-
-  /// Return true if this relocation is PC-relative. Return false otherwise.
-  bool isPCRelative() const { return isPCRelative(Type); }
-
-  /// Return true if this relocation is R_*_RELATIVE type. Return false
-  /// otherwise.
-  bool isRelative() const { return isRelative(Type); }
-
-  /// Return true if this relocation is R_*_IRELATIVE type. Return false
-  /// otherwise.
-  bool isIRelative() const { return isIRelative(Type); }
+  size_t getSize(const RelocationHandler &RH) const {
+    return RH.getSizeForType(Type);
+  }
 
   /// Emit relocation at a current \p Streamer' position. The caller is
   /// responsible for setting the position correctly.
-  size_t emit(MCStreamer *Streamer) const;
+  size_t emit(MCStreamer *Streamer, const RelocationHandler &RH) const;
 
   /// Emit a group of composed relocations. All relocations must have the same
   /// offset. If std::distance(Begin, End) == 1, this is equivalent to
   /// Begin->emit(Streamer).
   template <typename RelocIt>
-  static size_t emit(RelocIt Begin, RelocIt End, MCStreamer *Streamer) {
+  static size_t emit(RelocIt Begin, RelocIt End, MCStreamer *Streamer,
+                     const RelocationHandler &RH) {
     if (Begin == End)
       return 0;
 
@@ -199,23 +198,23 @@ class Relocation {
     for (auto RI = Begin; RI != End; ++RI) {
       assert(RI->Offset == Begin->Offset &&
              "emitting composed relocations with different offsets");
-      Value = RI->createExpr(Streamer, Value);
+      Value = RI->createExpr(Streamer, Value, RH);
     }
 
     assert(Value && "failed to create relocation value");
-    auto Size = std::prev(End)->getSize();
+    auto Size = std::prev(End)->getSize(RH);
     Streamer->emitValue(Value, Size);
     return Size;
   }
 
   /// Print a relocation to \p OS.
-  void print(raw_ostream &OS) const;
+  void print(raw_ostream &OS, const RelocationHandler &RH) const;
 
 private:
-  const MCExpr *createExpr(MCStreamer *Streamer) const;
   const MCExpr *createExpr(MCStreamer *Streamer,
-                           const MCExpr *RetainedValue) const;
-  static MCBinaryExpr::Opcode getComposeOpcodeFor(uint32_t Type);
+                           const RelocationHandler &RH) const;
+  const MCExpr *createExpr(MCStreamer *Streamer, const MCExpr *RetainedValue,
+                           const RelocationHandler &RH) const;
 };
 
 /// Relocation ordering by offset.
@@ -227,11 +226,6 @@ inline bool operator<(const Relocation &A, uint64_t B) { return A.Offset < B; }
 
 inline bool operator<(uint64_t A, const Relocation &B) { return A < B.Offset; }
 
-inline raw_ostream &operator<<(raw_ostream &OS, const Relocation &Rel) {
-  Rel.print(OS);
-  return OS;
-}
-
 } // namespace bolt
 } // namespace llvm
 
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index ae320ab695af3..d77ecbdf3411b 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -165,12 +165,14 @@ BinaryContext::BinaryContext(std::unique_ptr<MCContext> Ctx,
                              std::unique_ptr<MCDisassembler> DisAsm,
                              JournalingStreams Logger)
     : Ctx(std::move(Ctx)), DwCtx(std::move(DwCtx)),
-      TheTriple(std::move(TheTriple)), SSP(std::move(SSP)),
-      TheTarget(TheTarget), TripleName(TripleName), MCE(std::move(MCE)),
-      MOFI(std::move(MOFI)), AsmInfo(std::move(AsmInfo)), MII(std::move(MII)),
-      STI(std::move(STI)), InstPrinter(std::move(InstPrinter)),
-      MIA(std::move(MIA)), MIB(std::move(MIB)), MRI(std::move(MRI)),
-      DisAsm(std::move(DisAsm)), Logger(Logger), InitialDynoStats(isAArch64()) {
+      TheTriple(std::move(TheTriple)),
+      RelocHandler(createRelocationHandler(this->TheTriple->getArch())),
+      SSP(std::move(SSP)), TheTarget(TheTarget), TripleName(TripleName),
+      MCE(std::move(MCE)), MOFI(std::move(MOFI)), AsmInfo(std::move(AsmInfo)),
+      MII(std::move(MII)), STI(std::move(STI)),
+      InstPrinter(std::move(InstPrinter)), MIA(std::move(MIA)),
+      MIB(std::move(MIB)), MRI(std::move(MRI)), DisAsm(std::move(DisAsm)),
+      Logger(Logger), InitialDynoStats(isAArch64()) {
   RegularPageSize = isAArch64() ? RegularPageSizeAArch64 : RegularPageSizeX86;
   PageAlign = opts::NoHugePages ? RegularPageSize : HugePageSize;
 }
diff --git a/bolt/lib/Core/BinaryEmitter.cpp b/bolt/lib/Core/BinaryEmitter.cpp
index 29dbcab7945ec..b28d44d3eef93 100644
--- a/bolt/lib/Core/BinaryEmitter.cpp
+++ b/bolt/lib/Core/BinaryEmitter.cpp
@@ -581,6 +581,8 @@ void BinaryEmitter::emitConstantIslands(BinaryFunction &BF, bool EmitColdPart,
       if (FunctionOffset >= EndOffset)
         return;
 
+      const RelocationHandler &RH = BC.getRelocationHandler();
+
       for (auto It = Islands.Relocations.lower_bound(FunctionOffset);
            It != Islands.Relocations.end(); ++It) {
         if (It->first >= EndOffset)
@@ -593,13 +595,13 @@ void BinaryEmitter::emitConstantIslands(BinaryFunction &BF, bool EmitColdPart,
           FunctionOffset = Relocation.Offset;
         }
 
-        LLVM_DEBUG(
-            dbgs() << "BOLT-DEBUG: emitting constant island relocation"
-                   << " for " << BF << " at offset 0x"
-                   << Twine::utohexstr(Relocation.Offset) << " with size "
-                   << Relocation::getSizeForType(Relocation.Type) << '\n');
+        LLVM_DEBUG(dbgs() << "BOLT-DEBUG: emitting constant island relocation"
+                          << " for " << BF << " at offset 0x"
+                          << Twine::utohexstr(Relocation.Offset)
+                          << " with size " << RH.getSizeForType(Relocation.Type)
+                          << '\n');
 
-        FunctionOffset += Relocation.emit(&Streamer);
+        FunctionOffset += Relocation.emit(&Streamer, RH);
       }
 
       assert(FunctionOffset <= EndOffset && "overflow error");
diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index 39c4dd5595e3c..bc38cfdac4a83 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -703,7 +703,9 @@ void BinaryFunction::printRelocations(raw_ostream &OS, uint64_t Offset,
 
   auto RI = Relocations.lower_bound(Offset);
   while (RI != Relocations.end() && RI->first < Offset + Size) {
-    OS << Sep << "(R: " << RI->second << ")";
+    OS << Sep << "(R: ";
+    RI->second.print(OS, BC.getRelocationHandler());
+    OS << ")";
     Sep = ", ";
     ++RI;
   }
@@ -1827,7 +1829,7 @@ bool BinaryFunction::scanExternalRefs() {
       if (ignoreReference(Rel->Symbol))
         continue;
 
-      if (Relocation::getSizeForType(Rel->Type) < 4) {
+      if (BC.getRelocationHandler().getSizeForType(Rel->Type) < 4) {
         // If the instruction uses a short form, then we might not be able
         // to handle the rewrite without relaxation, and hence cannot reliably
         // create an external reference relocation.
@@ -2098,7 +2100,7 @@ bool BinaryFunction::validateInternalRefDataRelocations() {
       const Relocation *Relocation = BC.getRelocationAt(RelocationAddress);
       BC.errs() << "  ";
       if (Relocation)
-        BC.errs() << *Relocation;
+        Relocation->print(BC.errs(), BC.getRelocationHandler());
       else
         BC.errs() << "<missing relocation>";
       BC.errs() << '\n';
diff --git a/bolt/lib/Core/BinarySection.cpp b/bolt/lib/Core/BinarySection.cpp
index a8620ba83ebfb..5fcac5ac2e52b 100644
--- a/bolt/lib/Core/BinarySection.cpp
+++ b/bolt/lib/Core/BinarySection.cpp
@@ -29,6 +29,14 @@ extern cl::opt<bool> PrintRelocations;
 
 uint64_t BinarySection::Count = 0;
 
+void BinarySection::addDynamicRelocation(const Relocation &Reloc) {
+  assert(Reloc.Offset < getSize() && "offset not within section bounds");
+  assert(
+      (!Reloc.isRELR() || BC.getRelocationHandler().isRelative(Reloc.Type)) &&
+      "Only relative relocations can be relr.");
+  DynamicRelocations.emplace(Reloc);
+}
+
 bool BinarySection::isELF() const { return BC.isELF(); }
 
 bool BinarySection::isMachO() const { return BC.isMachO(); }
@@ -60,7 +68,7 @@ BinarySection::hash(const BinaryData &BD,
         Hash, hash_value(Contents.substr(Offset, Begin->Offset - Offset)));
     if (BinaryData *RelBD = BC.getBinaryDataByName(Rel.Symbol->getName()))
       Hash = hash_combine(Hash, hash(*RelBD, Cache));
-    Offset = Rel.Offset + Rel.getSize();
+    Offset = Rel.Offset + Rel.getSize(BC.getRelocationHandler());
   }
 
   Hash = hash_combine(Hash,
@@ -73,6 +81,7 @@ BinarySection::hash(const BinaryData &BD,
 
 void BinarySection::emitAsData(MCStreamer &Streamer,
                                const Twine &SectionName) const {
+  const RelocationHandler &RH = BC.getRelocationHandler();
   StringRef SectionContents =
       isFinalized() ? getOutputContents() : getContents();
   MCSectionELF *ELFSection =
@@ -123,17 +132,17 @@ void BinarySection::emitAsData(MCStreamer &Streamer,
 
 #ifndef NDEBUG
       for (const auto &Relocation : make_range(ROI, ROE)) {
-        LLVM_DEBUG(
-            dbgs() << "BOLT-DEBUG: emitting relocation for symbol "
-                   << (Relocation.Symbol ? Relocation.Symbol->getName()
-                                         : StringRef("<none>"))
-                   << " at offset 0x" << Twine::utohexstr(Relocation.Offset)
-                   << " with size "
-                   << Relocation::getSizeForType(Relocation.Type) << '\n');
+        LLVM_DEBUG(dbgs() << "BOLT-DEBUG: emitting relocation for symbol "
+                          << (Relocation.Symbol ? Relocation.Symbol->getName()
+                                                : StringRef("<none>"))
+                          << " at offset 0x"
+                          << Twine::utohexstr(Relocation.Offset)
+                          << " with size " << RH.getSizeForType(Relocation.Type)
+                          << '\n');
       }
 #endif
 
-      size_t RelocationSize = Relocation::emit(ROI, ROE, &Streamer);
+      size_t RelocationSize = Relocation::emit(ROI, ROE, &Streamer, RH);
       SectionOffset += RelocationSize;
     }
     assert(SectionOffset <= SectionContents.size() && "overflow error");
@@ -160,6 +169,7 @@ void BinarySection::flushPendingRelocations(raw_fd_ostream &OS,
     return;
 
   const uint64_t SectionAddress = getAddress();
+  const RelocationHandler &RH = BC.getRelocationHandler();
 
   // We apply relocations to original section contents. For allocatable sections
   // this means using their input file offsets, since the output file offset
@@ -185,22 +195,19 @@ void BinarySection::flushPendingRelocations(raw_fd_ostream &OS,
 
     // Safely skip any optional pending relocation that cannot be encoded.
     if (Reloc.isOptional() &&
-        !Relocation::canEncodeValue(Reloc.Type, Value,
-                                    SectionAddress + Reloc.Offset)) {
+        !RH.canEncodeValue(Reloc.Type, Value, SectionAddress + Reloc.Offset)) {
 
       ++SkippedPendingRelocations;
       continue;
     }
-    Value = Relocation::encodeValue(Reloc.Type, Value,
-                                    SectionAddress + Reloc.Offset);
+    Value = RH.encodeValue(Reloc.Type, Value, SectionAddress + Reloc.Offset);
 
     safePWrite(OS, reinterpret_cast<const char *>(&Value),
-               Relocation::getSizeForType(Reloc.Type),
-               SectionFileOffset + Reloc.Offset);
+               RH.getSizeForType(Reloc.Type), SectionFileOffset + Reloc.Offset);
 
     LLVM_DEBUG(
         dbgs() << "BOLT-DEBUG: writing value 0x" << Twine::utohexstr(Value)
-               << " of size " << Relocation::getSizeForType(Reloc.Type)
+               << " of size " << RH.getSizeForType(Reloc.Type)
                << " at section offset 0x" << Twine::utohexstr(Reloc.Offset)
                << " address 0x"
                << Twine::utohexstr(SectionAddress + Reloc.Offset)
@@ -236,8 +243,10 @@ void BinarySection::print(raw_ostream &OS) const {
     OS << " (tls)";
 
   if (opts::PrintRelocations)
-    for (const Relocation &R : relocations())
-      OS << "\n  " << R;
+    for (const Relocation &R : relocations()) {
+      OS << "\n  ";
+      R.print(OS, BC.getRelocationHandler());
+    }
 }
 
 BinarySection::RelocationSetType
@@ -258,8 +267,13 @@ BinarySection::reorderRelocations(bool Inplace) const {
     uint64_t RelOffset = RelAddr - BD->getAddress();
     NewRel.Offset = BD->getOutputOffset() + RelOffset;
     assert(NewRel.Offset < getSize());
-    LLVM_DEBUG(dbgs() << "BOLT-DEBUG: moving " << Rel << " -> " << NewRel
-                      << "\n");
+    LLVM_DEBUG({
+      dbgs() << "BOLT-DEBUG: moving ";
+      Rel.print(dbgs(), BC.getRelocationHandler());
+      dbgs() << " -> ";
+      NewRel.print(dbgs(), BC.getRelocationHandler());
+      dbgs() << "\n";
+    });
     NewRelocations.emplace(std::move(NewRel));
   }
   return NewRelocations;
@@ -267,6 +281,7 @@ BinarySection::reorderRelocations(bool Inplace) const {
 
 void BinarySection::reorderContents(const std::vector<BinaryData *> &Order,
                                     bool Inplace) {
+  const RelocationHandler &RH = BC.getRelocationHandler();
   IsReordered = true;
 
   Relocations = reorderRelocations(Inplace);
@@ -292,7 +307,7 @@ void BinarySection::reorderContents(const std::vector<BinaryData *> &Order,
     // of the reordered segment to force LLVM to recognize and map this
     // section.
     MCSymbol *ZeroSym = BC.registerNameAtAddress("Zero", 0, 0, 0);
-    addRelocation(OS.tell(), ZeroSym, Relocation::getAbs64(), 0xdeadbeef);
+    addRelocation(OS.tell(), ZeroSym, RH.getAbs64(), 0xdeadbeef);
 
     uint64_t Zero = 0;
     OS.write(reinterpret_cast<const char *>(&Zero), sizeof(Zero));
diff --git a/bolt/lib/Core/CMakeLists.txt b/bolt/lib/Core/CMakeLists.txt
index 430a1c93a9f0c..0a967b94c86e7 100644
--- a/bolt/lib/Core/CMakeLists.txt
+++ b/bolt/lib/Core/CMakeLists.txt
@@ -37,6 +37,9 @@ add_llvm_library(LLVMBOLTCore
   MCPlusBuilder.cpp
   ParallelUtilities.cpp
   Relocation.cpp
+  ../Target/AArch64/AArch64RelocationHandler.cpp
+  ../Target/RISCV/RISCVRelocationHandler.cpp
+  ../Target/X86/X86RelocationHandler.cpp
 
   NO_EXPORT
   DISABLE_LLVM_LINK_LLVM_DYLIB
diff --git a/bolt/lib/Core/Relocation.cpp b/bolt/lib/Core/Relocation.cpp
index b36c2cd8a4c82..ddf5e344b8ddc 100644
--- a/bolt/lib/Core/Relocation.cpp
+++ b/bolt/lib/Core/Relocation.cpp
@@ -15,939 +15,37 @@
 #include "llvm/MC/MCExpr.h"
 #include "llvm/MC/MCStreamer.h"
 #include "llvm/MC/MCSymbol.h"
-#include "llvm/Object/ELF.h"
 #include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/MathExtras.h"
 
 using namespace llvm;
 using namespace bolt;
 
-namespace ELFReserved {
-enum {
-  R_RISCV_TPREL_I = 49,
-  R_RISCV_TPREL_S = 50,
-};
-} // namespace ELFReserved
+namespace llvm::bolt {
+std::unique_ptr<RelocationHandler> createAArch64RelocationHandler();
+std::unique_ptr<RelocationHandler> createRISCVRelocationHandler(bool Is64Bit);
+std::unique_ptr<RelocationHandler> createX86RelocationHandler();
+} // namespace llvm::bolt
 
-Triple::ArchType Relocation::Arch;
-
-static bool isSupportedX86(uint32_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_GOTPC64:
-  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;
-  }
-}
-
-static bool isSupportedAArch64(uint32_t Type) {
-  switch (Type) {
-  default:
-    return false;
-  case ELF::R_AARCH64_CALL26:
-  case ELF::R_AARCH64_JUMP26:
-  case ELF::R_AARCH64_TSTBR14:
-  case ELF::R_AARCH64_CONDBR19:
-  case ELF::R_AARCH64_ADR_PREL_LO21:
-  case ELF::R_AARCH64_ADR_PREL_PG_HI21:
-  case ELF::R_AARCH64_ADR_PREL_PG_HI21_NC:
-  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_TLSGD_ADR_PAGE21:
-  case ELF::R_AARCH64_TLSGD_ADD_LO12_NC:
-  case ELF::R_AARCH64_TLSDESC_ADR_PREL21:
-  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_TLSLE_MOVW_TPREL_G0:
-  case ELF::R_AARCH64_TLSLE_MOVW_TPREL_G0_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_TLSIE_ADR_GOTTPREL_PAGE21:
-  case ELF::R_AARCH64_PREL16:
-  case ELF::R_AARCH64_PREL32:
-  case ELF::R_AARCH64_PREL64:
-  case ELF::R_AARCH64_ABS16:
-  case ELF::R_AARCH64_ABS32:
-  case ELF::R_AARCH64_ABS64:
-  case ELF::R_AARCH64_MOVW_UABS_G0:
-  case ELF::R_AARCH64_MOVW_UABS_G0_NC:
-  case ELF::R_AARCH64_MOVW_UABS_G1:
-  case ELF::R_AARCH64_MOVW_UABS_G1_NC:
-  case ELF::R_AARCH64_MOVW_UABS_G2:
-  case ELF::R_AARCH64_MOVW_UABS_G2_NC:
-  case ELF::R_AARCH64_MOVW_UABS_G3:
-  case ELF::R_AARCH64_PLT32:
-    return true;
-  }
-}
-
-static bool isSupportedRISCV(uint32_t Type) {
-  switch (Type) {
-  default:
-    return false;
-  case ELF::R_RISCV_JAL:
-  case ELF::R_RISCV_CALL:
-  case ELF::R_RISCV_CALL_PLT:
-  case ELF::R_RISCV_BRANCH:
-  case ELF::R_RISCV_RELAX:
-  case ELF::R_RISCV_GOT_HI20:
-  case ELF::R_RISCV_PCREL_HI20:
-  case ELF::R_RISCV_PCREL_LO12_I:
-  case ELF::R_RISCV_PCREL_LO12_S:
-  case ELF::R_RISCV_RVC_JUMP:
-  case ELF::R_RISCV_RVC_BRANCH:
-  case ELF::R_RISCV_ADD32:
-  case ELF::R_RISCV_SUB32:
-  case ELF::R_RISCV_HI20:
-  case ELF::R_RISCV_LO12_I:
-  case ELF::R_RISCV_LO12_S:
-  case ELF::R_RISCV_32:
-  case ELF::R_RISCV_64:
-  case ELF::R_RISCV_TLS_GOT_HI20:
-  case ELF::R_RISCV_TLS_GD_HI20:
-  case ELF::R_RISCV_TPREL_HI20:
-  case ELF::R_RISCV_TPREL_ADD:
-  case ELF::R_RISCV_TPREL_LO12_I:
-  case ELF::R_RISCV_TPREL_LO12_S:
-  case ELFReserved::R_RISCV_TPREL_I:
-  case ELFReserved::R_RISCV_TPREL_S:
-    return true;
-  }
-}
-
-static size_t getSizeForTypeX86(uint32_t Type) {
-  switch (Type) {
-  default:
-    errs() << object::getELFRelocationTypeName(ELF::EM_X86_64, Type) << '\n';
-    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:
-  case ELF::R_X86_64_GOTPC64:
-    return 8;
-  }
-}
-
-static size_t getSizeForTypeAArch64(uint32_t Type) {
-  switch (Type) {
-  default:
-    errs() << object::getELFRelocationTypeName(ELF::EM_AARCH64, Type) << '\n';
-    llvm_unreachable("unsupported relocation type");
-  case ELF::R_AARCH64_ABS16:
-  case ELF::R_AARCH64_PREL16:
-    return 2;
-  case ELF::R_AARCH64_CALL26:
-  case ELF::R_AARCH64_JUMP26:
-  case ELF::R_AARCH64_TSTBR14:
-  case ELF::R_AARCH64_CONDBR19:
-  case ELF::R_AARCH64_ADR_PREL_LO21:
-  case ELF::R_AARCH64_ADR_PREL_PG_HI21:
-  case ELF::R_AARCH64_ADR_PREL_PG_HI21_NC:
-  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_TLSGD_ADR_PAGE21:
-  case ELF::R_AARCH64_TLSGD_ADD_LO12_NC:
-  case ELF::R_AARCH64_TLSDESC_ADR_PREL21:
-  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_TLSLE_MOVW_TPREL_G0:
-  case ELF::R_AARCH64_TLSLE_MOVW_TPREL_G0_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_TLSIE_ADR_GOTTPREL_PAGE21:
-  case ELF::R_AARCH64_PREL32:
-  case ELF::R_AARCH64_MOVW_UABS_G0:
-  case ELF::R_AARCH64_MOVW_UABS_G0_NC:
-  case ELF::R_AARCH64_MOVW_UABS_G1:
-  case ELF::R_AARCH64_MOVW_UABS_G1_NC:
-  case ELF::R_AARCH64_MOVW_UABS_G2:
-  case ELF::R_AARCH64_MOVW_UABS_G2_NC:
-  case ELF::R_AARCH64_MOVW_UABS_G3:
-  case ELF::R_AARCH64_ABS32:
-  case ELF::R_AARCH64_PLT32:
-    return 4;
-  case ELF::R_AARCH64_ABS64:
-  case ELF::R_AARCH64_PREL64:
-    return 8;
-  }
-}
-
-static size_t getSizeForTypeRISCV(uint32_t Type) {
-  switch (Type) {
-  default:
-    errs() << object::getELFRelocationTypeName(ELF::EM_RISCV, Type) << '\n';
-    llvm_unreachable("unsupported relocation type");
-  case ELF::R_RISCV_RVC_JUMP:
-  case ELF::R_RISCV_RVC_BRANCH:
-    return 2;
-  case ELF::R_RISCV_JAL:
-  case ELF::R_RISCV_BRANCH:
-  case ELF::R_RISCV_PCREL_HI20:
-  case ELF::R_RISCV_PCREL_LO12_I:
-  case ELF::R_RISCV_PCREL_LO12_S:
-  case ELF::R_RISCV_32_PCREL:
-  case ELF::R_RISCV_ADD32:
-  case ELF::R_RISCV_SUB32:
-  case ELF::R_RISCV_HI20:
-  case ELF::R_RISCV_LO12_I:
-  case ELF::R_RISCV_LO12_S:
-  case ELF::R_RISCV_32:
-    return 4;
-  case ELF::R_RISCV_64:
-  case ELF::R_RISCV_CALL:
-  case ELF::R_RISCV_CALL_PLT:
-  case ELF::R_RISCV_GOT_HI20:
-  case ELF::R_RISCV_TLS_GOT_HI20:
-  case ELF::R_RISCV_TLS_GD_HI20:
-    // See extractValueRISCV for why this is necessary.
-    return 8;
-  }
-}
-
-static bool skipRelocationTypeX86(uint32_t Type) {
-  return Type == ELF::R_X86_64_NONE;
-}
-
-static bool skipRelocationTypeAArch64(uint32_t Type) {
-  switch (Type) {
-  default:
-    return false;
-  case ELF::R_AARCH64_NONE:
-  case ELF::R_AARCH64_LD_PREL_LO19:
-  case ELF::R_AARCH64_TLSDESC_CALL:
-    return true;
-  }
-}
-
-static bool skipRelocationTypeRISCV(uint32_t Type) {
-  switch (Type) {
-  default:
-    return false;
-  case ELF::R_RISCV_NONE:
-  case ELF::R_RISCV_RELAX:
-    return true;
-  }
-}
-
-static uint64_t encodeValueX86(uint32_t Type, uint64_t Value, uint64_t PC) {
-  switch (Type) {
-  default:
-    llvm_unreachable("unsupported relocation");
-  case ELF::R_X86_64_64:
-  case ELF::R_X86_64_32:
-    break;
-  case ELF::R_X86_64_PC32:
-    Value -= PC;
-    break;
-  }
-  return Value;
-}
-
-static bool canEncodeValueAArch64(uint32_t Type, uint64_t Value, uint64_t PC) {
-  switch (Type) {
-  default:
-    llvm_unreachable("unsupported relocation");
-  case ELF::R_AARCH64_CALL26:
-  case ELF::R_AARCH64_JUMP26:
-    return isInt<28>(Value - PC);
-  }
-}
-
-static uint64_t encodeValueAArch64(uint32_t Type, uint64_t Value, uint64_t PC) {
-  switch (Type) {
-  default:
-    llvm_unreachable("unsupported relocation");
-  case ELF::R_AARCH64_ABS16:
-  case ELF::R_AARCH64_ABS32:
-  case ELF::R_AARCH64_ABS64:
-    break;
-  case ELF::R_AARCH64_PREL16:
-  case ELF::R_AARCH64_PREL32:
-  case ELF::R_AARCH64_PREL64:
-    Value -= PC;
-    break;
-  case ELF::R_AARCH64_CALL26:
-    Value -= PC;
-    assert(isInt<28>(Value) && "only PC +/- 128MB is allowed for direct call");
-    // Immediate goes in bits 25:0 of BL.
-    // OP 1001_01 goes in bits 31:26 of BL.
-    Value = ((Value >> 2) & 0x3ffffff) | 0x94000000ULL;
-    break;
-  case ELF::R_AARCH64_JUMP26:
-    Value -= PC;
-    assert(isInt<28>(Value) &&
-           "only PC +/- 128MB is allowed for direct branch");
-    // Immediate goes in bits 25:0 of B.
-    // OP 0001_01 goes in bits 31:26 of B.
-    Value = ((Value >> 2) & 0x3ffffff) | 0x14000000ULL;
-    break;
-  }
-  return Value;
-}
-
-static uint64_t canEncodeValueRISCV(uint32_t Type, uint64_t Value,
-                                    uint64_t PC) {
-  switch (Type) {
-  default:
-    llvm_unreachable("unsupported relocation");
-  case ELF::R_RISCV_32:
-  case ELF::R_RISCV_64:
-    return true;
-  }
-}
-
-static uint64_t encodeValueRISCV(uint32_t Type, uint64_t Value, uint64_t PC) {
-  switch (Type) {
-  default:
-    llvm_unreachable("unsupported relocation");
-  case ELF::R_RISCV_32:
-  case ELF::R_RISCV_64:
-    break;
-  }
-  return Value;
-}
-
-static uint64_t extractValueX86(uint32_t Type, uint64_t Contents, uint64_t PC) {
-  if (Type == ELF::R_X86_64_32S)
-    return SignExtend64<32>(Contents);
-  if (Relocation::isPCRelative(Type))
-    return SignExtend64(Contents, 8 * Relocation::getSizeForType(Type));
-  return Contents;
-}
-
-static uint64_t extractValueAArch64(uint32_t Type, uint64_t Contents,
-                                    uint64_t PC) {
-  switch (Type) {
-  default:
-    errs() << object::getELFRelocationTypeName(ELF::EM_AARCH64, Type) << '\n';
-    llvm_unreachable("unsupported relocation type");
-  case ELF::R_AARCH64_ABS16:
-  case ELF::R_AARCH64_ABS32:
-  case ELF::R_AARCH64_ABS64:
-    return Contents;
-  case ELF::R_AARCH64_PREL16:
-    return static_cast<int64_t>(PC) + SignExtend64<16>(Contents & 0xffff);
-  case ELF::R_AARCH64_PREL32:
-  case ELF::R_AARCH64_PLT32:
-    return static_cast<int64_t>(PC) + SignExtend64<32>(Contents & 0xffffffff);
-  case ELF::R_AARCH64_PREL64:
-    return static_cast<int64_t>(PC) + Contents;
-  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_TSTBR14:
-    // Immediate:15:2 goes in bits 18:5 of TBZ, TBNZ
-    Contents &= ~0xfffffffffff8001fULL;
-    return static_cast<int64_t>(PC) + SignExtend64<16>(Contents >> 3);
-  case ELF::R_AARCH64_CONDBR19:
-    // Immediate:20:2 goes in bits 23:5 of Bcc, CBZ, CBNZ
-    Contents &= ~0xffffffffff00001fULL;
-    return static_cast<int64_t>(PC) + SignExtend64<21>(Contents >> 3);
-  case ELF::R_AARCH64_ADR_GOT_PAGE:
-  case ELF::R_AARCH64_TLSGD_ADR_PAGE21:
-  case ELF::R_AARCH64_TLSDESC_ADR_PREL21:
-  case ELF::R_AARCH64_TLSDESC_ADR_PAGE21:
-  case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
-  case ELF::R_AARCH64_ADR_PREL_LO21:
-  case ELF::R_AARCH64_ADR_PREL_PG_HI21:
-  case ELF::R_AARCH64_ADR_PREL_PG_HI21_NC: {
-    // Bits 32:12 of Symbol address goes in bits 30:29 + 23:5 of ADRP
-    // and ADR instructions
-    bool IsAdr = !!(((Contents >> 31) & 0x1) == 0);
-    Contents &= ~0xffffffff9f00001fUll;
-    uint64_t LowBits = (Contents >> 29) & 0x3;
-    uint64_t HighBits = (Contents >> 5) & 0x7ffff;
-    Contents = LowBits | (HighBits << 2);
-    if (IsAdr)
-      return static_cast<int64_t>(PC) + SignExtend64<21>(Contents);
-
-    // ADRP instruction
-    Contents = static_cast<int64_t>(PC) + SignExtend64<33>(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_TLSGD_ADD_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);
-  }
-  case ELF::R_AARCH64_MOVW_UABS_G3:
-  case ELF::R_AARCH64_MOVW_UABS_G2_NC:
-  case ELF::R_AARCH64_MOVW_UABS_G2:
-  case ELF::R_AARCH64_MOVW_UABS_G1_NC:
-  case ELF::R_AARCH64_MOVW_UABS_G1:
-  case ELF::R_AARCH64_MOVW_UABS_G0_NC:
-  case ELF::R_AARCH64_MOVW_UABS_G0:
-    // The shift goes in bits 22:21 of MOV* instructions
-    uint8_t Shift = (Contents >> 21) & 0x3;
-    // Immediate goes in bits 20:5
-    Contents = (Contents >> 5) & 0xffff;
-    return Contents << (16 * Shift);
-  }
-}
-
-static uint64_t extractUImmRISCV(uint32_t Contents) {
-  return SignExtend64<32>(Contents & 0xfffff000);
-}
-
-static uint64_t extractIImmRISCV(uint32_t Contents) {
-  return SignExtend64<12>(Contents >> 20);
-}
-
-static uint64_t extractSImmRISCV(uint32_t Contents) {
-  return SignExtend64<12>(((Contents >> 7) & 0x1f) | ((Contents >> 25) << 5));
-}
-
-static uint64_t extractJImmRISCV(uint32_t Contents) {
-  return SignExtend64<21>(
-      (((Contents >> 21) & 0x3ff) << 1) | (((Contents >> 20) & 0x1) << 11) |
-      (((Contents >> 12) & 0xff) << 12) | (((Contents >> 31) & 0x1) << 20));
-}
-
-static uint64_t extractBImmRISCV(uint32_t Contents) {
-  return SignExtend64<13>(
-      (((Contents >> 8) & 0xf) << 1) | (((Contents >> 25) & 0x3f) << 5) |
-      (((Contents >> 7) & 0x1) << 11) | (((Contents >> 31) & 0x1) << 12));
-}
-
-static uint64_t extractValueRISCV(uint32_t Type, uint64_t Contents,
-                                  uint64_t PC) {
-  switch (Type) {
-  default:
-    errs() << object::getELFRelocationTypeName(ELF::EM_RISCV, Type) << '\n';
-    llvm_unreachable("unsupported relocation type");
-  case ELF::R_RISCV_JAL:
-    return extractJImmRISCV(Contents);
-  case ELF::R_RISCV_CALL:
-  case ELF::R_RISCV_CALL_PLT:
-    // The psABI "Relocations" chapter's "Procedure Calls" section defines
-    // R_RISCV_CALL and R_RISCV_CALL_PLT over an AUIPC/JALR pair. Decode both
-    // instructions so the addend includes the low 12 bits carried by JALR.
-    return extractUImmRISCV(Contents & 0xffffffff) +
-           extractIImmRISCV(Contents >> 32);
-  case ELF::R_RISCV_BRANCH:
-    return extractBImmRISCV(Contents);
-  case ELF::R_RISCV_GOT_HI20:
-  case ELF::R_RISCV_TLS_GOT_HI20:
-  case ELF::R_RISCV_TLS_GD_HI20:
-    // We need to know the exact address of the GOT entry so we extract the
-    // value from both the AUIPC and L[D|W]. We cannot rely on the symbol in the
-    // relocation for this since it simply refers to the object that is stored
-    // in the GOT entry, not to the entry itself.
-    return extractUImmRISCV(Contents & 0xffffffff) +
-           extractIImmRISCV(Contents >> 32);
-  case ELF::R_RISCV_PCREL_HI20:
-  case ELF::R_RISCV_HI20:
-    return extractUImmRISCV(Contents);
-  case ELF::R_RISCV_PCREL_LO12_I:
-  case ELF::R_RISCV_LO12_I:
-    return extractIImmRISCV(Contents);
-  case ELF::R_RISCV_PCREL_LO12_S:
-  case ELF::R_RISCV_LO12_S:
-    return extractSImmRISCV(Contents);
-  case ELF::R_RISCV_RVC_JUMP:
-    return SignExtend64<11>(Contents >> 2);
-  case ELF::R_RISCV_RVC_BRANCH:
-    return SignExtend64<8>(((Contents >> 2) & 0x1f) | ((Contents >> 5) & 0xe0));
-  case ELF::R_RISCV_ADD32:
-  case ELF::R_RISCV_SUB32:
-  case ELF::R_RISCV_32:
-  case ELF::R_RISCV_64:
-    return Contents;
-  }
-}
-
-static bool isGOTX86(uint32_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;
-  }
-}
-
-static bool isGOTAArch64(uint32_t Type) {
-  switch (Type) {
-  default:
-    return false;
-  case ELF::R_AARCH64_ADR_GOT_PAGE:
-  case ELF::R_AARCH64_LD64_GOT_LO12_NC:
-  case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
-  case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
-  case ELF::R_AARCH64_TLSGD_ADR_PAGE21:
-  case ELF::R_AARCH64_TLSGD_ADD_LO12_NC:
-  case ELF::R_AARCH64_TLSDESC_ADR_PREL21:
-  case ELF::R_AARCH64_TLSDESC_ADR_PAGE21:
-  case ELF::R_AARCH64_TLSDESC_LD64_LO12:
-  case ELF::R_AARCH64_TLSDESC_ADD_LO12:
-    return true;
-  }
-}
-
-static bool isGOTRISCV(uint32_t Type) {
-  switch (Type) {
-  default:
-    return false;
-  case ELF::R_RISCV_GOT_HI20:
-  case ELF::R_RISCV_TLS_GOT_HI20:
-  case ELF::R_RISCV_TLS_GD_HI20:
-    return true;
-  }
-}
-
-static bool isTLSX86(uint32_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;
-  }
-}
-
-static bool isTLSAArch64(uint32_t Type) {
-  switch (Type) {
-  default:
-    return false;
-  case ELF::R_AARCH64_TLSGD_ADR_PAGE21:
-  case ELF::R_AARCH64_TLSGD_ADD_LO12_NC:
-  case ELF::R_AARCH64_TLSDESC_ADR_PREL21:
-  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_TLSLE_MOVW_TPREL_G0:
-  case ELF::R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
-  case ELF::R_AARCH64_TLSDESC_LD64_LO12:
-  case ELF::R_AARCH64_TLSDESC_ADD_LO12:
-  case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
-    return true;
-  }
-}
-
-static bool isTLSRISCV(uint32_t Type) {
-  switch (Type) {
-  default:
-    return false;
-  case ELF::R_RISCV_TLS_GOT_HI20:
-  case ELF::R_RISCV_TLS_GD_HI20:
-  case ELF::R_RISCV_TPREL_HI20:
-  case ELF::R_RISCV_TPREL_ADD:
-  case ELF::R_RISCV_TPREL_LO12_I:
-  case ELF::R_RISCV_TPREL_LO12_S:
-  case ELFReserved::R_RISCV_TPREL_I:
-  case ELFReserved::R_RISCV_TPREL_S:
-    return true;
-  }
-}
-
-static bool isPCRelativeX86(uint32_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_GOTOFF64:
-  case ELF::R_X86_64_GOTPC32:
-  case ELF::R_X86_64_GOTPC64:
-  case ELF::R_X86_64_GOTTPOFF:
-  case ELF::R_X86_64_GOTPCRELX:
-  case ELF::R_X86_64_REX_GOTPCRELX:
-    return true;
-  }
-}
-
-static bool isPCRelativeAArch64(uint32_t Type) {
-  switch (Type) {
-  default:
-    llvm_unreachable("Unknown relocation type");
-  case ELF::R_AARCH64_ABS16:
-  case ELF::R_AARCH64_ABS32:
-  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_TLSGD_ADD_LO12_NC:
-  case ELF::R_AARCH64_TLSLE_ADD_TPREL_HI12:
-  case ELF::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
-  case ELF::R_AARCH64_TLSLE_MOVW_TPREL_G0:
-  case ELF::R_AARCH64_TLSLE_MOVW_TPREL_G0_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_MOVW_UABS_G0:
-  case ELF::R_AARCH64_MOVW_UABS_G0_NC:
-  case ELF::R_AARCH64_MOVW_UABS_G1:
-  case ELF::R_AARCH64_MOVW_UABS_G1_NC:
-  case ELF::R_AARCH64_MOVW_UABS_G2:
-  case ELF::R_AARCH64_MOVW_UABS_G2_NC:
-  case ELF::R_AARCH64_MOVW_UABS_G3:
-    return false;
-  case ELF::R_AARCH64_CALL26:
-  case ELF::R_AARCH64_JUMP26:
-  case ELF::R_AARCH64_TSTBR14:
-  case ELF::R_AARCH64_CONDBR19:
-  case ELF::R_AARCH64_ADR_PREL_LO21:
-  case ELF::R_AARCH64_ADR_PREL_PG_HI21:
-  case ELF::R_AARCH64_ADR_PREL_PG_HI21_NC:
-  case ELF::R_AARCH64_ADR_GOT_PAGE:
-  case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
-  case ELF::R_AARCH64_TLSGD_ADR_PAGE21:
-  case ELF::R_AARCH64_TLSDESC_ADR_PREL21:
-  case ELF::R_AARCH64_TLSDESC_ADR_PAGE21:
-  case ELF::R_AARCH64_PREL16:
-  case ELF::R_AARCH64_PREL32:
-  case ELF::R_AARCH64_PREL64:
-  case ELF::R_AARCH64_PLT32:
-    return true;
-  }
-}
-
-static bool isPCRelativeRISCV(uint32_t Type) {
-  switch (Type) {
-  default:
-    llvm_unreachable("Unknown relocation type");
-  case ELF::R_RISCV_ADD32:
-  case ELF::R_RISCV_SUB32:
-  case ELF::R_RISCV_HI20:
-  case ELF::R_RISCV_LO12_I:
-  case ELF::R_RISCV_LO12_S:
-  case ELF::R_RISCV_32:
-  case ELF::R_RISCV_64:
-    return false;
-  case ELF::R_RISCV_JAL:
-  case ELF::R_RISCV_CALL:
-  case ELF::R_RISCV_CALL_PLT:
-  case ELF::R_RISCV_BRANCH:
-  case ELF::R_RISCV_GOT_HI20:
-  case ELF::R_RISCV_PCREL_HI20:
-  case ELF::R_RISCV_PCREL_LO12_I:
-  case ELF::R_RISCV_PCREL_LO12_S:
-  case ELF::R_RISCV_RVC_JUMP:
-  case ELF::R_RISCV_RVC_BRANCH:
-  case ELF::R_RISCV_32_PCREL:
-  case ELF::R_RISCV_TLS_GOT_HI20:
-  case ELF::R_RISCV_TLS_GD_HI20:
-    return true;
-  }
-}
-
-bool Relocation::isSupported(uint32_t Type) {
-  switch (Arch) {
-  default:
-    return false;
-  case Triple::aarch64:
-    return isSupportedAArch64(Type);
-  case Triple::riscv64:
-  case Triple::riscv32:
-    return isSupportedRISCV(Type);
-  case Triple::x86_64:
-    return isSupportedX86(Type);
-  }
-}
-
-size_t Relocation::getSizeForType(uint32_t Type) {
-  switch (Arch) {
-  default:
-    llvm_unreachable("Unsupported architecture");
-  case Triple::aarch64:
-    return getSizeForTypeAArch64(Type);
-  case Triple::riscv64:
-  case Triple::riscv32:
-    return getSizeForTypeRISCV(Type);
-  case Triple::x86_64:
-    return getSizeForTypeX86(Type);
-  }
-}
-
-bool Relocation::skipRelocationType(uint32_t Type) {
-  switch (Arch) {
-  default:
-    llvm_unreachable("Unsupported architecture");
-  case Triple::aarch64:
-    return skipRelocationTypeAArch64(Type);
-  case Triple::riscv64:
-  case Triple::riscv32:
-    return skipRelocationTypeRISCV(Type);
-  case Triple::x86_64:
-    return skipRelocationTypeX86(Type);
-  }
-}
-
-uint64_t Relocation::encodeValue(uint32_t Type, uint64_t Value, uint64_t PC) {
-  switch (Arch) {
-  default:
-    llvm_unreachable("Unsupported architecture");
-  case Triple::aarch64:
-    return encodeValueAArch64(Type, Value, PC);
-  case Triple::riscv64:
-  case Triple::riscv32:
-    return encodeValueRISCV(Type, Value, PC);
-  case Triple::x86_64:
-    return encodeValueX86(Type, Value, PC);
-  }
-}
-
-bool Relocation::canEncodeValue(uint32_t Type, uint64_t Value, uint64_t PC) {
-  switch (Arch) {
-  default:
-    llvm_unreachable("Unsupported architecture");
-  case Triple::aarch64:
-    return canEncodeValueAArch64(Type, Value, PC);
-  case Triple::riscv64:
-  case Triple::riscv32:
-    return canEncodeValueRISCV(Type, Value, PC);
-  case Triple::x86_64:
-    return true;
-  }
-}
-
-uint64_t Relocation::extractValue(uint32_t Type, uint64_t Contents,
-                                  uint64_t PC) {
-  switch (Arch) {
-  default:
-    llvm_unreachable("Unsupported architecture");
-  case Triple::aarch64:
-    return extractValueAArch64(Type, Contents, PC);
-  case Triple::riscv64:
-  case Triple::riscv32:
-    return extractValueRISCV(Type, Contents, PC);
-  case Triple::x86_64:
-    return extractValueX86(Type, Contents, PC);
-  }
-}
-
-bool Relocation::isGOT(uint32_t Type) {
-  switch (Arch) {
-  default:
-    llvm_unreachable("Unsupported architecture");
-  case Triple::aarch64:
-    return isGOTAArch64(Type);
-  case Triple::riscv64:
-  case Triple::riscv32:
-    return isGOTRISCV(Type);
-  case Triple::x86_64:
-    return isGOTX86(Type);
-  }
-}
-
-bool Relocation::isX86GOTPCRELX(uint32_t Type) {
-  if (Arch != Triple::x86_64)
-    return false;
-  return Type == ELF::R_X86_64_GOTPCRELX || Type == ELF::R_X86_64_REX_GOTPCRELX;
-}
-
-bool Relocation::isX86GOTPC64(uint32_t Type) {
-  if (Arch != Triple::x86_64)
-    return false;
-  return Type == ELF::R_X86_64_GOTPC64;
-}
-
-bool Relocation::isNone(uint32_t Type) { return Type == getNone(); }
-
-bool Relocation::isRelative(uint32_t Type) {
-  switch (Arch) {
-  default:
-    llvm_unreachable("Unsupported architecture");
-  case Triple::aarch64:
-    return Type == ELF::R_AARCH64_RELATIVE;
-  case Triple::riscv64:
-  case Triple::riscv32:
-    return Type == ELF::R_RISCV_RELATIVE;
-  case Triple::x86_64:
-    return Type == ELF::R_X86_64_RELATIVE;
-  }
-}
-
-bool Relocation::isIRelative(uint32_t Type) {
-  switch (Arch) {
-  default:
-    llvm_unreachable("Unsupported architecture");
-  case Triple::aarch64:
-    return Type == ELF::R_AARCH64_IRELATIVE;
-  case Triple::riscv64:
-    return Type == ELF::R_RISCV_IRELATIVE;
-  case Triple::riscv32:
-    llvm_unreachable("not implemented");
-  case Triple::x86_64:
-    return Type == ELF::R_X86_64_IRELATIVE;
-  }
-}
-
-bool Relocation::isTLS(uint32_t Type) {
-  switch (Arch) {
-  default:
-    llvm_unreachable("Unsupported architecture");
-  case Triple::aarch64:
-    return isTLSAArch64(Type);
-  case Triple::riscv64:
-  case Triple::riscv32:
-    return isTLSRISCV(Type);
-  case Triple::x86_64:
-    return isTLSX86(Type);
-  }
-}
-
-bool Relocation::isInstructionReference(uint32_t Type) {
-  if (Arch != Triple::riscv64 && Arch != Triple::riscv32)
-    return false;
-
-  switch (Type) {
-  default:
-    return false;
-  case ELF::R_RISCV_PCREL_LO12_I:
-  case ELF::R_RISCV_PCREL_LO12_S:
-    return true;
-  }
+MCBinaryExpr::Opcode
+RelocationHandler::getComposeOpcodeFor(uint32_t Type) const {
+  llvm_unreachable("composed relocations are unsupported for this target");
 }
 
-uint32_t Relocation::getNone() {
+std::unique_ptr<RelocationHandler>
+llvm::bolt::createRelocationHandler(Triple::ArchType Arch) {
   switch (Arch) {
   default:
     llvm_unreachable("Unsupported architecture");
   case Triple::aarch64:
-    return ELF::R_AARCH64_NONE;
-  case Triple::riscv64:
+    return createAArch64RelocationHandler();
   case Triple::riscv32:
-    return ELF::R_RISCV_NONE;
-  case Triple::x86_64:
-    return ELF::R_X86_64_NONE;
-  }
-}
-
-uint32_t Relocation::getPC32() {
-  switch (Arch) {
-  default:
-    llvm_unreachable("Unsupported architecture");
-  case Triple::aarch64:
-    return ELF::R_AARCH64_PREL32;
+    return createRISCVRelocationHandler(false);
   case Triple::riscv64:
-  case Triple::riscv32:
-    return ELF::R_RISCV_32_PCREL;
-  case Triple::x86_64:
-    return ELF::R_X86_64_PC32;
-  }
-}
-
-uint32_t Relocation::getPC64() {
-  switch (Arch) {
-  default:
-    llvm_unreachable("Unsupported architecture");
-  case Triple::aarch64:
-    return ELF::R_AARCH64_PREL64;
-  case Triple::riscv64:
-  case Triple::riscv32:
-    llvm_unreachable("not implemented");
+    return createRISCVRelocationHandler(true);
   case Triple::x86_64:
-    return ELF::R_X86_64_PC64;
+    return createX86RelocationHandler();
   }
 }
 
@@ -957,57 +55,16 @@ uint32_t Relocation::getType(const object::RelocationRef &Rel) {
   return static_cast<uint32_t>(RelType);
 }
 
-bool Relocation::isPCRelative(uint32_t Type) {
-  switch (Arch) {
-  default:
-    llvm_unreachable("Unsupported architecture");
-  case Triple::aarch64:
-    return isPCRelativeAArch64(Type);
-  case Triple::riscv64:
-  case Triple::riscv32:
-    return isPCRelativeRISCV(Type);
-  case Triple::x86_64:
-    return isPCRelativeX86(Type);
-  }
-}
-
-uint32_t Relocation::getAbs64() {
-  switch (Arch) {
-  default:
-    llvm_unreachable("Unsupported architecture");
-  case Triple::aarch64:
-    return ELF::R_AARCH64_ABS64;
-  case Triple::riscv64:
-  case Triple::riscv32:
-    return ELF::R_RISCV_64;
-  case Triple::x86_64:
-    return ELF::R_X86_64_64;
-  }
-}
-
-uint32_t Relocation::getRelative() {
-  switch (Arch) {
-  default:
-    llvm_unreachable("Unsupported architecture");
-  case Triple::aarch64:
-    return ELF::R_AARCH64_RELATIVE;
-  case Triple::riscv64:
-    return ELF::R_RISCV_RELATIVE;
-  case Triple::riscv32:
-    llvm_unreachable("not implemented");
-  case Triple::x86_64:
-    return ELF::R_X86_64_RELATIVE;
-  }
-}
-
-size_t Relocation::emit(MCStreamer *Streamer) const {
-  const size_t Size = getSizeForType(Type);
-  const auto *Value = createExpr(Streamer);
+size_t Relocation::emit(MCStreamer *Streamer,
+                        const RelocationHandler &RH) const {
+  const size_t Size = RH.getSizeForType(Type);
+  const auto *Value = createExpr(Streamer, RH);
   Streamer->emitValue(Value, Size);
   return Size;
 }
 
-const MCExpr *Relocation::createExpr(MCStreamer *Streamer) const {
+const MCExpr *Relocation::createExpr(MCStreamer *Streamer,
+                                     const RelocationHandler &RH) const {
   MCContext &Ctx = Streamer->getContext();
   const MCExpr *Value = nullptr;
 
@@ -1020,7 +77,7 @@ const MCExpr *Relocation::createExpr(MCStreamer *Streamer) const {
     Value = MCConstantExpr::create(Addend, Ctx);
   }
 
-  if (isPCRelative(Type)) {
+  if (RH.isPCRelative(Type)) {
     MCSymbol *TempLabel = Ctx.createNamedTempSymbol();
     Streamer->emitLabel(TempLabel);
     Value = MCBinaryExpr::createSub(
@@ -1031,47 +88,20 @@ const MCExpr *Relocation::createExpr(MCStreamer *Streamer) const {
 }
 
 const MCExpr *Relocation::createExpr(MCStreamer *Streamer,
-                                     const MCExpr *RetainedValue) const {
-  const auto *Value = createExpr(Streamer);
+                                     const MCExpr *RetainedValue,
+                                     const RelocationHandler &RH) const {
+  const auto *Value = createExpr(Streamer, RH);
 
   if (RetainedValue) {
-    Value = MCBinaryExpr::create(getComposeOpcodeFor(Type), RetainedValue,
+    Value = MCBinaryExpr::create(RH.getComposeOpcodeFor(Type), RetainedValue,
                                  Value, Streamer->getContext());
   }
 
   return Value;
 }
 
-MCBinaryExpr::Opcode Relocation::getComposeOpcodeFor(uint32_t Type) {
-  assert((Arch == Triple::riscv32 || Arch == Triple::riscv64) &&
-         "only implemented for RISC-V");
-
-  switch (Type) {
-  default:
-    llvm_unreachable("not implemented");
-  case ELF::R_RISCV_ADD32:
-    return MCBinaryExpr::Add;
-  case ELF::R_RISCV_SUB32:
-    return MCBinaryExpr::Sub;
-  }
-}
-
-void Relocation::print(raw_ostream &OS) const {
-  switch (Arch) {
-  default:
-    OS << "RType:" << Twine::utohexstr(Type);
-    break;
-  case Triple::aarch64:
-    OS << object::getELFRelocationTypeName(ELF::EM_AARCH64, Type);
-    break;
-  case Triple::riscv64:
-  case Triple::riscv32:
-    OS << object::getELFRelocationTypeName(ELF::EM_RISCV, Type);
-    break;
-  case Triple::x86_64:
-    OS << object::getELFRelocationTypeName(ELF::EM_X86_64, Type);
-    break;
-  }
+void Relocation::print(raw_ostream &OS, const RelocationHandler &RH) const {
+  RH.printType(OS, Type);
   OS << ", 0x" << Twine::utohexstr(Offset);
   if (Symbol) {
     OS << ", " << Symbol->getName();
diff --git a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp
index 0916f6f915758..722015ab499bf 100644
--- a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp
+++ b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp
@@ -480,6 +480,7 @@ void LinuxKernelRewriter::processLKKSymtab(bool IsGPL) {
          "__ksymtab[_gpl] section not found in Linux Kernel binary");
   const uint64_t SectionSize = SectionOrError->getSize();
   const uint64_t SectionAddress = SectionOrError->getAddress();
+  const uint32_t PC32 = BC.getRelocationHandler().getPC32();
   assert((SectionSize % 4) == 0 &&
          "The size of the __ksymtab[_gpl] section should be a multiple of 4");
 
@@ -493,8 +494,7 @@ void LinuxKernelRewriter::processLKKSymtab(bool IsGPL) {
     if (!BF)
       continue;
 
-    BC.addRelocation(EntryAddress, BF->getSymbol(), Relocation::getPC32(), 0,
-                     *Offset);
+    BC.addRelocation(EntryAddress, BF->getSymbol(), PC32, 0, *Offset);
   }
 }
 
@@ -801,6 +801,8 @@ Error LinuxKernelRewriter::rewriteORCTables() {
   if (!NumORCEntries)
     return Error::success();
 
+  const uint32_t PC32 = BC.getRelocationHandler().getPC32();
+
   // Update ORC sections in-place. As we change the code, the number of ORC
   // entries may increase for some functions. However, as we remove terminator
   // redundancy (see below), more space is freed up and we should always be able
@@ -831,8 +833,8 @@ Error LinuxKernelRewriter::rewriteORCTables() {
                                "exceeded the number of allocated ORC entries");
 
     if (Label)
-      ORCUnwindIPSection->addRelocation(UnwindIPWriter.getOffset(), Label,
-                                        Relocation::getPC32(), /*Addend*/ 0);
+      ORCUnwindIPSection->addRelocation(UnwindIPWriter.getOffset(), Label, PC32,
+                                        /*Addend*/ 0);
 
     const int32_t IPValue =
         IP - ORCUnwindIPSection->getAddress() - UnwindIPWriter.getOffset();
diff --git a/bolt/lib/Rewrite/MachORewriteInstance.cpp b/bolt/lib/Rewrite/MachORewriteInstance.cpp
index 1aff93dd26edb..6190d28ff2ad8 100644
--- a/bolt/lib/Rewrite/MachORewriteInstance.cpp
+++ b/bolt/lib/Rewrite/MachORewriteInstance.cpp
@@ -71,7 +71,6 @@ MachORewriteInstance::MachORewriteInstance(object::MachOObjectFile *InputFile,
                                            StringRef ToolPath, Error &Err)
     : InputFile(InputFile), ToolPath(ToolPath) {
   ErrorAsOutParameter EAO(&Err);
-  Relocation::Arch = InputFile->makeTriple().getArch();
   auto BCOrErr = BinaryContext::createBinaryContext(
       InputFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),
       InputFile->getFileName(), nullptr,
diff --git a/bolt/lib/Rewrite/RSeqRewriter.cpp b/bolt/lib/Rewrite/RSeqRewriter.cpp
index 46bce66d13ddf..687ad91012588 100644
--- a/bolt/lib/Rewrite/RSeqRewriter.cpp
+++ b/bolt/lib/Rewrite/RSeqRewriter.cpp
@@ -39,7 +39,7 @@ class RSeqRewriter final : public MetadataRewriter {
         BinaryFunction *BF = nullptr;
         if (Rel.Symbol)
           BF = BC.getFunctionForSymbol(Rel.Symbol);
-        else if (Relocation::isRelative(Rel.Type))
+        else if (BC.getRelocationHandler().isRelative(Rel.Type))
           BF = BC.getBinaryFunctionContainingAddress(Rel.Addend);
 
         if (!BF) {
diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index d7078907cf514..da6293f331275 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -453,7 +453,6 @@ RewriteInstance::RewriteInstance(ELFObjectFileBase *File, const int Argc,
     }
   }
 
-  Relocation::Arch = TheTriple.getArch();
   auto BCOrErr = BinaryContext::createBinaryContext(
       TheTriple, std::make_shared<orc::SymbolStringPool>(), File->getFileName(),
       Features.get(), IsPIC,
@@ -534,10 +533,11 @@ static bool shouldDisassemble(const BinaryFunction &BF) {
 
 static void createRISCVIFuncResolverFunctions(BinaryContext &BC) {
   assert(BC.isRISCV() && "expected RISC-V target");
+  const RelocationHandler &RH = BC.getRelocationHandler();
 
   for (const BinarySection &Section : BC.allocatableSections()) {
     for (const Relocation &Rel : Section.dynamicRelocations()) {
-      if (!Rel.isIRelative() || !Rel.Addend ||
+      if (!RH.isIRelative(Rel.Type) || !Rel.Addend ||
           BC.getBinaryFunctionAtAddress(Rel.Addend))
         continue;
 
@@ -1444,6 +1444,7 @@ void RewriteInstance::discoverFileObjects() {
   }
 
   if (BC->isAArch64()) {
+    const RelocationHandler &RH = BC->getRelocationHandler();
     // Check for dynamic relocations that might be contained in
     // constant islands.
     for (const BinarySection &Section : BC->allocatableSections()) {
@@ -1455,7 +1456,8 @@ void RewriteInstance::discoverFileObjects() {
                                                    /*CheckPastEnd*/ false,
                                                    /*UseMaxSize*/ true);
         if (BF) {
-          assert(Rel.isRelative() && "Expected relative relocation for island");
+          assert(RH.isRelative(Rel.Type) &&
+                 "Expected relative relocation for island");
           BC->logBOLTErrorsAndQuitOnFatal(
               BF->markIslandDynamicRelocationAtAddress(RelAddress));
         }
@@ -1551,7 +1553,7 @@ Error RewriteInstance::discoverRtInitAddress() {
   }
 
   if (const Relocation *Reloc = InitArraySection->getDynamicRelocationAt(0)) {
-    if (Reloc->isRelative()) {
+    if (BC->getRelocationHandler().isRelative(Reloc->Type)) {
       BC->StartFunctionAddress = Reloc->Addend;
     } else {
       MCSymbol *Sym = Reloc->Symbol;
@@ -1624,6 +1626,7 @@ Error RewriteInstance::discoverRtFiniAddress() {
 }
 
 Error RewriteInstance::updateRtInitReloc() {
+  const RelocationHandler &RH = BC->getRelocationHandler();
   if (BC->HasInterpHeader && opts::RuntimeLibInitHook == opts::RLIH_ENTRY_POINT)
     return Error::success();
 
@@ -1649,7 +1652,7 @@ Error RewriteInstance::updateRtInitReloc() {
 
   if (std::optional<Relocation> Reloc =
           InitArraySection->takeDynamicRelocationAt(0)) {
-    if (Reloc->isRelative()) {
+    if (RH.isRelative(Reloc->Type)) {
       if (Reloc->Addend != BC->StartFunctionAddress)
         return createStringError(std::errc::not_supported,
                                  "inconsistent .init_array dynamic relocation");
@@ -1669,9 +1672,9 @@ Error RewriteInstance::updateRtInitReloc() {
       if (BF->getAddress() + Reloc->Addend != BC->StartFunctionAddress)
         return createStringError(std::errc::not_supported,
                                  "inconsistent .init_array dynamic relocation");
-      InitArraySection->addDynamicRelocation(Relocation{
-          /*Offset*/ 0, /*Symbol*/ nullptr, /*Type*/ Relocation::getAbs64(),
-          /*Addend*/ RT->getRuntimeStartAddress(), /*Value*/ 0});
+      InitArraySection->addDynamicRelocation(
+          Relocation{/*Offset*/ 0, /*Symbol*/ nullptr, /*Type*/ RH.getAbs64(),
+                     /*Addend*/ RT->getRuntimeStartAddress(), /*Value*/ 0});
     }
   }
   // Update the static relocation by adding a pending relocation which will get
@@ -1679,9 +1682,9 @@ Error RewriteInstance::updateRtInitReloc() {
   // flushPendingRelocations will calculate the value to patch as
   // "Symbol + Addend". Since we don't have a symbol, just set the addend to the
   // desired value.
-  InitArraySection->addPendingRelocation(Relocation{
-      /*Offset*/ 0, /*Symbol*/ nullptr, /*Type*/ Relocation::getAbs64(),
-      /*Addend*/ RT->getRuntimeStartAddress(), /*Value*/ 0});
+  InitArraySection->addPendingRelocation(
+      Relocation{/*Offset*/ 0, /*Symbol*/ nullptr, /*Type*/ RH.getAbs64(),
+                 /*Addend*/ RT->getRuntimeStartAddress(), /*Value*/ 0});
   BC->outs()
       << "BOLT-INFO: runtime library initialization was hooked via .init_array "
          "entry, set to 0x"
@@ -1690,6 +1693,7 @@ Error RewriteInstance::updateRtInitReloc() {
 }
 
 Error RewriteInstance::updateRtFiniReloc() {
+  const RelocationHandler &RH = BC->getRelocationHandler();
   // Updating DT_FINI is handled by patchELFDynamic.
   if (BC->FiniAddress)
     return Error::success();
@@ -1725,9 +1729,9 @@ Error RewriteInstance::updateRtFiniReloc() {
   // flushPendingRelocations will calculate the value to patch as
   // "Symbol + Addend". Since we don't have a symbol, just set the addend to the
   // desired value.
-  FiniArraySection->addPendingRelocation(Relocation{
-      /*Offset*/ 0, /*Symbol*/ nullptr, /*Type*/ Relocation::getAbs64(),
-      /*Addend*/ RT->getRuntimeFiniAddress(), /*Value*/ 0});
+  FiniArraySection->addPendingRelocation(
+      Relocation{/*Offset*/ 0, /*Symbol*/ nullptr, /*Type*/ RH.getAbs64(),
+                 /*Addend*/ RT->getRuntimeFiniAddress(), /*Value*/ 0});
   BC->outs() << "BOLT-INFO: runtime library finalization was hooked via "
                 ".fini_array entry, set to 0x"
              << Twine::utohexstr(RT->getRuntimeFiniAddress()) << "\n";
@@ -1915,7 +1919,7 @@ void RewriteInstance::createPLTBinaryFunction(uint64_t TargetAddress,
 
   MCSymbol *Symbol = Rel->Symbol;
   if (!Symbol) {
-    if (!Rel->Addend || !Rel->isIRelative())
+    if (!Rel->Addend || !BC->getRelocationHandler().isIRelative(Rel->Type))
       return;
 
     // IFUNC trampoline without symbol
@@ -1945,7 +1949,7 @@ void RewriteInstance::createPLTBinaryFunction(uint64_t TargetAddress,
   // symbols can alias that resolver, and R_RISCV_CALL_PLT relocations may use
   // any of their names. Register every such name for the same IPLT entry so
   // getPLTBinaryDataByName() can resolve those call sites.
-  if (BC->isRISCV() && Rel->isIRelative()) {
+  if (BC->isRISCV() && BC->getRelocationHandler().isIRelative(Rel->Type)) {
     auto ResolverSyms = FileSymRefs.equal_range(Rel->Addend);
     for (const SymbolRef &AliasSymbol : llvm::make_second_range(
              llvm::make_range(ResolverSyms.first, ResolverSyms.second))) {
@@ -2422,12 +2426,12 @@ void RewriteInstance::relocateEHFrameSection() {
       llvm_unreachable("unsupported DWARF encoding type");
     case dwarf::DW_EH_PE_sdata4:
     case dwarf::DW_EH_PE_udata4:
-      RelType = Relocation::getPC32();
+      RelType = BC->getRelocationHandler().getPC32();
       Offset -= 4;
       break;
     case dwarf::DW_EH_PE_sdata8:
     case dwarf::DW_EH_PE_udata8:
-      RelType = Relocation::getPC64();
+      RelType = BC->getRelocationHandler().getPC64();
       Offset -= 8;
       break;
     }
@@ -2830,7 +2834,8 @@ bool RewriteInstance::analyzeRelocation(
     const RelocationRef &Rel, uint32_t &RType, std::string &SymbolName,
     bool &IsSectionRelocation, uint64_t &SymbolAddress, int64_t &Addend,
     uint64_t &ExtractedValue) const {
-  if (!Relocation::isSupported(RType))
+  const RelocationHandler &RH = BC->getRelocationHandler();
+  if (!RH.isSupported(RType))
     return false;
 
   auto IsWeakReference = [](const SymbolRef &Symbol) {
@@ -2844,16 +2849,16 @@ bool RewriteInstance::analyzeRelocation(
   const bool IsAArch64 = BC->isAArch64();
   const bool IsRISCV = BC->isRISCV();
 
-  const size_t RelSize = Relocation::getSizeForType(RType);
+  const size_t RelSize = RH.getSizeForType(RType);
 
   ErrorOr<uint64_t> Value =
       BC->getUnsignedValueAtAddress(Rel.getOffset(), RelSize);
   assert(Value && "failed to extract relocated value");
 
-  ExtractedValue = Relocation::extractValue(RType, *Value, Rel.getOffset());
+  ExtractedValue = RH.extractValue(RType, *Value, Rel.getOffset());
   Addend = getRelocationAddend(InputFile, Rel);
 
-  const bool IsPCRelative = Relocation::isPCRelative(RType);
+  const bool IsPCRelative = RH.isPCRelative(RType);
   const uint64_t PCRelOffset = IsPCRelative && !IsAArch64 ? Rel.getOffset() : 0;
   bool SkipVerification = false;
   auto SymbolIter = Rel.getSymbol();
@@ -2914,7 +2919,7 @@ bool RewriteInstance::analyzeRelocation(
   // causing the extracted value mismatch. Similar cases can happen for TLS.
   // Pass the relocation information as is to the disassembler and let it decide
   // how to use it for the operand symbolization.
-  if (Relocation::isGOT(RType) || Relocation::isTLS(RType)) {
+  if (RH.isGOT(RType) || RH.isTLS(RType)) {
     SkipVerification = true;
   } else if (!SymbolAddress) {
     assert(!IsSectionRelocation);
@@ -3055,7 +3060,7 @@ void RewriteInstance::readDynamicRelocations(const SectionRef &Section,
     }
 
     const uint32_t RType = Relocation::getType(Rel);
-    if (Relocation::isNone(RType))
+    if (BC->getRelocationHandler().isNone(RType))
       continue;
 
     StringRef SymbolName = "<none>";
@@ -3087,7 +3092,7 @@ void RewriteInstance::readDynamicRelocations(const SectionRef &Section,
     // Check if this relocation targets an address within a function. This
     // happens with indirect goto.
     const uint64_t ReferencedAddress = SymbolAddress + Addend;
-    if (Relocation::isRelative(RType)) {
+    if (BC->getRelocationHandler().isRelative(RType)) {
       if (SymbolAddress != 0) {
         BC->errs() << "BOLT-ERROR: symbol address non zero for RELATIVE "
                       "relocation type\n";
@@ -3110,7 +3115,7 @@ void RewriteInstance::readDynamicRelrRelocations(BinarySection &Section) {
            << ":\n";
   });
 
-  const uint32_t RType = Relocation::getRelative();
+  const uint32_t RType = BC->getRelocationHandler().getRelative();
   const uint8_t PSize = BC->AsmInfo->getCodePointerSize();
   const uint64_t MaxDelta = ((CHAR_BIT * DynamicRelrEntrySize) - 1) * PSize;
 
@@ -3240,6 +3245,7 @@ void RewriteInstance::readRelocations(const SectionRef &Section) {
 
 void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
                                        const RelocationRef &Rel) {
+  const RelocationHandler &RH = BC->getRelocationHandler();
   const bool IsAArch64 = BC->isAArch64();
   const bool IsX86 = BC->isX86();
   const bool IsFromCode = RelocatedSection.isText();
@@ -3248,7 +3254,7 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
   SmallString<16> TypeName;
   Rel.getTypeName(TypeName);
   uint32_t RType = Relocation::getType(Rel);
-  if (Relocation::skipRelocationType(RType))
+  if (RH.skipRelocationType(RType))
     return;
 
   // Adjust the relocation type as the linker might have skewed it.
@@ -3258,14 +3264,14 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
     RType &= ~ELF::R_X86_64_converted_reloc_bit;
   }
 
-  if (Relocation::isTLS(RType)) {
+  if (RH.isTLS(RType)) {
     // No special handling required for TLS relocations on X86.
     if (IsX86)
       return;
 
     // The non-got related TLS relocations on AArch64 and RISC-V also could be
     // skipped.
-    if (!Relocation::isGOT(RType))
+    if (!RH.isGOT(RType))
       return;
   }
 
@@ -3293,7 +3299,7 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
   }
 
   if (!IsFromCode && !IsWritable && (IsX86 || IsAArch64) &&
-      Relocation::isPCRelative(RType)) {
+      RH.isPCRelative(RType)) {
     BinaryData *BD = BC->getBinaryDataContainingAddress(Rel.getOffset());
     if (BD && (BD->nameStartsWith("_ZTV") ||   // vtable
                BD->nameStartsWith("_ZTCN"))) { // construction vtable
@@ -3378,7 +3384,7 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
   const bool IsToCode = ReferencedSection && ReferencedSection->isText();
 
   // Special handling of PC-relative relocations.
-  if (IsX86 && Relocation::isPCRelative(RType)) {
+  if (IsX86 && RH.isPCRelative(RType)) {
     if (!IsFromCode && IsToCode) {
       // PC-relative relocations from data to code are tricky since the
       // original information is typically lost after linking, even with
@@ -3411,7 +3417,7 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
   }
 
   bool ForceRelocation = BC->forceSymbolRelocations(SymbolName);
-  if ((BC->isAArch64() || BC->isRISCV()) && Relocation::isGOT(RType))
+  if ((BC->isAArch64() || BC->isRISCV()) && RH.isGOT(RType))
     ForceRelocation = true;
 
   if (!ReferencedSection && !ForceRelocation) {
@@ -3457,15 +3463,14 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
 
   // Workaround for a member function pointer de-virtualization bug. We check
   // if a non-pc-relative relocation in the code is pointing to (fptr - 1).
-  if (IsToCode && ContainingBF && !Relocation::isPCRelative(RType) &&
+  if (IsToCode && ContainingBF && !RH.isPCRelative(RType) &&
       (!ReferencedBF || (ReferencedBF->getAddress() != Address))) {
     if (const BinaryFunction *RogueBF =
             BC->getBinaryFunctionAtAddress(Address + 1)) {
       // Do an extra check that the function was referenced previously.
       // It's a linear search, but it should rarely happen.
       auto CheckReloc = [&](const Relocation &Rel) {
-        return Rel.Symbol == RogueBF->getSymbol() &&
-               !Relocation::isPCRelative(Rel.Type);
+        return Rel.Symbol == RogueBF->getSymbol() && !RH.isPCRelative(Rel.Type);
       };
       bool Found = llvm::any_of(
           llvm::make_second_range(ContainingBF->Relocations), CheckReloc);
@@ -3497,7 +3502,7 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
     // Adjust the point of reference to a code location inside a function.
     if (ReferencedBF->containsAddress(Address, /*UseMaxSize = */ true)) {
       RefFunctionOffset = Address - ReferencedBF->getAddress();
-      if (Relocation::isInstructionReference(RType)) {
+      if (RH.isInstructionReference(RType)) {
         // Instruction labels are created while disassembling so we just leave
         // the symbol empty for now. Since the extracted value is typically
         // unrelated to the referenced symbol (e.g., %pcrel_lo in RISC-V
@@ -6287,6 +6292,7 @@ RewriteInstance::patchELFAllocatableRelaSections(ELFObjectFile<ELFT> *File) {
   using Elf_Rela = typename ELFT::Rela;
   raw_fd_ostream &OS = Out->os();
   const ELFFile<ELFT> &EF = File->getELFFile();
+  const RelocationHandler &RH = BC->getRelocationHandler();
 
   uint64_t RelDynOffset = 0, RelDynEndOffset = 0;
   uint64_t RelPltOffset = 0, RelPltEndOffset = 0;
@@ -6363,7 +6369,7 @@ RewriteInstance::patchELFAllocatableRelaSections(ELFObjectFile<ELFT> *File) {
         if (Rel.isJmpRelocation())
           continue;
 
-        const bool IsRelative = Rel.isRelative();
+        const bool IsRelative = RH.isRelative(Rel.Type);
         if (PatchRelative != IsRelative || Rel.isRELR())
           continue;
 
@@ -6385,7 +6391,7 @@ RewriteInstance::patchELFAllocatableRelaSections(ELFObjectFile<ELFT> *File) {
       return;
 
     typename ELFObjectFile<ELFT>::Elf_Rela RelA;
-    RelA.setSymbolAndType(0, Relocation::getNone(), EF.isMips64EL());
+    RelA.setSymbolAndType(0, RH.getNone(), EF.isMips64EL());
     RelA.r_offset = 0;
     RelA.r_addend = 0;
     while (Offset < EndOffset) {
diff --git a/bolt/lib/Target/AArch64/AArch64MCSymbolizer.cpp b/bolt/lib/Target/AArch64/AArch64MCSymbolizer.cpp
index da469a9a5ab95..0a1f19d639ec3 100644
--- a/bolt/lib/Target/AArch64/AArch64MCSymbolizer.cpp
+++ b/bolt/lib/Target/AArch64/AArch64MCSymbolizer.cpp
@@ -83,6 +83,7 @@ std::optional<Relocation>
 AArch64MCSymbolizer::adjustRelocation(const Relocation &Rel,
                                       const MCInst &Inst) const {
   BinaryContext &BC = Function.getBinaryContext();
+  const RelocationHandler &RH = BC.getRelocationHandler();
 
   // The linker can convert ADRP+ADD and ADRP+LDR instruction sequences into
   // NOP+ADR. After the conversion, the linker might keep the relocations and
@@ -114,7 +115,7 @@ AArch64MCSymbolizer::adjustRelocation(const Relocation &Rel,
     }
   }
 
-  if (!Relocation::isGOT(Rel.Type))
+  if (!RH.isGOT(Rel.Type))
     return Rel;
 
   Relocation AdjustedRel = Rel;
@@ -137,7 +138,7 @@ AArch64MCSymbolizer::adjustRelocation(const Relocation &Rel,
   // Note that ADRP relaxation described above cannot happen for TLS relocation.
   // Since TLS relocations may not even have a valid symbol (not supported by
   // BOLT), we explicitly exclude them from the check.
-  if (BC.MIB->isADRP(Inst) && Rel.Addend == 0 && !Relocation::isTLS(Rel.Type)) {
+  if (BC.MIB->isADRP(Inst) && Rel.Addend == 0 && !RH.isTLS(Rel.Type)) {
     ErrorOr<uint64_t> SymbolValue = BC.getSymbolValue(*Rel.Symbol);
     assert(SymbolValue && "Symbol value should be set");
     const uint64_t SymbolPageAddr = *SymbolValue & ~0xfffULL;
diff --git a/bolt/lib/Target/AArch64/AArch64RelocationHandler.cpp b/bolt/lib/Target/AArch64/AArch64RelocationHandler.cpp
new file mode 100644
index 0000000000000..3c04660ea0b1e
--- /dev/null
+++ b/bolt/lib/Target/AArch64/AArch64RelocationHandler.cpp
@@ -0,0 +1,448 @@
+//===- AArch64RelocationHandler.cpp
+//-----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the AArch64 relocation handler.
+//
+//===----------------------------------------------------------------------===//
+
+#include "bolt/Core/Relocation.h"
+#include "llvm/Object/ELF.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace bolt;
+
+namespace {
+
+class AArch64RelocationHandler final : public RelocationHandler {
+public:
+  bool isSupported(uint32_t Type) const override;
+  size_t getSizeForType(uint32_t Type) const override;
+  bool skipRelocationType(uint32_t Type) const override;
+
+  uint64_t encodeValue(uint32_t Type, uint64_t Value,
+                       uint64_t PC) const override;
+  bool canEncodeValue(uint32_t Type, uint64_t Value,
+                      uint64_t PC) const override;
+  uint64_t extractValue(uint32_t Type, uint64_t Contents,
+                        uint64_t PC) const override;
+
+  bool isGOT(uint32_t Type) const override;
+  bool isRelative(uint32_t Type) const override;
+  bool isIRelative(uint32_t Type) const override;
+  bool isTLS(uint32_t Type) const override;
+  bool isPCRelative(uint32_t Type) const override;
+
+  uint32_t getNone() const override;
+  uint32_t getPC32() const override;
+  uint32_t getPC64() const override;
+  uint32_t getAbs64() const override;
+  uint32_t getRelative() const override;
+
+  void printType(raw_ostream &OS, uint32_t Type) const override;
+};
+
+} // namespace
+
+bool AArch64RelocationHandler::isRelative(uint32_t Type) const {
+  return Type == ELF::R_AARCH64_RELATIVE;
+}
+
+bool AArch64RelocationHandler::isIRelative(uint32_t Type) const {
+  return Type == ELF::R_AARCH64_IRELATIVE;
+}
+
+uint32_t AArch64RelocationHandler::getNone() const {
+  return ELF::R_AARCH64_NONE;
+}
+
+uint32_t AArch64RelocationHandler::getPC32() const {
+  return ELF::R_AARCH64_PREL32;
+}
+
+uint32_t AArch64RelocationHandler::getPC64() const {
+  return ELF::R_AARCH64_PREL64;
+}
+
+uint32_t AArch64RelocationHandler::getAbs64() const {
+  return ELF::R_AARCH64_ABS64;
+}
+
+uint32_t AArch64RelocationHandler::getRelative() const {
+  return ELF::R_AARCH64_RELATIVE;
+}
+
+void AArch64RelocationHandler::printType(raw_ostream &OS, uint32_t Type) const {
+  OS << object::getELFRelocationTypeName(ELF::EM_AARCH64, Type);
+}
+
+bool AArch64RelocationHandler::isSupported(uint32_t Type) const {
+  switch (Type) {
+  default:
+    return false;
+  case ELF::R_AARCH64_CALL26:
+  case ELF::R_AARCH64_JUMP26:
+  case ELF::R_AARCH64_TSTBR14:
+  case ELF::R_AARCH64_CONDBR19:
+  case ELF::R_AARCH64_ADR_PREL_LO21:
+  case ELF::R_AARCH64_ADR_PREL_PG_HI21:
+  case ELF::R_AARCH64_ADR_PREL_PG_HI21_NC:
+  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_TLSGD_ADR_PAGE21:
+  case ELF::R_AARCH64_TLSGD_ADD_LO12_NC:
+  case ELF::R_AARCH64_TLSDESC_ADR_PREL21:
+  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_TLSLE_MOVW_TPREL_G0:
+  case ELF::R_AARCH64_TLSLE_MOVW_TPREL_G0_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_TLSIE_ADR_GOTTPREL_PAGE21:
+  case ELF::R_AARCH64_PREL16:
+  case ELF::R_AARCH64_PREL32:
+  case ELF::R_AARCH64_PREL64:
+  case ELF::R_AARCH64_ABS16:
+  case ELF::R_AARCH64_ABS32:
+  case ELF::R_AARCH64_ABS64:
+  case ELF::R_AARCH64_MOVW_UABS_G0:
+  case ELF::R_AARCH64_MOVW_UABS_G0_NC:
+  case ELF::R_AARCH64_MOVW_UABS_G1:
+  case ELF::R_AARCH64_MOVW_UABS_G1_NC:
+  case ELF::R_AARCH64_MOVW_UABS_G2:
+  case ELF::R_AARCH64_MOVW_UABS_G2_NC:
+  case ELF::R_AARCH64_MOVW_UABS_G3:
+  case ELF::R_AARCH64_PLT32:
+    return true;
+  }
+}
+
+size_t AArch64RelocationHandler::getSizeForType(uint32_t Type) const {
+  switch (Type) {
+  default:
+    errs() << object::getELFRelocationTypeName(ELF::EM_AARCH64, Type) << '\n';
+    llvm_unreachable("unsupported relocation type");
+  case ELF::R_AARCH64_ABS16:
+  case ELF::R_AARCH64_PREL16:
+    return 2;
+  case ELF::R_AARCH64_CALL26:
+  case ELF::R_AARCH64_JUMP26:
+  case ELF::R_AARCH64_TSTBR14:
+  case ELF::R_AARCH64_CONDBR19:
+  case ELF::R_AARCH64_ADR_PREL_LO21:
+  case ELF::R_AARCH64_ADR_PREL_PG_HI21:
+  case ELF::R_AARCH64_ADR_PREL_PG_HI21_NC:
+  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_TLSGD_ADR_PAGE21:
+  case ELF::R_AARCH64_TLSGD_ADD_LO12_NC:
+  case ELF::R_AARCH64_TLSDESC_ADR_PREL21:
+  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_TLSLE_MOVW_TPREL_G0:
+  case ELF::R_AARCH64_TLSLE_MOVW_TPREL_G0_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_TLSIE_ADR_GOTTPREL_PAGE21:
+  case ELF::R_AARCH64_PREL32:
+  case ELF::R_AARCH64_MOVW_UABS_G0:
+  case ELF::R_AARCH64_MOVW_UABS_G0_NC:
+  case ELF::R_AARCH64_MOVW_UABS_G1:
+  case ELF::R_AARCH64_MOVW_UABS_G1_NC:
+  case ELF::R_AARCH64_MOVW_UABS_G2:
+  case ELF::R_AARCH64_MOVW_UABS_G2_NC:
+  case ELF::R_AARCH64_MOVW_UABS_G3:
+  case ELF::R_AARCH64_ABS32:
+  case ELF::R_AARCH64_PLT32:
+    return 4;
+  case ELF::R_AARCH64_ABS64:
+  case ELF::R_AARCH64_PREL64:
+    return 8;
+  }
+}
+
+bool AArch64RelocationHandler::skipRelocationType(uint32_t Type) const {
+  switch (Type) {
+  default:
+    return false;
+  case ELF::R_AARCH64_NONE:
+  case ELF::R_AARCH64_LD_PREL_LO19:
+  case ELF::R_AARCH64_TLSDESC_CALL:
+    return true;
+  }
+}
+
+bool AArch64RelocationHandler::canEncodeValue(uint32_t Type, uint64_t Value,
+                                              uint64_t PC) const {
+  switch (Type) {
+  default:
+    llvm_unreachable("unsupported relocation");
+  case ELF::R_AARCH64_CALL26:
+  case ELF::R_AARCH64_JUMP26:
+    return isInt<28>(Value - PC);
+  }
+}
+
+uint64_t AArch64RelocationHandler::encodeValue(uint32_t Type, uint64_t Value,
+                                               uint64_t PC) const {
+  switch (Type) {
+  default:
+    llvm_unreachable("unsupported relocation");
+  case ELF::R_AARCH64_ABS16:
+  case ELF::R_AARCH64_ABS32:
+  case ELF::R_AARCH64_ABS64:
+    break;
+  case ELF::R_AARCH64_PREL16:
+  case ELF::R_AARCH64_PREL32:
+  case ELF::R_AARCH64_PREL64:
+    Value -= PC;
+    break;
+  case ELF::R_AARCH64_CALL26:
+    Value -= PC;
+    assert(isInt<28>(Value) && "only PC +/- 128MB is allowed for direct call");
+    // Immediate goes in bits 25:0 of BL.
+    // OP 1001_01 goes in bits 31:26 of BL.
+    Value = ((Value >> 2) & 0x3ffffff) | 0x94000000ULL;
+    break;
+  case ELF::R_AARCH64_JUMP26:
+    Value -= PC;
+    assert(isInt<28>(Value) &&
+           "only PC +/- 128MB is allowed for direct branch");
+    // Immediate goes in bits 25:0 of B.
+    // OP 0001_01 goes in bits 31:26 of B.
+    Value = ((Value >> 2) & 0x3ffffff) | 0x14000000ULL;
+    break;
+  }
+  return Value;
+}
+
+uint64_t AArch64RelocationHandler::extractValue(uint32_t Type,
+                                                uint64_t Contents,
+                                                uint64_t PC) const {
+  switch (Type) {
+  default:
+    errs() << object::getELFRelocationTypeName(ELF::EM_AARCH64, Type) << '\n';
+    llvm_unreachable("unsupported relocation type");
+  case ELF::R_AARCH64_ABS16:
+  case ELF::R_AARCH64_ABS32:
+  case ELF::R_AARCH64_ABS64:
+    return Contents;
+  case ELF::R_AARCH64_PREL16:
+    return static_cast<int64_t>(PC) + SignExtend64<16>(Contents & 0xffff);
+  case ELF::R_AARCH64_PREL32:
+  case ELF::R_AARCH64_PLT32:
+    return static_cast<int64_t>(PC) + SignExtend64<32>(Contents & 0xffffffff);
+  case ELF::R_AARCH64_PREL64:
+    return static_cast<int64_t>(PC) + Contents;
+  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_TSTBR14:
+    // Immediate:15:2 goes in bits 18:5 of TBZ, TBNZ
+    Contents &= ~0xfffffffffff8001fULL;
+    return static_cast<int64_t>(PC) + SignExtend64<16>(Contents >> 3);
+  case ELF::R_AARCH64_CONDBR19:
+    // Immediate:20:2 goes in bits 23:5 of Bcc, CBZ, CBNZ
+    Contents &= ~0xffffffffff00001fULL;
+    return static_cast<int64_t>(PC) + SignExtend64<21>(Contents >> 3);
+  case ELF::R_AARCH64_ADR_GOT_PAGE:
+  case ELF::R_AARCH64_TLSGD_ADR_PAGE21:
+  case ELF::R_AARCH64_TLSDESC_ADR_PREL21:
+  case ELF::R_AARCH64_TLSDESC_ADR_PAGE21:
+  case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
+  case ELF::R_AARCH64_ADR_PREL_LO21:
+  case ELF::R_AARCH64_ADR_PREL_PG_HI21:
+  case ELF::R_AARCH64_ADR_PREL_PG_HI21_NC: {
+    // Bits 32:12 of Symbol address goes in bits 30:29 + 23:5 of ADRP
+    // and ADR instructions
+    bool IsAdr = !!(((Contents >> 31) & 0x1) == 0);
+    Contents &= ~0xffffffff9f00001fUll;
+    uint64_t LowBits = (Contents >> 29) & 0x3;
+    uint64_t HighBits = (Contents >> 5) & 0x7ffff;
+    Contents = LowBits | (HighBits << 2);
+    if (IsAdr)
+      return static_cast<int64_t>(PC) + SignExtend64<21>(Contents);
+
+    // ADRP instruction
+    Contents = static_cast<int64_t>(PC) + SignExtend64<33>(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_TLSGD_ADD_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);
+  }
+  case ELF::R_AARCH64_MOVW_UABS_G3:
+  case ELF::R_AARCH64_MOVW_UABS_G2_NC:
+  case ELF::R_AARCH64_MOVW_UABS_G2:
+  case ELF::R_AARCH64_MOVW_UABS_G1_NC:
+  case ELF::R_AARCH64_MOVW_UABS_G1:
+  case ELF::R_AARCH64_MOVW_UABS_G0_NC:
+  case ELF::R_AARCH64_MOVW_UABS_G0:
+    // The shift goes in bits 22:21 of MOV* instructions
+    uint8_t Shift = (Contents >> 21) & 0x3;
+    // Immediate goes in bits 20:5
+    Contents = (Contents >> 5) & 0xffff;
+    return Contents << (16 * Shift);
+  }
+}
+
+bool AArch64RelocationHandler::isGOT(uint32_t Type) const {
+  switch (Type) {
+  default:
+    return false;
+  case ELF::R_AARCH64_ADR_GOT_PAGE:
+  case ELF::R_AARCH64_LD64_GOT_LO12_NC:
+  case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
+  case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
+  case ELF::R_AARCH64_TLSGD_ADR_PAGE21:
+  case ELF::R_AARCH64_TLSGD_ADD_LO12_NC:
+  case ELF::R_AARCH64_TLSDESC_ADR_PREL21:
+  case ELF::R_AARCH64_TLSDESC_ADR_PAGE21:
+  case ELF::R_AARCH64_TLSDESC_LD64_LO12:
+  case ELF::R_AARCH64_TLSDESC_ADD_LO12:
+    return true;
+  }
+}
+
+bool AArch64RelocationHandler::isTLS(uint32_t Type) const {
+  switch (Type) {
+  default:
+    return false;
+  case ELF::R_AARCH64_TLSGD_ADR_PAGE21:
+  case ELF::R_AARCH64_TLSGD_ADD_LO12_NC:
+  case ELF::R_AARCH64_TLSDESC_ADR_PREL21:
+  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_TLSLE_MOVW_TPREL_G0:
+  case ELF::R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
+  case ELF::R_AARCH64_TLSDESC_LD64_LO12:
+  case ELF::R_AARCH64_TLSDESC_ADD_LO12:
+  case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
+    return true;
+  }
+}
+
+bool AArch64RelocationHandler::isPCRelative(uint32_t Type) const {
+  switch (Type) {
+  default:
+    llvm_unreachable("Unknown relocation type");
+  case ELF::R_AARCH64_ABS16:
+  case ELF::R_AARCH64_ABS32:
+  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_TLSGD_ADD_LO12_NC:
+  case ELF::R_AARCH64_TLSLE_ADD_TPREL_HI12:
+  case ELF::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
+  case ELF::R_AARCH64_TLSLE_MOVW_TPREL_G0:
+  case ELF::R_AARCH64_TLSLE_MOVW_TPREL_G0_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_MOVW_UABS_G0:
+  case ELF::R_AARCH64_MOVW_UABS_G0_NC:
+  case ELF::R_AARCH64_MOVW_UABS_G1:
+  case ELF::R_AARCH64_MOVW_UABS_G1_NC:
+  case ELF::R_AARCH64_MOVW_UABS_G2:
+  case ELF::R_AARCH64_MOVW_UABS_G2_NC:
+  case ELF::R_AARCH64_MOVW_UABS_G3:
+    return false;
+  case ELF::R_AARCH64_CALL26:
+  case ELF::R_AARCH64_JUMP26:
+  case ELF::R_AARCH64_TSTBR14:
+  case ELF::R_AARCH64_CONDBR19:
+  case ELF::R_AARCH64_ADR_PREL_LO21:
+  case ELF::R_AARCH64_ADR_PREL_PG_HI21:
+  case ELF::R_AARCH64_ADR_PREL_PG_HI21_NC:
+  case ELF::R_AARCH64_ADR_GOT_PAGE:
+  case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
+  case ELF::R_AARCH64_TLSGD_ADR_PAGE21:
+  case ELF::R_AARCH64_TLSDESC_ADR_PREL21:
+  case ELF::R_AARCH64_TLSDESC_ADR_PAGE21:
+  case ELF::R_AARCH64_PREL16:
+  case ELF::R_AARCH64_PREL32:
+  case ELF::R_AARCH64_PREL64:
+  case ELF::R_AARCH64_PLT32:
+    return true;
+  }
+}
+
+namespace llvm::bolt {
+
+std::unique_ptr<RelocationHandler> createAArch64RelocationHandler() {
+  return std::make_unique<AArch64RelocationHandler>();
+}
+
+} // namespace llvm::bolt
diff --git a/bolt/lib/Target/AArch64/CMakeLists.txt b/bolt/lib/Target/AArch64/CMakeLists.txt
index 715efc1e8c5a4..ec33fc1656841 100644
--- a/bolt/lib/Target/AArch64/CMakeLists.txt
+++ b/bolt/lib/Target/AArch64/CMakeLists.txt
@@ -22,6 +22,9 @@ add_llvm_library(LLVMBOLTTargetAArch64
   AArch64MCPlusBuilder.cpp
   AArch64MCSymbolizer.cpp
 
+  # AArch64RelocationHandler.cpp is part of LLVMBOLTCore to avoid a circular
+  # dependency between the core relocation factory and LLVMBOLTTargetAArch64.
+  PARTIAL_SOURCES_INTENDED
   NO_EXPORT
   DISABLE_LLVM_LINK_LLVM_DYLIB
 
diff --git a/bolt/lib/Target/RISCV/CMakeLists.txt b/bolt/lib/Target/RISCV/CMakeLists.txt
index e7fa950de29df..922edafae5d48 100644
--- a/bolt/lib/Target/RISCV/CMakeLists.txt
+++ b/bolt/lib/Target/RISCV/CMakeLists.txt
@@ -22,6 +22,9 @@ add_llvm_library(LLVMBOLTTargetRISCV
   RISCVMCPlusBuilder.cpp
   RISCVMCSymbolizer.cpp
 
+  # RISCVRelocationHandler.cpp is part of LLVMBOLTCore to avoid a circular
+  # dependency between the core relocation factory and LLVMBOLTTargetRISCV.
+  PARTIAL_SOURCES_INTENDED
   NO_EXPORT
   DISABLE_LLVM_LINK_LLVM_DYLIB
 
diff --git a/bolt/lib/Target/RISCV/RISCVMCSymbolizer.cpp b/bolt/lib/Target/RISCV/RISCVMCSymbolizer.cpp
index 87a702a8c9464..d2d1fa29cdb66 100644
--- a/bolt/lib/Target/RISCV/RISCVMCSymbolizer.cpp
+++ b/bolt/lib/Target/RISCV/RISCVMCSymbolizer.cpp
@@ -64,6 +64,8 @@ RISCVMCSymbolizer::RISCVMCSymbolizer(BinaryFunction &Function,
                                      bool CreateNewSymbols)
     : MCSymbolizer(*Function.getBinaryContext().Ctx, nullptr),
       Function(Function), CreateNewSymbols(CreateNewSymbols) {
+  BinaryContext &BC = Function.getBinaryContext();
+  const RelocationHandler &RH = BC.getRelocationHandler();
   // Discover instruction references before decoding starts. This lets us
   // attach a label while decoding the referenced %pcrel_hi instruction even
   // though its %pcrel_lo user is normally decoded later.
@@ -73,7 +75,7 @@ RISCVMCSymbolizer::RISCVMCSymbolizer(BinaryFunction &Function,
     if (!Rel)
       break;
 
-    if (Relocation::isInstructionReference(Rel->Type)) {
+    if (RH.isInstructionReference(Rel->Type)) {
       assert(Rel->Value >= Function.getAddress() &&
              Rel->Value < Function.getAddress() + Function.getSize() &&
              "RISC-V instruction reference outside of function");
@@ -97,6 +99,7 @@ MCSymbol *RISCVMCSymbolizer::getOrCreateInstructionLabel(
 
 uint64_t RISCVMCSymbolizer::getGOTValue(const Relocation &Rel) const {
   BinaryContext &BC = Function.getBinaryContext();
+  const RelocationHandler &RH = BC.getRelocationHandler();
   const uint64_t HiAddress = Function.getAddress() + Rel.Offset;
 
   // A GOT high relocation records a combined high/low value. Locate the low
@@ -106,16 +109,14 @@ uint64_t RISCVMCSymbolizer::getGOTValue(const Relocation &Rel) const {
   if (It != InstructionReferences.end() && It->second.LowRelocation) {
     const Relocation *LoRel = It->second.LowRelocation;
     ErrorOr<uint64_t> HiContents = BC.getUnsignedValueAtAddress(HiAddress, 4);
-    ErrorOr<uint64_t> LoContents =
-        BC.getUnsignedValueAtAddress(Function.getAddress() + LoRel->Offset,
-                                     Relocation::getSizeForType(LoRel->Type));
+    ErrorOr<uint64_t> LoContents = BC.getUnsignedValueAtAddress(
+        Function.getAddress() + LoRel->Offset, RH.getSizeForType(LoRel->Type));
     assert(HiContents && LoContents &&
            "cannot read RISC-V GOT relocation pair");
 
-    return Relocation::extractValue(ELF::R_RISCV_PCREL_HI20, *HiContents,
-                                    HiAddress) +
-           Relocation::extractValue(LoRel->Type, *LoContents,
-                                    Function.getAddress() + LoRel->Offset);
+    return RH.extractValue(ELF::R_RISCV_PCREL_HI20, *HiContents, HiAddress) +
+           RH.extractValue(LoRel->Type, *LoContents,
+                           Function.getAddress() + LoRel->Offset);
   }
 
   return Rel.Value;
@@ -181,6 +182,7 @@ bool RISCVMCSymbolizer::tryAddingSymbolicOperand(
     MCInst &Inst, raw_ostream &CStream, int64_t Value, uint64_t InstAddress,
     bool IsBranch, uint64_t ImmOffset, uint64_t ImmSize, uint64_t InstSize) {
   BinaryContext &BC = Function.getBinaryContext();
+  const RelocationHandler &RH = BC.getRelocationHandler();
   MCContext *Ctx = BC.Ctx.get();
   const uint64_t InstOffset = InstAddress - Function.getAddress();
 
@@ -201,7 +203,7 @@ bool RISCVMCSymbolizer::tryAddingSymbolicOperand(
   MCSymbol *Symbol = Rel->Symbol;
   uint64_t Addend = Rel->Addend;
 
-  if (Relocation::isInstructionReference(Rel->Type)) {
+  if (RH.isInstructionReference(Rel->Type)) {
     if (!CreateNewSymbols)
       return false;
     auto [It, _] =
@@ -216,8 +218,8 @@ bool RISCVMCSymbolizer::tryAddingSymbolicOperand(
   // GOT high relocations name the object stored in the GOT, not the GOT entry
   // addressed by AUIPC. Preserve the actual entry address using a zero-based
   // symbol, as the RISC-V emitter reuses the input GOT.
-  if (Relocation::isGOT(Rel->Type)) {
-    assert(Relocation::isPCRelative(Rel->Type) &&
+  if (RH.isGOT(Rel->Type)) {
+    assert(RH.isPCRelative(Rel->Type) &&
            "GOT relocation must be PC-relative on RISC-V");
     Symbol = BC.registerNameAtAddress("__BOLT_got_zero", 0, 0, 0);
     Addend = getGOTValue(*Rel) + InstAddress;
diff --git a/bolt/lib/Target/RISCV/RISCVRelocationHandler.cpp b/bolt/lib/Target/RISCV/RISCVRelocationHandler.cpp
new file mode 100644
index 0000000000000..e778a9d93bbec
--- /dev/null
+++ b/bolt/lib/Target/RISCV/RISCVRelocationHandler.cpp
@@ -0,0 +1,348 @@
+//===- RISCVRelocationHandler.cpp
+//-------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the RISCV relocation handler.
+//
+//===----------------------------------------------------------------------===//
+
+#include "bolt/Core/Relocation.h"
+#include "llvm/Object/ELF.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace bolt;
+
+namespace ELFReserved {
+enum {
+  R_RISCV_TPREL_I = 49,
+  R_RISCV_TPREL_S = 50,
+};
+} // namespace ELFReserved
+
+namespace {
+
+class RISCVRelocationHandler final : public RelocationHandler {
+  bool Is64Bit;
+
+public:
+  explicit RISCVRelocationHandler(bool Is64Bit) : Is64Bit(Is64Bit) {}
+
+  bool isSupported(uint32_t Type) const override;
+  size_t getSizeForType(uint32_t Type) const override;
+  bool skipRelocationType(uint32_t Type) const override;
+
+  uint64_t encodeValue(uint32_t Type, uint64_t Value,
+                       uint64_t PC) const override;
+  bool canEncodeValue(uint32_t Type, uint64_t Value,
+                      uint64_t PC) const override;
+  uint64_t extractValue(uint32_t Type, uint64_t Contents,
+                        uint64_t PC) const override;
+
+  bool isGOT(uint32_t Type) const override;
+  bool isRelative(uint32_t Type) const override;
+  bool isIRelative(uint32_t Type) const override;
+  bool isTLS(uint32_t Type) const override;
+  bool isInstructionReference(uint32_t Type) const override;
+  bool isPCRelative(uint32_t Type) const override;
+
+  uint32_t getNone() const override;
+  uint32_t getPC32() const override;
+  uint32_t getPC64() const override;
+  uint32_t getAbs64() const override;
+  uint32_t getRelative() const override;
+
+  MCBinaryExpr::Opcode getComposeOpcodeFor(uint32_t Type) const override;
+  void printType(raw_ostream &OS, uint32_t Type) const override;
+};
+
+} // namespace
+
+bool RISCVRelocationHandler::isRelative(uint32_t Type) const {
+  return Type == ELF::R_RISCV_RELATIVE;
+}
+
+bool RISCVRelocationHandler::isIRelative(uint32_t Type) const {
+  if (!Is64Bit)
+    llvm_unreachable("not implemented");
+  return Type == ELF::R_RISCV_IRELATIVE;
+}
+
+bool RISCVRelocationHandler::isInstructionReference(uint32_t Type) const {
+  return Type == ELF::R_RISCV_PCREL_LO12_I || Type == ELF::R_RISCV_PCREL_LO12_S;
+}
+
+uint32_t RISCVRelocationHandler::getNone() const { return ELF::R_RISCV_NONE; }
+
+uint32_t RISCVRelocationHandler::getPC32() const {
+  return ELF::R_RISCV_32_PCREL;
+}
+
+uint32_t RISCVRelocationHandler::getPC64() const {
+  llvm_unreachable("not implemented");
+}
+
+uint32_t RISCVRelocationHandler::getAbs64() const { return ELF::R_RISCV_64; }
+
+uint32_t RISCVRelocationHandler::getRelative() const {
+  if (!Is64Bit)
+    llvm_unreachable("not implemented");
+  return ELF::R_RISCV_RELATIVE;
+}
+
+MCBinaryExpr::Opcode
+RISCVRelocationHandler::getComposeOpcodeFor(uint32_t Type) const {
+  switch (Type) {
+  default:
+    llvm_unreachable("not implemented");
+  case ELF::R_RISCV_ADD32:
+    return MCBinaryExpr::Add;
+  case ELF::R_RISCV_SUB32:
+    return MCBinaryExpr::Sub;
+  }
+}
+
+void RISCVRelocationHandler::printType(raw_ostream &OS, uint32_t Type) const {
+  OS << object::getELFRelocationTypeName(ELF::EM_RISCV, Type);
+}
+
+bool RISCVRelocationHandler::isSupported(uint32_t Type) const {
+  switch (Type) {
+  default:
+    return false;
+  case ELF::R_RISCV_JAL:
+  case ELF::R_RISCV_CALL:
+  case ELF::R_RISCV_CALL_PLT:
+  case ELF::R_RISCV_BRANCH:
+  case ELF::R_RISCV_RELAX:
+  case ELF::R_RISCV_GOT_HI20:
+  case ELF::R_RISCV_PCREL_HI20:
+  case ELF::R_RISCV_PCREL_LO12_I:
+  case ELF::R_RISCV_PCREL_LO12_S:
+  case ELF::R_RISCV_RVC_JUMP:
+  case ELF::R_RISCV_RVC_BRANCH:
+  case ELF::R_RISCV_ADD32:
+  case ELF::R_RISCV_SUB32:
+  case ELF::R_RISCV_HI20:
+  case ELF::R_RISCV_LO12_I:
+  case ELF::R_RISCV_LO12_S:
+  case ELF::R_RISCV_32:
+  case ELF::R_RISCV_64:
+  case ELF::R_RISCV_TLS_GOT_HI20:
+  case ELF::R_RISCV_TLS_GD_HI20:
+  case ELF::R_RISCV_TPREL_HI20:
+  case ELF::R_RISCV_TPREL_ADD:
+  case ELF::R_RISCV_TPREL_LO12_I:
+  case ELF::R_RISCV_TPREL_LO12_S:
+  case ELFReserved::R_RISCV_TPREL_I:
+  case ELFReserved::R_RISCV_TPREL_S:
+    return true;
+  }
+}
+
+size_t RISCVRelocationHandler::getSizeForType(uint32_t Type) const {
+  switch (Type) {
+  default:
+    errs() << object::getELFRelocationTypeName(ELF::EM_RISCV, Type) << '\n';
+    llvm_unreachable("unsupported relocation type");
+  case ELF::R_RISCV_RVC_JUMP:
+  case ELF::R_RISCV_RVC_BRANCH:
+    return 2;
+  case ELF::R_RISCV_JAL:
+  case ELF::R_RISCV_BRANCH:
+  case ELF::R_RISCV_PCREL_HI20:
+  case ELF::R_RISCV_PCREL_LO12_I:
+  case ELF::R_RISCV_PCREL_LO12_S:
+  case ELF::R_RISCV_32_PCREL:
+  case ELF::R_RISCV_ADD32:
+  case ELF::R_RISCV_SUB32:
+  case ELF::R_RISCV_HI20:
+  case ELF::R_RISCV_LO12_I:
+  case ELF::R_RISCV_LO12_S:
+  case ELF::R_RISCV_32:
+    return 4;
+  case ELF::R_RISCV_64:
+  case ELF::R_RISCV_CALL:
+  case ELF::R_RISCV_CALL_PLT:
+  case ELF::R_RISCV_GOT_HI20:
+  case ELF::R_RISCV_TLS_GOT_HI20:
+  case ELF::R_RISCV_TLS_GD_HI20:
+    // See extractValue for why this is necessary.
+    return 8;
+  }
+}
+
+bool RISCVRelocationHandler::skipRelocationType(uint32_t Type) const {
+  switch (Type) {
+  default:
+    return false;
+  case ELF::R_RISCV_NONE:
+  case ELF::R_RISCV_RELAX:
+    return true;
+  }
+}
+
+bool RISCVRelocationHandler::canEncodeValue(uint32_t Type, uint64_t Value,
+                                            uint64_t PC) const {
+  switch (Type) {
+  default:
+    llvm_unreachable("unsupported relocation");
+  case ELF::R_RISCV_32:
+  case ELF::R_RISCV_64:
+    return true;
+  }
+}
+
+uint64_t RISCVRelocationHandler::encodeValue(uint32_t Type, uint64_t Value,
+                                             uint64_t PC) const {
+  switch (Type) {
+  default:
+    llvm_unreachable("unsupported relocation");
+  case ELF::R_RISCV_32:
+  case ELF::R_RISCV_64:
+    break;
+  }
+  return Value;
+}
+
+static uint64_t extractUImmRISCV(uint32_t Contents) {
+  return SignExtend64<32>(Contents & 0xfffff000);
+}
+
+static uint64_t extractIImmRISCV(uint32_t Contents) {
+  return SignExtend64<12>(Contents >> 20);
+}
+
+static uint64_t extractSImmRISCV(uint32_t Contents) {
+  return SignExtend64<12>(((Contents >> 7) & 0x1f) | ((Contents >> 25) << 5));
+}
+
+static uint64_t extractJImmRISCV(uint32_t Contents) {
+  return SignExtend64<21>(
+      (((Contents >> 21) & 0x3ff) << 1) | (((Contents >> 20) & 0x1) << 11) |
+      (((Contents >> 12) & 0xff) << 12) | (((Contents >> 31) & 0x1) << 20));
+}
+
+static uint64_t extractBImmRISCV(uint32_t Contents) {
+  return SignExtend64<13>(
+      (((Contents >> 8) & 0xf) << 1) | (((Contents >> 25) & 0x3f) << 5) |
+      (((Contents >> 7) & 0x1) << 11) | (((Contents >> 31) & 0x1) << 12));
+}
+
+uint64_t RISCVRelocationHandler::extractValue(uint32_t Type, uint64_t Contents,
+                                              uint64_t PC) const {
+  switch (Type) {
+  default:
+    errs() << object::getELFRelocationTypeName(ELF::EM_RISCV, Type) << '\n';
+    llvm_unreachable("unsupported relocation type");
+  case ELF::R_RISCV_JAL:
+    return extractJImmRISCV(Contents);
+  case ELF::R_RISCV_CALL:
+  case ELF::R_RISCV_CALL_PLT:
+    // The psABI "Relocations" chapter's "Procedure Calls" section defines
+    // R_RISCV_CALL and R_RISCV_CALL_PLT over an AUIPC/JALR pair. Decode both
+    // instructions so the addend includes the low 12 bits carried by JALR.
+    return extractUImmRISCV(Contents & 0xffffffff) +
+           extractIImmRISCV(Contents >> 32);
+  case ELF::R_RISCV_BRANCH:
+    return extractBImmRISCV(Contents);
+  case ELF::R_RISCV_GOT_HI20:
+  case ELF::R_RISCV_TLS_GOT_HI20:
+  case ELF::R_RISCV_TLS_GD_HI20:
+    // We need to know the exact address of the GOT entry so we extract the
+    // value from both the AUIPC and L[D|W]. We cannot rely on the symbol in the
+    // relocation for this since it simply refers to the object that is stored
+    // in the GOT entry, not to the entry itself.
+    return extractUImmRISCV(Contents & 0xffffffff) +
+           extractIImmRISCV(Contents >> 32);
+  case ELF::R_RISCV_PCREL_HI20:
+  case ELF::R_RISCV_HI20:
+    return extractUImmRISCV(Contents);
+  case ELF::R_RISCV_PCREL_LO12_I:
+  case ELF::R_RISCV_LO12_I:
+    return extractIImmRISCV(Contents);
+  case ELF::R_RISCV_PCREL_LO12_S:
+  case ELF::R_RISCV_LO12_S:
+    return extractSImmRISCV(Contents);
+  case ELF::R_RISCV_RVC_JUMP:
+    return SignExtend64<11>(Contents >> 2);
+  case ELF::R_RISCV_RVC_BRANCH:
+    return SignExtend64<8>(((Contents >> 2) & 0x1f) | ((Contents >> 5) & 0xe0));
+  case ELF::R_RISCV_ADD32:
+  case ELF::R_RISCV_SUB32:
+  case ELF::R_RISCV_32:
+  case ELF::R_RISCV_64:
+    return Contents;
+  }
+}
+
+bool RISCVRelocationHandler::isGOT(uint32_t Type) const {
+  switch (Type) {
+  default:
+    return false;
+  case ELF::R_RISCV_GOT_HI20:
+  case ELF::R_RISCV_TLS_GOT_HI20:
+  case ELF::R_RISCV_TLS_GD_HI20:
+    return true;
+  }
+}
+
+bool RISCVRelocationHandler::isTLS(uint32_t Type) const {
+  switch (Type) {
+  default:
+    return false;
+  case ELF::R_RISCV_TLS_GOT_HI20:
+  case ELF::R_RISCV_TLS_GD_HI20:
+  case ELF::R_RISCV_TPREL_HI20:
+  case ELF::R_RISCV_TPREL_ADD:
+  case ELF::R_RISCV_TPREL_LO12_I:
+  case ELF::R_RISCV_TPREL_LO12_S:
+  case ELFReserved::R_RISCV_TPREL_I:
+  case ELFReserved::R_RISCV_TPREL_S:
+    return true;
+  }
+}
+
+bool RISCVRelocationHandler::isPCRelative(uint32_t Type) const {
+  switch (Type) {
+  default:
+    llvm_unreachable("Unknown relocation type");
+  case ELF::R_RISCV_ADD32:
+  case ELF::R_RISCV_SUB32:
+  case ELF::R_RISCV_HI20:
+  case ELF::R_RISCV_LO12_I:
+  case ELF::R_RISCV_LO12_S:
+  case ELF::R_RISCV_32:
+  case ELF::R_RISCV_64:
+    return false;
+  case ELF::R_RISCV_JAL:
+  case ELF::R_RISCV_CALL:
+  case ELF::R_RISCV_CALL_PLT:
+  case ELF::R_RISCV_BRANCH:
+  case ELF::R_RISCV_GOT_HI20:
+  case ELF::R_RISCV_PCREL_HI20:
+  case ELF::R_RISCV_PCREL_LO12_I:
+  case ELF::R_RISCV_PCREL_LO12_S:
+  case ELF::R_RISCV_RVC_JUMP:
+  case ELF::R_RISCV_RVC_BRANCH:
+  case ELF::R_RISCV_32_PCREL:
+  case ELF::R_RISCV_TLS_GOT_HI20:
+  case ELF::R_RISCV_TLS_GD_HI20:
+    return true;
+  }
+}
+
+namespace llvm::bolt {
+
+std::unique_ptr<RelocationHandler> createRISCVRelocationHandler(bool Is64Bit) {
+  return std::make_unique<RISCVRelocationHandler>(Is64Bit);
+}
+
+} // namespace llvm::bolt
diff --git a/bolt/lib/Target/X86/CMakeLists.txt b/bolt/lib/Target/X86/CMakeLists.txt
index 0da4713b191aa..bdf8ff317253a 100644
--- a/bolt/lib/Target/X86/CMakeLists.txt
+++ b/bolt/lib/Target/X86/CMakeLists.txt
@@ -21,6 +21,9 @@ add_llvm_library(LLVMBOLTTargetX86
   X86MCPlusBuilder.cpp
   X86MCSymbolizer.cpp
 
+  # X86RelocationHandler.cpp is part of LLVMBOLTCore to avoid a circular
+  # dependency between the core relocation factory and LLVMBOLTTargetX86.
+  PARTIAL_SOURCES_INTENDED
   NO_EXPORT
   DISABLE_LLVM_LINK_LLVM_DYLIB
 
diff --git a/bolt/lib/Target/X86/X86MCSymbolizer.cpp b/bolt/lib/Target/X86/X86MCSymbolizer.cpp
index 0e0ad9270550c..41e5eb9a495da 100644
--- a/bolt/lib/Target/X86/X86MCSymbolizer.cpp
+++ b/bolt/lib/Target/X86/X86MCSymbolizer.cpp
@@ -12,6 +12,7 @@
 #include "bolt/Core/BinaryFunction.h"
 #include "bolt/Core/MCPlusBuilder.h"
 #include "bolt/Core/Relocation.h"
+#include "llvm/BinaryFormat/ELF.h"
 #include "llvm/MC/MCInst.h"
 #include "llvm/MC/MCRegisterInfo.h"
 
@@ -20,6 +21,12 @@
 namespace llvm {
 namespace bolt {
 
+static bool isGOTPCRELX(uint32_t Type) {
+  return Type == ELF::R_X86_64_GOTPCRELX || Type == ELF::R_X86_64_REX_GOTPCRELX;
+}
+
+static bool isGOTPC64(uint32_t Type) { return Type == ELF::R_X86_64_GOTPC64; }
+
 X86MCSymbolizer::~X86MCSymbolizer() {}
 
 bool X86MCSymbolizer::tryAddingSymbolicOperand(
@@ -97,7 +104,7 @@ bool X86MCSymbolizer::tryAddingSymbolicOperand(
   const uint64_t InstOffset = InstAddress - Function.getAddress();
   const Relocation *Relocation =
       Function.getRelocationInRange(InstOffset, InstOffset + InstSize);
-  if (Relocation && Relocation::isX86GOTPCRELX(Relocation->Type)) {
+  if (Relocation && isGOTPCRELX(Relocation->Type)) {
     // If the operand is PC-relative, convert it without using the relocation
     // information. For GOTPCRELX, it is safe to use the absolute address
     // instead of extracting the addend from the relocation, as non-standard
@@ -133,7 +140,7 @@ bool X86MCSymbolizer::tryAddingSymbolicOperand(
   // GOTPC64 is special because the X86 Assembler doesn't know how to emit
   // a PC-relative 8-byte fixup, which is what we need to cover this. The
   // only way to do this is to use the symbol name _GLOBAL_OFFSET_TABLE_.
-  if (Relocation::isX86GOTPC64(Relocation->Type)) {
+  if (isGOTPC64(Relocation->Type)) {
     auto PairOrErr = handleGOTPC64(*Relocation, InstAddress);
     if (auto E = PairOrErr.takeError()) {
       Function.setSimple(false);
@@ -145,18 +152,20 @@ bool X86MCSymbolizer::tryAddingSymbolicOperand(
     return true;
   }
 
+  const bool IsPCRelative =
+      BC.getRelocationHandler().isPCRelative(Relocation->Type);
   uint64_t SymbolValue = Relocation->Value - Relocation->Addend;
-  if (Relocation->isPCRelative())
+  if (IsPCRelative)
     SymbolValue += InstAddress + ImmOffset;
 
   // Process reference to the symbol.
   if (CreateNewSymbols)
-    BC.handleAddressRef(SymbolValue, Function, Relocation->isPCRelative());
+    BC.handleAddressRef(SymbolValue, Function, IsPCRelative);
 
   uint64_t Addend = Relocation->Addend;
   // Real addend for pc-relative targets is adjusted with a delta from
   // the relocation placement to the next instruction.
-  if (Relocation->isPCRelative())
+  if (IsPCRelative)
     Addend += InstOffset + InstSize - Relocation->Offset;
 
   addOperand(Relocation->Symbol, Addend);
diff --git a/bolt/lib/Target/X86/X86RelocationHandler.cpp b/bolt/lib/Target/X86/X86RelocationHandler.cpp
new file mode 100644
index 0000000000000..4895bfd4fd449
--- /dev/null
+++ b/bolt/lib/Target/X86/X86RelocationHandler.cpp
@@ -0,0 +1,224 @@
+//===- X86RelocationHandler.cpp
+//---------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the X86 relocation handler.
+//
+//===----------------------------------------------------------------------===//
+
+#include "bolt/Core/Relocation.h"
+#include "llvm/Object/ELF.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace bolt;
+
+namespace {
+
+class X86RelocationHandler final : public RelocationHandler {
+public:
+  bool isSupported(uint32_t Type) const override;
+  size_t getSizeForType(uint32_t Type) const override;
+  bool skipRelocationType(uint32_t Type) const override;
+
+  uint64_t encodeValue(uint32_t Type, uint64_t Value,
+                       uint64_t PC) const override;
+  bool canEncodeValue(uint32_t Type, uint64_t Value,
+                      uint64_t PC) const override;
+  uint64_t extractValue(uint32_t Type, uint64_t Contents,
+                        uint64_t PC) const override;
+
+  bool isGOT(uint32_t Type) const override;
+  bool isRelative(uint32_t Type) const override;
+  bool isIRelative(uint32_t Type) const override;
+  bool isTLS(uint32_t Type) const override;
+  bool isPCRelative(uint32_t Type) const override;
+
+  uint32_t getNone() const override;
+  uint32_t getPC32() const override;
+  uint32_t getPC64() const override;
+  uint32_t getAbs64() const override;
+  uint32_t getRelative() const override;
+
+  void printType(raw_ostream &OS, uint32_t Type) const override;
+};
+
+} // namespace
+
+bool X86RelocationHandler::canEncodeValue(uint32_t, uint64_t, uint64_t) const {
+  return true;
+}
+
+bool X86RelocationHandler::isRelative(uint32_t Type) const {
+  return Type == ELF::R_X86_64_RELATIVE;
+}
+
+bool X86RelocationHandler::isIRelative(uint32_t Type) const {
+  return Type == ELF::R_X86_64_IRELATIVE;
+}
+
+uint32_t X86RelocationHandler::getNone() const { return ELF::R_X86_64_NONE; }
+
+uint32_t X86RelocationHandler::getPC32() const { return ELF::R_X86_64_PC32; }
+
+uint32_t X86RelocationHandler::getPC64() const { return ELF::R_X86_64_PC64; }
+
+uint32_t X86RelocationHandler::getAbs64() const { return ELF::R_X86_64_64; }
+
+uint32_t X86RelocationHandler::getRelative() const {
+  return ELF::R_X86_64_RELATIVE;
+}
+
+void X86RelocationHandler::printType(raw_ostream &OS, uint32_t Type) const {
+  OS << object::getELFRelocationTypeName(ELF::EM_X86_64, Type);
+}
+
+bool X86RelocationHandler::isSupported(uint32_t Type) const {
+  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_GOTPC64:
+  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;
+  }
+}
+
+size_t X86RelocationHandler::getSizeForType(uint32_t Type) const {
+  switch (Type) {
+  default:
+    errs() << object::getELFRelocationTypeName(ELF::EM_X86_64, Type) << '\n';
+    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:
+  case ELF::R_X86_64_GOTPC64:
+    return 8;
+  }
+}
+
+bool X86RelocationHandler::skipRelocationType(uint32_t Type) const {
+  return Type == ELF::R_X86_64_NONE;
+}
+
+uint64_t X86RelocationHandler::encodeValue(uint32_t Type, uint64_t Value,
+                                           uint64_t PC) const {
+  switch (Type) {
+  default:
+    llvm_unreachable("unsupported relocation");
+  case ELF::R_X86_64_64:
+  case ELF::R_X86_64_32:
+    break;
+  case ELF::R_X86_64_PC32:
+    Value -= PC;
+    break;
+  }
+  return Value;
+}
+
+uint64_t X86RelocationHandler::extractValue(uint32_t Type, uint64_t Contents,
+                                            uint64_t PC) const {
+  if (Type == ELF::R_X86_64_32S)
+    return SignExtend64<32>(Contents);
+  if (isPCRelative(Type))
+    return SignExtend64(Contents, 8 * getSizeForType(Type));
+  return Contents;
+}
+
+bool X86RelocationHandler::isGOT(uint32_t Type) const {
+  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 X86RelocationHandler::isTLS(uint32_t Type) const {
+  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 X86RelocationHandler::isPCRelative(uint32_t Type) const {
+  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_GOTOFF64:
+  case ELF::R_X86_64_GOTPC32:
+  case ELF::R_X86_64_GOTPC64:
+  case ELF::R_X86_64_GOTTPOFF:
+  case ELF::R_X86_64_GOTPCRELX:
+  case ELF::R_X86_64_REX_GOTPCRELX:
+    return true;
+  }
+}
+
+namespace llvm::bolt {
+
+std::unique_ptr<RelocationHandler> createX86RelocationHandler() {
+  return std::make_unique<X86RelocationHandler>();
+}
+
+} // namespace llvm::bolt
diff --git a/bolt/unittests/Core/BinaryContext.cpp b/bolt/unittests/Core/BinaryContext.cpp
index f48eba1b74405..a8c00a3bb24b6 100644
--- a/bolt/unittests/Core/BinaryContext.cpp
+++ b/bolt/unittests/Core/BinaryContext.cpp
@@ -53,7 +53,6 @@ struct BinaryContextTester : public testing::TestWithParam<Triple::ArchType> {
 
   void initializeBOLT() {
     const Triple TheTriple = GetParam();
-    Relocation::Arch = TheTriple.getArch();
     // Minimal test ELFs have no RISC-V attributes. RISC-V needs an empty
     // feature set for +relax, while other targets reject a non-null one.
     SubtargetFeatures Features;
@@ -68,6 +67,20 @@ struct BinaryContextTester : public testing::TestWithParam<Triple::ArchType> {
   std::unique_ptr<ObjectFile> ObjFile;
   std::unique_ptr<BinaryContext> BC;
 };
+
+TEST(RelocationHandlerTest, ArchitectureStateIsIndependent) {
+  std::unique_ptr<RelocationHandler> X86Handler =
+      createRelocationHandler(Triple::x86_64);
+  std::unique_ptr<RelocationHandler> AArch64Handler =
+      createRelocationHandler(Triple::aarch64);
+
+  EXPECT_EQ(X86Handler->getPC32(), ELF::R_X86_64_PC32);
+  EXPECT_EQ(AArch64Handler->getPC32(), ELF::R_AARCH64_PREL32);
+  EXPECT_TRUE(X86Handler->isSupported(ELF::R_X86_64_PC32));
+  EXPECT_FALSE(X86Handler->isSupported(ELF::R_AARCH64_CALL26));
+  EXPECT_TRUE(AArch64Handler->isSupported(ELF::R_AARCH64_CALL26));
+  EXPECT_FALSE(AArch64Handler->isSupported(ELF::R_X86_64_PC32));
+}
 } // namespace
 
 #ifdef X86_AVAILABLE
@@ -93,7 +106,7 @@ TEST_P(BinaryContextTester, FlushPendingRelocCALL26) {
   if (GetParam() != Triple::aarch64)
     GTEST_SKIP();
 
-  // This test checks that encodeValueAArch64 used by flushPendingRelocations
+  // This test checks that AArch64 encodeValue used by flushPendingRelocations
   // returns correctly encoded values for CALL26 relocation for both backward
   // and forward branches.
   //
@@ -140,7 +153,7 @@ TEST_P(BinaryContextTester, FlushPendingRelocJUMP26) {
   if (GetParam() != Triple::aarch64)
     GTEST_SKIP();
 
-  // This test checks that encodeValueAArch64 used by flushPendingRelocations
+  // This test checks that AArch64 encodeValue used by flushPendingRelocations
   // returns correctly encoded values for R_AARCH64_JUMP26 relocation for both
   // backward and forward branches.
   //
diff --git a/bolt/unittests/Core/CMakeLists.txt b/bolt/unittests/Core/CMakeLists.txt
index 6b9b73bd16052..477562b2eef1d 100644
--- a/bolt/unittests/Core/CMakeLists.txt
+++ b/bolt/unittests/Core/CMakeLists.txt
@@ -13,6 +13,7 @@ add_bolt_unittest(CoreTests
   MCPlusBuilder.cpp
   MemoryMaps.cpp
   DynoStats.cpp
+  Relocation.cpp
 
   # FIXME CoreTests uses `llvm::detail::TakeError(llvm::Error)`, but linking
   #       to LLVMTestingSupport introduces a transitive dependency on the
diff --git a/bolt/unittests/Core/MCPlusBuilder.cpp b/bolt/unittests/Core/MCPlusBuilder.cpp
index 6cd25644d378f..606de9fde80f4 100644
--- a/bolt/unittests/Core/MCPlusBuilder.cpp
+++ b/bolt/unittests/Core/MCPlusBuilder.cpp
@@ -82,7 +82,6 @@ struct MCPlusBuilderTester : public testing::TestWithParam<Triple::ArchType> {
 
   void initializeBolt() {
     const Triple TheTriple = GetParam();
-    Relocation::Arch = TheTriple.getArch();
     // Minimal test ELFs have no RISC-V attributes. RISC-V needs an empty
     // feature set for +relax, while other targets reject a non-null one.
     SubtargetFeatures Features;
diff --git a/bolt/unittests/Core/MemoryMaps.cpp b/bolt/unittests/Core/MemoryMaps.cpp
index 0bbd98c36c06e..c725bb88bb10b 100644
--- a/bolt/unittests/Core/MemoryMaps.cpp
+++ b/bolt/unittests/Core/MemoryMaps.cpp
@@ -62,7 +62,6 @@ struct MemoryMapsTester : public testing::TestWithParam<Triple::ArchType> {
 
   void initializeBOLT() {
     const Triple TheTriple = GetParam();
-    Relocation::Arch = TheTriple.getArch();
     // Minimal test ELFs have no RISC-V attributes. RISC-V needs an empty
     // feature set for +relax, while other targets reject a non-null one.
     SubtargetFeatures Features;
diff --git a/bolt/unittests/Core/Relocation.cpp b/bolt/unittests/Core/Relocation.cpp
new file mode 100644
index 0000000000000..93695aae8a913
--- /dev/null
+++ b/bolt/unittests/Core/Relocation.cpp
@@ -0,0 +1,75 @@
+//===- bolt/unittest/Core/Relocation.cpp ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "bolt/Core/Relocation.h"
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/TargetParser/Triple.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::bolt;
+
+#ifdef AARCH64_AVAILABLE
+
+namespace {
+
+TEST(AArch64RelocationHandlerTest, ClassificationAndSize) {
+  std::unique_ptr<RelocationHandler> Handler =
+      createRelocationHandler(Triple::aarch64);
+
+  EXPECT_TRUE(Handler->isSupported(ELF::R_AARCH64_CALL26));
+  EXPECT_TRUE(Handler->isSupported(ELF::R_AARCH64_PREL32));
+  EXPECT_FALSE(Handler->isSupported(ELF::R_AARCH64_NONE));
+
+  EXPECT_EQ(Handler->getSizeForType(ELF::R_AARCH64_CALL26), 4u);
+  EXPECT_EQ(Handler->getSizeForType(ELF::R_AARCH64_PREL32), 4u);
+  EXPECT_EQ(Handler->getSizeForType(ELF::R_AARCH64_ABS64), 8u);
+
+  EXPECT_TRUE(Handler->isPCRelative(ELF::R_AARCH64_CALL26));
+  EXPECT_TRUE(Handler->isPCRelative(ELF::R_AARCH64_PREL32));
+  EXPECT_FALSE(Handler->isPCRelative(ELF::R_AARCH64_ABS64));
+}
+
+TEST(AArch64RelocationHandlerTest, EncodeAndExtractCall26) {
+  std::unique_ptr<RelocationHandler> Handler =
+      createRelocationHandler(Triple::aarch64);
+  constexpr uint64_t PC = 0x1000;
+  constexpr uint64_t Target = 0x1100;
+
+  const uint64_t Encoded =
+      Handler->encodeValue(ELF::R_AARCH64_CALL26, Target, PC);
+  EXPECT_EQ(Encoded, 0x94000040u);
+  EXPECT_EQ(Handler->extractValue(ELF::R_AARCH64_CALL26, Encoded, PC), Target);
+}
+
+TEST(AArch64RelocationHandlerTest, EncodeAndExtractPrel32) {
+  std::unique_ptr<RelocationHandler> Handler =
+      createRelocationHandler(Triple::aarch64);
+  constexpr uint64_t PC = 0x1000;
+  constexpr uint64_t Target = 0xf00;
+
+  const uint64_t Encoded =
+      Handler->encodeValue(ELF::R_AARCH64_PREL32, Target, PC);
+  EXPECT_EQ(Encoded, static_cast<uint64_t>(-0x100));
+  EXPECT_EQ(Handler->extractValue(ELF::R_AARCH64_PREL32, Encoded, PC), Target);
+}
+
+TEST(AArch64RelocationHandlerTest, Call26EncodingRange) {
+  std::unique_ptr<RelocationHandler> Handler =
+      createRelocationHandler(Triple::aarch64);
+  constexpr uint64_t PC = 0x1000;
+  constexpr uint64_t Range = uint64_t{1} << 27;
+
+  EXPECT_TRUE(
+      Handler->canEncodeValue(ELF::R_AARCH64_CALL26, PC + Range - 4, PC));
+  EXPECT_FALSE(Handler->canEncodeValue(ELF::R_AARCH64_CALL26, PC + Range, PC));
+}
+
+} // namespace
+
+#endif
diff --git a/bolt/unittests/Passes/LivenessAnalysis.cpp b/bolt/unittests/Passes/LivenessAnalysis.cpp
index 7ddf683a855ec..8ef3504a9c45b 100644
--- a/bolt/unittests/Passes/LivenessAnalysis.cpp
+++ b/bolt/unittests/Passes/LivenessAnalysis.cpp
@@ -65,7 +65,6 @@ struct LivenessAnalysisTester
   }
 
   void initializeBolt() {
-    Relocation::Arch = ObjFile->makeTriple().getArch();
     BC = cantFail(BinaryContext::createBinaryContext(
         ObjFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),
         ObjFile->getFileName(), nullptr, true, DWARFContext::create(*ObjFile),
diff --git a/bolt/unittests/Passes/PointerAuthCFIFixup.cpp b/bolt/unittests/Passes/PointerAuthCFIFixup.cpp
index 89896d0ce3fdc..33286d2551c34 100644
--- a/bolt/unittests/Passes/PointerAuthCFIFixup.cpp
+++ b/bolt/unittests/Passes/PointerAuthCFIFixup.cpp
@@ -77,7 +77,6 @@ struct PassTester : public testing::TestWithParam<Triple::ArchType> {
     ObjFile = cantFail(ObjectFile::createObjectFile(Source));
   }
   void initializeBolt() {
-    Relocation::Arch = ObjFile->makeTriple().getArch();
     BC = cantFail(BinaryContext::createBinaryContext(
         ObjFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),
         ObjFile->getFileName(), nullptr, true, DWARFContext::create(*ObjFile),
diff --git a/bolt/unittests/Profile/DataAggregator.cpp b/bolt/unittests/Profile/DataAggregator.cpp
index e5e4611a639c9..5d5ad98dc02a2 100644
--- a/bolt/unittests/Profile/DataAggregator.cpp
+++ b/bolt/unittests/Profile/DataAggregator.cpp
@@ -70,7 +70,6 @@ struct PreAggregatedTestHelper : public testing::Test {
     MemoryBufferRef Source(StringRef(ElfBuf, sizeof(ElfBuf)), "ELF");
     ObjFile = cantFail(ObjectFile::createObjectFile(Source));
 
-    Relocation::Arch = ObjFile->makeTriple().getArch();
     BC = cantFail(BinaryContext::createBinaryContext(
         ObjFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),
         ObjFile->getFileName(), nullptr, /*IsPIC*/ false,
diff --git a/bolt/unittests/Profile/PerfScripts.cpp b/bolt/unittests/Profile/PerfScripts.cpp
index c430460a08827..a429b494562a8 100644
--- a/bolt/unittests/Profile/PerfScripts.cpp
+++ b/bolt/unittests/Profile/PerfScripts.cpp
@@ -64,7 +64,6 @@ struct PerfScriptTestHelper : public testing::TestWithParam<Triple::ArchType> {
 
   void initializeBOLT() {
     const Triple TheTriple = GetParam();
-    Relocation::Arch = TheTriple.getArch();
     // Minimal test ELFs have no RISC-V attributes. RISC-V needs an empty
     // feature set for +relax, while other targets reject a non-null one.
     SubtargetFeatures Features;
diff --git a/bolt/unittests/Profile/PerfSpeEvents.cpp b/bolt/unittests/Profile/PerfSpeEvents.cpp
index 556b49619386b..5f3ecb4618858 100644
--- a/bolt/unittests/Profile/PerfSpeEvents.cpp
+++ b/bolt/unittests/Profile/PerfSpeEvents.cpp
@@ -61,7 +61,6 @@ struct PerfSpeEventsTestHelper : public testing::Test {
   }
 
   void initializeBOLT() {
-    Relocation::Arch = ObjFile->makeTriple().getArch();
     BC = cantFail(BinaryContext::createBinaryContext(
         ObjFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),
         ObjFile->getFileName(), nullptr, /*IsPIC*/ false,

>From 4cb799227ee9b809d41eb8c344809b0e0d80f1d0 Mon Sep 17 00:00:00 2001
From: Maksim <rachinskii.mv at phystech.edu>
Date: Sun, 20 Sep 2026 23:42:22 +0300
Subject: [PATCH 2/2] [BOLT] Review feedback

---
 bolt/include/bolt/Core/BinaryContext.h        | 15 ++--
 bolt/include/bolt/Core/Relocation.h           |  7 +-
 bolt/include/bolt/Rewrite/RewriteInstance.h   |  3 +
 .../Target/AArch64/AArch64RelocationHandler.h | 24 ++++++
 .../Target/RISCV/RISCVRelocationHandler.h     | 24 ++++++
 .../bolt/Target/X86/X86RelocationHandler.h    | 24 ++++++
 bolt/lib/Core/BinaryContext.cpp               | 14 ++--
 bolt/lib/Core/CMakeLists.txt                  |  3 -
 bolt/lib/Core/Relocation.cpp                  | 22 -----
 bolt/lib/Rewrite/MachORewriteInstance.cpp     |  2 +
 bolt/lib/Rewrite/RewriteInstance.cpp          | 40 ++++++++-
 .../AArch64/AArch64RelocationHandler.cpp      |  4 +-
 bolt/lib/Target/AArch64/CMakeLists.txt        |  4 +-
 bolt/lib/Target/RISCV/CMakeLists.txt          |  4 +-
 .../Target/RISCV/RISCVRelocationHandler.cpp   |  4 +-
 bolt/lib/Target/X86/CMakeLists.txt            |  4 +-
 bolt/lib/Target/X86/X86RelocationHandler.cpp  |  4 +-
 bolt/unittests/Core/BinaryContext.cpp         | 47 ++++++++++-
 bolt/unittests/Core/Relocation.cpp            | 82 +++++++++++++++++--
 19 files changed, 256 insertions(+), 75 deletions(-)
 create mode 100644 bolt/include/bolt/Target/AArch64/AArch64RelocationHandler.h
 create mode 100644 bolt/include/bolt/Target/RISCV/RISCVRelocationHandler.h
 create mode 100644 bolt/include/bolt/Target/X86/X86RelocationHandler.h

diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h
index 38aaa128f96a4..94f7a58fdeb8e 100644
--- a/bolt/include/bolt/Core/BinaryContext.h
+++ b/bolt/include/bolt/Core/BinaryContext.h
@@ -320,6 +320,13 @@ class BinaryContext {
     MIB = std::move(TargetBuilder);
   }
 
+  /// Set up target-specific relocation handling.
+  void initializeRelocationHandler(std::unique_ptr<RelocationHandler> Handler) {
+    assert(Handler && "expected a relocation handler");
+    assert(!RelocHandler && "relocation handler is already initialized");
+    RelocHandler = std::move(Handler);
+  }
+
   /// Return function fragments to skip.
   const std::unordered_set<BinaryFunction *> &getFragmentsToSkip() {
     return FragmentsToSkip;
@@ -729,11 +736,6 @@ class BinaryContext {
   std::unique_ptr<DWARFContext> DwCtx;
 
   std::unique_ptr<Triple> TheTriple;
-
-private:
-  std::unique_ptr<RelocationHandler> RelocHandler;
-
-public:
   std::shared_ptr<orc::SymbolStringPool> SSP;
 
   const Target *TheTarget;
@@ -756,6 +758,8 @@ class BinaryContext {
 
   std::unique_ptr<MCPlusBuilder> MIB;
 
+  std::unique_ptr<RelocationHandler> RelocHandler;
+
   std::unique_ptr<const MCRegisterInfo> MRI;
 
   std::unique_ptr<MCDisassembler> DisAsm;
@@ -992,6 +996,7 @@ class BinaryContext {
   bool isMachO() const { return TheTriple->isOSBinFormatMachO(); }
 
   const RelocationHandler &getRelocationHandler() const {
+    assert(RelocHandler && "relocation handler is not initialized");
     return *RelocHandler;
   }
 
diff --git a/bolt/include/bolt/Core/Relocation.h b/bolt/include/bolt/Core/Relocation.h
index 897bb0758a007..19448bed8d485 100644
--- a/bolt/include/bolt/Core/Relocation.h
+++ b/bolt/include/bolt/Core/Relocation.h
@@ -16,8 +16,6 @@
 
 #include "llvm/MC/MCExpr.h"
 #include "llvm/MC/MCStreamer.h"
-#include "llvm/TargetParser/Triple.h"
-#include <memory>
 
 namespace llvm {
 class MCSymbol;
@@ -105,9 +103,6 @@ class RelocationHandler {
   virtual void printType(raw_ostream &OS, uint32_t Type) const = 0;
 };
 
-std::unique_ptr<RelocationHandler>
-createRelocationHandler(Triple::ArchType Arch);
-
 /// Relocation class.
 class Relocation {
 public:
@@ -171,11 +166,11 @@ class Relocation {
 
   bool isRELR() const { return IsRELR; }
 
-  /// Return size of this relocation.
   /// Return the relocation type of \p Rel from llvm::object. It checks for
   /// overflows as BOLT uses 32 bits for the type.
   static uint32_t getType(const object::RelocationRef &Rel);
 
+  /// Return size of this relocation.
   size_t getSize(const RelocationHandler &RH) const {
     return RH.getSizeForType(Type);
   }
diff --git a/bolt/include/bolt/Rewrite/RewriteInstance.h b/bolt/include/bolt/Rewrite/RewriteInstance.h
index 4a253f579c6c2..551c0341b9946 100644
--- a/bolt/include/bolt/Rewrite/RewriteInstance.h
+++ b/bolt/include/bolt/Rewrite/RewriteInstance.h
@@ -657,6 +657,9 @@ MCPlusBuilder *createMCPlusBuilder(const Triple::ArchType Arch,
                                    const MCRegisterInfo *RegInfo,
                                    const MCSubtargetInfo *STI);
 
+std::unique_ptr<RelocationHandler>
+createRelocationHandler(Triple::ArchType Arch);
+
 } // namespace bolt
 } // namespace llvm
 
diff --git a/bolt/include/bolt/Target/AArch64/AArch64RelocationHandler.h b/bolt/include/bolt/Target/AArch64/AArch64RelocationHandler.h
new file mode 100644
index 0000000000000..8ad33e5e74d41
--- /dev/null
+++ b/bolt/include/bolt/Target/AArch64/AArch64RelocationHandler.h
@@ -0,0 +1,24 @@
+//===- AArch64RelocationHandler.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef BOLT_TARGET_AARCH64_AARCH64RELOCATIONHANDLER_H
+#define BOLT_TARGET_AARCH64_AARCH64RELOCATIONHANDLER_H
+
+#include <memory>
+
+namespace llvm {
+namespace bolt {
+
+class RelocationHandler;
+
+std::unique_ptr<RelocationHandler> createAArch64RelocationHandler();
+
+} // namespace bolt
+} // namespace llvm
+
+#endif
diff --git a/bolt/include/bolt/Target/RISCV/RISCVRelocationHandler.h b/bolt/include/bolt/Target/RISCV/RISCVRelocationHandler.h
new file mode 100644
index 0000000000000..0e68f6f09bc93
--- /dev/null
+++ b/bolt/include/bolt/Target/RISCV/RISCVRelocationHandler.h
@@ -0,0 +1,24 @@
+//===- RISCVRelocationHandler.h --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef BOLT_TARGET_RISCV_RISCVRELOCATIONHANDLER_H
+#define BOLT_TARGET_RISCV_RISCVRELOCATIONHANDLER_H
+
+#include <memory>
+
+namespace llvm {
+namespace bolt {
+
+class RelocationHandler;
+
+std::unique_ptr<RelocationHandler> createRISCVRelocationHandler(bool Is64Bit);
+
+} // namespace bolt
+} // namespace llvm
+
+#endif
diff --git a/bolt/include/bolt/Target/X86/X86RelocationHandler.h b/bolt/include/bolt/Target/X86/X86RelocationHandler.h
new file mode 100644
index 0000000000000..bf0d2b886fb07
--- /dev/null
+++ b/bolt/include/bolt/Target/X86/X86RelocationHandler.h
@@ -0,0 +1,24 @@
+//===- X86RelocationHandler.h ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef BOLT_TARGET_X86_X86RELOCATIONHANDLER_H
+#define BOLT_TARGET_X86_X86RELOCATIONHANDLER_H
+
+#include <memory>
+
+namespace llvm {
+namespace bolt {
+
+class RelocationHandler;
+
+std::unique_ptr<RelocationHandler> createX86RelocationHandler();
+
+} // namespace bolt
+} // namespace llvm
+
+#endif
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index d77ecbdf3411b..ae320ab695af3 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -165,14 +165,12 @@ BinaryContext::BinaryContext(std::unique_ptr<MCContext> Ctx,
                              std::unique_ptr<MCDisassembler> DisAsm,
                              JournalingStreams Logger)
     : Ctx(std::move(Ctx)), DwCtx(std::move(DwCtx)),
-      TheTriple(std::move(TheTriple)),
-      RelocHandler(createRelocationHandler(this->TheTriple->getArch())),
-      SSP(std::move(SSP)), TheTarget(TheTarget), TripleName(TripleName),
-      MCE(std::move(MCE)), MOFI(std::move(MOFI)), AsmInfo(std::move(AsmInfo)),
-      MII(std::move(MII)), STI(std::move(STI)),
-      InstPrinter(std::move(InstPrinter)), MIA(std::move(MIA)),
-      MIB(std::move(MIB)), MRI(std::move(MRI)), DisAsm(std::move(DisAsm)),
-      Logger(Logger), InitialDynoStats(isAArch64()) {
+      TheTriple(std::move(TheTriple)), SSP(std::move(SSP)),
+      TheTarget(TheTarget), TripleName(TripleName), MCE(std::move(MCE)),
+      MOFI(std::move(MOFI)), AsmInfo(std::move(AsmInfo)), MII(std::move(MII)),
+      STI(std::move(STI)), InstPrinter(std::move(InstPrinter)),
+      MIA(std::move(MIA)), MIB(std::move(MIB)), MRI(std::move(MRI)),
+      DisAsm(std::move(DisAsm)), Logger(Logger), InitialDynoStats(isAArch64()) {
   RegularPageSize = isAArch64() ? RegularPageSizeAArch64 : RegularPageSizeX86;
   PageAlign = opts::NoHugePages ? RegularPageSize : HugePageSize;
 }
diff --git a/bolt/lib/Core/CMakeLists.txt b/bolt/lib/Core/CMakeLists.txt
index 0a967b94c86e7..430a1c93a9f0c 100644
--- a/bolt/lib/Core/CMakeLists.txt
+++ b/bolt/lib/Core/CMakeLists.txt
@@ -37,9 +37,6 @@ add_llvm_library(LLVMBOLTCore
   MCPlusBuilder.cpp
   ParallelUtilities.cpp
   Relocation.cpp
-  ../Target/AArch64/AArch64RelocationHandler.cpp
-  ../Target/RISCV/RISCVRelocationHandler.cpp
-  ../Target/X86/X86RelocationHandler.cpp
 
   NO_EXPORT
   DISABLE_LLVM_LINK_LLVM_DYLIB
diff --git a/bolt/lib/Core/Relocation.cpp b/bolt/lib/Core/Relocation.cpp
index ddf5e344b8ddc..f4722a7e96368 100644
--- a/bolt/lib/Core/Relocation.cpp
+++ b/bolt/lib/Core/Relocation.cpp
@@ -22,33 +22,11 @@
 using namespace llvm;
 using namespace bolt;
 
-namespace llvm::bolt {
-std::unique_ptr<RelocationHandler> createAArch64RelocationHandler();
-std::unique_ptr<RelocationHandler> createRISCVRelocationHandler(bool Is64Bit);
-std::unique_ptr<RelocationHandler> createX86RelocationHandler();
-} // namespace llvm::bolt
-
 MCBinaryExpr::Opcode
 RelocationHandler::getComposeOpcodeFor(uint32_t Type) const {
   llvm_unreachable("composed relocations are unsupported for this target");
 }
 
-std::unique_ptr<RelocationHandler>
-llvm::bolt::createRelocationHandler(Triple::ArchType Arch) {
-  switch (Arch) {
-  default:
-    llvm_unreachable("Unsupported architecture");
-  case Triple::aarch64:
-    return createAArch64RelocationHandler();
-  case Triple::riscv32:
-    return createRISCVRelocationHandler(false);
-  case Triple::riscv64:
-    return createRISCVRelocationHandler(true);
-  case Triple::x86_64:
-    return createX86RelocationHandler();
-  }
-}
-
 uint32_t Relocation::getType(const object::RelocationRef &Rel) {
   uint64_t RelType = Rel.getType();
   assert(isUInt<32>(RelType) && "BOLT relocation types are 32 bits");
diff --git a/bolt/lib/Rewrite/MachORewriteInstance.cpp b/bolt/lib/Rewrite/MachORewriteInstance.cpp
index 6190d28ff2ad8..72597681ad530 100644
--- a/bolt/lib/Rewrite/MachORewriteInstance.cpp
+++ b/bolt/lib/Rewrite/MachORewriteInstance.cpp
@@ -81,6 +81,8 @@ MachORewriteInstance::MachORewriteInstance(object::MachOObjectFile *InputFile,
     return;
   }
   BC = std::move(BCOrErr.get());
+  BC->initializeRelocationHandler(
+      createRelocationHandler(BC->TheTriple->getArch()));
   BC->initializeTarget(std::unique_ptr<MCPlusBuilder>(
       createMCPlusBuilder(BC->TheTriple->getArch(), BC->MIA.get(),
                           BC->MII.get(), BC->MRI.get(), BC->STI.get())));
diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index da6293f331275..afa87ac99e80f 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -34,6 +34,15 @@
 #include "bolt/Rewrite/MetadataRewriters.h"
 #include "bolt/RuntimeLibs/HugifyRuntimeLibrary.h"
 #include "bolt/RuntimeLibs/InstrumentationRuntimeLibrary.h"
+#ifdef AARCH64_AVAILABLE
+#include "bolt/Target/AArch64/AArch64RelocationHandler.h"
+#endif
+#ifdef RISCV_AVAILABLE
+#include "bolt/Target/RISCV/RISCVRelocationHandler.h"
+#endif
+#ifdef X86_AVAILABLE
+#include "bolt/Target/X86/X86RelocationHandler.h"
+#endif
 #include "bolt/Utils/CommandLineOpts.h"
 #include "bolt/Utils/Utils.h"
 #include "llvm/ADT/AddressRanges.h"
@@ -361,10 +370,9 @@ namespace bolt {
 
 extern const char *BoltRevision;
 
-// Weird location for createMCPlusBuilder, but this is here to avoid a
-// cyclic dependency of libCore (its natural place) and libTarget. libRewrite
-// can depend on libTarget, but not libCore. Since libRewrite is the only
-// user of this function, we define it here.
+// Weird location for the target factories, but this is here to avoid a cyclic
+// dependency of libCore (their natural place) and libTarget. libRewrite can
+// depend on libTarget, but libCore cannot.
 MCPlusBuilder *createMCPlusBuilder(const Triple::ArchType Arch,
                                    const MCInstrAnalysis *Analysis,
                                    const MCInstrInfo *Info,
@@ -388,6 +396,28 @@ MCPlusBuilder *createMCPlusBuilder(const Triple::ArchType Arch,
   llvm_unreachable("architecture unsupported by MCPlusBuilder");
 }
 
+std::unique_ptr<RelocationHandler>
+createRelocationHandler(Triple::ArchType Arch) {
+#ifdef X86_AVAILABLE
+  if (Arch == Triple::x86_64)
+    return createX86RelocationHandler();
+#endif
+
+#ifdef AARCH64_AVAILABLE
+  if (Arch == Triple::aarch64)
+    return createAArch64RelocationHandler();
+#endif
+
+#ifdef RISCV_AVAILABLE
+  if (Arch == Triple::riscv32)
+    return createRISCVRelocationHandler(false);
+  if (Arch == Triple::riscv64)
+    return createRISCVRelocationHandler(true);
+#endif
+
+  llvm_unreachable("architecture unsupported by RelocationHandler");
+}
+
 } // namespace bolt
 } // namespace llvm
 
@@ -466,6 +496,8 @@ RewriteInstance::RewriteInstance(ELFObjectFileBase *File, const int Argc,
     return;
   }
   BC = std::move(BCOrErr.get());
+  BC->initializeRelocationHandler(
+      createRelocationHandler(BC->TheTriple->getArch()));
   BC->initializeTarget(std::unique_ptr<MCPlusBuilder>(
       createMCPlusBuilder(BC->TheTriple->getArch(), BC->MIA.get(),
                           BC->MII.get(), BC->MRI.get(), BC->STI.get())));
diff --git a/bolt/lib/Target/AArch64/AArch64RelocationHandler.cpp b/bolt/lib/Target/AArch64/AArch64RelocationHandler.cpp
index 3c04660ea0b1e..61afb79cd8967 100644
--- a/bolt/lib/Target/AArch64/AArch64RelocationHandler.cpp
+++ b/bolt/lib/Target/AArch64/AArch64RelocationHandler.cpp
@@ -1,5 +1,4 @@
-//===- AArch64RelocationHandler.cpp
-//-----------------------------------------------===//
+//===- AArch64RelocationHandler.cpp - AArch64 relocation handling -------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -11,6 +10,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "bolt/Target/AArch64/AArch64RelocationHandler.h"
 #include "bolt/Core/Relocation.h"
 #include "llvm/Object/ELF.h"
 #include "llvm/Support/ErrorHandling.h"
diff --git a/bolt/lib/Target/AArch64/CMakeLists.txt b/bolt/lib/Target/AArch64/CMakeLists.txt
index ec33fc1656841..b56d382b581d4 100644
--- a/bolt/lib/Target/AArch64/CMakeLists.txt
+++ b/bolt/lib/Target/AArch64/CMakeLists.txt
@@ -21,10 +21,8 @@ endif()
 add_llvm_library(LLVMBOLTTargetAArch64
   AArch64MCPlusBuilder.cpp
   AArch64MCSymbolizer.cpp
+  AArch64RelocationHandler.cpp
 
-  # AArch64RelocationHandler.cpp is part of LLVMBOLTCore to avoid a circular
-  # dependency between the core relocation factory and LLVMBOLTTargetAArch64.
-  PARTIAL_SOURCES_INTENDED
   NO_EXPORT
   DISABLE_LLVM_LINK_LLVM_DYLIB
 
diff --git a/bolt/lib/Target/RISCV/CMakeLists.txt b/bolt/lib/Target/RISCV/CMakeLists.txt
index 922edafae5d48..76dbe2bc2789c 100644
--- a/bolt/lib/Target/RISCV/CMakeLists.txt
+++ b/bolt/lib/Target/RISCV/CMakeLists.txt
@@ -21,10 +21,8 @@ endif()
 add_llvm_library(LLVMBOLTTargetRISCV
   RISCVMCPlusBuilder.cpp
   RISCVMCSymbolizer.cpp
+  RISCVRelocationHandler.cpp
 
-  # RISCVRelocationHandler.cpp is part of LLVMBOLTCore to avoid a circular
-  # dependency between the core relocation factory and LLVMBOLTTargetRISCV.
-  PARTIAL_SOURCES_INTENDED
   NO_EXPORT
   DISABLE_LLVM_LINK_LLVM_DYLIB
 
diff --git a/bolt/lib/Target/RISCV/RISCVRelocationHandler.cpp b/bolt/lib/Target/RISCV/RISCVRelocationHandler.cpp
index e778a9d93bbec..f7f7cb51e9176 100644
--- a/bolt/lib/Target/RISCV/RISCVRelocationHandler.cpp
+++ b/bolt/lib/Target/RISCV/RISCVRelocationHandler.cpp
@@ -1,5 +1,4 @@
-//===- RISCVRelocationHandler.cpp
-//-------------------------------------------------===//
+//===- RISCVRelocationHandler.cpp - RISC-V relocation handling -----------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -11,6 +10,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "bolt/Target/RISCV/RISCVRelocationHandler.h"
 #include "bolt/Core/Relocation.h"
 #include "llvm/Object/ELF.h"
 #include "llvm/Support/ErrorHandling.h"
diff --git a/bolt/lib/Target/X86/CMakeLists.txt b/bolt/lib/Target/X86/CMakeLists.txt
index bdf8ff317253a..f08d6b25ff87a 100644
--- a/bolt/lib/Target/X86/CMakeLists.txt
+++ b/bolt/lib/Target/X86/CMakeLists.txt
@@ -20,10 +20,8 @@ endif()
 add_llvm_library(LLVMBOLTTargetX86
   X86MCPlusBuilder.cpp
   X86MCSymbolizer.cpp
+  X86RelocationHandler.cpp
 
-  # X86RelocationHandler.cpp is part of LLVMBOLTCore to avoid a circular
-  # dependency between the core relocation factory and LLVMBOLTTargetX86.
-  PARTIAL_SOURCES_INTENDED
   NO_EXPORT
   DISABLE_LLVM_LINK_LLVM_DYLIB
 
diff --git a/bolt/lib/Target/X86/X86RelocationHandler.cpp b/bolt/lib/Target/X86/X86RelocationHandler.cpp
index 4895bfd4fd449..3c1eaf69e0294 100644
--- a/bolt/lib/Target/X86/X86RelocationHandler.cpp
+++ b/bolt/lib/Target/X86/X86RelocationHandler.cpp
@@ -1,5 +1,4 @@
-//===- X86RelocationHandler.cpp
-//---------------------------------------------------===//
+//===- X86RelocationHandler.cpp - X86 relocation handling ---------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -11,6 +10,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "bolt/Target/X86/X86RelocationHandler.h"
 #include "bolt/Core/Relocation.h"
 #include "llvm/Object/ELF.h"
 #include "llvm/Support/ErrorHandling.h"
diff --git a/bolt/unittests/Core/BinaryContext.cpp b/bolt/unittests/Core/BinaryContext.cpp
index a8c00a3bb24b6..2c6745563144f 100644
--- a/bolt/unittests/Core/BinaryContext.cpp
+++ b/bolt/unittests/Core/BinaryContext.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "bolt/Core/BinaryContext.h"
+#include "bolt/Rewrite/RewriteInstance.h"
 #include "bolt/Utils/CommandLineOpts.h"
 #include "llvm/BinaryFormat/ELF.h"
 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
@@ -61,6 +62,8 @@ struct BinaryContextTester : public testing::TestWithParam<Triple::ArchType> {
         ObjFile->getFileName(), TheTriple.isRISCV() ? &Features : nullptr, true,
         DWARFContext::create(*ObjFile), {llvm::outs(), llvm::errs()}));
     ASSERT_FALSE(!BC);
+    BC->initializeRelocationHandler(
+        createRelocationHandler(TheTriple.getArch()));
   }
 
   char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {};
@@ -68,19 +71,57 @@ struct BinaryContextTester : public testing::TestWithParam<Triple::ArchType> {
   std::unique_ptr<BinaryContext> BC;
 };
 
+TEST_P(BinaryContextTester, InitializesRelocationHandler) {
+  const RelocationHandler &Handler = BC->getRelocationHandler();
+
+  switch (GetParam()) {
+  case Triple::x86_64:
+    EXPECT_EQ(Handler.getPC32(), ELF::R_X86_64_PC32);
+    break;
+  case Triple::aarch64:
+    EXPECT_EQ(Handler.getPC32(), ELF::R_AARCH64_PREL32);
+    break;
+  case Triple::riscv64:
+    EXPECT_EQ(Handler.getPC32(), ELF::R_RISCV_32_PCREL);
+    break;
+  default:
+    FAIL() << "unsupported test architecture";
+  }
+}
+
 TEST(RelocationHandlerTest, ArchitectureStateIsIndependent) {
+#ifdef X86_AVAILABLE
   std::unique_ptr<RelocationHandler> X86Handler =
       createRelocationHandler(Triple::x86_64);
+#endif
+
+#ifdef AARCH64_AVAILABLE
   std::unique_ptr<RelocationHandler> AArch64Handler =
       createRelocationHandler(Triple::aarch64);
+#endif
+
+#ifdef RISCV_AVAILABLE
+  std::unique_ptr<RelocationHandler> RISCVHandler =
+      createRelocationHandler(Triple::riscv64);
+#endif
 
+#ifdef X86_AVAILABLE
   EXPECT_EQ(X86Handler->getPC32(), ELF::R_X86_64_PC32);
-  EXPECT_EQ(AArch64Handler->getPC32(), ELF::R_AARCH64_PREL32);
   EXPECT_TRUE(X86Handler->isSupported(ELF::R_X86_64_PC32));
-  EXPECT_FALSE(X86Handler->isSupported(ELF::R_AARCH64_CALL26));
+#endif
+
+#ifdef AARCH64_AVAILABLE
+  EXPECT_EQ(AArch64Handler->getPC32(), ELF::R_AARCH64_PREL32);
   EXPECT_TRUE(AArch64Handler->isSupported(ELF::R_AARCH64_CALL26));
-  EXPECT_FALSE(AArch64Handler->isSupported(ELF::R_X86_64_PC32));
+#endif
+
+#ifdef RISCV_AVAILABLE
+  EXPECT_EQ(RISCVHandler->getPC32(), ELF::R_RISCV_32_PCREL);
+  EXPECT_EQ(RISCVHandler->getRelative(), ELF::R_RISCV_RELATIVE);
+  EXPECT_TRUE(RISCVHandler->isIRelative(ELF::R_RISCV_IRELATIVE));
+#endif
 }
+
 } // namespace
 
 #ifdef X86_AVAILABLE
diff --git a/bolt/unittests/Core/Relocation.cpp b/bolt/unittests/Core/Relocation.cpp
index 93695aae8a913..5765c490a369e 100644
--- a/bolt/unittests/Core/Relocation.cpp
+++ b/bolt/unittests/Core/Relocation.cpp
@@ -7,8 +7,16 @@
 //===----------------------------------------------------------------------===//
 
 #include "bolt/Core/Relocation.h"
+#ifdef AARCH64_AVAILABLE
+#include "bolt/Target/AArch64/AArch64RelocationHandler.h"
+#endif
+#ifdef RISCV_AVAILABLE
+#include "bolt/Target/RISCV/RISCVRelocationHandler.h"
+#endif
+#ifdef X86_AVAILABLE
+#include "bolt/Target/X86/X86RelocationHandler.h"
+#endif
 #include "llvm/BinaryFormat/ELF.h"
-#include "llvm/TargetParser/Triple.h"
 #include "gtest/gtest.h"
 
 using namespace llvm;
@@ -19,8 +27,7 @@ using namespace llvm::bolt;
 namespace {
 
 TEST(AArch64RelocationHandlerTest, ClassificationAndSize) {
-  std::unique_ptr<RelocationHandler> Handler =
-      createRelocationHandler(Triple::aarch64);
+  std::unique_ptr<RelocationHandler> Handler = createAArch64RelocationHandler();
 
   EXPECT_TRUE(Handler->isSupported(ELF::R_AARCH64_CALL26));
   EXPECT_TRUE(Handler->isSupported(ELF::R_AARCH64_PREL32));
@@ -36,8 +43,7 @@ TEST(AArch64RelocationHandlerTest, ClassificationAndSize) {
 }
 
 TEST(AArch64RelocationHandlerTest, EncodeAndExtractCall26) {
-  std::unique_ptr<RelocationHandler> Handler =
-      createRelocationHandler(Triple::aarch64);
+  std::unique_ptr<RelocationHandler> Handler = createAArch64RelocationHandler();
   constexpr uint64_t PC = 0x1000;
   constexpr uint64_t Target = 0x1100;
 
@@ -48,8 +54,7 @@ TEST(AArch64RelocationHandlerTest, EncodeAndExtractCall26) {
 }
 
 TEST(AArch64RelocationHandlerTest, EncodeAndExtractPrel32) {
-  std::unique_ptr<RelocationHandler> Handler =
-      createRelocationHandler(Triple::aarch64);
+  std::unique_ptr<RelocationHandler> Handler = createAArch64RelocationHandler();
   constexpr uint64_t PC = 0x1000;
   constexpr uint64_t Target = 0xf00;
 
@@ -60,8 +65,7 @@ TEST(AArch64RelocationHandlerTest, EncodeAndExtractPrel32) {
 }
 
 TEST(AArch64RelocationHandlerTest, Call26EncodingRange) {
-  std::unique_ptr<RelocationHandler> Handler =
-      createRelocationHandler(Triple::aarch64);
+  std::unique_ptr<RelocationHandler> Handler = createAArch64RelocationHandler();
   constexpr uint64_t PC = 0x1000;
   constexpr uint64_t Range = uint64_t{1} << 27;
 
@@ -73,3 +77,63 @@ TEST(AArch64RelocationHandlerTest, Call26EncodingRange) {
 } // namespace
 
 #endif
+
+#ifdef X86_AVAILABLE
+
+namespace {
+
+TEST(X86RelocationHandlerTest, ClassificationAndEncoding) {
+  std::unique_ptr<RelocationHandler> Handler = createX86RelocationHandler();
+  constexpr uint64_t PC = 0x1000;
+  constexpr uint64_t Target = 0xf00;
+
+  EXPECT_TRUE(Handler->isSupported(ELF::R_X86_64_PC32));
+  EXPECT_EQ(Handler->getSizeForType(ELF::R_X86_64_PC32), 4u);
+  EXPECT_TRUE(Handler->isPCRelative(ELF::R_X86_64_PC32));
+
+  const uint64_t Encoded = Handler->encodeValue(ELF::R_X86_64_PC32, Target, PC);
+  EXPECT_EQ(Encoded, static_cast<uint64_t>(-0x100));
+  EXPECT_EQ(Handler->extractValue(ELF::R_X86_64_PC32, Encoded, PC),
+            static_cast<uint64_t>(-0x100));
+}
+
+} // namespace
+
+#endif
+
+#ifdef RISCV_AVAILABLE
+
+namespace {
+
+TEST(RISCVRelocationHandlerTest, ClassificationAndEncoding) {
+  std::unique_ptr<RelocationHandler> Handler =
+      createRISCVRelocationHandler(true);
+  constexpr uint64_t Value = 0x12345678;
+
+  EXPECT_TRUE(Handler->isSupported(ELF::R_RISCV_CALL));
+  EXPECT_EQ(Handler->getSizeForType(ELF::R_RISCV_CALL), 8u);
+  EXPECT_TRUE(Handler->isPCRelative(ELF::R_RISCV_CALL));
+  EXPECT_TRUE(Handler->isInstructionReference(ELF::R_RISCV_PCREL_LO12_I));
+
+  const uint64_t Encoded = Handler->encodeValue(ELF::R_RISCV_32, Value, 0);
+  EXPECT_EQ(Encoded, Value);
+  EXPECT_EQ(Handler->extractValue(ELF::R_RISCV_32, Encoded, 0), Value);
+}
+
+TEST(RISCVRelocationHandlerTest, Preserves32And64BitBehavior) {
+  std::unique_ptr<RelocationHandler> RISCV32Handler =
+      createRISCVRelocationHandler(false);
+  std::unique_ptr<RelocationHandler> RISCV64Handler =
+      createRISCVRelocationHandler(true);
+
+  EXPECT_TRUE(
+      RISCV32Handler->isInstructionReference(ELF::R_RISCV_PCREL_LO12_S));
+  EXPECT_TRUE(
+      RISCV64Handler->isInstructionReference(ELF::R_RISCV_PCREL_LO12_S));
+  EXPECT_EQ(RISCV64Handler->getRelative(), ELF::R_RISCV_RELATIVE);
+  EXPECT_TRUE(RISCV64Handler->isIRelative(ELF::R_RISCV_IRELATIVE));
+}
+
+} // namespace
+
+#endif



More information about the llvm-commits mailing list