[BOLT] Add minimal RISC-V 64-bit support

Just enough features are implemented to process a simple "hello world"
executable and produce something that still runs (including libc calls).
This was mainly a matter of implementing support for various
relocations. Currently, the following are handled:

- R_RISCV_JAL
- R_RISCV_CALL
- R_RISCV_CALL_PLT
- R_RISCV_BRANCH
- R_RISCV_RVC_BRANCH
- R_RISCV_RVC_JUMP
- R_RISCV_GOT_HI20
- R_RISCV_PCREL_HI20
- R_RISCV_PCREL_LO12_I
- R_RISCV_RELAX
- R_RISCV_NONE

Executables linked with linker relaxation will probably fail to be
processed. BOLT relocates .text to a high address while leaving .plt at
its original (low) address. This causes PC-relative PLT calls that were
relaxed to a JAL to not fit their offset in an I-immediate anymore. This
is something that will be addressed in a later patch.

Changes to the BOLT core are relatively minor. Two things were tricky to
implement and needed slightly larger changes. I'll explain those below.

The R_RISCV_CALL(_PLT) relocation is put on the first instruction of a
AUIPC/JALR pair, the second does not get any relocation (unlike other
PCREL pairs). This causes issues with the combinations of the way BOLT
processes binaries and the RISC-V MC-layer handles relocations:
- BOLT reassembles instructions one by one and since the JALR doesn't
  have a relocation, it simply gets copied without modification;
- Even though the MC-layer handles R_RISCV_CALL properly (adjusts both
  the AUIPC and the JALR), it assumes the immediates of both
  instructions are 0 (to be able to or-in a new value). This will most
  likely not be the case for the JALR that got copied over.

To handle this difficulty without resorting to RISC-V-specific hacks in
the BOLT core, a new binary pass was added that searches for
AUIPC/JALR pairs and zeroes-out the immediate of the JALR.

A second difficulty was supporting ABS symbols. As far as I can tell,
ABS symbols were not handled at all, causing __global_pointer$ to break.
RewriteInstance::analyzeRelocation was updated to handle these
generically.

Tests are provided for all supported relocations. Note that in order to
test the correct handling of PLT entries, an ELF file produced by GCC
had to be used. While I tried to strip the YAML representation, it's
still quite large. Any suggestions on how to improve this would be
appreciated.

Reviewed By: rafauler

Differential Revision: https://reviews.llvm.org/D145687
diff --git a/bolt/CMakeLists.txt b/bolt/CMakeLists.txt
index 2e702d4..4ff90c1 100644
--- a/bolt/CMakeLists.txt
+++ b/bolt/CMakeLists.txt
@@ -9,7 +9,7 @@
 
 # Determine default set of targets to build -- the intersection of
 # those BOLT supports and those LLVM is targeting.
-set(BOLT_TARGETS_TO_BUILD_all "AArch64;X86")
+set(BOLT_TARGETS_TO_BUILD_all "AArch64;X86;RISCV")
 set(BOLT_TARGETS_TO_BUILD_default)
 foreach (tgt ${BOLT_TARGETS_TO_BUILD_all})
   if (tgt IN_LIST LLVM_TARGETS_TO_BUILD)
diff --git a/bolt/CODE_OWNERS.TXT b/bolt/CODE_OWNERS.TXT
index 00c92e8..39439bb 100644
--- a/bolt/CODE_OWNERS.TXT
+++ b/bolt/CODE_OWNERS.TXT
@@ -20,3 +20,7 @@
 N: Vladislav Khmelevsky
 E: [email protected]
 D: AArch64 backend
+
+N: Job Noorman
+E: [email protected]
+D: RISC-V backend
diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h
index d1a0662..9daa2eb 100644
--- a/bolt/include/bolt/Core/BinaryContext.h
+++ b/bolt/include/bolt/Core/BinaryContext.h
@@ -722,6 +722,8 @@
            TheTriple->getArch() == llvm::Triple::x86_64;
   }
 
+  bool isRISCV() const { return TheTriple->getArch() == llvm::Triple::riscv64; }
+
   // AArch64-specific functions to check if symbol is used to delimit
   // code/data in .text. Code is marked by $x, data by $d.
   MarkerSymType getMarkerType(const SymbolRef &Symbol) const;
diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h
index 2d6ea6b..db3f7e7 100644
--- a/bolt/include/bolt/Core/MCPlusBuilder.h
+++ b/bolt/include/bolt/Core/MCPlusBuilder.h
@@ -633,6 +633,12 @@
     return false;
   }
 
+  /// Returns true if First/Second is a AUIPC/JALR call pair.
+  virtual bool isRISCVCall(const MCInst &First, const MCInst &Second) const {
+    llvm_unreachable("not implemented");
+    return false;
+  }
+
   /// If non-zero, this is used to fill the executable space with instructions
   /// that will trap. Defaults to 0.
   virtual unsigned getTrapFillValue() const { return 0; }
@@ -2032,6 +2038,10 @@
                                           const MCInstrInfo *,
                                           const MCRegisterInfo *);
 
+MCPlusBuilder *createRISCVMCPlusBuilder(const MCInstrAnalysis *,
+                                        const MCInstrInfo *,
+                                        const MCRegisterInfo *);
+
 } // namespace bolt
 } // namespace llvm
 
diff --git a/bolt/include/bolt/Passes/FixRISCVCallsPass.h b/bolt/include/bolt/Passes/FixRISCVCallsPass.h
new file mode 100644
index 0000000..8e4473f
--- /dev/null
+++ b/bolt/include/bolt/Passes/FixRISCVCallsPass.h
@@ -0,0 +1,38 @@
+//===- bolt/Passes/FixRISCVCallsPass.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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares the FixRISCVCallsPass class, which sets the JALR immediate
+// to 0 for AUIPC/JALR pairs with a R_RISCV_CALL(_PLT) relocation. This is
+// necessary since MC expects it to be zero in order to or-in fixups.
+//===----------------------------------------------------------------------===//
+
+#ifndef BOLT_PASSES_FIXRISCVCALLSPASS_H
+#define BOLT_PASSES_FIXRISCVCALLSPASS_H
+
+#include "bolt/Passes/BinaryPasses.h"
+
+namespace llvm {
+namespace bolt {
+
+class FixRISCVCallsPass : public BinaryFunctionPass {
+  void runOnFunction(BinaryFunction &Function);
+
+public:
+  explicit FixRISCVCallsPass(const cl::opt<bool> &PrintPass)
+      : BinaryFunctionPass(PrintPass) {}
+
+  const char *getName() const override { return "fix-riscv-calls"; }
+
+  /// Pass entry point
+  void runOnFunctions(BinaryContext &BC) override;
+};
+
+} // namespace bolt
+} // namespace llvm
+
+#endif
diff --git a/bolt/include/bolt/Rewrite/RewriteInstance.h b/bolt/include/bolt/Rewrite/RewriteInstance.h
index 8ebc9eb..da6475c 100644
--- a/bolt/include/bolt/Rewrite/RewriteInstance.h
+++ b/bolt/include/bolt/Rewrite/RewriteInstance.h
@@ -279,6 +279,9 @@
   /// is the expected .plt \p Section entry function size.
   void disassemblePLTSectionX86(BinarySection &Section, uint64_t EntrySize);
 
+  /// Disassemble riscv-specific .plt \p Section auxiliary function
+  void disassemblePLTSectionRISCV(BinarySection &Section);
+
   /// ELF-specific part. TODO: refactor into new class.
 #define ELF_FUNCTION(TYPE, FUNC)                                               \
   template <typename ELFT> TYPE FUNC(object::ELFObjectFile<ELFT> *Obj);        \
@@ -536,6 +539,9 @@
   const PLTSectionInfo AArch64_PLTSections[3] = {
       {".plt"}, {".iplt"}, {nullptr}};
 
+  /// RISCV PLT sections.
+  const PLTSectionInfo RISCV_PLTSections[3] = {{".plt"}, {nullptr}};
+
   /// Return PLT information for a section with \p SectionName or nullptr
   /// if the section is not PLT.
   const PLTSectionInfo *getPLTSectionInfo(StringRef SectionName) {
@@ -549,6 +555,9 @@
     case Triple::aarch64:
       PLTSI = AArch64_PLTSections;
       break;
+    case Triple::riscv64:
+      PLTSI = RISCV_PLTSections;
+      break;
     }
     for (; PLTSI && PLTSI->Name; ++PLTSI)
       if (SectionName == PLTSI->Name)
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index db44d56..2d2b35e 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -128,6 +128,11 @@
     ArchName = "aarch64";
     FeaturesStr = "+all";
     break;
+  case llvm::Triple::riscv64:
+    ArchName = "riscv64";
+    // RV64GC
+    FeaturesStr = "+m,+a,+f,+d,+zicsr,+zifencei,+c";
+    break;
   default:
     return createStringError(std::errc::not_supported,
                              "BOLT-ERROR: Unrecognized machine in ELF file");
diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index 12ded3d..dac32c4 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -1323,7 +1323,7 @@
         if (BC.isAArch64())
           handleAArch64IndirectCall(Instruction, Offset);
       }
-    } else if (BC.isAArch64()) {
+    } else if (BC.isAArch64() || BC.isRISCV()) {
       // Check if there's a relocation associated with this instruction.
       bool UsedReloc = false;
       for (auto Itr = Relocations.lower_bound(Offset),
@@ -1343,7 +1343,7 @@
         UsedReloc = true;
       }
 
-      if (MIB->hasPCRelOperand(Instruction) && !UsedReloc)
+      if (!BC.isRISCV() && MIB->hasPCRelOperand(Instruction) && !UsedReloc)
         handlePCRelOperand(Instruction, AbsoluteInstrAddr, Size);
     }
 
diff --git a/bolt/lib/Core/Relocation.cpp b/bolt/lib/Core/Relocation.cpp
index d3e7fcc..0901a14 100644
--- a/bolt/lib/Core/Relocation.cpp
+++ b/bolt/lib/Core/Relocation.cpp
@@ -89,6 +89,24 @@
   }
 }
 
+static bool isSupportedRISCV(uint64_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_RVC_JUMP:
+  case ELF::R_RISCV_RVC_BRANCH:
+    return true;
+  }
+}
+
 static size_t getSizeForTypeX86(uint64_t Type) {
   switch (Type) {
   default:
@@ -163,6 +181,28 @@
   }
 }
 
+static size_t getSizeForTypeRISCV(uint64_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_32_PCREL:
+  case ELF::R_RISCV_CALL:
+  case ELF::R_RISCV_CALL_PLT:
+    return 4;
+  case ELF::R_RISCV_GOT_HI20:
+    // See extractValueRISCV for why this is necessary.
+    return 8;
+  }
+}
+
 static bool skipRelocationTypeX86(uint64_t Type) {
   return Type == ELF::R_X86_64_NONE;
 }
@@ -171,6 +211,16 @@
   return Type == ELF::R_AARCH64_NONE || Type == ELF::R_AARCH64_LD_PREL_LO19;
 }
 
+static bool skipRelocationTypeRISCV(uint64_t Type) {
+  switch (Type) {
+  default:
+    return false;
+  case ELF::R_RISCV_NONE:
+  case ELF::R_RISCV_RELAX:
+    return true;
+  }
+}
+
 static bool skipRelocationProcessX86(uint64_t &Type, uint64_t Contents) {
   return false;
 }
@@ -262,6 +312,10 @@
   return false;
 }
 
+static bool skipRelocationProcessRISCV(uint64_t &Type, uint64_t Contents) {
+  return false;
+}
+
 static uint64_t encodeValueX86(uint64_t Type, uint64_t Value, uint64_t PC) {
   switch (Type) {
   default:
@@ -407,6 +461,57 @@
   }
 }
 
+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 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(uint64_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:
+    return extractUImmRISCV(Contents);
+  case ELF::R_RISCV_BRANCH:
+    return extractBImmRISCV(Contents);
+  case ELF::R_RISCV_GOT_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:
+    return extractUImmRISCV(Contents);
+  case ELF::R_RISCV_PCREL_LO12_I:
+    return extractIImmRISCV(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));
+  }
+}
+
 static bool isGOTX86(uint64_t Type) {
   switch (Type) {
   default:
@@ -444,6 +549,15 @@
   }
 }
 
+static bool isGOTRISCV(uint64_t Type) {
+  switch (Type) {
+  default:
+    return false;
+  case ELF::R_RISCV_GOT_HI20:
+    return true;
+  }
+}
+
 static bool isTLSX86(uint64_t Type) {
   switch (Type) {
   default:
@@ -472,6 +586,13 @@
   }
 }
 
+static bool isTLSRISCV(uint64_t Type) {
+  switch (Type) {
+  default:
+    return false;
+  }
+}
+
 static bool isPCRelativeX86(uint64_t Type) {
   switch (Type) {
   default:
@@ -543,33 +664,61 @@
   }
 }
 
+static bool isPCRelativeRISCV(uint64_t Type) {
+  switch (Type) {
+  default:
+    llvm_unreachable("Unknown relocation type");
+  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_RVC_JUMP:
+  case ELF::R_RISCV_RVC_BRANCH:
+  case ELF::R_RISCV_32_PCREL:
+    return true;
+  }
+}
+
 bool Relocation::isSupported(uint64_t Type) {
   if (Arch == Triple::aarch64)
     return isSupportedAArch64(Type);
+  if (Arch == Triple::riscv64)
+    return isSupportedRISCV(Type);
   return isSupportedX86(Type);
 }
 
 size_t Relocation::getSizeForType(uint64_t Type) {
   if (Arch == Triple::aarch64)
     return getSizeForTypeAArch64(Type);
+  if (Arch == Triple::riscv64)
+    return getSizeForTypeRISCV(Type);
   return getSizeForTypeX86(Type);
 }
 
 bool Relocation::skipRelocationType(uint64_t Type) {
   if (Arch == Triple::aarch64)
     return skipRelocationTypeAArch64(Type);
+  if (Arch == Triple::riscv64)
+    return skipRelocationTypeRISCV(Type);
   return skipRelocationTypeX86(Type);
 }
 
 bool Relocation::skipRelocationProcess(uint64_t &Type, uint64_t Contents) {
   if (Arch == Triple::aarch64)
     return skipRelocationProcessAArch64(Type, Contents);
+  if (Arch == Triple::riscv64)
+    skipRelocationProcessRISCV(Type, Contents);
   return skipRelocationProcessX86(Type, Contents);
 }
 
 uint64_t Relocation::encodeValue(uint64_t Type, uint64_t Value, uint64_t PC) {
   if (Arch == Triple::aarch64)
     return encodeValueAArch64(Type, Value, PC);
+  if (Arch == Triple::riscv64)
+    llvm_unreachable("not implemented");
   return encodeValueX86(Type, Value, PC);
 }
 
@@ -577,12 +726,16 @@
                                   uint64_t PC) {
   if (Arch == Triple::aarch64)
     return extractValueAArch64(Type, Contents, PC);
+  if (Arch == Triple::riscv64)
+    return extractValueRISCV(Type, Contents, PC);
   return extractValueX86(Type, Contents, PC);
 }
 
 bool Relocation::isGOT(uint64_t Type) {
   if (Arch == Triple::aarch64)
     return isGOTAArch64(Type);
+  if (Arch == Triple::riscv64)
+    return isGOTRISCV(Type);
   return isGOTX86(Type);
 }
 
@@ -597,42 +750,56 @@
 bool Relocation::isRelative(uint64_t Type) {
   if (Arch == Triple::aarch64)
     return Type == ELF::R_AARCH64_RELATIVE;
+  if (Arch == Triple::riscv64)
+    return Type == ELF::R_RISCV_RELATIVE;
   return Type == ELF::R_X86_64_RELATIVE;
 }
 
 bool Relocation::isIRelative(uint64_t Type) {
   if (Arch == Triple::aarch64)
     return Type == ELF::R_AARCH64_IRELATIVE;
+  if (Arch == Triple::riscv64)
+    llvm_unreachable("not implemented");
   return Type == ELF::R_X86_64_IRELATIVE;
 }
 
 bool Relocation::isTLS(uint64_t Type) {
   if (Arch == Triple::aarch64)
     return isTLSAArch64(Type);
+  if (Arch == Triple::riscv64)
+    return isTLSRISCV(Type);
   return isTLSX86(Type);
 }
 
 uint64_t Relocation::getNone() {
   if (Arch == Triple::aarch64)
     return ELF::R_AARCH64_NONE;
+  if (Arch == Triple::riscv64)
+    return ELF::R_RISCV_NONE;
   return ELF::R_X86_64_NONE;
 }
 
 uint64_t Relocation::getPC32() {
   if (Arch == Triple::aarch64)
     return ELF::R_AARCH64_PREL32;
+  if (Arch == Triple::riscv64)
+    return ELF::R_RISCV_32_PCREL;
   return ELF::R_X86_64_PC32;
 }
 
 uint64_t Relocation::getPC64() {
   if (Arch == Triple::aarch64)
     return ELF::R_AARCH64_PREL64;
+  if (Arch == Triple::riscv64)
+    llvm_unreachable("not implemented");
   return ELF::R_X86_64_PC64;
 }
 
 bool Relocation::isPCRelative(uint64_t Type) {
   if (Arch == Triple::aarch64)
     return isPCRelativeAArch64(Type);
+  if (Arch == Triple::riscv64)
+    return isPCRelativeRISCV(Type);
   return isPCRelativeX86(Type);
 }
 
@@ -696,7 +863,20 @@
   };
   if (Arch == Triple::aarch64)
     OS << AArch64RelocNames[Type];
-  else
+  else if (Arch == Triple::riscv64) {
+    // RISC-V relocations are not sequentially numbered so we cannot use an
+    // array
+    switch (Type) {
+    default:
+      llvm_unreachable("illegal RISC-V relocation");
+#undef ELF_RELOC
+#define ELF_RELOC(name, value)                                                 \
+  case value:                                                                  \
+    OS << #name;                                                               \
+    break;
+#include "llvm/BinaryFormat/ELFRelocs/RISCV.def"
+    }
+  } else
     OS << X86RelocNames[Type];
   OS << ", 0x" << Twine::utohexstr(Offset);
   if (Symbol) {
diff --git a/bolt/lib/Passes/CMakeLists.txt b/bolt/lib/Passes/CMakeLists.txt
index 9bc0779..b8bbe59 100644
--- a/bolt/lib/Passes/CMakeLists.txt
+++ b/bolt/lib/Passes/CMakeLists.txt
@@ -14,6 +14,7 @@
   FrameAnalysis.cpp
   FrameOptimizer.cpp
   FixRelaxationPass.cpp
+  FixRISCVCallsPass.cpp
   HFSort.cpp
   HFSortPlus.cpp
   Hugify.cpp
diff --git a/bolt/lib/Passes/FixRISCVCallsPass.cpp b/bolt/lib/Passes/FixRISCVCallsPass.cpp
new file mode 100644
index 0000000..5da28e7
--- /dev/null
+++ b/bolt/lib/Passes/FixRISCVCallsPass.cpp
@@ -0,0 +1,48 @@
+#include "bolt/Passes/FixRISCVCallsPass.h"
+#include "bolt/Core/ParallelUtilities.h"
+
+#include <iterator>
+
+using namespace llvm;
+
+namespace llvm {
+namespace bolt {
+
+void FixRISCVCallsPass::runOnFunction(BinaryFunction &BF) {
+  auto &BC = BF.getBinaryContext();
+
+  for (auto &BB : BF) {
+    for (auto II = BB.begin(), IE = BB.end(); II != IE; ++II) {
+      auto NextII = std::next(II);
+
+      if (NextII == IE)
+        break;
+
+      if (!BC.MIB->isRISCVCall(*II, *NextII))
+        continue;
+
+      auto L = BC.scopeLock();
+
+      // The MC layer handles R_RISCV_CALL_PLT but assumes that the immediate
+      // in the JALR is zero (fixups are or'ed into instructions). Note that
+      // NextII is guaranteed to point to a JALR by isRISCVCall.
+      NextII->getOperand(2).setImm(0);
+    }
+  }
+}
+
+void FixRISCVCallsPass::runOnFunctions(BinaryContext &BC) {
+  if (!BC.isRISCV() || !BC.HasRelocations)
+    return;
+
+  ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) {
+    runOnFunction(BF);
+  };
+
+  ParallelUtilities::runOnEachFunction(
+      BC, ParallelUtilities::SchedulingPolicy::SP_INST_LINEAR, WorkFun, nullptr,
+      "FixRISCVCalls");
+}
+
+} // namespace bolt
+} // namespace llvm
diff --git a/bolt/lib/Rewrite/BinaryPassManager.cpp b/bolt/lib/Rewrite/BinaryPassManager.cpp
index dd00d62..517984d 100644
--- a/bolt/lib/Rewrite/BinaryPassManager.cpp
+++ b/bolt/lib/Rewrite/BinaryPassManager.cpp
@@ -12,6 +12,7 @@
 #include "bolt/Passes/AllocCombiner.h"
 #include "bolt/Passes/AsmDump.h"
 #include "bolt/Passes/CMOVConversion.h"
+#include "bolt/Passes/FixRISCVCallsPass.h"
 #include "bolt/Passes/FixRelaxationPass.h"
 #include "bolt/Passes/FrameOptimizer.h"
 #include "bolt/Passes/Hugify.h"
@@ -185,6 +186,11 @@
                         cl::desc("print functions after fix relaxations pass"),
                         cl::cat(BoltOptCategory));
 
+static cl::opt<bool>
+    PrintFixRISCVCalls("print-fix-riscv-calls",
+                       cl::desc("print functions after fix RISCV calls pass"),
+                       cl::cat(BoltOptCategory));
+
 static cl::opt<bool> PrintVeneerElimination(
     "print-veneer-elimination",
     cl::desc("print functions after veneer elimination pass"),
@@ -328,6 +334,11 @@
         std::make_unique<VeneerElimination>(PrintVeneerElimination));
   }
 
+  if (BC.isRISCV()) {
+    Manager.registerPass(
+        std::make_unique<FixRISCVCallsPass>(PrintFixRISCVCalls));
+  }
+
   // Here we manage dependencies/order manually, since passes are run in the
   // order they're registered.
 
diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index 5dfe930..659b295 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -300,6 +300,11 @@
     return createAArch64MCPlusBuilder(Analysis, Info, RegInfo);
 #endif
 
+#ifdef RISCV_AVAILABLE
+  if (Arch == Triple::riscv64)
+    return createRISCVMCPlusBuilder(Analysis, Info, RegInfo);
+#endif
+
   llvm_unreachable("architecture unsupported by MCPlusBuilder");
 }
 
@@ -1042,7 +1047,9 @@
     section_iterator Section =
         cantFail(Symbol.getSection(), "cannot get symbol section");
     if (Section == InputFile->section_end()) {
-      // Could be an absolute symbol. Could record for pretty printing.
+      // Could be an absolute symbol. Used on RISC-V for __global_pointer$ so we
+      // need to record it to handle relocations against it. For other instances
+      // of absolute symbols, we record for pretty printing.
       LLVM_DEBUG(if (opts::Verbosity > 1) {
         dbgs() << "BOLT-INFO: absolute sym " << UniqueName << "\n";
       });
@@ -1464,6 +1471,50 @@
   }
 }
 
+void RewriteInstance::disassemblePLTSectionRISCV(BinarySection &Section) {
+  const uint64_t SectionAddress = Section.getAddress();
+  const uint64_t SectionSize = Section.getSize();
+  StringRef PLTContents = Section.getContents();
+  ArrayRef<uint8_t> PLTData(
+      reinterpret_cast<const uint8_t *>(PLTContents.data()), SectionSize);
+
+  auto disassembleInstruction = [&](uint64_t InstrOffset, MCInst &Instruction,
+                                    uint64_t &InstrSize) {
+    const uint64_t InstrAddr = SectionAddress + InstrOffset;
+    if (!BC->DisAsm->getInstruction(Instruction, InstrSize,
+                                    PLTData.slice(InstrOffset), InstrAddr,
+                                    nulls())) {
+      errs() << "BOLT-ERROR: unable to disassemble instruction in PLT section "
+             << Section.getName() << " at offset 0x"
+             << Twine::utohexstr(InstrOffset) << '\n';
+      exit(1);
+    }
+  };
+
+  // Skip the first special entry since no relocation points to it.
+  uint64_t InstrOffset = 32;
+
+  while (InstrOffset < SectionSize) {
+    InstructionListType Instructions;
+    MCInst Instruction;
+    const uint64_t EntryOffset = InstrOffset;
+    const uint64_t EntrySize = 16;
+    uint64_t InstrSize;
+
+    while (InstrOffset < EntryOffset + EntrySize) {
+      disassembleInstruction(InstrOffset, Instruction, InstrSize);
+      Instructions.emplace_back(Instruction);
+      InstrOffset += InstrSize;
+    }
+
+    const uint64_t EntryAddress = SectionAddress + EntryOffset;
+    const uint64_t TargetAddress = BC->MIB->analyzePLTEntry(
+        Instruction, Instructions.begin(), Instructions.end(), EntryAddress);
+
+    createPLTBinaryFunction(TargetAddress, EntryAddress, EntrySize);
+  }
+}
+
 void RewriteInstance::disassemblePLTSectionX86(BinarySection &Section,
                                                uint64_t EntrySize) {
   const uint64_t SectionAddress = Section.getAddress();
@@ -1523,6 +1574,8 @@
   auto analyzeOnePLTSection = [&](BinarySection &Section, uint64_t EntrySize) {
     if (BC->isAArch64())
       return disassemblePLTSectionAArch64(Section);
+    if (BC->isRISCV())
+      return disassemblePLTSectionRISCV(Section);
     return disassemblePLTSectionX86(Section, EntrySize);
   };
 
@@ -1990,7 +2043,7 @@
     // Section symbols are marked as ST_Debug.
     IsSectionRelocation = (cantFail(Symbol.getType()) == SymbolRef::ST_Debug);
     // Check for PLT entry registered with symbol name
-    if (!SymbolAddress && IsAArch64) {
+    if (!SymbolAddress && (IsAArch64 || BC->isRISCV())) {
       const BinaryData *BD = BC->getPLTBinaryDataByName(SymbolName);
       SymbolAddress = BD ? BD->getAddress() : 0;
     }
@@ -2053,7 +2106,7 @@
     if (SkipVerification)
       return true;
 
-    if (IsAArch64)
+    if (IsAArch64 || BC->isRISCV())
       return true;
 
     if (SymbolName == "__hot_start" || SymbolName == "__hot_end")
@@ -2107,7 +2160,11 @@
     if (!DynamicRelSectionOrErr)
       report_error("unable to find section corresponding to DT_RELA",
                    DynamicRelSectionOrErr.getError());
-    if (DynamicRelSectionOrErr->getSize() != DynamicRelocationsSize)
+    auto DynamicRelSectionSize = DynamicRelSectionOrErr->getSize();
+    // On RISC-V DT_RELASZ seems to include both .rela.dyn and .rela.plt
+    if (DynamicRelocationsSize == DynamicRelSectionSize + PLTRelocationsSize)
+      DynamicRelocationsSize = DynamicRelSectionSize;
+    if (DynamicRelSectionSize != DynamicRelocationsSize)
       report_error("section size mismatch for DT_RELASZ",
                    errc::executable_format_error);
     readDynamicRelocations(DynamicRelSectionOrErr->getSectionRef(),
@@ -2598,6 +2655,14 @@
       Expected<StringRef> SectionName = Section->getName();
       if (SectionName && !SectionName->empty())
         ReferencedSection = BC->getUniqueSectionByName(*SectionName);
+    } else if (ReferencedSymbol &&
+               (cantFail(Symbol.getFlags()) & SymbolRef::SF_Absolute)) {
+      // This might be a relocation for an ABS symbols like __global_pointer$ on
+      // RISC-V
+      ContainingBF->addRelocation(Rel.getOffset(), ReferencedSymbol,
+                                  Rel.getType(), 0,
+                                  cantFail(Symbol.getValue()));
+      return;
     }
   }
 
@@ -2607,7 +2672,7 @@
   const bool IsToCode = ReferencedSection && ReferencedSection->isText();
 
   // Special handling of PC-relative relocations.
-  if (!IsAArch64 && Relocation::isPCRelative(RType)) {
+  if (!IsAArch64 && !BC->isRISCV() && Relocation::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
@@ -2640,7 +2705,7 @@
   }
 
   bool ForceRelocation = BC->forceSymbolRelocations(SymbolName);
-  if (BC->isAArch64() && Relocation::isGOT(RType))
+  if ((BC->isAArch64() || BC->isRISCV()) && Relocation::isGOT(RType))
     ForceRelocation = true;
 
   if (!ReferencedSection && !ForceRelocation) {
@@ -2788,7 +2853,7 @@
       // These are mostly local data symbols but undefined symbols
       // in relocation sections can get through here too, from .plt.
       assert(
-          (IsAArch64 || IsSectionRelocation ||
+          (IsAArch64 || BC->isRISCV() || IsSectionRelocation ||
            BC->getSectionNameForAddress(SymbolAddress)->startswith(".plt")) &&
           "known symbols should not resolve to anonymous locals");
 
diff --git a/bolt/lib/Target/RISCV/CMakeLists.txt b/bolt/lib/Target/RISCV/CMakeLists.txt
new file mode 100644
index 0000000..7f95576
--- /dev/null
+++ b/bolt/lib/Target/RISCV/CMakeLists.txt
@@ -0,0 +1,21 @@
+set(LLVM_LINK_COMPONENTS
+  MC
+  Support
+  RISCVDesc
+  )
+
+add_llvm_library(LLVMBOLTTargetRISCV
+  RISCVMCPlusBuilder.cpp
+
+  DISABLE_LLVM_LINK_LLVM_DYLIB
+
+  DEPENDS
+  RISCVCommonTableGen
+  )
+
+target_link_libraries(LLVMBOLTTargetRISCV PRIVATE LLVMBOLTCore)
+
+include_directories(
+  ${LLVM_MAIN_SRC_DIR}/lib/Target/RISCV
+  ${LLVM_BINARY_DIR}/lib/Target/RISCV
+  )
diff --git a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
new file mode 100644
index 0000000..993dc8b
--- /dev/null
+++ b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
@@ -0,0 +1,328 @@
+//===- bolt/Target/RISCV/RISCVMCPlusBuilder.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 provides RISCV-specific MCPlus builder.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MCTargetDesc/RISCVMCExpr.h"
+#include "MCTargetDesc/RISCVMCTargetDesc.h"
+#include "bolt/Core/MCPlusBuilder.h"
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_ostream.h"
+
+#define DEBUG_TYPE "mcplus"
+
+using namespace llvm;
+using namespace bolt;
+
+namespace {
+
+class RISCVMCPlusBuilder : public MCPlusBuilder {
+public:
+  using MCPlusBuilder::MCPlusBuilder;
+
+  bool shouldRecordCodeRelocation(uint64_t RelType) const override {
+    switch (RelType) {
+    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_RVC_BRANCH:
+    case ELF::R_RISCV_RVC_JUMP:
+    case ELF::R_RISCV_GOT_HI20:
+    case ELF::R_RISCV_PCREL_HI20:
+    case ELF::R_RISCV_PCREL_LO12_I:
+      return true;
+    default:
+      llvm_unreachable("Unexpected RISCV relocation type in code");
+    }
+  }
+
+  bool isNop(const MCInst &Inst) const {
+    return Inst.getOpcode() == RISCV::ADDI &&
+           Inst.getOperand(0).getReg() == RISCV::X0 &&
+           Inst.getOperand(1).getReg() == RISCV::X0 &&
+           Inst.getOperand(2).getImm() == 0;
+  }
+
+  bool isCNop(const MCInst &Inst) const {
+    return Inst.getOpcode() == RISCV::C_NOP;
+  }
+
+  bool isNoop(const MCInst &Inst) const override {
+    return isNop(Inst) || isCNop(Inst);
+  }
+
+  bool hasPCRelOperand(const MCInst &Inst) const override {
+    switch (Inst.getOpcode()) {
+    default:
+      return false;
+    case RISCV::JAL:
+    case RISCV::AUIPC:
+      return true;
+    }
+  }
+
+  bool replaceBranchTarget(MCInst &Inst, const MCSymbol *TBB,
+                           MCContext *Ctx) const override {
+    assert((isCall(Inst) || isBranch(Inst)) && !isIndirectBranch(Inst) &&
+           "Invalid instruction");
+
+    unsigned SymOpIndex;
+
+    switch (Inst.getOpcode()) {
+    default:
+      llvm_unreachable("not implemented");
+    case RISCV::C_J:
+      SymOpIndex = 0;
+      break;
+    case RISCV::JAL:
+    case RISCV::C_BEQZ:
+    case RISCV::C_BNEZ:
+      SymOpIndex = 1;
+      break;
+    case RISCV::BEQ:
+    case RISCV::BGE:
+      SymOpIndex = 2;
+      break;
+    }
+
+    Inst.getOperand(SymOpIndex) = MCOperand::createExpr(
+        MCSymbolRefExpr::create(TBB, MCSymbolRefExpr::VK_None, *Ctx));
+    return true;
+  }
+
+  IndirectBranchType analyzeIndirectBranch(
+      MCInst &Instruction, InstructionIterator Begin, InstructionIterator End,
+      const unsigned PtrSize, MCInst *&MemLocInstr, unsigned &BaseRegNum,
+      unsigned &IndexRegNum, int64_t &DispValue, const MCExpr *&DispExpr,
+      MCInst *&PCRelBaseOut) const override {
+    MemLocInstr = nullptr;
+    BaseRegNum = 0;
+    IndexRegNum = 0;
+    DispValue = 0;
+    DispExpr = nullptr;
+    PCRelBaseOut = nullptr;
+    return IndirectBranchType::UNKNOWN;
+  }
+
+  bool convertJmpToTailCall(MCInst &Inst) override {
+    if (isTailCall(Inst))
+      return false;
+
+    switch (Inst.getOpcode()) {
+    default:
+      llvm_unreachable("unsupported tail call opcode");
+    case RISCV::JAL:
+    case RISCV::JALR:
+    case RISCV::C_J:
+    case RISCV::C_JR:
+      break;
+    }
+
+    setTailCall(Inst);
+    return true;
+  }
+
+  bool analyzeBranch(InstructionIterator Begin, InstructionIterator End,
+                     const MCSymbol *&TBB, const MCSymbol *&FBB,
+                     MCInst *&CondBranch,
+                     MCInst *&UncondBranch) const override {
+    auto I = End;
+
+    while (I != Begin) {
+      --I;
+
+      // Ignore nops and CFIs
+      if (isPseudo(*I) || isNoop(*I))
+        continue;
+
+      // Stop when we find the first non-terminator
+      if (!isTerminator(*I) || isTailCall(*I) || !isBranch(*I))
+        break;
+
+      // Handle unconditional branches.
+      if (isUnconditionalBranch(*I)) {
+        // If any code was seen after this unconditional branch, we've seen
+        // unreachable code. Ignore them.
+        CondBranch = nullptr;
+        UncondBranch = &*I;
+        const MCSymbol *Sym = getTargetSymbol(*I);
+        assert(Sym != nullptr &&
+               "Couldn't extract BB symbol from jump operand");
+        TBB = Sym;
+        continue;
+      }
+
+      // Handle conditional branches and ignore indirect branches
+      if (isIndirectBranch(*I))
+        return false;
+
+      if (CondBranch == nullptr) {
+        const MCSymbol *TargetBB = getTargetSymbol(*I);
+        if (TargetBB == nullptr) {
+          // Unrecognized branch target
+          return false;
+        }
+        FBB = TBB;
+        TBB = TargetBB;
+        CondBranch = &*I;
+        continue;
+      }
+
+      llvm_unreachable("multiple conditional branches in one BB");
+    }
+
+    return true;
+  }
+
+  const MCSymbol *getTargetSymbol(const MCInst &Inst,
+                                  unsigned OpNum = 0) const override {
+    const MCOperand &Op = Inst.getOperand(OpNum);
+    if (!Op.isExpr())
+      return nullptr;
+
+    return MCPlusBuilder::getTargetSymbol(Op.getExpr());
+  }
+
+  bool lowerTailCall(MCInst &Inst) override {
+    removeAnnotation(Inst, MCPlus::MCAnnotation::kTailCall);
+    if (getConditionalTailCall(Inst))
+      unsetConditionalTailCall(Inst);
+    return true;
+  }
+
+  uint64_t analyzePLTEntry(MCInst &Instruction, InstructionIterator Begin,
+                           InstructionIterator End,
+                           uint64_t BeginPC) const override {
+    auto I = Begin;
+
+    assert(I != End);
+    auto &AUIPC = *I++;
+    assert(AUIPC.getOpcode() == RISCV::AUIPC);
+    assert(AUIPC.getOperand(0).getReg() == RISCV::X28);
+
+    assert(I != End);
+    auto &LD = *I++;
+    assert(LD.getOpcode() == RISCV::LD);
+    assert(LD.getOperand(0).getReg() == RISCV::X28);
+    assert(LD.getOperand(1).getReg() == RISCV::X28);
+
+    assert(I != End);
+    auto &JALR = *I++;
+    (void)JALR;
+    assert(JALR.getOpcode() == RISCV::JALR);
+    assert(JALR.getOperand(0).getReg() == RISCV::X6);
+    assert(JALR.getOperand(1).getReg() == RISCV::X28);
+
+    assert(I != End);
+    auto &NOP = *I++;
+    (void)NOP;
+    assert(isNoop(NOP));
+
+    assert(I == End);
+
+    auto AUIPCOffset = AUIPC.getOperand(1).getImm() << 12;
+    auto LDOffset = LD.getOperand(2).getImm();
+    return BeginPC + AUIPCOffset + LDOffset;
+  }
+
+  bool replaceImmWithSymbolRef(MCInst &Inst, const MCSymbol *Symbol,
+                               int64_t Addend, MCContext *Ctx, int64_t &Value,
+                               uint64_t RelType) const override {
+    unsigned ImmOpNo = -1U;
+
+    for (unsigned Index = 0; Index < MCPlus::getNumPrimeOperands(Inst);
+         ++Index) {
+      if (Inst.getOperand(Index).isImm()) {
+        ImmOpNo = Index;
+        break;
+      }
+    }
+
+    if (ImmOpNo == -1U)
+      return false;
+
+    Value = Inst.getOperand(ImmOpNo).getImm();
+    setOperandToSymbolRef(Inst, ImmOpNo, Symbol, Addend, Ctx, RelType);
+    return true;
+  }
+
+  const MCExpr *getTargetExprFor(MCInst &Inst, const MCExpr *Expr,
+                                 MCContext &Ctx,
+                                 uint64_t RelType) const override {
+    switch (RelType) {
+    default:
+      return Expr;
+    case ELF::R_RISCV_GOT_HI20:
+      // The GOT is reused so no need to create GOT relocations
+    case ELF::R_RISCV_PCREL_HI20:
+      return RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_PCREL_HI, Ctx);
+    case ELF::R_RISCV_PCREL_LO12_I:
+      return RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_PCREL_LO, Ctx);
+    case ELF::R_RISCV_CALL:
+      return RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_CALL, Ctx);
+    case ELF::R_RISCV_CALL_PLT:
+      return RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_CALL_PLT, Ctx);
+    }
+  }
+
+  bool evaluateMemOperandTarget(const MCInst &Inst, uint64_t &Target,
+                                uint64_t Address,
+                                uint64_t Size) const override {
+    return false;
+  }
+
+  bool isCallAuipc(const MCInst &Inst) const {
+    if (Inst.getOpcode() != RISCV::AUIPC)
+      return false;
+
+    const auto &ImmOp = Inst.getOperand(1);
+    if (!ImmOp.isExpr())
+      return false;
+
+    const auto *ImmExpr = ImmOp.getExpr();
+    if (!isa<RISCVMCExpr>(ImmExpr))
+      return false;
+
+    switch (cast<RISCVMCExpr>(ImmExpr)->getKind()) {
+    default:
+      return false;
+    case RISCVMCExpr::VK_RISCV_CALL:
+    case RISCVMCExpr::VK_RISCV_CALL_PLT:
+      return true;
+    }
+  }
+
+  bool isRISCVCall(const MCInst &First, const MCInst &Second) const override {
+    if (!isCallAuipc(First))
+      return false;
+
+    assert(Second.getOpcode() == RISCV::JALR);
+    return true;
+  }
+};
+
+} // end anonymous namespace
+
+namespace llvm {
+namespace bolt {
+
+MCPlusBuilder *createRISCVMCPlusBuilder(const MCInstrAnalysis *Analysis,
+                                        const MCInstrInfo *Info,
+                                        const MCRegisterInfo *RegInfo) {
+  return new RISCVMCPlusBuilder(Analysis, Info, RegInfo);
+}
+
+} // namespace bolt
+} // namespace llvm
diff --git a/bolt/test/RISCV/Inputs/plt-gnu-ld.yaml b/bolt/test/RISCV/Inputs/plt-gnu-ld.yaml
new file mode 100644
index 0000000..c03f308
--- /dev/null
+++ b/bolt/test/RISCV/Inputs/plt-gnu-ld.yaml
@@ -0,0 +1,830 @@
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_DYN
+  Machine:         EM_RISCV
+  Flags:           [ EF_RISCV_RVC, EF_RISCV_FLOAT_ABI_DOUBLE ]
+  Entry:           0x5B0
+ProgramHeaders:
+  - Type:            PT_PHDR
+    Flags:           [ PF_R ]
+    VAddr:           0x40
+    Align:           0x8
+  - Type:            PT_INTERP
+    Flags:           [ PF_R ]
+    FirstSec:        .interp
+    LastSec:         .interp
+    VAddr:           0x270
+  - Type:            0x70000003
+    Flags:           [ PF_R ]
+    FirstSec:        .riscv.attributes
+    LastSec:         .riscv.attributes
+  - Type:            PT_LOAD
+    Flags:           [ PF_X, PF_R ]
+    FirstSec:        .interp
+    LastSec:         .eh_frame
+    Align:           0x1000
+    Offset:          0x0
+  - Type:            PT_LOAD
+    Flags:           [ PF_W, PF_R ]
+    FirstSec:        .preinit_array
+    LastSec:         .bss
+    VAddr:           0x1E08
+    Align:           0x1000
+  - Type:            PT_DYNAMIC
+    Flags:           [ PF_W, PF_R ]
+    FirstSec:        .dynamic
+    LastSec:         .dynamic
+    VAddr:           0x1E20
+    Align:           0x8
+  - Type:            PT_GNU_EH_FRAME
+    Flags:           [ PF_R ]
+    FirstSec:        .eh_frame_hdr
+    LastSec:         .eh_frame_hdr
+    VAddr:           0x6AC
+    Align:           0x4
+  - Type:            PT_GNU_STACK
+    Flags:           [ PF_W, PF_R ]
+    Align:           0x10
+  - Type:            PT_GNU_RELRO
+    Flags:           [ PF_R ]
+    FirstSec:        .preinit_array
+    LastSec:         .dynamic
+    VAddr:           0x1E08
+Sections:
+  - Name:            .interp
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x270
+    AddressAlign:    0x1
+    Content:         2F6C69622F6C642D6C696E75782D726973637636342D6C703634642E736F2E3100
+  - Name:            .dynsym
+    Type:            SHT_DYNSYM
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x300
+    Link:            .dynstr
+    AddressAlign:    0x8
+  - Name:            .dynstr
+    Type:            SHT_STRTAB
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x3C0
+    AddressAlign:    0x1
+  - Name:            .rela.dyn
+    Type:            SHT_RELA
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x480
+    Link:            .dynsym
+    AddressAlign:    0x8
+    Relocations:
+      - Offset:          0x1E08
+        Type:            R_RISCV_RELATIVE
+        Addend:          1498
+      - Offset:          0x1E10
+        Type:            R_RISCV_RELATIVE
+        Addend:          1650
+      - Offset:          0x1E18
+        Type:            R_RISCV_RELATIVE
+        Addend:          1588
+      - Offset:          0x2000
+        Type:            R_RISCV_RELATIVE
+        Addend:          8192
+      - Offset:          0x2038
+        Type:            R_RISCV_RELATIVE
+        Addend:          1658
+      - Offset:          0x2030
+        Symbol:          _ITM_deregisterTMCloneTable
+        Type:            R_RISCV_64
+      - Offset:          0x2040
+        Symbol:          __cxa_finalize
+        Type:            R_RISCV_64
+      - Offset:          0x2048
+        Symbol:          _ITM_registerTMCloneTable
+        Type:            R_RISCV_64
+  - Name:            .rela.plt
+    Type:            SHT_RELA
+    Flags:           [ SHF_ALLOC, SHF_INFO_LINK ]
+    Address:         0x540
+    Link:            .dynsym
+    AddressAlign:    0x8
+    Info:            .got
+    Relocations:
+      - Offset:          0x2018
+        Symbol:          __libc_start_main
+        Type:            R_RISCV_JUMP_SLOT
+      - Offset:          0x2020
+        Symbol:          puts
+        Type:            R_RISCV_JUMP_SLOT
+  - Name:            .plt
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x570
+    AddressAlign:    0x10
+    EntSize:         0x10
+    Content:         972300003303C34103BE83A9130343FD938283A91353130083B2820067000E00172E0000033E8EA867030E0013000000172E0000033E0EA867030E0013000000
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x5B0
+    AddressAlign:    0x4
+    Content:         97000000E780A002AA87172500000335E5A782653000137101FF814601470A8897000000E78000FC029097210000938161228280000017250000130525A2972700009387A7A16388A7009727000083B767A391C38287828017250000130505A0972500009385859F898D93D73540FD91BE95858599C59727000083B727A291C382878280411122E017240000130484A18347040006E495E39727000083B7879F91C7172500000335E59A829797000000E780A0F885472300F400A26002644101828017030000670063F9411106E422E00008170500001305650297000000E78060F181473E85A260026441018280
+  - Name:            .rodata
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x6A0
+    AddressAlign:    0x8
+    Content:         '0100020000000000686900'
+  - Name:            .eh_frame_hdr
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x6AC
+    AddressAlign:    0x4
+    Content:         011B033B100000000100000004FFFFFF28000000
+  - Name:            .eh_frame
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x6C0
+    AddressAlign:    0x8
+    Content:         1000000000000000037A5200017C01011B0D02001000000018000000D4FEFFFF2A0000000007010000000000
+  - Name:            .preinit_array
+    Type:            SHT_PREINIT_ARRAY
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x1E08
+    AddressAlign:    0x1
+    EntSize:         0x8
+    Offset:          0xE08
+    Content:         '0000000000000000'
+  - Name:            .init_array
+    Type:            SHT_INIT_ARRAY
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x1E10
+    AddressAlign:    0x8
+    EntSize:         0x8
+    Content:         '0000000000000000'
+  - Name:            .fini_array
+    Type:            SHT_FINI_ARRAY
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x1E18
+    AddressAlign:    0x8
+    EntSize:         0x8
+    Content:         '0000000000000000'
+  - Name:            .dynamic
+    Type:            SHT_DYNAMIC
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x1E20
+    Link:            .dynstr
+    AddressAlign:    0x8
+    Entries:
+      - Tag:             DT_NEEDED
+        Value:           0x27
+      - Tag:             DT_PREINIT_ARRAY
+        Value:           0x1E08
+      - Tag:             DT_PREINIT_ARRAYSZ
+        Value:           0x8
+      - Tag:             DT_INIT_ARRAY
+        Value:           0x1E10
+      - Tag:             DT_INIT_ARRAYSZ
+        Value:           0x8
+      - Tag:             DT_FINI_ARRAY
+        Value:           0x1E18
+      - Tag:             DT_FINI_ARRAYSZ
+        Value:           0x8
+      - Tag:             DT_GNU_HASH
+        Value:           0x2D8
+      - Tag:             DT_STRTAB
+        Value:           0x3C0
+      - Tag:             DT_SYMTAB
+        Value:           0x300
+      - Tag:             DT_STRSZ
+        Value:           0x7D
+      - Tag:             DT_SYMENT
+        Value:           0x18
+      - Tag:             DT_DEBUG
+        Value:           0x0
+      - Tag:             DT_PLTGOT
+        Value:           0x2008
+      - Tag:             DT_PLTRELSZ
+        Value:           0x30
+      - Tag:             DT_PLTREL
+        Value:           0x7
+      - Tag:             DT_JMPREL
+        Value:           0x540
+      - Tag:             DT_RELA
+        Value:           0x480
+      - Tag:             DT_RELASZ
+        Value:           0xF0
+      - Tag:             DT_RELAENT
+        Value:           0x18
+      - Tag:             DT_FLAGS_1
+        Value:           0x8000000
+      - Tag:             DT_VERNEED
+        Value:           0x450
+      - Tag:             DT_VERNEEDNUM
+        Value:           0x1
+      - Tag:             DT_VERSYM
+        Value:           0x43E
+      - Tag:             DT_RELACOUNT
+        Value:           0x5
+      - Tag:             DT_NULL
+        Value:           0x0
+      - Tag:             DT_NULL
+        Value:           0x0
+      - Tag:             DT_NULL
+        Value:           0x0
+      - Tag:             DT_NULL
+        Value:           0x0
+      - Tag:             DT_NULL
+        Value:           0x0
+  - Name:            .data
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x2000
+    AddressAlign:    0x8
+    Content:         '0000000000000000'
+  - Name:            .tm_clone_table
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x2008
+    AddressAlign:    0x8
+  - Name:            .got
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x2008
+    AddressAlign:    0x8
+    EntSize:         0x8
+    Content:         FFFFFFFFFFFFFFFF000000000000000070050000000000007005000000000000201E0000000000000000000000000000000000000000000000000000000000000000000000000000
+  - Name:            .bss
+    Type:            SHT_NOBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x2050
+    AddressAlign:    0x1
+    Size:            0x8
+  - Name:            .riscv.attributes
+    Type:            SHT_RISCV_ATTRIBUTES
+    AddressAlign:    0x1
+    Content:         4149000000726973637600013F00000004100572763634693270315F6D3270305F613270315F663270325F643270325F633270305F7A696373723270305F7A6966656E63656932703000
+  - Name:            .rela.text
+    Type:            SHT_RELA
+    Flags:           [ SHF_INFO_LINK ]
+    Link:            .symtab
+    AddressAlign:    0x8
+    Info:            .text
+    Relocations:
+      - Offset:          0x5B0
+        Type:            R_RISCV_NONE
+        Addend:          2
+      - Offset:          0x5B0
+        Symbol:          load_gp
+        Type:            R_RISCV_CALL
+      - Offset:          0x5B0
+        Type:            R_RISCV_RELAX
+      - Offset:          0x5BA
+        Symbol:          main
+        Type:            R_RISCV_GOT_HI20
+      - Offset:          0x5BE
+        Symbol:          '.L0 '
+        Type:            R_RISCV_PCREL_LO12_I
+      - Offset:          0x5BE
+        Type:            R_RISCV_RELAX
+      - Offset:          0x5D0
+        Symbol:          '__libc_start_main@GLIBC_2.34'
+        Type:            R_RISCV_CALL_PLT
+      - Offset:          0x5D0
+        Type:            R_RISCV_RELAX
+      - Offset:          0x5DA
+        Symbol:          '__global_pointer$'
+        Type:            R_RISCV_PCREL_HI20
+      - Offset:          0x5DE
+        Symbol:          '.L0  (1)'
+        Type:            R_RISCV_PCREL_LO12_I
+      - Offset:          0x5E6
+        Symbol:          __TMC_LIST__
+        Type:            R_RISCV_PCREL_HI20
+      - Offset:          0x5E6
+        Type:            R_RISCV_RELAX
+      - Offset:          0x5EA
+        Symbol:          '.L0  (4)'
+        Type:            R_RISCV_PCREL_LO12_I
+      - Offset:          0x5EA
+        Type:            R_RISCV_RELAX
+      - Offset:          0x5EE
+        Symbol:          __TMC_END__
+        Type:            R_RISCV_PCREL_HI20
+      - Offset:          0x5EE
+        Type:            R_RISCV_RELAX
+      - Offset:          0x5F2
+        Symbol:          '.L0  (5)'
+        Type:            R_RISCV_PCREL_LO12_I
+      - Offset:          0x5F2
+        Type:            R_RISCV_RELAX
+      - Offset:          0x5F6
+        Symbol:          .L1
+        Type:            R_RISCV_BRANCH
+      - Offset:          0x5FA
+        Symbol:          _ITM_deregisterTMCloneTable
+        Type:            R_RISCV_GOT_HI20
+      - Offset:          0x5FE
+        Symbol:          '.L0  (6)'
+        Type:            R_RISCV_PCREL_LO12_I
+      - Offset:          0x5FE
+        Type:            R_RISCV_RELAX
+      - Offset:          0x602
+        Symbol:          .L1
+        Type:            R_RISCV_RVC_BRANCH
+      - Offset:          0x608
+        Symbol:          __TMC_LIST__
+        Type:            R_RISCV_PCREL_HI20
+      - Offset:          0x608
+        Type:            R_RISCV_RELAX
+      - Offset:          0x60C
+        Symbol:          '.L0  (7)'
+        Type:            R_RISCV_PCREL_LO12_I
+      - Offset:          0x60C
+        Type:            R_RISCV_RELAX
+      - Offset:          0x610
+        Symbol:          __TMC_END__
+        Type:            R_RISCV_PCREL_HI20
+      - Offset:          0x610
+        Type:            R_RISCV_RELAX
+      - Offset:          0x614
+        Symbol:          '.L0  (8)'
+        Type:            R_RISCV_PCREL_LO12_I
+      - Offset:          0x614
+        Type:            R_RISCV_RELAX
+      - Offset:          0x624
+        Symbol:          .L7
+        Type:            R_RISCV_RVC_BRANCH
+      - Offset:          0x626
+        Symbol:          _ITM_registerTMCloneTable
+        Type:            R_RISCV_GOT_HI20
+      - Offset:          0x62A
+        Symbol:          '.L0  (9)'
+        Type:            R_RISCV_PCREL_LO12_I
+      - Offset:          0x62A
+        Type:            R_RISCV_RELAX
+      - Offset:          0x62E
+        Symbol:          .L7
+        Type:            R_RISCV_RVC_BRANCH
+      - Offset:          0x638
+        Symbol:          completed.0
+        Type:            R_RISCV_PCREL_HI20
+      - Offset:          0x638
+        Type:            R_RISCV_RELAX
+      - Offset:          0x63C
+        Symbol:          '.L0  (10)'
+        Type:            R_RISCV_PCREL_LO12_I
+      - Offset:          0x63C
+        Type:            R_RISCV_RELAX
+      - Offset:          0x646
+        Symbol:          .L15
+        Type:            R_RISCV_RVC_BRANCH
+      - Offset:          0x648
+        Symbol:          '__cxa_finalize@GLIBC_2.27'
+        Type:            R_RISCV_GOT_HI20
+      - Offset:          0x64C
+        Symbol:          '.L0  (11)'
+        Type:            R_RISCV_PCREL_LO12_I
+      - Offset:          0x64C
+        Type:            R_RISCV_RELAX
+      - Offset:          0x650
+        Symbol:          .L17
+        Type:            R_RISCV_RVC_BRANCH
+      - Offset:          0x652
+        Symbol:          __dso_handle
+        Type:            R_RISCV_PCREL_HI20
+      - Offset:          0x652
+        Type:            R_RISCV_RELAX
+      - Offset:          0x656
+        Symbol:          '.L0  (12)'
+        Type:            R_RISCV_PCREL_LO12_I
+      - Offset:          0x656
+        Type:            R_RISCV_RELAX
+      - Offset:          0x65C
+        Symbol:          deregister_tm_clones
+        Type:            R_RISCV_CALL
+      - Offset:          0x65C
+        Type:            R_RISCV_RELAX
+      - Offset:          0x672
+        Symbol:          register_tm_clones
+        Type:            R_RISCV_CALL
+      - Offset:          0x672
+        Type:            R_RISCV_RELAX
+      - Offset:          0x682
+        Symbol:          .LC0
+        Type:            R_RISCV_PCREL_HI20
+      - Offset:          0x682
+        Type:            R_RISCV_RELAX
+      - Offset:          0x686
+        Symbol:          '.L0  (13)'
+        Type:            R_RISCV_PCREL_LO12_I
+      - Offset:          0x686
+        Type:            R_RISCV_RELAX
+      - Offset:          0x68A
+        Symbol:          'puts@GLIBC_2.27'
+        Type:            R_RISCV_CALL_PLT
+      - Offset:          0x68A
+        Type:            R_RISCV_RELAX
+  - Name:            .rela.eh_frame
+    Type:            SHT_RELA
+    Flags:           [ SHF_INFO_LINK ]
+    Link:            .symtab
+    AddressAlign:    0x8
+    Info:            .eh_frame
+    Relocations:
+      - Offset:          0x6DC
+        Symbol:          '.L0  (2)'
+        Type:            R_RISCV_32_PCREL
+      - Offset:          0x6E0
+        Symbol:          '.L0  (3)'
+        Type:            R_RISCV_ADD32
+      - Offset:          0x6E0
+        Symbol:          '.L0  (2)'
+        Type:            R_RISCV_SUB32
+  - Name:            .rela.preinit_array
+    Type:            SHT_RELA
+    Flags:           [ SHF_INFO_LINK ]
+    Link:            .symtab
+    AddressAlign:    0x8
+    Info:            .preinit_array
+    Relocations:
+      - Offset:          0x1E08
+        Symbol:          load_gp
+        Type:            R_RISCV_64
+  - Name:            .rela.init_array
+    Type:            SHT_RELA
+    Flags:           [ SHF_INFO_LINK ]
+    Link:            .symtab
+    AddressAlign:    0x8
+    Info:            .init_array
+    Relocations:
+      - Offset:          0x1E10
+        Symbol:          frame_dummy
+        Type:            R_RISCV_64
+  - Name:            .rela.fini_array
+    Type:            SHT_RELA
+    Flags:           [ SHF_INFO_LINK ]
+    Link:            .symtab
+    AddressAlign:    0x8
+    Info:            .fini_array
+    Relocations:
+      - Offset:          0x1E18
+        Symbol:          __do_global_dtors_aux
+        Type:            R_RISCV_64
+  - Name:            .rela.data
+    Type:            SHT_RELA
+    Flags:           [ SHF_INFO_LINK ]
+    Link:            .symtab
+    AddressAlign:    0x8
+    Info:            .data
+    Relocations:
+      - Offset:          0x2000
+        Symbol:          __dso_handle
+        Type:            R_RISCV_64
+  - Type:            SectionHeaderTable
+    Sections:
+      - Name:            .interp
+      - Name:            .dynsym
+      - Name:            .dynstr
+      - Name:            .rela.dyn
+      - Name:            .rela.plt
+      - Name:            .plt
+      - Name:            .text
+      - Name:            .rela.text
+      - Name:            .rodata
+      - Name:            .eh_frame_hdr
+      - Name:            .eh_frame
+      - Name:            .rela.eh_frame
+      - Name:            .preinit_array
+      - Name:            .rela.preinit_array
+      - Name:            .init_array
+      - Name:            .rela.init_array
+      - Name:            .fini_array
+      - Name:            .rela.fini_array
+      - Name:            .dynamic
+      - Name:            .data
+      - Name:            .rela.data
+      - Name:            .tm_clone_table
+      - Name:            .got
+      - Name:            .bss
+      - Name:            .riscv.attributes
+      - Name:            .symtab
+      - Name:            .strtab
+      - Name:            .shstrtab
+Symbols:
+  - Name:            .interp
+    Type:            STT_SECTION
+    Section:         .interp
+    Value:           0x270
+  - Name:            .dynsym
+    Type:            STT_SECTION
+    Section:         .dynsym
+    Value:           0x300
+  - Name:            .dynstr
+    Type:            STT_SECTION
+    Section:         .dynstr
+    Value:           0x3C0
+  - Name:            .rela.dyn
+    Type:            STT_SECTION
+    Section:         .rela.dyn
+    Value:           0x480
+  - Name:            .rela.plt
+    Type:            STT_SECTION
+    Section:         .rela.plt
+    Value:           0x540
+  - Name:            .plt
+    Type:            STT_SECTION
+    Section:         .plt
+    Value:           0x570
+  - Name:            .text
+    Type:            STT_SECTION
+    Section:         .text
+    Value:           0x5B0
+  - Name:            .rodata
+    Type:            STT_SECTION
+    Section:         .rodata
+    Value:           0x6A0
+  - Name:            .eh_frame_hdr
+    Type:            STT_SECTION
+    Section:         .eh_frame_hdr
+    Value:           0x6AC
+  - Name:            .eh_frame
+    Type:            STT_SECTION
+    Section:         .eh_frame
+    Value:           0x6C0
+  - Name:            .preinit_array
+    Type:            STT_SECTION
+    Section:         .preinit_array
+    Value:           0x1E08
+  - Name:            .init_array
+    Type:            STT_SECTION
+    Section:         .init_array
+    Value:           0x1E10
+  - Name:            .fini_array
+    Type:            STT_SECTION
+    Section:         .fini_array
+    Value:           0x1E18
+  - Name:            .dynamic
+    Type:            STT_SECTION
+    Section:         .dynamic
+    Value:           0x1E20
+  - Name:            .data
+    Type:            STT_SECTION
+    Section:         .data
+    Value:           0x2000
+  - Name:            .tm_clone_table
+    Type:            STT_SECTION
+    Section:         .tm_clone_table
+    Value:           0x2008
+  - Name:            .got
+    Type:            STT_SECTION
+    Section:         .got
+    Value:           0x2008
+  - Name:            .bss
+    Type:            STT_SECTION
+    Section:         .bss
+    Value:           0x2050
+  - Name:            .riscv.attributes
+    Type:            STT_SECTION
+    Section:         .riscv.attributes
+  - Name:            start.os
+    Type:            STT_FILE
+    Index:           SHN_ABS
+  - Name:            '$x'
+    Section:         .text
+    Value:           0x5B0
+  - Name:            load_gp
+    Section:         .text
+    Value:           0x5DA
+  - Name:            init.c
+    Type:            STT_FILE
+    Index:           SHN_ABS
+  - Name:            '.L0 '
+    Section:         .text
+    Value:           0x5BA
+  - Name:            '.L0  (1)'
+    Section:         .text
+    Value:           0x5DA
+  - Name:            '.L0  (2)'
+    Section:         .text
+    Value:           0x5B0
+  - Name:            '.L0  (3)'
+    Section:         .text
+    Value:           0x5DA
+  - Name:            crtstuff.c
+    Type:            STT_FILE
+    Index:           SHN_ABS
+  - Name:            __TMC_LIST__
+    Type:            STT_OBJECT
+    Section:         .tm_clone_table
+    Value:           0x2008
+  - Name:            deregister_tm_clones
+    Type:            STT_FUNC
+    Section:         .text
+    Value:           0x5E6
+  - Name:            '$x (1)'
+    Section:         .text
+    Value:           0x5E6
+  - Name:            register_tm_clones
+    Type:            STT_FUNC
+    Section:         .text
+    Value:           0x608
+  - Name:            __do_global_dtors_aux
+    Type:            STT_FUNC
+    Section:         .text
+    Value:           0x634
+  - Name:            completed.0
+    Type:            STT_OBJECT
+    Section:         .bss
+    Value:           0x2050
+    Size:            0x1
+  - Name:            __do_global_dtors_aux_fini_array_entry
+    Type:            STT_OBJECT
+    Section:         .fini_array
+    Value:           0x1E18
+  - Name:            frame_dummy
+    Type:            STT_FUNC
+    Section:         .text
+    Value:           0x672
+  - Name:            __frame_dummy_init_array_entry
+    Type:            STT_OBJECT
+    Section:         .init_array
+    Value:           0x1E10
+  - Name:            '.L0  (4)'
+    Section:         .text
+    Value:           0x5E6
+  - Name:            '.L0  (5)'
+    Section:         .text
+    Value:           0x5EE
+  - Name:            .L1
+    Section:         .text
+    Value:           0x606
+  - Name:            '.L0  (6)'
+    Section:         .text
+    Value:           0x5FA
+  - Name:            '.L0  (7)'
+    Section:         .text
+    Value:           0x608
+  - Name:            '.L0  (8)'
+    Section:         .text
+    Value:           0x610
+  - Name:            .L7
+    Section:         .text
+    Value:           0x632
+  - Name:            '.L0  (9)'
+    Section:         .text
+    Value:           0x626
+  - Name:            '.L0  (10)'
+    Section:         .text
+    Value:           0x638
+  - Name:            .L15
+    Section:         .text
+    Value:           0x66A
+  - Name:            '.L0  (11)'
+    Section:         .text
+    Value:           0x648
+  - Name:            .L17
+    Section:         .text
+    Value:           0x65C
+  - Name:            '.L0  (12)'
+    Section:         .text
+    Value:           0x652
+  - Name:            test.c
+    Type:            STT_FILE
+    Index:           SHN_ABS
+  - Name:            '$x (2)'
+    Section:         .text
+    Value:           0x67A
+  - Name:            .LC0
+    Section:         .rodata
+    Value:           0x6A8
+  - Name:            '.L0  (13)'
+    Section:         .text
+    Value:           0x682
+  - Name:            'crtstuff.c (1)'
+    Type:            STT_FILE
+    Index:           SHN_ABS
+  - Name:            __FRAME_END__
+    Type:            STT_OBJECT
+    Section:         .eh_frame
+    Value:           0x6E8
+  - Type:            STT_FILE
+    Index:           SHN_ABS
+  - Name:            _PROCEDURE_LINKAGE_TABLE_
+    Type:            STT_OBJECT
+    Index:           SHN_ABS
+    Value:           0x570
+  - Name:            _DYNAMIC
+    Type:            STT_OBJECT
+    Index:           SHN_ABS
+    Value:           0x1E20
+  - Name:            __GNU_EH_FRAME_HDR
+    Section:         .eh_frame_hdr
+    Value:           0x6AC
+  - Name:            _GLOBAL_OFFSET_TABLE_
+    Type:            STT_OBJECT
+    Index:           SHN_ABS
+    Value:           0x2028
+  - Name:            '__libc_start_main@GLIBC_2.34'
+    Type:            STT_FUNC
+    Binding:         STB_GLOBAL
+  - Name:            _ITM_deregisterTMCloneTable
+    Binding:         STB_WEAK
+  - Name:            data_start
+    Section:         .data
+    Binding:         STB_WEAK
+    Value:           0x2000
+  - Name:            __BSS_END__
+    Section:         .bss
+    Binding:         STB_GLOBAL
+    Value:           0x2058
+  - Name:            _edata
+    Section:         .got
+    Binding:         STB_GLOBAL
+    Value:           0x2050
+  - Name:            __SDATA_BEGIN__
+    Section:         .got
+    Binding:         STB_GLOBAL
+    Value:           0x2050
+  - Name:            __DATA_BEGIN__
+    Section:         .data
+    Binding:         STB_GLOBAL
+    Value:           0x2000
+  - Name:            __data_start
+    Section:         .data
+    Binding:         STB_GLOBAL
+    Value:           0x2000
+  - Name:            __dso_handle
+    Type:            STT_OBJECT
+    Section:         .data
+    Binding:         STB_GLOBAL
+    Value:           0x2000
+    Other:           [ STV_HIDDEN ]
+  - Name:            _IO_stdin_used
+    Type:            STT_OBJECT
+    Section:         .rodata
+    Binding:         STB_GLOBAL
+    Value:           0x6A0
+    Size:            0x4
+  - Name:            _end
+    Section:         .bss
+    Binding:         STB_GLOBAL
+    Value:           0x2058
+  - Name:            _start
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Value:           0x5B0
+    Size:            0x2A
+  - Name:            '__global_pointer$'
+    Index:           SHN_ABS
+    Binding:         STB_GLOBAL
+    Value:           0x2800
+  - Name:            'puts@GLIBC_2.27'
+    Type:            STT_FUNC
+    Binding:         STB_GLOBAL
+  - Name:            __bss_start
+    Section:         .bss
+    Binding:         STB_GLOBAL
+    Value:           0x2050
+  - Name:            main
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Value:           0x67A
+    Size:            0x24
+  - Name:            '__cxa_finalize@GLIBC_2.27'
+    Type:            STT_FUNC
+    Binding:         STB_WEAK
+  - Name:            __TMC_END__
+    Type:            STT_OBJECT
+    Section:         .tm_clone_table
+    Binding:         STB_GLOBAL
+    Value:           0x2008
+    Other:           [ STV_HIDDEN ]
+  - Name:            _ITM_registerTMCloneTable
+    Binding:         STB_WEAK
+DynamicSymbols:
+  - Name:            .text
+    Type:            STT_SECTION
+    Section:         .text
+    Value:           0x5B0
+  - Name:            __libc_start_main
+    Type:            STT_FUNC
+    Binding:         STB_GLOBAL
+  - Name:            _ITM_deregisterTMCloneTable
+    Binding:         STB_WEAK
+  - Name:            puts
+    Type:            STT_FUNC
+    Binding:         STB_GLOBAL
+  - Name:            __cxa_finalize
+    Type:            STT_FUNC
+    Binding:         STB_WEAK
+  - Name:            _ITM_registerTMCloneTable
+    Binding:         STB_WEAK
+  - Name:            main
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Value:           0x67A
+    Size:            0x24
+...
diff --git a/bolt/test/RISCV/lit.local.cfg b/bolt/test/RISCV/lit.local.cfg
new file mode 100644
index 0000000..a1ebbfc
--- /dev/null
+++ b/bolt/test/RISCV/lit.local.cfg
@@ -0,0 +1,7 @@
+if 'RISCV' not in config.root.targets:
+    config.unsupported = True
+
+flags = '--target=riscv64 -nostdlib -ffreestanding -Wl,--no-relax,--emit-relocs'
+
+config.substitutions.insert(0, ('%cflags', f'%cflags {flags}'))
+config.substitutions.insert(0, ('%cxxflags', f'%cxxflags {flags}'))
diff --git a/bolt/test/RISCV/plt-gnu-ld.test b/bolt/test/RISCV/plt-gnu-ld.test
new file mode 100644
index 0000000..f87f51d
--- /dev/null
+++ b/bolt/test/RISCV/plt-gnu-ld.test
@@ -0,0 +1,9 @@
+// This test checks that the PLT symbols are properly recognized
+// by the BOLT tool.
+
+// RUN: yaml2obj %p/Inputs/plt-gnu-ld.yaml &> %t.exe
+// RUN: llvm-bolt %t.exe -o %t.bolt.exe --print-cfg --print-only=main \
+// RUN:   | FileCheck %s
+
+// CHECK: Binary Function "main" after building cfg {
+// CHECK: auipc ra, puts@PLT
diff --git a/bolt/test/RISCV/reloc-abs.s b/bolt/test/RISCV/reloc-abs.s
new file mode 100644
index 0000000..3e4b8b1
--- /dev/null
+++ b/bolt/test/RISCV/reloc-abs.s
@@ -0,0 +1,26 @@
+// RUN: %clang %cflags -Wl,--defsym='__global_pointer$'=0x2800 -o %t %s
+// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
+// RUN:    | FileCheck %s
+
+  .data
+  .globl d
+  .p2align 3
+d:
+  .dword 0
+
+  .text
+  .globl _start
+  .p2align 1
+// CHECK: Binary Function "_start" after building cfg {
+_start:
+  nop
+  .option push
+  .option norelax
+1:
+// CHECK: .Ltmp0
+// CHECK: auipc gp, %pcrel_hi(__global_pointer$)
+// CHECK-NEXT: addi gp, gp, %pcrel_lo(.Ltmp0)
+  auipc gp, %pcrel_hi(__global_pointer$)
+  addi  gp, gp, %pcrel_lo(1b)
+  .option pop
+  .size _start, .-_start
diff --git a/bolt/test/RISCV/reloc-branch.s b/bolt/test/RISCV/reloc-branch.s
new file mode 100644
index 0000000..4399114
--- /dev/null
+++ b/bolt/test/RISCV/reloc-branch.s
@@ -0,0 +1,16 @@
+// RUN: %clang %cflags -o %t %s
+// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
+// RUN:    | FileCheck %s
+
+  .text
+  .globl _start
+  .p2align 1
+// CHECK: Binary Function "_start" after building cfg {
+_start:
+// CHECK: beq zero, zero, .Ltmp0
+  beq zero, zero, 1f
+  nop
+// CHECK: .Ltmp0
+1:
+  ret
+  .size _start, .-_start
diff --git a/bolt/test/RISCV/reloc-call.s b/bolt/test/RISCV/reloc-call.s
new file mode 100644
index 0000000..17b952b
--- /dev/null
+++ b/bolt/test/RISCV/reloc-call.s
@@ -0,0 +1,21 @@
+// RUN: %clang %cflags -o %t %s
+// RUN: llvm-bolt --print-fix-riscv-calls --print-only=_start -o /dev/null %t \
+// RUN:    | FileCheck %s
+
+  .text
+
+  .global f
+  .p2align 1
+f:
+  ret
+  .size f, .-f
+
+// CHECK: Binary Function "_start" after fix-riscv-calls {
+  .globl _start
+  .p2align 1
+_start:
+// CHECK: auipc ra, f
+// CHECK-NEXT: jalr ra
+  call f
+  ret
+  .size _start, .-_start
diff --git a/bolt/test/RISCV/reloc-got.s b/bolt/test/RISCV/reloc-got.s
new file mode 100644
index 0000000..b6cd61b
--- /dev/null
+++ b/bolt/test/RISCV/reloc-got.s
@@ -0,0 +1,24 @@
+// RUN: %clang %cflags -o %t %s
+// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
+// RUN:    | FileCheck %s
+
+  .data
+  .globl d
+  .p2align 3
+d:
+  .dword 0
+
+  .text
+  .globl _start
+  .p2align 1
+// CHECK: Binary Function "_start" after building cfg {
+_start:
+  nop // Here to not make the _start and .Ltmp0 symbols coincide
+// CHECK: .Ltmp0
+// CHECK: auipc t0, %pcrel_hi(__BOLT_got_zero+{{[0-9]+}})
+// CHECK-NEXT: ld t0, %pcrel_lo(.Ltmp0)(t0)
+1:
+  auipc t0, %got_pcrel_hi(d)
+  ld t0, %pcrel_lo(1b)(t0)
+  ret
+  .size _start, .-_start
diff --git a/bolt/test/RISCV/reloc-jal.s b/bolt/test/RISCV/reloc-jal.s
new file mode 100644
index 0000000..427e8f2
--- /dev/null
+++ b/bolt/test/RISCV/reloc-jal.s
@@ -0,0 +1,20 @@
+// RUN: %clang %cflags -o %t %s
+// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
+// RUN:    | FileCheck %s
+
+  .text
+
+  .global f
+  .p2align 1
+f:
+  ret
+  .size f, .-f
+
+// CHECK: Binary Function "_start" after building cfg {
+  .globl _start
+  .p2align 1
+_start:
+// CHECK: jal ra, f
+  jal ra, f
+  ret
+  .size _start, .-_start
diff --git a/bolt/test/RISCV/reloc-pcrel.s b/bolt/test/RISCV/reloc-pcrel.s
new file mode 100644
index 0000000..2d5a349
--- /dev/null
+++ b/bolt/test/RISCV/reloc-pcrel.s
@@ -0,0 +1,22 @@
+// RUN: %clang %cflags -o %t %s
+// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
+// RUN:    | FileCheck %s
+
+  .data
+  .globl d
+  .p2align 3
+d:
+  .dword 0
+
+  .text
+  .globl _start
+  .p2align 1
+// CHECK: Binary Function "_start" after building cfg {
+_start:
+  nop // Here to not make the _start and .Ltmp0 symbols coincide
+// CHECK: .Ltmp0
+// CHECK: auipc t0, %pcrel_hi(d)
+// CHECK-NEXT: ld t0, %pcrel_lo(.Ltmp0)(t0)
+  ld t0, d
+  ret
+  .size _start, .-_start
diff --git a/bolt/test/RISCV/reloc-rvc-branch.s b/bolt/test/RISCV/reloc-rvc-branch.s
new file mode 100644
index 0000000..34221c1
--- /dev/null
+++ b/bolt/test/RISCV/reloc-rvc-branch.s
@@ -0,0 +1,16 @@
+// RUN: %clang %cflags -o %t %s
+// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
+// RUN:    | FileCheck %s
+
+  .text
+  .globl _start
+  .p2align 1
+// CHECK: Binary Function "_start" after building cfg {
+_start:
+// CHECK: beqz a0, .Ltmp0
+  c.beqz a0, 1f
+  nop
+// CHECK: .Ltmp0
+1:
+  ret
+  .size _start, .-_start
diff --git a/bolt/test/RISCV/reloc-rvc-jump.s b/bolt/test/RISCV/reloc-rvc-jump.s
new file mode 100644
index 0000000..21b10ba
--- /dev/null
+++ b/bolt/test/RISCV/reloc-rvc-jump.s
@@ -0,0 +1,16 @@
+// RUN: %clang %cflags -o %t %s
+// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
+// RUN:    | FileCheck %s
+
+  .text
+  .globl _start
+  .p2align 1
+// CHECK: Binary Function "_start" after building cfg {
+_start:
+// CHECK: j .Ltmp0
+  c.j 1f
+  nop
+// CHECK: .Ltmp0
+1:
+  ret
+  .size _start, .-_start