1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "relative_patcher_riscv64.h"
18
19 #include "linker/linker_patch.h"
20 #include "linker/relative_patcher_test.h"
21 #include "oat/oat_quick_method_header.h"
22
23 namespace art {
24 namespace linker {
25
26 class Riscv64RelativePatcherTest : public RelativePatcherTest {
27 public:
Riscv64RelativePatcherTest()28 Riscv64RelativePatcherTest()
29 : RelativePatcherTest(InstructionSet::kRiscv64, "default") { }
30
31 protected:
32 // C.NOP instruction.
33 static constexpr uint32_t kCNopInsn = 0x0001u;
34 static constexpr size_t kCNopSize = 2u;
35
36 // Placeholder instructions with unset (zero) registers and immediates.
37 static constexpr uint32_t kAuipcInsn = 0x00000017u;
38 static constexpr uint32_t kAddiInsn = 0x00000013u;
39 static constexpr uint32_t kLwuInsn = 0x00006003u;
40 static constexpr uint32_t kLdInsn = 0x00003003u;
41
42 // Placeholder offset encoded in AUIPC and used before patching.
43 static constexpr uint32_t kUnpatchedOffset = 0x12345678u;
44
PushBackInsn16(std::vector<uint8_t> * code,uint32_t insn16)45 static void PushBackInsn16(std::vector<uint8_t>* code, uint32_t insn16) {
46 const uint8_t insn_code[] = {
47 static_cast<uint8_t>(insn16),
48 static_cast<uint8_t>(insn16 >> 8),
49 };
50 code->insert(code->end(), insn_code, insn_code + sizeof(insn_code));
51 }
52
PushBackInsn32(std::vector<uint8_t> * code,uint32_t insn32)53 static void PushBackInsn32(std::vector<uint8_t>* code, uint32_t insn32) {
54 const uint8_t insn_code[] = {
55 static_cast<uint8_t>(insn32),
56 static_cast<uint8_t>(insn32 >> 8),
57 static_cast<uint8_t>(insn32 >> 16),
58 static_cast<uint8_t>(insn32 >> 24),
59 };
60 code->insert(code->end(), insn_code, insn_code + sizeof(insn_code));
61 }
62
GetMethodOffset(uint32_t method_idx)63 uint32_t GetMethodOffset(uint32_t method_idx) {
64 auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
65 CHECK(result.first);
66 CHECK_ALIGNED(result.second, 4u);
67 return result.second;
68 }
69
ExtractRs1ToRd(uint32_t insn)70 static constexpr uint32_t ExtractRs1ToRd(uint32_t insn) {
71 // The `rs1` is in bits 15..19 and we need to move it to bits 7..11.
72 return (insn >> (15 - 7)) & (0x1fu << 7);
73 }
74
GenNopsAndAuipcAndUse(size_t start_cnops,size_t mid_cnops,uint32_t method_offset,uint32_t target_offset,uint32_t use_insn)75 std::vector<uint8_t> GenNopsAndAuipcAndUse(size_t start_cnops,
76 size_t mid_cnops,
77 uint32_t method_offset,
78 uint32_t target_offset,
79 uint32_t use_insn) {
80 CHECK_ALIGNED(method_offset, 4u);
81 uint32_t auipc_offset = method_offset + start_cnops * kCNopSize;
82 uint32_t offset = target_offset - auipc_offset;
83 if (offset != /* unpatched */ 0x12345678u) {
84 CHECK_ALIGNED(target_offset, 4u);
85 }
86 CHECK_EQ(use_insn & 0xfff00000u, 0u);
87 // Prepare `imm12` for `use_insn` and `imm20` for AUIPC, adjusted for sign-extension of `imm12`.
88 uint32_t imm12 = offset & 0xfffu;
89 uint32_t imm20 = (offset >> 12) + ((offset >> 11) & 1u);
90 // Prepare the AUIPC and use instruction.
91 DCHECK_EQ(use_insn & 0xfff00000u, 0u); // Check that `imm12` in `use_insn` is empty.
92 use_insn |= imm12 << 20; // Update `imm12` in `use_insn`.
93 uint32_t auipc = kAuipcInsn | // AUIPC rd, imm20
94 ExtractRs1ToRd(use_insn) | // where `rd` is `rs1` from `use_insn`.
95 (imm20 << 12);
96 // Create the code.
97 std::vector<uint8_t> result;
98 result.reserve((start_cnops + mid_cnops) * kCNopSize + 8u);
99 for (size_t i = 0; i != start_cnops; ++i) {
100 PushBackInsn16(&result, kCNopInsn);
101 }
102 PushBackInsn32(&result, auipc);
103 for (size_t i = 0; i != mid_cnops; ++i) {
104 PushBackInsn16(&result, kCNopInsn);
105 }
106 PushBackInsn32(&result, use_insn);
107 return result;
108 }
109
GenNopsAndAuipcAndUseUnpatched(size_t start_cnops,size_t mid_cnops,uint32_t use_insn)110 std::vector<uint8_t> GenNopsAndAuipcAndUseUnpatched(size_t start_cnops,
111 size_t mid_cnops,
112 uint32_t use_insn) {
113 uint32_t target_offset = start_cnops * kCNopSize + kUnpatchedOffset;
114 return GenNopsAndAuipcAndUse(start_cnops, mid_cnops, 0u, target_offset, use_insn);
115 }
116
TestNopsAuipcAddi(size_t start_cnops,size_t mid_cnops,uint32_t string_offset)117 void TestNopsAuipcAddi(size_t start_cnops, size_t mid_cnops, uint32_t string_offset) {
118 constexpr uint32_t kStringIndex = 1u;
119 string_index_to_offset_map_.Put(kStringIndex, string_offset);
120 constexpr uint32_t kAddi = kAddiInsn | (10 << 15) | (11 << 7); // ADDI A1, A0, <unfilled>
121 auto code = GenNopsAndAuipcAndUseUnpatched(start_cnops, mid_cnops, kAddi);
122 size_t auipc_offset = start_cnops * kCNopSize;
123 size_t addi_offset = auipc_offset + 4u + mid_cnops * kCNopSize;
124 const LinkerPatch patches[] = {
125 LinkerPatch::RelativeStringPatch(auipc_offset, nullptr, auipc_offset, kStringIndex),
126 LinkerPatch::RelativeStringPatch(addi_offset, nullptr, auipc_offset, kStringIndex),
127 };
128 AddCompiledMethod(MethodRef(1u),
129 ArrayRef<const uint8_t>(code),
130 ArrayRef<const LinkerPatch>(patches));
131 Link();
132
133 uint32_t method1_offset = GetMethodOffset(1u);
134 auto expected_code =
135 GenNopsAndAuipcAndUse(start_cnops, mid_cnops, method1_offset, string_offset, kAddi);
136 EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
137 }
138
TestNopsAuipcLwu(size_t start_cnops,size_t mid_cnops,uint32_t bss_begin,uint32_t string_entry_offset)139 void TestNopsAuipcLwu(
140 size_t start_cnops, size_t mid_cnops, uint32_t bss_begin, uint32_t string_entry_offset) {
141 constexpr uint32_t kStringIndex = 1u;
142 string_index_to_offset_map_.Put(kStringIndex, string_entry_offset);
143 bss_begin_ = bss_begin;
144 constexpr uint32_t kLwu = kLwuInsn | (10 << 15) | (10 << 7); // LWU A0, A0, <unfilled>
145 auto code = GenNopsAndAuipcAndUseUnpatched(start_cnops, mid_cnops, kLwu);
146 size_t auipc_offset = start_cnops * kCNopSize;
147 size_t lwu_offset = auipc_offset + 4u + mid_cnops * kCNopSize;
148 const LinkerPatch patches[] = {
149 LinkerPatch::StringBssEntryPatch(auipc_offset, nullptr, auipc_offset, kStringIndex),
150 LinkerPatch::StringBssEntryPatch(lwu_offset, nullptr, auipc_offset, kStringIndex),
151 };
152 AddCompiledMethod(MethodRef(1u),
153 ArrayRef<const uint8_t>(code),
154 ArrayRef<const LinkerPatch>(patches));
155 Link();
156
157 uint32_t method1_offset = GetMethodOffset(1u);
158 uint32_t target_offset = bss_begin_ + string_entry_offset;
159 auto expected_code =
160 GenNopsAndAuipcAndUse(start_cnops, mid_cnops, method1_offset, target_offset, kLwu);
161 EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
162 }
163
TestNopsAuipcLd(size_t start_cnops,size_t mid_cnops,uint32_t bss_begin,uint32_t method_entry_offset)164 void TestNopsAuipcLd(
165 size_t start_cnops, size_t mid_cnops, uint32_t bss_begin, uint32_t method_entry_offset) {
166 constexpr uint32_t kMethodIndex = 100u;
167 method_index_to_offset_map_.Put(kMethodIndex, method_entry_offset);
168 bss_begin_ = bss_begin;
169 constexpr uint32_t kLd = kLdInsn | (11 << 15) | (10 << 7); // LD A0, A1, <unfilled>
170 auto code = GenNopsAndAuipcAndUseUnpatched(start_cnops, mid_cnops, kLd);
171 size_t auipc_offset = start_cnops * kCNopSize;
172 size_t ld_offset = auipc_offset + 4u + mid_cnops * kCNopSize;
173 const LinkerPatch patches[] = {
174 LinkerPatch::MethodBssEntryPatch(auipc_offset, nullptr, auipc_offset, kMethodIndex),
175 LinkerPatch::MethodBssEntryPatch(ld_offset, nullptr, auipc_offset, kMethodIndex),
176 };
177 AddCompiledMethod(MethodRef(1u),
178 ArrayRef<const uint8_t>(code),
179 ArrayRef<const LinkerPatch>(patches));
180 Link();
181
182 uint32_t method1_offset = GetMethodOffset(1u);
183 uint32_t target_offset = bss_begin_ + method_entry_offset;
184 auto expected_code =
185 GenNopsAndAuipcAndUse(start_cnops, mid_cnops, method1_offset, target_offset, kLd);
186 EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
187 }
188 };
189
TEST_F(Riscv64RelativePatcherTest,StringReference)190 TEST_F(Riscv64RelativePatcherTest, StringReference) {
191 for (size_t start_cnops : {0, 1, 2, 3, 4, 5, 6, 7}) {
192 for (size_t mid_cnops : {0, 1, 2, 3, 4, 5, 6, 7}) {
193 for (uint32_t string_offset : { 0x12345678u, -0x12345678u, 0x123457fcu, 0x12345800u}) {
194 Reset();
195 TestNopsAuipcAddi(start_cnops, mid_cnops, string_offset);
196 }
197 }
198 }
199 }
200
TEST_F(Riscv64RelativePatcherTest,StringBssEntry)201 TEST_F(Riscv64RelativePatcherTest, StringBssEntry) {
202 for (size_t start_cnops : {0, 1, 2, 3, 4, 5, 6, 7}) {
203 for (size_t mid_cnops : {0, 1, 2, 3, 4, 5, 6, 7}) {
204 for (uint32_t bss_begin : { 0x12345678u, -0x12345678u, 0x10000000u, 0x12345000u }) {
205 for (uint32_t string_entry_offset : { 0x1234u, 0x4444u, 0x37fcu, 0x3800u }) {
206 Reset();
207 TestNopsAuipcLwu(start_cnops, mid_cnops, bss_begin, string_entry_offset);
208 }
209 }
210 }
211 }
212 }
213
TEST_F(Riscv64RelativePatcherTest,MethodBssEntry)214 TEST_F(Riscv64RelativePatcherTest, MethodBssEntry) {
215 for (size_t start_cnops : {0, 1, 2, 3, 4, 5, 6, 7}) {
216 for (size_t mid_cnops : {0, 1, 2, 3, 4, 5, 6, 7}) {
217 for (uint32_t bss_begin : { 0x12345678u, -0x12345678u, 0x10000000u, 0x12345000u }) {
218 for (uint32_t method_entry_offset : { 0x1234u, 0x4444u, 0x37f8u, 0x3800u }) {
219 Reset();
220 TestNopsAuipcLd(start_cnops, mid_cnops, bss_begin, method_entry_offset);
221 }
222 }
223 }
224 }
225 }
226
227 } // namespace linker
228 } // namespace art
229