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