xref: /aosp_15_r20/art/dex2oat/linker/riscv64/relative_patcher_riscv64_test.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
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