Reland "riscv64: Add relative patcher."

This reverts commit 6aa484945f7c77aef73643a3a8aab3ac06ecaea1.

Reason for revert: Reland with a fix.

Change-Id: I2de4cc8c59f1ad9d0807470ab2336106dcef430a
Test: m  # aosp_cf_riscv64_phone-userdebug
Test: m test-art-host-gtest
Bug: 283082089
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 761b073..7f8bd05 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -55,6 +55,11 @@
                 "linker/arm64/relative_patcher_arm64.cc",
             ],
         },
+        riscv64: {
+            srcs: [
+                "linker/riscv64/relative_patcher_riscv64.cc",
+            ],
+        },
         x86: {
             srcs: [
                 "linker/x86/relative_patcher_x86.cc",
@@ -531,6 +536,11 @@
                 "linker/arm64/relative_patcher_arm64_test.cc",
             ],
         },
+        riscv64: {
+            srcs: [
+                "linker/riscv64/relative_patcher_riscv64_test.cc",
+            ],
+        },
         x86: {
             srcs: [
                 "linker/x86/relative_patcher_x86_test.cc",
diff --git a/dex2oat/linker/arm/relative_patcher_thumb2.h b/dex2oat/linker/arm/relative_patcher_thumb2.h
index d360482..b3a3eb5 100644
--- a/dex2oat/linker/arm/relative_patcher_thumb2.h
+++ b/dex2oat/linker/arm/relative_patcher_thumb2.h
@@ -23,10 +23,6 @@
 
 namespace art {
 
-namespace arm {
-class ArmVIXLAssembler;
-}  // namespace arm
-
 namespace linker {
 
 class Thumb2RelativePatcher final : public ArmBaseRelativePatcher {
diff --git a/dex2oat/linker/arm64/relative_patcher_arm64.h b/dex2oat/linker/arm64/relative_patcher_arm64.h
index 9ad2c96..ce90b80 100644
--- a/dex2oat/linker/arm64/relative_patcher_arm64.h
+++ b/dex2oat/linker/arm64/relative_patcher_arm64.h
@@ -22,10 +22,6 @@
 
 namespace art {
 
-namespace arm64 {
-class Arm64Assembler;
-}  // namespace arm64
-
 namespace linker {
 
 class Arm64RelativePatcher final : public ArmBaseRelativePatcher {
diff --git a/dex2oat/linker/arm64/relative_patcher_arm64_test.cc b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc
index ce61f43..09fbfc7 100644
--- a/dex2oat/linker/arm64/relative_patcher_arm64_test.cc
+++ b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc
@@ -82,7 +82,7 @@
   // CBNZ x17, +0. Bits 5-23 are a placeholder for target offset from PC in units of 4-bytes.
   static constexpr uint32_t kCbnzIP1Plus0Insn = 0xb5000011u;
 
-  void InsertInsn(std::vector<uint8_t>* code, size_t pos, uint32_t insn) {
+  static void InsertInsn(std::vector<uint8_t>* code, size_t pos, uint32_t insn) {
     CHECK_LE(pos, code->size());
     const uint8_t insn_code[] = {
         static_cast<uint8_t>(insn),
@@ -94,11 +94,11 @@
     code->insert(code->begin() + pos, insn_code, insn_code + sizeof(insn_code));
   }
 
-  void PushBackInsn(std::vector<uint8_t>* code, uint32_t insn) {
+  static void PushBackInsn(std::vector<uint8_t>* code, uint32_t insn) {
     InsertInsn(code, code->size(), insn);
   }
 
-  std::vector<uint8_t> RawCode(std::initializer_list<uint32_t> insns) {
+  static std::vector<uint8_t> RawCode(std::initializer_list<uint32_t> insns) {
     std::vector<uint8_t> raw_code;
     raw_code.reserve(insns.size() * 4u);
     for (uint32_t insn : insns) {
@@ -235,7 +235,7 @@
     return false;
   }
 
-  std::vector<uint8_t> GenNops(size_t num_nops) {
+  static std::vector<uint8_t> GenNops(size_t num_nops) {
     std::vector<uint8_t> result;
     result.reserve(num_nops * 4u);
     for (size_t i = 0; i != num_nops; ++i) {
@@ -244,7 +244,7 @@
     return result;
   }
 
-  std::vector<uint8_t> GenNopsAndBl(size_t num_nops, uint32_t bl) {
+  static std::vector<uint8_t> GenNopsAndBl(size_t num_nops, uint32_t bl) {
     std::vector<uint8_t> result;
     result.reserve(num_nops * 4u + 4u);
     for (size_t i = 0; i != num_nops; ++i) {
@@ -933,7 +933,7 @@
       [&](uint32_t adrp_offset, uint32_t string_offset) {
         Reset();
         /* ADD that uses the result register of "ADRP x0, addr" as both source and destination. */
-        uint32_t add = kSubXInsn | (100 << 10) | (0u << 5) | 0u;  /* ADD x0, x0, #100 */
+        uint32_t add = kAddXInsn | (100 << 10) | (0u << 5) | 0u;  /* ADD x0, x0, #100 */
         TestAdrpInsn2Add(add, adrp_offset, /*has_thunk=*/ false, string_offset);
       },
       { 0x12345678u, 0xffffc840 });
diff --git a/dex2oat/linker/relative_patcher.cc b/dex2oat/linker/relative_patcher.cc
index 1c04812..231c4a5 100644
--- a/dex2oat/linker/relative_patcher.cc
+++ b/dex2oat/linker/relative_patcher.cc
@@ -23,6 +23,9 @@
 #ifdef ART_ENABLE_CODEGEN_arm64
 #include "linker/arm64/relative_patcher_arm64.h"
 #endif
+#ifdef ART_ENABLE_CODEGEN_riscv64
+#include "linker/riscv64/relative_patcher_riscv64.h"
+#endif
 #ifdef ART_ENABLE_CODEGEN_x86
 #include "linker/x86/relative_patcher_x86.h"
 #endif
@@ -118,6 +121,13 @@
                                    target_provider,
                                    features->AsArm64InstructionSetFeatures()));
 #endif
+#ifdef ART_ENABLE_CODEGEN_riscv64
+    case InstructionSet::kRiscv64:
+      return std::unique_ptr<RelativePatcher>(
+          new Riscv64RelativePatcher(thunk_provider,
+                                     target_provider,
+                                     features->AsRiscv64InstructionSetFeatures()));
+#endif
     default:
       return std::unique_ptr<RelativePatcher>(new RelativePatcherNone);
   }
diff --git a/dex2oat/linker/relative_patcher_test.h b/dex2oat/linker/relative_patcher_test.h
index 2900523..1e9b0e1 100644
--- a/dex2oat/linker/relative_patcher_test.h
+++ b/dex2oat/linker/relative_patcher_test.h
@@ -68,6 +68,7 @@
     patcher_.reset();
     bss_begin_ = 0u;
     string_index_to_offset_map_.clear();
+    method_index_to_offset_map_.clear();
     compiled_method_refs_.clear();
     compiled_methods_.clear();
     patched_code_.clear();
@@ -86,6 +87,7 @@
                                        &method_offset_map_);
     bss_begin_ = 0u;
     string_index_to_offset_map_.clear();
+    method_index_to_offset_map_.clear();
     compiled_method_refs_.clear();
     compiled_methods_.clear();
     patched_code_.clear();
@@ -185,6 +187,13 @@
                                                patch,
                                                offset + patch.LiteralOffset(),
                                                target_offset);
+          } else if (patch.GetType() == LinkerPatch::Type::kMethodBssEntry) {
+            uint32_t target_offset =
+                bss_begin_ + method_index_to_offset_map_.Get(patch.TargetMethod().index);
+            patcher_->PatchPcRelativeReference(&patched_code_,
+                                               patch,
+                                               offset + patch.LiteralOffset(),
+                                               target_offset);
           } else if (patch.GetType() == LinkerPatch::Type::kStringRelative) {
             uint32_t target_offset =
                 string_index_to_offset_map_.Get(patch.TargetStringIndex().index_);
@@ -391,6 +400,7 @@
   std::unique_ptr<RelativePatcher> patcher_;
   uint32_t bss_begin_;
   SafeMap<uint32_t, uint32_t> string_index_to_offset_map_;
+  SafeMap<uint32_t, uint32_t> method_index_to_offset_map_;
   std::vector<MethodReference> compiled_method_refs_;
   std::vector<std::unique_ptr<CompiledMethod>> compiled_methods_;
   std::vector<uint8_t> patched_code_;
diff --git a/dex2oat/linker/riscv64/relative_patcher_riscv64.cc b/dex2oat/linker/riscv64/relative_patcher_riscv64.cc
new file mode 100644
index 0000000..09b7ac8
--- /dev/null
+++ b/dex2oat/linker/riscv64/relative_patcher_riscv64.cc
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "relative_patcher_riscv64.h"
+
+#include "base/bit_utils.h"
+#include "debug/method_debug_info.h"
+#include "linker/linker_patch.h"
+
+namespace art {
+namespace linker {
+
+Riscv64RelativePatcher::Riscv64RelativePatcher(
+    [[maybe_unused]] RelativePatcherThunkProvider* thunk_provider,
+    [[maybe_unused]] RelativePatcherTargetProvider* target_provider,
+    [[maybe_unused]] const Riscv64InstructionSetFeatures* features)
+    : RelativePatcher() {
+}
+
+uint32_t Riscv64RelativePatcher::ReserveSpace(
+    uint32_t offset,
+    [[maybe_unused]] const CompiledMethod* compiled_method,
+    [[maybe_unused]] MethodReference method_ref) {
+  // TODO(riscv64): Reduce code size for AOT by using shared trampolines for slow path
+  // runtime calls across the entire oat file. These need space reserved here.
+  return offset;
+}
+
+uint32_t Riscv64RelativePatcher::ReserveSpaceEnd(uint32_t offset) {
+  // TODO(riscv64): Reduce code size for AOT by using shared trampolines for slow path
+  // runtime calls across the entire oat file. These need space reserved here.
+  return offset;
+}
+
+uint32_t Riscv64RelativePatcher::WriteThunks([[maybe_unused]] OutputStream* out, uint32_t offset) {
+  // TODO(riscv64): Reduce code size for AOT by using shared trampolines for slow path
+  // runtime calls across the entire oat file. These need to be written here.
+  return offset;
+}
+
+void Riscv64RelativePatcher::PatchCall([[maybe_unused]] std::vector<uint8_t>* code,
+                                       [[maybe_unused]] uint32_t literal_offset,
+                                       [[maybe_unused]] uint32_t patch_offset,
+                                       [[maybe_unused]] uint32_t target_offset) {
+  // Direct calls are currently not used on any architecture.
+  UNIMPLEMENTED(FATAL) << "Unsupported direct call.";
+}
+
+void Riscv64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
+                                                      const LinkerPatch& patch,
+                                                      uint32_t patch_offset,
+                                                      uint32_t target_offset) {
+  DCHECK_ALIGNED(patch_offset, 2u);
+  DCHECK_ALIGNED(target_offset, 2u);
+  uint32_t literal_offset = patch.LiteralOffset();
+  uint32_t insn = GetInsn(code, literal_offset);
+  uint32_t pc_insn_offset = patch.PcInsnOffset();
+  uint32_t disp = target_offset - (patch_offset - literal_offset + pc_insn_offset);
+  if (literal_offset == pc_insn_offset) {
+    // Check it's an AUIPC with imm == 0x12345 (unset).
+    DCHECK_EQ((insn & 0xfffff07fu), 0x12345017u)
+        << literal_offset << ", " << pc_insn_offset << ", 0x" << std::hex << insn;
+    insn = PatchAuipc(insn, disp);
+  } else {
+    DCHECK_EQ((insn & 0xfff00000u), 0x67800000u);
+    CHECK((insn & 0x0000707fu) == 0x00000013u ||  // ADD
+          (insn & 0x0000707fu) == 0x00006003u ||  // LWU
+          (insn & 0x0000707fu) == 0x00003003u)    // LD
+        << "insn: 0x" << std::hex << insn << ", type: " << patch.GetType();
+    // Check that pc_insn_offset points to AUIPC with matching register.
+    DCHECK_EQ(GetInsn(code, pc_insn_offset) & 0x00000fffu,
+              0x00000017 | (((insn >> 15) & 0x1fu) << 7));
+    uint32_t imm12 = disp & 0xfffu;  // The instruction shall sign-extend this immediate.
+    insn = (insn & ~(0xfffu << 20)) | (imm12 << 20);
+  }
+  SetInsn(code, literal_offset, insn);
+}
+
+void Riscv64RelativePatcher::PatchEntrypointCall([[maybe_unused]] std::vector<uint8_t>* code,
+                                                 [[maybe_unused]] const LinkerPatch& patch,
+                                                 [[maybe_unused]] uint32_t patch_offset) {
+  // TODO(riscv64): Reduce code size for AOT by using shared trampolines for slow path
+  // runtime calls across the entire oat file. Calls to these trapolines need to be patched here.
+  UNIMPLEMENTED(FATAL) << "Shared entrypoint trampolines are not implemented.";
+}
+
+void Riscv64RelativePatcher::PatchBakerReadBarrierBranch(
+    [[maybe_unused]] std::vector<uint8_t>* code,
+    [[maybe_unused]] const LinkerPatch& patch,
+    [[maybe_unused]] uint32_t patch_offset) {
+  // Baker read barrier with introspection is not implemented.
+  // Such implementation is impractical given the short reach of conditional branches.
+  UNIMPLEMENTED(FATAL) << "Baker read barrier branches are not used on riscv64.";
+}
+
+std::vector<debug::MethodDebugInfo> Riscv64RelativePatcher::GenerateThunkDebugInfo(
+      [[maybe_unused]] uint32_t executable_offset) {
+  // TODO(riscv64): Reduce code size for AOT by using shared trampolines for slow path
+  // runtime calls across the entire oat file. These need debug info generated here.
+  return {};
+}
+
+uint32_t Riscv64RelativePatcher::PatchAuipc(uint32_t auipc, int32_t offset) {
+  // The highest 0x800 values are out of range.
+  DCHECK_LT(offset, 0x7ffff800);
+  // Round `offset` to nearest 4KiB offset because short offset has range [-0x800, 0x800).
+  int32_t near_offset = (offset + 0x800) & ~0xfff;
+  // Extract the `imm20`.
+  uint32_t imm20 = static_cast<uint32_t>(near_offset) >> 12;
+  return (auipc & 0x00000fffu) |  // Clear offset bits, keep AUIPC with destination reg.
+         (imm20 << 12);           // Encode the immediate.
+}
+
+void Riscv64RelativePatcher::SetInsn(std::vector<uint8_t>* code, uint32_t offset, uint32_t value) {
+  DCHECK_LE(offset + 4u, code->size());
+  DCHECK_ALIGNED(offset, 2u);
+  uint8_t* addr = &(*code)[offset];
+  addr[0] = (value >> 0) & 0xff;
+  addr[1] = (value >> 8) & 0xff;
+  addr[2] = (value >> 16) & 0xff;
+  addr[3] = (value >> 24) & 0xff;
+}
+
+uint32_t Riscv64RelativePatcher::GetInsn(ArrayRef<const uint8_t> code, uint32_t offset) {
+  DCHECK_LE(offset + 4u, code.size());
+  DCHECK_ALIGNED(offset, 2u);
+  const uint8_t* addr = &code[offset];
+  return
+      (static_cast<uint32_t>(addr[0]) << 0) +
+      (static_cast<uint32_t>(addr[1]) << 8) +
+      (static_cast<uint32_t>(addr[2]) << 16)+
+      (static_cast<uint32_t>(addr[3]) << 24);
+}
+
+template <typename Alloc>
+uint32_t Riscv64RelativePatcher::GetInsn(std::vector<uint8_t, Alloc>* code, uint32_t offset) {
+  return GetInsn(ArrayRef<const uint8_t>(*code), offset);
+}
+
+}  // namespace linker
+}  // namespace art
diff --git a/dex2oat/linker/riscv64/relative_patcher_riscv64.h b/dex2oat/linker/riscv64/relative_patcher_riscv64.h
new file mode 100644
index 0000000..0f70aa8
--- /dev/null
+++ b/dex2oat/linker/riscv64/relative_patcher_riscv64.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_DEX2OAT_LINKER_RISCV64_RELATIVE_PATCHER_RISCV64_H_
+#define ART_DEX2OAT_LINKER_RISCV64_RELATIVE_PATCHER_RISCV64_H_
+
+#include "base/array_ref.h"
+#include "linker/relative_patcher.h"
+
+namespace art {
+
+namespace linker {
+
+class Riscv64RelativePatcher final : public RelativePatcher {
+ public:
+  Riscv64RelativePatcher(RelativePatcherThunkProvider* thunk_provider,
+                         RelativePatcherTargetProvider* target_provider,
+                         const Riscv64InstructionSetFeatures* features);
+
+  uint32_t ReserveSpace(uint32_t offset,
+                        const CompiledMethod* compiled_method,
+                        MethodReference method_ref) override;
+  uint32_t ReserveSpaceEnd(uint32_t offset) override;
+  uint32_t WriteThunks(OutputStream* out, uint32_t offset) override;
+  void PatchCall(std::vector<uint8_t>* code,
+                 uint32_t literal_offset,
+                 uint32_t patch_offset,
+                 uint32_t target_offset) override;
+  void PatchPcRelativeReference(std::vector<uint8_t>* code,
+                                const LinkerPatch& patch,
+                                uint32_t patch_offset,
+                                uint32_t target_offset) override;
+  void PatchEntrypointCall(std::vector<uint8_t>* code,
+                           const LinkerPatch& patch,
+                           uint32_t patch_offset) override;
+  void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
+                                   const LinkerPatch& patch,
+                                   uint32_t patch_offset) override;
+  std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(
+      uint32_t executable_offset) override;
+
+ private:
+  static uint32_t PatchAuipc(uint32_t auipc, int32_t offset);
+
+  static void SetInsn(std::vector<uint8_t>* code, uint32_t offset, uint32_t value);
+  static uint32_t GetInsn(ArrayRef<const uint8_t> code, uint32_t offset);
+  template <typename Alloc>
+  uint32_t GetInsn(std::vector<uint8_t, Alloc>* code, uint32_t offset);
+
+  DISALLOW_COPY_AND_ASSIGN(Riscv64RelativePatcher);
+};
+
+}  // namespace linker
+}  // namespace art
+
+#endif  // ART_DEX2OAT_LINKER_RISCV64_RELATIVE_PATCHER_RISCV64_H_
diff --git a/dex2oat/linker/riscv64/relative_patcher_riscv64_test.cc b/dex2oat/linker/riscv64/relative_patcher_riscv64_test.cc
new file mode 100644
index 0000000..9e30ffd
--- /dev/null
+++ b/dex2oat/linker/riscv64/relative_patcher_riscv64_test.cc
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "relative_patcher_riscv64.h"
+
+#include "oat_quick_method_header.h"
+#include "linker/linker_patch.h"
+#include "linker/relative_patcher_test.h"
+
+namespace art {
+namespace linker {
+
+class Riscv64RelativePatcherTest : public RelativePatcherTest {
+ public:
+  Riscv64RelativePatcherTest()
+      : RelativePatcherTest(InstructionSet::kRiscv64, "default") { }
+
+ protected:
+  // C.NOP instruction.
+  static constexpr uint32_t kCNopInsn = 0x0001u;
+  static constexpr size_t kCNopSize = 2u;
+
+  // Placeholder instructions with unset (zero) registers and immediates.
+  static constexpr uint32_t kAuipcInsn = 0x00000017u;
+  static constexpr uint32_t kAddiInsn = 0x00000013u;
+  static constexpr uint32_t kLwuInsn = 0x00006003u;
+  static constexpr uint32_t kLdInsn = 0x00003003u;
+
+  // Placeholder offset encoded in AUIPC and used before patching.
+  static constexpr uint32_t kUnpatchedOffset = 0x12345678u;
+
+  static void PushBackInsn16(std::vector<uint8_t>* code, uint32_t insn16) {
+    const uint8_t insn_code[] = {
+        static_cast<uint8_t>(insn16),
+        static_cast<uint8_t>(insn16 >> 8),
+    };
+    code->insert(code->end(), insn_code, insn_code + sizeof(insn_code));
+  }
+
+  static void PushBackInsn32(std::vector<uint8_t>* code, uint32_t insn32) {
+    const uint8_t insn_code[] = {
+        static_cast<uint8_t>(insn32),
+        static_cast<uint8_t>(insn32 >> 8),
+        static_cast<uint8_t>(insn32 >> 16),
+        static_cast<uint8_t>(insn32 >> 24),
+    };
+    code->insert(code->end(), insn_code, insn_code + sizeof(insn_code));
+  }
+
+  uint32_t GetMethodOffset(uint32_t method_idx) {
+    auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
+    CHECK(result.first);
+    CHECK_ALIGNED(result.second, 4u);
+    return result.second;
+  }
+
+  static constexpr uint32_t ExtractRs1ToRd(uint32_t insn) {
+    // The `rs1` is in bits 15..19 and we need to move it to bits 7..11.
+    return (insn >> (15 - 7)) & (0x1fu << 7);
+  }
+
+  std::vector<uint8_t> GenNopsAndAuipcAndUse(size_t start_cnops,
+                                             size_t mid_cnops,
+                                             uint32_t method_offset,
+                                             uint32_t target_offset,
+                                             uint32_t use_insn) {
+    CHECK_ALIGNED(method_offset, 4u);
+    uint32_t auipc_offset = method_offset + start_cnops * kCNopSize;
+    uint32_t offset = target_offset - auipc_offset;
+    if (offset != /* unpatched */ 0x12345678u) {
+      CHECK_ALIGNED(target_offset, 4u);
+    }
+    CHECK_EQ(use_insn & 0xfff00000u, 0u);
+    // Prepare `imm12` for `use_insn` and `imm20` for AUIPC, adjusted for sign-extension of `imm12`.
+    uint32_t imm12 = offset & 0xfffu;
+    uint32_t imm20 = (offset >> 12) + ((offset >> 11) & 1u);
+    // Prepare the AUIPC and use instruction.
+    DCHECK_EQ(use_insn & 0xfff00000u, 0u);    // Check that `imm12` in `use_insn` is empty.
+    use_insn |= imm12 << 20;                  // Update `imm12` in `use_insn`.
+    uint32_t auipc = kAuipcInsn |             // AUIPC rd, imm20
+        ExtractRs1ToRd(use_insn) |            // where `rd` is `rs1` from `use_insn`.
+        (imm20 << 12);
+    // Create the code.
+    std::vector<uint8_t> result;
+    result.reserve((start_cnops + mid_cnops) * kCNopSize + 8u);
+    for (size_t i = 0; i != start_cnops; ++i) {
+      PushBackInsn16(&result, kCNopInsn);
+    }
+    PushBackInsn32(&result, auipc);
+    for (size_t i = 0; i != mid_cnops; ++i) {
+      PushBackInsn16(&result, kCNopInsn);
+    }
+    PushBackInsn32(&result, use_insn);
+    return result;
+  }
+
+  std::vector<uint8_t> GenNopsAndAuipcAndUseUnpatched(size_t start_cnops,
+                                                      size_t mid_cnops,
+                                                      uint32_t use_insn) {
+    uint32_t target_offset = start_cnops * kCNopSize + kUnpatchedOffset;
+    return GenNopsAndAuipcAndUse(start_cnops, mid_cnops, 0u, target_offset, use_insn);
+  }
+
+  void TestNopsAuipcAddi(size_t start_cnops, size_t mid_cnops, uint32_t string_offset) {
+    constexpr uint32_t kStringIndex = 1u;
+    string_index_to_offset_map_.Put(kStringIndex, string_offset);
+    constexpr uint32_t kAddi = kAddiInsn | (10 << 15) | (11 << 7);  // ADDI A1, A0, <unfilled>
+    auto code = GenNopsAndAuipcAndUseUnpatched(start_cnops, mid_cnops, kAddi);
+    size_t auipc_offset = start_cnops * kCNopSize;
+    size_t addi_offset = auipc_offset + 4u + mid_cnops * kCNopSize;
+    const LinkerPatch patches[] = {
+        LinkerPatch::RelativeStringPatch(auipc_offset, nullptr, auipc_offset, kStringIndex),
+        LinkerPatch::RelativeStringPatch(addi_offset, nullptr, auipc_offset, kStringIndex),
+    };
+    AddCompiledMethod(MethodRef(1u),
+                      ArrayRef<const uint8_t>(code),
+                      ArrayRef<const LinkerPatch>(patches));
+    Link();
+
+    uint32_t method1_offset = GetMethodOffset(1u);
+    auto expected_code =
+        GenNopsAndAuipcAndUse(start_cnops, mid_cnops, method1_offset, string_offset, kAddi);
+    EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
+  }
+
+  void TestNopsAuipcLwu(
+      size_t start_cnops, size_t mid_cnops, uint32_t bss_begin, uint32_t string_entry_offset) {
+    constexpr uint32_t kStringIndex = 1u;
+    string_index_to_offset_map_.Put(kStringIndex, string_entry_offset);
+    bss_begin_ = bss_begin;
+    constexpr uint32_t kLwu = kLwuInsn | (10 << 15) | (10 << 7);  // LWU A0, A0, <unfilled>
+    auto code = GenNopsAndAuipcAndUseUnpatched(start_cnops, mid_cnops, kLwu);
+    size_t auipc_offset = start_cnops * kCNopSize;
+    size_t lwu_offset = auipc_offset + 4u + mid_cnops * kCNopSize;
+    const LinkerPatch patches[] = {
+        LinkerPatch::StringBssEntryPatch(auipc_offset, nullptr, auipc_offset, kStringIndex),
+        LinkerPatch::StringBssEntryPatch(lwu_offset, nullptr, auipc_offset, kStringIndex),
+    };
+    AddCompiledMethod(MethodRef(1u),
+                      ArrayRef<const uint8_t>(code),
+                      ArrayRef<const LinkerPatch>(patches));
+    Link();
+
+    uint32_t method1_offset = GetMethodOffset(1u);
+    uint32_t target_offset = bss_begin_ + string_entry_offset;
+    auto expected_code =
+        GenNopsAndAuipcAndUse(start_cnops, mid_cnops, method1_offset, target_offset, kLwu);
+    EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
+  }
+
+  void TestNopsAuipcLd(
+      size_t start_cnops, size_t mid_cnops, uint32_t bss_begin, uint32_t method_entry_offset) {
+    constexpr uint32_t kMethodIndex = 100u;
+    method_index_to_offset_map_.Put(kMethodIndex, method_entry_offset);
+    bss_begin_ = bss_begin;
+    constexpr uint32_t kLd = kLdInsn | (11 << 15) | (10 << 7);  // LD A0, A1, <unfilled>
+    auto code = GenNopsAndAuipcAndUseUnpatched(start_cnops, mid_cnops, kLd);
+    size_t auipc_offset = start_cnops * kCNopSize;
+    size_t ld_offset = auipc_offset + 4u + mid_cnops * kCNopSize;
+    const LinkerPatch patches[] = {
+        LinkerPatch::MethodBssEntryPatch(auipc_offset, nullptr, auipc_offset, kMethodIndex),
+        LinkerPatch::MethodBssEntryPatch(ld_offset, nullptr, auipc_offset, kMethodIndex),
+    };
+    AddCompiledMethod(MethodRef(1u),
+                      ArrayRef<const uint8_t>(code),
+                      ArrayRef<const LinkerPatch>(patches));
+    Link();
+
+    uint32_t method1_offset = GetMethodOffset(1u);
+    uint32_t target_offset = bss_begin_ + method_entry_offset;
+    auto expected_code =
+        GenNopsAndAuipcAndUse(start_cnops, mid_cnops, method1_offset, target_offset, kLd);
+    EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
+  }
+};
+
+TEST_F(Riscv64RelativePatcherTest, StringReference) {
+  for (size_t start_cnops : {0, 1, 2, 3, 4, 5, 6, 7}) {
+    for (size_t mid_cnops : {0, 1, 2, 3, 4, 5, 6, 7}) {
+      for (uint32_t string_offset : { 0x12345678u, -0x12345678u, 0x123457fcu, 0x12345800u}) {
+        Reset();
+        TestNopsAuipcAddi(start_cnops, mid_cnops, string_offset);
+      }
+    }
+  }
+}
+
+TEST_F(Riscv64RelativePatcherTest, StringBssEntry) {
+  for (size_t start_cnops : {0, 1, 2, 3, 4, 5, 6, 7}) {
+    for (size_t mid_cnops : {0, 1, 2, 3, 4, 5, 6, 7}) {
+      for (uint32_t bss_begin : { 0x12345678u, -0x12345678u, 0x10000000u, 0x12345000u }) {
+        for (uint32_t string_entry_offset : { 0x1234u, 0x4444u, 0x37fcu, 0x3800u }) {
+          Reset();
+          TestNopsAuipcLwu(start_cnops, mid_cnops, bss_begin, string_entry_offset);
+        }
+      }
+    }
+  }
+}
+
+TEST_F(Riscv64RelativePatcherTest, MethodBssEntry) {
+  for (size_t start_cnops : {0, 1, 2, 3, 4, 5, 6, 7}) {
+    for (size_t mid_cnops : {0, 1, 2, 3, 4, 5, 6, 7}) {
+      for (uint32_t bss_begin : { 0x12345678u, -0x12345678u, 0x10000000u, 0x12345000u }) {
+        for (uint32_t method_entry_offset : { 0x1234u, 0x4444u, 0x37f8u, 0x3800u }) {
+          Reset();
+          TestNopsAuipcLd(start_cnops, mid_cnops, bss_begin, method_entry_offset);
+        }
+      }
+    }
+  }
+}
+
+}  // namespace linker
+}  // namespace art