1 /*
2  * Copyright (C) 2015 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 "linker/arm64/relative_patcher_arm64.h"
18 
19 #include "arch/arm64/instruction_set_features_arm64.h"
20 #include "base/casts.h"
21 #include "driver/compiler_options.h"
22 #include "linker/relative_patcher_test.h"
23 #include "lock_word.h"
24 #include "mirror/array-inl.h"
25 #include "mirror/object.h"
26 #include "oat_quick_method_header.h"
27 #include "optimizing/code_generator_arm64.h"
28 #include "optimizing/optimizing_unit_test.h"
29 
30 namespace art {
31 namespace linker {
32 
33 class Arm64RelativePatcherTest : public RelativePatcherTest {
34  public:
Arm64RelativePatcherTest(const std::string & variant)35   explicit Arm64RelativePatcherTest(const std::string& variant)
36       : RelativePatcherTest(InstructionSet::kArm64, variant) { }
37 
38  protected:
39   static const uint8_t kCallRawCode[];
40   static const ArrayRef<const uint8_t> kCallCode;
41   static const uint8_t kNopRawCode[];
42   static const ArrayRef<const uint8_t> kNopCode;
43 
44   // NOP instruction.
45   static constexpr uint32_t kNopInsn = 0xd503201f;
46 
47   // All branches can be created from kBlPlus0 or kBPlus0 by adding the low 26 bits.
48   static constexpr uint32_t kBlPlus0 = 0x94000000u;
49   static constexpr uint32_t kBPlus0 = 0x14000000u;
50 
51   // Special BL values.
52   static constexpr uint32_t kBlPlusMax = 0x95ffffffu;
53   static constexpr uint32_t kBlMinusMax = 0x96000000u;
54 
55   // LDR immediate, 32-bit, unsigned offset.
56   static constexpr uint32_t kLdrWInsn = 0xb9400000u;
57 
58   // LDR register, 32-bit, LSL #2.
59   static constexpr uint32_t kLdrWLsl2Insn = 0xb8607800u;
60 
61   // LDUR, 32-bit.
62   static constexpr uint32_t kLdurWInsn = 0xb8400000u;
63 
64   // ADD/ADDS/SUB/SUBS immediate, 64-bit.
65   static constexpr uint32_t kAddXInsn = 0x91000000u;
66   static constexpr uint32_t kAddsXInsn = 0xb1000000u;
67   static constexpr uint32_t kSubXInsn = 0xd1000000u;
68   static constexpr uint32_t kSubsXInsn = 0xf1000000u;
69 
70   // LDUR x2, [sp, #4], i.e. unaligned load crossing 64-bit boundary (assuming aligned sp).
71   static constexpr uint32_t kLdurInsn = 0xf840405fu;
72 
73   // LDR w12, <label> and LDR x12, <label>. Bits 5-23 contain label displacement in 4-byte units.
74   static constexpr uint32_t kLdrWPcRelInsn = 0x1800000cu;
75   static constexpr uint32_t kLdrXPcRelInsn = 0x5800000cu;
76 
77   // LDR w13, [SP, #<pimm>] and LDR x13, [SP, #<pimm>]. Bits 10-21 contain displacement from SP
78   // in units of 4-bytes (for 32-bit load) or 8-bytes (for 64-bit load).
79   static constexpr uint32_t kLdrWSpRelInsn = 0xb94003edu;
80   static constexpr uint32_t kLdrXSpRelInsn = 0xf94003edu;
81 
82   // CBNZ x17, +0. Bits 5-23 are a placeholder for target offset from PC in units of 4-bytes.
83   static constexpr uint32_t kCbnzIP1Plus0Insn = 0xb5000011u;
84 
InsertInsn(std::vector<uint8_t> * code,size_t pos,uint32_t insn)85   void InsertInsn(std::vector<uint8_t>* code, size_t pos, uint32_t insn) {
86     CHECK_LE(pos, code->size());
87     const uint8_t insn_code[] = {
88         static_cast<uint8_t>(insn),
89         static_cast<uint8_t>(insn >> 8),
90         static_cast<uint8_t>(insn >> 16),
91         static_cast<uint8_t>(insn >> 24),
92     };
93     static_assert(sizeof(insn_code) == 4u, "Invalid sizeof(insn_code).");
94     code->insert(code->begin() + pos, insn_code, insn_code + sizeof(insn_code));
95   }
96 
PushBackInsn(std::vector<uint8_t> * code,uint32_t insn)97   void PushBackInsn(std::vector<uint8_t>* code, uint32_t insn) {
98     InsertInsn(code, code->size(), insn);
99   }
100 
RawCode(std::initializer_list<uint32_t> insns)101   std::vector<uint8_t> RawCode(std::initializer_list<uint32_t> insns) {
102     std::vector<uint8_t> raw_code;
103     raw_code.reserve(insns.size() * 4u);
104     for (uint32_t insn : insns) {
105       PushBackInsn(&raw_code, insn);
106     }
107     return raw_code;
108   }
109 
Create2MethodsWithGap(const ArrayRef<const uint8_t> & method1_code,const ArrayRef<const LinkerPatch> & method1_patches,const ArrayRef<const uint8_t> & last_method_code,const ArrayRef<const LinkerPatch> & last_method_patches,uint32_t distance_without_thunks)110   uint32_t Create2MethodsWithGap(const ArrayRef<const uint8_t>& method1_code,
111                                  const ArrayRef<const LinkerPatch>& method1_patches,
112                                  const ArrayRef<const uint8_t>& last_method_code,
113                                  const ArrayRef<const LinkerPatch>& last_method_patches,
114                                  uint32_t distance_without_thunks) {
115     CHECK_EQ(distance_without_thunks % kArm64Alignment, 0u);
116     uint32_t method1_offset =
117         kTrampolineSize + CodeAlignmentSize(kTrampolineSize) + sizeof(OatQuickMethodHeader);
118     AddCompiledMethod(MethodRef(1u), method1_code, method1_patches);
119     const uint32_t gap_start = method1_offset + method1_code.size();
120 
121     // We want to put the last method at a very precise offset.
122     const uint32_t last_method_offset = method1_offset + distance_without_thunks;
123     CHECK_ALIGNED(last_method_offset, kArm64Alignment);
124     const uint32_t gap_end = last_method_offset - sizeof(OatQuickMethodHeader);
125 
126     // Fill the gap with intermediate methods in chunks of 2MiB and the first in [2MiB, 4MiB).
127     // (This allows deduplicating the small chunks to avoid using 256MiB of memory for +-128MiB
128     // offsets by this test. Making the first chunk bigger makes it easy to give all intermediate
129     // methods the same alignment of the end, so the thunk insertion adds a predictable size as
130     // long as it's after the first chunk.)
131     uint32_t method_idx = 2u;
132     constexpr uint32_t kSmallChunkSize = 2 * MB;
133     std::vector<uint8_t> gap_code;
134     uint32_t gap_size = gap_end - gap_start;
135     uint32_t num_small_chunks = std::max(gap_size / kSmallChunkSize, 1u) - 1u;
136     uint32_t chunk_start = gap_start;
137     uint32_t chunk_size = gap_size - num_small_chunks * kSmallChunkSize;
138     for (uint32_t i = 0; i <= num_small_chunks; ++i) {  // num_small_chunks+1 iterations.
139       uint32_t chunk_code_size =
140           chunk_size - CodeAlignmentSize(chunk_start) - sizeof(OatQuickMethodHeader);
141       gap_code.resize(chunk_code_size, 0u);
142       AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(gap_code));
143       method_idx += 1u;
144       chunk_start += chunk_size;
145       chunk_size = kSmallChunkSize;  // For all but the first chunk.
146       DCHECK_EQ(CodeAlignmentSize(gap_end), CodeAlignmentSize(chunk_start));
147     }
148 
149     // Add the last method and link
150     AddCompiledMethod(MethodRef(method_idx), last_method_code, last_method_patches);
151     Link();
152 
153     // Check assumptions.
154     CHECK_EQ(GetMethodOffset(1), method1_offset);
155     auto last_result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
156     CHECK(last_result.first);
157     // There may be a thunk before method2.
158     if (last_result.second != last_method_offset) {
159       // Thunk present. Check that there's only one.
160       uint32_t thunk_end =
161           CompiledCode::AlignCode(gap_end, InstructionSet::kArm64) + MethodCallThunkSize();
162       uint32_t header_offset = thunk_end + CodeAlignmentSize(thunk_end);
163       CHECK_EQ(last_result.second, header_offset + sizeof(OatQuickMethodHeader));
164     }
165     return method_idx;
166   }
167 
GetMethodOffset(uint32_t method_idx)168   uint32_t GetMethodOffset(uint32_t method_idx) {
169     auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
170     CHECK(result.first);
171     CHECK_ALIGNED(result.second, 4u);
172     return result.second;
173   }
174 
CompileThunk(const LinkerPatch & patch,std::string * debug_name=nullptr)175   std::vector<uint8_t> CompileThunk(const LinkerPatch& patch,
176                                     /*out*/ std::string* debug_name = nullptr) {
177     OptimizingUnitTestHelper helper;
178     HGraph* graph = helper.CreateGraph();
179     CompilerOptions compiler_options;
180 
181     // Set isa to arm64.
182     compiler_options.instruction_set_ = instruction_set_;
183     compiler_options.instruction_set_features_ =
184         InstructionSetFeatures::FromBitmap(instruction_set_, instruction_set_features_->AsBitmap());
185     CHECK(compiler_options.instruction_set_features_->Equals(instruction_set_features_.get()));
186 
187     arm64::CodeGeneratorARM64 codegen(graph, compiler_options);
188     ArenaVector<uint8_t> code(helper.GetAllocator()->Adapter());
189     codegen.EmitThunkCode(patch, &code, debug_name);
190     return std::vector<uint8_t>(code.begin(), code.end());
191   }
192 
AddCompiledMethod(MethodReference method_ref,const ArrayRef<const uint8_t> & code,const ArrayRef<const LinkerPatch> & patches=ArrayRef<const LinkerPatch> ())193   void AddCompiledMethod(
194       MethodReference method_ref,
195       const ArrayRef<const uint8_t>& code,
196       const ArrayRef<const LinkerPatch>& patches = ArrayRef<const LinkerPatch>()) {
197     RelativePatcherTest::AddCompiledMethod(method_ref, code, patches);
198 
199     // Make sure the ThunkProvider has all the necessary thunks.
200     for (const LinkerPatch& patch : patches) {
201       if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch ||
202           patch.GetType() == LinkerPatch::Type::kCallRelative) {
203         std::string debug_name;
204         std::vector<uint8_t> thunk_code = CompileThunk(patch, &debug_name);
205         thunk_provider_.SetThunkCode(patch, ArrayRef<const uint8_t>(thunk_code), debug_name);
206       }
207     }
208   }
209 
CompileMethodCallThunk()210   std::vector<uint8_t> CompileMethodCallThunk() {
211     LinkerPatch patch = LinkerPatch::RelativeCodePatch(/* literal_offset */ 0u,
212                                                        /* target_dex_file*/ nullptr,
213                                                        /* target_method_idx */ 0u);
214     return CompileThunk(patch);
215   }
216 
MethodCallThunkSize()217   uint32_t MethodCallThunkSize() {
218     return CompileMethodCallThunk().size();
219   }
220 
CheckThunk(uint32_t thunk_offset)221   bool CheckThunk(uint32_t thunk_offset) {
222     const std::vector<uint8_t> expected_code = CompileMethodCallThunk();
223     if (output_.size() < thunk_offset + expected_code.size()) {
224       LOG(ERROR) << "output_.size() == " << output_.size() << " < "
225           << "thunk_offset + expected_code.size() == " << (thunk_offset + expected_code.size());
226       return false;
227     }
228     ArrayRef<const uint8_t> linked_code(&output_[thunk_offset], expected_code.size());
229     if (linked_code == ArrayRef<const uint8_t>(expected_code)) {
230       return true;
231     }
232     // Log failure info.
233     DumpDiff(ArrayRef<const uint8_t>(expected_code), linked_code);
234     return false;
235   }
236 
GenNops(size_t num_nops)237   std::vector<uint8_t> GenNops(size_t num_nops) {
238     std::vector<uint8_t> result;
239     result.reserve(num_nops * 4u);
240     for (size_t i = 0; i != num_nops; ++i) {
241       PushBackInsn(&result, kNopInsn);
242     }
243     return result;
244   }
245 
GenNopsAndBl(size_t num_nops,uint32_t bl)246   std::vector<uint8_t> GenNopsAndBl(size_t num_nops, uint32_t bl) {
247     std::vector<uint8_t> result;
248     result.reserve(num_nops * 4u + 4u);
249     for (size_t i = 0; i != num_nops; ++i) {
250       PushBackInsn(&result, kNopInsn);
251     }
252     PushBackInsn(&result, bl);
253     return result;
254   }
255 
GenNopsAndAdrpAndUse(size_t num_nops,uint32_t method_offset,uint32_t target_offset,uint32_t use_insn)256   std::vector<uint8_t> GenNopsAndAdrpAndUse(size_t num_nops,
257                                             uint32_t method_offset,
258                                             uint32_t target_offset,
259                                             uint32_t use_insn) {
260     std::vector<uint8_t> result;
261     result.reserve(num_nops * 4u + 8u);
262     for (size_t i = 0; i != num_nops; ++i) {
263       PushBackInsn(&result, kNopInsn);
264     }
265     CHECK_ALIGNED(method_offset, 4u);
266     CHECK_ALIGNED(target_offset, 4u);
267     uint32_t adrp_offset = method_offset + num_nops * 4u;
268     uint32_t disp = target_offset - (adrp_offset & ~0xfffu);
269     if (use_insn == kLdrWInsn) {
270       DCHECK_ALIGNED(disp, 1u << 2);
271       use_insn |= 1 |                         // LDR x1, [x0, #(imm12 << 2)]
272           ((disp & 0xfffu) << (10 - 2));      // imm12 = ((disp & 0xfffu) >> 2) is at bit 10.
273     } else if (use_insn == kAddXInsn) {
274       use_insn |= 1 |                         // ADD x1, x0, #imm
275           (disp & 0xfffu) << 10;              // imm12 = (disp & 0xfffu) is at bit 10.
276     } else {
277       LOG(FATAL) << "Unexpected instruction: 0x" << std::hex << use_insn;
278     }
279     uint32_t adrp = 0x90000000u |             // ADRP x0, +SignExtend(immhi:immlo:Zeros(12), 64)
280         ((disp & 0x3000u) << (29 - 12)) |     // immlo = ((disp & 0x3000u) >> 12) is at bit 29,
281         ((disp & 0xffffc000) >> (14 - 5)) |   // immhi = (disp >> 14) is at bit 5,
282         // We take the sign bit from the disp, limiting disp to +- 2GiB.
283         ((disp & 0x80000000) >> (31 - 23));   // sign bit in immhi is at bit 23.
284     PushBackInsn(&result, adrp);
285     PushBackInsn(&result, use_insn);
286     return result;
287   }
288 
GenNopsAndAdrpLdr(size_t num_nops,uint32_t method_offset,uint32_t target_offset)289   std::vector<uint8_t> GenNopsAndAdrpLdr(size_t num_nops,
290                                          uint32_t method_offset,
291                                          uint32_t target_offset) {
292     return GenNopsAndAdrpAndUse(num_nops, method_offset, target_offset, kLdrWInsn);
293   }
294 
TestNopsAdrpLdr(size_t num_nops,uint32_t bss_begin,uint32_t string_entry_offset)295   void TestNopsAdrpLdr(size_t num_nops, uint32_t bss_begin, uint32_t string_entry_offset) {
296     constexpr uint32_t kStringIndex = 1u;
297     string_index_to_offset_map_.Put(kStringIndex, string_entry_offset);
298     bss_begin_ = bss_begin;
299     auto code = GenNopsAndAdrpLdr(num_nops, 0u, 0u);  // Unpatched.
300     const LinkerPatch patches[] = {
301         LinkerPatch::StringBssEntryPatch(num_nops * 4u     , nullptr, num_nops * 4u, kStringIndex),
302         LinkerPatch::StringBssEntryPatch(num_nops * 4u + 4u, nullptr, num_nops * 4u, kStringIndex),
303     };
304     AddCompiledMethod(MethodRef(1u),
305                       ArrayRef<const uint8_t>(code),
306                       ArrayRef<const LinkerPatch>(patches));
307     Link();
308 
309     uint32_t method1_offset = GetMethodOffset(1u);
310     uint32_t target_offset = bss_begin_ + string_entry_offset;
311     auto expected_code = GenNopsAndAdrpLdr(num_nops, method1_offset, target_offset);
312     EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
313   }
314 
GenNopsAndAdrpAdd(size_t num_nops,uint32_t method_offset,uint32_t target_offset)315   std::vector<uint8_t> GenNopsAndAdrpAdd(size_t num_nops,
316                                          uint32_t method_offset,
317                                          uint32_t target_offset) {
318     return GenNopsAndAdrpAndUse(num_nops, method_offset, target_offset, kAddXInsn);
319   }
320 
TestNopsAdrpAdd(size_t num_nops,uint32_t string_offset)321   void TestNopsAdrpAdd(size_t num_nops, uint32_t string_offset) {
322     constexpr uint32_t kStringIndex = 1u;
323     string_index_to_offset_map_.Put(kStringIndex, string_offset);
324     auto code = GenNopsAndAdrpAdd(num_nops, 0u, 0u);  // Unpatched.
325     const LinkerPatch patches[] = {
326         LinkerPatch::RelativeStringPatch(num_nops * 4u     , nullptr, num_nops * 4u, kStringIndex),
327         LinkerPatch::RelativeStringPatch(num_nops * 4u + 4u, nullptr, num_nops * 4u, kStringIndex),
328     };
329     AddCompiledMethod(MethodRef(1u),
330                       ArrayRef<const uint8_t>(code),
331                       ArrayRef<const LinkerPatch>(patches));
332     Link();
333 
334     uint32_t method1_offset = GetMethodOffset(1u);
335     auto expected_code = GenNopsAndAdrpAdd(num_nops, method1_offset, string_offset);
336     EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
337   }
338 
PrepareNopsAdrpInsn2Ldr(size_t num_nops,uint32_t insn2,uint32_t bss_begin,uint32_t string_entry_offset)339   void PrepareNopsAdrpInsn2Ldr(size_t num_nops,
340                                uint32_t insn2,
341                                uint32_t bss_begin,
342                                uint32_t string_entry_offset) {
343     constexpr uint32_t kStringIndex = 1u;
344     string_index_to_offset_map_.Put(kStringIndex, string_entry_offset);
345     bss_begin_ = bss_begin;
346     auto code = GenNopsAndAdrpLdr(num_nops, 0u, 0u);  // Unpatched.
347     InsertInsn(&code, num_nops * 4u + 4u, insn2);
348     const LinkerPatch patches[] = {
349         LinkerPatch::StringBssEntryPatch(num_nops * 4u     , nullptr, num_nops * 4u, kStringIndex),
350         LinkerPatch::StringBssEntryPatch(num_nops * 4u + 8u, nullptr, num_nops * 4u, kStringIndex),
351     };
352     AddCompiledMethod(MethodRef(1u),
353                       ArrayRef<const uint8_t>(code),
354                       ArrayRef<const LinkerPatch>(patches));
355     Link();
356   }
357 
PrepareNopsAdrpInsn2Add(size_t num_nops,uint32_t insn2,uint32_t string_offset)358   void PrepareNopsAdrpInsn2Add(size_t num_nops, uint32_t insn2, uint32_t string_offset) {
359     constexpr uint32_t kStringIndex = 1u;
360     string_index_to_offset_map_.Put(kStringIndex, string_offset);
361     auto code = GenNopsAndAdrpAdd(num_nops, 0u, 0u);  // Unpatched.
362     InsertInsn(&code, num_nops * 4u + 4u, insn2);
363     const LinkerPatch patches[] = {
364         LinkerPatch::RelativeStringPatch(num_nops * 4u     , nullptr, num_nops * 4u, kStringIndex),
365         LinkerPatch::RelativeStringPatch(num_nops * 4u + 8u, nullptr, num_nops * 4u, kStringIndex),
366     };
367     AddCompiledMethod(MethodRef(1u),
368                       ArrayRef<const uint8_t>(code),
369                       ArrayRef<const LinkerPatch>(patches));
370     Link();
371   }
372 
TestNopsAdrpInsn2AndUse(size_t num_nops,uint32_t insn2,uint32_t target_offset,uint32_t use_insn)373   void TestNopsAdrpInsn2AndUse(size_t num_nops,
374                                uint32_t insn2,
375                                uint32_t target_offset,
376                                uint32_t use_insn) {
377     uint32_t method1_offset = GetMethodOffset(1u);
378     auto expected_code = GenNopsAndAdrpAndUse(num_nops, method1_offset, target_offset, use_insn);
379     InsertInsn(&expected_code, num_nops * 4u + 4u, insn2);
380     EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
381   }
382 
TestNopsAdrpInsn2AndUseHasThunk(size_t num_nops,uint32_t insn2,uint32_t target_offset,uint32_t use_insn)383   void TestNopsAdrpInsn2AndUseHasThunk(size_t num_nops,
384                                        uint32_t insn2,
385                                        uint32_t target_offset,
386                                        uint32_t use_insn) {
387     uint32_t method1_offset = GetMethodOffset(1u);
388     CHECK(!compiled_method_refs_.empty());
389     CHECK_EQ(compiled_method_refs_[0].index, 1u);
390     CHECK_EQ(compiled_method_refs_.size(), compiled_methods_.size());
391     uint32_t method1_size = compiled_methods_[0]->GetQuickCode().size();
392     uint32_t thunk_offset =
393         CompiledCode::AlignCode(method1_offset + method1_size, InstructionSet::kArm64);
394     uint32_t b_diff = thunk_offset - (method1_offset + num_nops * 4u);
395     CHECK_ALIGNED(b_diff, 4u);
396     ASSERT_LT(b_diff, 128 * MB);
397     uint32_t b_out = kBPlus0 + ((b_diff >> 2) & 0x03ffffffu);
398     uint32_t b_in = kBPlus0 + ((-b_diff >> 2) & 0x03ffffffu);
399 
400     auto expected_code = GenNopsAndAdrpAndUse(num_nops, method1_offset, target_offset, use_insn);
401     InsertInsn(&expected_code, num_nops * 4u + 4u, insn2);
402     // Replace adrp with bl.
403     expected_code.erase(expected_code.begin() + num_nops * 4u,
404                         expected_code.begin() + num_nops * 4u + 4u);
405     InsertInsn(&expected_code, num_nops * 4u, b_out);
406     EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
407 
408     auto expected_thunk_code = GenNopsAndAdrpLdr(0u, thunk_offset, target_offset);
409     ASSERT_EQ(expected_thunk_code.size(), 8u);
410     expected_thunk_code.erase(expected_thunk_code.begin() + 4u, expected_thunk_code.begin() + 8u);
411     InsertInsn(&expected_thunk_code, 4u, b_in);
412     ASSERT_EQ(expected_thunk_code.size(), 8u);
413 
414     uint32_t thunk_size = MethodCallThunkSize();
415     ASSERT_EQ(thunk_offset + thunk_size, output_.size());
416     ASSERT_EQ(thunk_size, expected_thunk_code.size());
417     ArrayRef<const uint8_t> thunk_code(&output_[thunk_offset], thunk_size);
418     if (ArrayRef<const uint8_t>(expected_thunk_code) != thunk_code) {
419       DumpDiff(ArrayRef<const uint8_t>(expected_thunk_code), thunk_code);
420       FAIL();
421     }
422   }
423 
TestAdrpInsn2Ldr(uint32_t insn2,uint32_t adrp_offset,bool has_thunk,uint32_t bss_begin,uint32_t string_entry_offset)424   void TestAdrpInsn2Ldr(uint32_t insn2,
425                         uint32_t adrp_offset,
426                         bool has_thunk,
427                         uint32_t bss_begin,
428                         uint32_t string_entry_offset) {
429     uint32_t method1_offset =
430         kTrampolineSize + CodeAlignmentSize(kTrampolineSize) + sizeof(OatQuickMethodHeader);
431     ASSERT_LT(method1_offset, adrp_offset);
432     CHECK_ALIGNED(adrp_offset, 4u);
433     uint32_t num_nops = (adrp_offset - method1_offset) / 4u;
434     PrepareNopsAdrpInsn2Ldr(num_nops, insn2, bss_begin, string_entry_offset);
435     uint32_t target_offset = bss_begin_ + string_entry_offset;
436     if (has_thunk) {
437       TestNopsAdrpInsn2AndUseHasThunk(num_nops, insn2, target_offset, kLdrWInsn);
438     } else {
439       TestNopsAdrpInsn2AndUse(num_nops, insn2, target_offset, kLdrWInsn);
440     }
441     ASSERT_EQ(method1_offset, GetMethodOffset(1u));  // If this fails, num_nops is wrong.
442   }
443 
TestAdrpLdurLdr(uint32_t adrp_offset,bool has_thunk,uint32_t bss_begin,uint32_t string_entry_offset)444   void TestAdrpLdurLdr(uint32_t adrp_offset,
445                        bool has_thunk,
446                        uint32_t bss_begin,
447                        uint32_t string_entry_offset) {
448     TestAdrpInsn2Ldr(kLdurInsn, adrp_offset, has_thunk, bss_begin, string_entry_offset);
449   }
450 
TestAdrpLdrPcRelLdr(uint32_t pcrel_ldr_insn,int32_t pcrel_disp,uint32_t adrp_offset,bool has_thunk,uint32_t bss_begin,uint32_t string_entry_offset)451   void TestAdrpLdrPcRelLdr(uint32_t pcrel_ldr_insn,
452                            int32_t pcrel_disp,
453                            uint32_t adrp_offset,
454                            bool has_thunk,
455                            uint32_t bss_begin,
456                            uint32_t string_entry_offset) {
457     ASSERT_LT(pcrel_disp, 0x100000);
458     ASSERT_GE(pcrel_disp, -0x100000);
459     ASSERT_EQ(pcrel_disp & 0x3, 0);
460     uint32_t insn2 = pcrel_ldr_insn | (((static_cast<uint32_t>(pcrel_disp) >> 2) & 0x7ffffu) << 5);
461     TestAdrpInsn2Ldr(insn2, adrp_offset, has_thunk, bss_begin, string_entry_offset);
462   }
463 
TestAdrpLdrSpRelLdr(uint32_t sprel_ldr_insn,uint32_t sprel_disp_in_load_units,uint32_t adrp_offset,bool has_thunk,uint32_t bss_begin,uint32_t string_entry_offset)464   void TestAdrpLdrSpRelLdr(uint32_t sprel_ldr_insn,
465                            uint32_t sprel_disp_in_load_units,
466                            uint32_t adrp_offset,
467                            bool has_thunk,
468                            uint32_t bss_begin,
469                            uint32_t string_entry_offset) {
470     ASSERT_LT(sprel_disp_in_load_units, 0x1000u);
471     uint32_t insn2 = sprel_ldr_insn | ((sprel_disp_in_load_units & 0xfffu) << 10);
472     TestAdrpInsn2Ldr(insn2, adrp_offset, has_thunk, bss_begin, string_entry_offset);
473   }
474 
TestAdrpInsn2Add(uint32_t insn2,uint32_t adrp_offset,bool has_thunk,uint32_t string_offset)475   void TestAdrpInsn2Add(uint32_t insn2,
476                         uint32_t adrp_offset,
477                         bool has_thunk,
478                         uint32_t string_offset) {
479     uint32_t method1_offset =
480         kTrampolineSize + CodeAlignmentSize(kTrampolineSize) + sizeof(OatQuickMethodHeader);
481     ASSERT_LT(method1_offset, adrp_offset);
482     CHECK_ALIGNED(adrp_offset, 4u);
483     uint32_t num_nops = (adrp_offset - method1_offset) / 4u;
484     PrepareNopsAdrpInsn2Add(num_nops, insn2, string_offset);
485     if (has_thunk) {
486       TestNopsAdrpInsn2AndUseHasThunk(num_nops, insn2, string_offset, kAddXInsn);
487     } else {
488       TestNopsAdrpInsn2AndUse(num_nops, insn2, string_offset, kAddXInsn);
489     }
490     ASSERT_EQ(method1_offset, GetMethodOffset(1u));  // If this fails, num_nops is wrong.
491   }
492 
TestAdrpLdurAdd(uint32_t adrp_offset,bool has_thunk,uint32_t string_offset)493   void TestAdrpLdurAdd(uint32_t adrp_offset, bool has_thunk, uint32_t string_offset) {
494     TestAdrpInsn2Add(kLdurInsn, adrp_offset, has_thunk, string_offset);
495   }
496 
TestAdrpLdrPcRelAdd(uint32_t pcrel_ldr_insn,int32_t pcrel_disp,uint32_t adrp_offset,bool has_thunk,uint32_t string_offset)497   void TestAdrpLdrPcRelAdd(uint32_t pcrel_ldr_insn,
498                            int32_t pcrel_disp,
499                            uint32_t adrp_offset,
500                            bool has_thunk,
501                            uint32_t string_offset) {
502     ASSERT_LT(pcrel_disp, 0x100000);
503     ASSERT_GE(pcrel_disp, -0x100000);
504     ASSERT_EQ(pcrel_disp & 0x3, 0);
505     uint32_t insn2 = pcrel_ldr_insn | (((static_cast<uint32_t>(pcrel_disp) >> 2) & 0x7ffffu) << 5);
506     TestAdrpInsn2Add(insn2, adrp_offset, has_thunk, string_offset);
507   }
508 
TestAdrpLdrSpRelAdd(uint32_t sprel_ldr_insn,uint32_t sprel_disp_in_load_units,uint32_t adrp_offset,bool has_thunk,uint32_t string_offset)509   void TestAdrpLdrSpRelAdd(uint32_t sprel_ldr_insn,
510                            uint32_t sprel_disp_in_load_units,
511                            uint32_t adrp_offset,
512                            bool has_thunk,
513                            uint32_t string_offset) {
514     ASSERT_LT(sprel_disp_in_load_units, 0x1000u);
515     uint32_t insn2 = sprel_ldr_insn | ((sprel_disp_in_load_units & 0xfffu) << 10);
516     TestAdrpInsn2Add(insn2, adrp_offset, has_thunk, string_offset);
517   }
518 
EncodeBakerReadBarrierFieldData(uint32_t base_reg,uint32_t holder_reg)519   static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, uint32_t holder_reg) {
520     return arm64::CodeGeneratorARM64::EncodeBakerReadBarrierFieldData(base_reg, holder_reg);
521   }
522 
EncodeBakerReadBarrierArrayData(uint32_t base_reg)523   static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) {
524     return arm64::CodeGeneratorARM64::EncodeBakerReadBarrierArrayData(base_reg);
525   }
526 
EncodeBakerReadBarrierGcRootData(uint32_t root_reg)527   static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg) {
528     return arm64::CodeGeneratorARM64::EncodeBakerReadBarrierGcRootData(root_reg);
529   }
530 
CompileBakerOffsetThunk(uint32_t base_reg,uint32_t holder_reg)531   std::vector<uint8_t> CompileBakerOffsetThunk(uint32_t base_reg, uint32_t holder_reg) {
532     const LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch(
533         /* literal_offset */ 0u, EncodeBakerReadBarrierFieldData(base_reg, holder_reg));
534     return CompileThunk(patch);
535   }
536 
CompileBakerArrayThunk(uint32_t base_reg)537   std::vector<uint8_t> CompileBakerArrayThunk(uint32_t base_reg) {
538     LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch(
539         /* literal_offset */ 0u, EncodeBakerReadBarrierArrayData(base_reg));
540     return CompileThunk(patch);
541   }
542 
CompileBakerGcRootThunk(uint32_t root_reg)543   std::vector<uint8_t> CompileBakerGcRootThunk(uint32_t root_reg) {
544     LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch(
545         /* literal_offset */ 0u, EncodeBakerReadBarrierGcRootData(root_reg));
546     return CompileThunk(patch);
547   }
548 
GetOutputInsn(uint32_t offset)549   uint32_t GetOutputInsn(uint32_t offset) {
550     CHECK_LE(offset, output_.size());
551     CHECK_GE(output_.size() - offset, 4u);
552     return (static_cast<uint32_t>(output_[offset]) << 0) |
553            (static_cast<uint32_t>(output_[offset + 1]) << 8) |
554            (static_cast<uint32_t>(output_[offset + 2]) << 16) |
555            (static_cast<uint32_t>(output_[offset + 3]) << 24);
556   }
557 
558   void TestBakerField(uint32_t offset, uint32_t ref_reg);
559 };
560 
561 const uint8_t Arm64RelativePatcherTest::kCallRawCode[] = {
562     0x00, 0x00, 0x00, 0x94
563 };
564 
565 const ArrayRef<const uint8_t> Arm64RelativePatcherTest::kCallCode(kCallRawCode);
566 
567 const uint8_t Arm64RelativePatcherTest::kNopRawCode[] = {
568     0x1f, 0x20, 0x03, 0xd5
569 };
570 
571 const ArrayRef<const uint8_t> Arm64RelativePatcherTest::kNopCode(kNopRawCode);
572 
573 class Arm64RelativePatcherTestDefault : public Arm64RelativePatcherTest {
574  public:
Arm64RelativePatcherTestDefault()575   Arm64RelativePatcherTestDefault() : Arm64RelativePatcherTest("default") { }
576 };
577 
TEST_F(Arm64RelativePatcherTestDefault,CallSelf)578 TEST_F(Arm64RelativePatcherTestDefault, CallSelf) {
579   const LinkerPatch patches[] = {
580       LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
581   };
582   AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
583   Link();
584 
585   const std::vector<uint8_t> expected_code = RawCode({kBlPlus0});
586   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
587 }
588 
TEST_F(Arm64RelativePatcherTestDefault,CallOther)589 TEST_F(Arm64RelativePatcherTestDefault, CallOther) {
590   const LinkerPatch method1_patches[] = {
591       LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
592   };
593   AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(method1_patches));
594   const LinkerPatch method2_patches[] = {
595       LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
596   };
597   AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<const LinkerPatch>(method2_patches));
598   Link();
599 
600   uint32_t method1_offset = GetMethodOffset(1u);
601   uint32_t method2_offset = GetMethodOffset(2u);
602   uint32_t diff_after = method2_offset - method1_offset;
603   CHECK_ALIGNED(diff_after, 4u);
604   ASSERT_LT(diff_after >> 2, 1u << 8);  // Simple encoding, (diff_after >> 2) fits into 8 bits.
605   const std::vector<uint8_t> method1_expected_code = RawCode({kBlPlus0 + (diff_after >> 2)});
606   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code)));
607   uint32_t diff_before = method1_offset - method2_offset;
608   CHECK_ALIGNED(diff_before, 4u);
609   ASSERT_GE(diff_before, -1u << 27);
610   auto method2_expected_code = GenNopsAndBl(0u, kBlPlus0 | ((diff_before >> 2) & 0x03ffffffu));
611   EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code)));
612 }
613 
TEST_F(Arm64RelativePatcherTestDefault,CallTrampoline)614 TEST_F(Arm64RelativePatcherTestDefault, CallTrampoline) {
615   const LinkerPatch patches[] = {
616       LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
617   };
618   AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
619   Link();
620 
621   uint32_t method1_offset = GetMethodOffset(1u);
622   uint32_t diff = kTrampolineOffset - method1_offset;
623   ASSERT_EQ(diff & 1u, 0u);
624   ASSERT_GE(diff, -1u << 9);  // Simple encoding, -256 <= (diff >> 1) < 0 (checked as unsigned).
625   auto expected_code = GenNopsAndBl(0u, kBlPlus0 | ((diff >> 2) & 0x03ffffffu));
626   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
627 }
628 
TEST_F(Arm64RelativePatcherTestDefault,CallTrampolineTooFar)629 TEST_F(Arm64RelativePatcherTestDefault, CallTrampolineTooFar) {
630   constexpr uint32_t missing_method_index = 1024u;
631   auto last_method_raw_code = GenNopsAndBl(1u, kBlPlus0);
632   constexpr uint32_t bl_offset_in_last_method = 1u * 4u;  // After NOPs.
633   ArrayRef<const uint8_t> last_method_code(last_method_raw_code);
634   ASSERT_EQ(bl_offset_in_last_method + 4u, last_method_code.size());
635   const LinkerPatch last_method_patches[] = {
636       LinkerPatch::RelativeCodePatch(bl_offset_in_last_method, nullptr, missing_method_index),
637   };
638 
639   constexpr uint32_t just_over_max_negative_disp = 128 * MB + 4;
640   uint32_t last_method_idx = Create2MethodsWithGap(
641       kNopCode,
642       ArrayRef<const LinkerPatch>(),
643       last_method_code,
644       ArrayRef<const LinkerPatch>(last_method_patches),
645       just_over_max_negative_disp - bl_offset_in_last_method);
646   uint32_t method1_offset = GetMethodOffset(1u);
647   uint32_t last_method_offset = GetMethodOffset(last_method_idx);
648   ASSERT_EQ(method1_offset,
649             last_method_offset + bl_offset_in_last_method - just_over_max_negative_disp);
650   ASSERT_FALSE(method_offset_map_.FindMethodOffset(MethodRef(missing_method_index)).first);
651 
652   // Check linked code.
653   uint32_t thunk_offset =
654       CompiledCode::AlignCode(last_method_offset + last_method_code.size(), InstructionSet::kArm64);
655   uint32_t diff = thunk_offset - (last_method_offset + bl_offset_in_last_method);
656   ASSERT_TRUE(IsAligned<4u>(diff));
657   ASSERT_LT(diff, 128 * MB);
658   auto expected_code = GenNopsAndBl(1u, kBlPlus0 | (diff >> 2));
659   EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx),
660                                 ArrayRef<const uint8_t>(expected_code)));
661   EXPECT_TRUE(CheckThunk(thunk_offset));
662 }
663 
TEST_F(Arm64RelativePatcherTestDefault,CallOtherAlmostTooFarAfter)664 TEST_F(Arm64RelativePatcherTestDefault, CallOtherAlmostTooFarAfter) {
665   auto method1_raw_code = GenNopsAndBl(1u, kBlPlus0);
666   constexpr uint32_t bl_offset_in_method1 = 1u * 4u;  // After NOPs.
667   ArrayRef<const uint8_t> method1_code(method1_raw_code);
668   ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
669   const uint32_t kExpectedLastMethodIdx = 65u;  // Based on 2MiB chunks in Create2MethodsWithGap().
670   const LinkerPatch method1_patches[] = {
671       LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, kExpectedLastMethodIdx),
672   };
673 
674   constexpr uint32_t max_positive_disp = 128 * MB - 4u;
675   uint32_t last_method_idx = Create2MethodsWithGap(method1_code,
676                                                    ArrayRef<const LinkerPatch>(method1_patches),
677                                                    kNopCode,
678                                                    ArrayRef<const LinkerPatch>(),
679                                                    bl_offset_in_method1 + max_positive_disp);
680   ASSERT_EQ(kExpectedLastMethodIdx, last_method_idx);
681 
682   uint32_t method1_offset = GetMethodOffset(1u);
683   uint32_t last_method_offset = GetMethodOffset(last_method_idx);
684   ASSERT_EQ(method1_offset + bl_offset_in_method1 + max_positive_disp, last_method_offset);
685 
686   // Check linked code.
687   auto expected_code = GenNopsAndBl(1u, kBlPlusMax);
688   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
689 }
690 
TEST_F(Arm64RelativePatcherTestDefault,CallOtherAlmostTooFarBefore)691 TEST_F(Arm64RelativePatcherTestDefault, CallOtherAlmostTooFarBefore) {
692   auto last_method_raw_code = GenNopsAndBl(0u, kBlPlus0);
693   constexpr uint32_t bl_offset_in_last_method = 0u * 4u;  // After NOPs.
694   ArrayRef<const uint8_t> last_method_code(last_method_raw_code);
695   ASSERT_EQ(bl_offset_in_last_method + 4u, last_method_code.size());
696   const LinkerPatch last_method_patches[] = {
697       LinkerPatch::RelativeCodePatch(bl_offset_in_last_method, nullptr, 1u),
698   };
699 
700   constexpr uint32_t max_negative_disp = 128 * MB;
701   uint32_t last_method_idx = Create2MethodsWithGap(kNopCode,
702                                                    ArrayRef<const LinkerPatch>(),
703                                                    last_method_code,
704                                                    ArrayRef<const LinkerPatch>(last_method_patches),
705                                                    max_negative_disp - bl_offset_in_last_method);
706   uint32_t method1_offset = GetMethodOffset(1u);
707   uint32_t last_method_offset = GetMethodOffset(last_method_idx);
708   ASSERT_EQ(method1_offset, last_method_offset + bl_offset_in_last_method - max_negative_disp);
709 
710   // Check linked code.
711   auto expected_code = GenNopsAndBl(0u, kBlMinusMax);
712   EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx),
713                                 ArrayRef<const uint8_t>(expected_code)));
714 }
715 
TEST_F(Arm64RelativePatcherTestDefault,CallOtherJustTooFarAfter)716 TEST_F(Arm64RelativePatcherTestDefault, CallOtherJustTooFarAfter) {
717   auto method1_raw_code = GenNopsAndBl(0u, kBlPlus0);
718   constexpr uint32_t bl_offset_in_method1 = 0u * 4u;  // After NOPs.
719   ArrayRef<const uint8_t> method1_code(method1_raw_code);
720   ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
721   const uint32_t kExpectedLastMethodIdx = 65u;  // Based on 2MiB chunks in Create2MethodsWithGap().
722   const LinkerPatch method1_patches[] = {
723       LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, kExpectedLastMethodIdx),
724   };
725 
726   constexpr uint32_t just_over_max_positive_disp = 128 * MB;
727   uint32_t last_method_idx = Create2MethodsWithGap(
728       method1_code,
729       ArrayRef<const LinkerPatch>(method1_patches),
730       kNopCode,
731       ArrayRef<const LinkerPatch>(),
732       bl_offset_in_method1 + just_over_max_positive_disp);
733   ASSERT_EQ(kExpectedLastMethodIdx, last_method_idx);
734   uint32_t method_after_thunk_idx = last_method_idx;
735   if (sizeof(OatQuickMethodHeader) < kArm64Alignment) {
736     // The thunk needs to start on a kArm64Alignment-aligned address before the address where the
737     // last method would have been if there was no thunk. If the size of the OatQuickMethodHeader
738     // is at least kArm64Alignment, the thunk start shall fit between the previous filler method
739     // and that address. Otherwise, it shall be inserted before that filler method.
740     method_after_thunk_idx -= 1u;
741   }
742 
743   uint32_t method1_offset = GetMethodOffset(1u);
744   uint32_t method_after_thunk_offset = GetMethodOffset(method_after_thunk_idx);
745   ASSERT_TRUE(IsAligned<kArm64Alignment>(method_after_thunk_offset));
746   uint32_t method_after_thunk_header_offset =
747       method_after_thunk_offset - sizeof(OatQuickMethodHeader);
748   uint32_t thunk_size = MethodCallThunkSize();
749   uint32_t thunk_offset = RoundDown(method_after_thunk_header_offset - thunk_size, kArm64Alignment);
750   DCHECK_EQ(thunk_offset + thunk_size + CodeAlignmentSize(thunk_offset + thunk_size),
751             method_after_thunk_header_offset);
752   ASSERT_TRUE(IsAligned<kArm64Alignment>(thunk_offset));
753   uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1);
754   ASSERT_TRUE(IsAligned<4u>(diff));
755   ASSERT_LT(diff, 128 * MB);
756   auto expected_code = GenNopsAndBl(0u, kBlPlus0 | (diff >> 2));
757   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
758   CheckThunk(thunk_offset);
759 }
760 
TEST_F(Arm64RelativePatcherTestDefault,CallOtherJustTooFarBefore)761 TEST_F(Arm64RelativePatcherTestDefault, CallOtherJustTooFarBefore) {
762   auto last_method_raw_code = GenNopsAndBl(1u, kBlPlus0);
763   constexpr uint32_t bl_offset_in_last_method = 1u * 4u;  // After NOPs.
764   ArrayRef<const uint8_t> last_method_code(last_method_raw_code);
765   ASSERT_EQ(bl_offset_in_last_method + 4u, last_method_code.size());
766   const LinkerPatch last_method_patches[] = {
767       LinkerPatch::RelativeCodePatch(bl_offset_in_last_method, nullptr, 1u),
768   };
769 
770   constexpr uint32_t just_over_max_negative_disp = 128 * MB + 4;
771   uint32_t last_method_idx = Create2MethodsWithGap(
772       kNopCode, ArrayRef<const LinkerPatch>(), last_method_code,
773       ArrayRef<const LinkerPatch>(last_method_patches),
774       just_over_max_negative_disp - bl_offset_in_last_method);
775   uint32_t method1_offset = GetMethodOffset(1u);
776   uint32_t last_method_offset = GetMethodOffset(last_method_idx);
777   ASSERT_EQ(method1_offset,
778             last_method_offset + bl_offset_in_last_method - just_over_max_negative_disp);
779 
780   // Check linked code.
781   uint32_t thunk_offset =
782       CompiledCode::AlignCode(last_method_offset + last_method_code.size(), InstructionSet::kArm64);
783   uint32_t diff = thunk_offset - (last_method_offset + bl_offset_in_last_method);
784   ASSERT_TRUE(IsAligned<4u>(diff));
785   ASSERT_LT(diff, 128 * MB);
786   auto expected_code = GenNopsAndBl(1u, kBlPlus0 | (diff >> 2));
787   EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx),
788                                 ArrayRef<const uint8_t>(expected_code)));
789   EXPECT_TRUE(CheckThunk(thunk_offset));
790 }
791 
TEST_F(Arm64RelativePatcherTestDefault,StringBssEntry)792 TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry) {
793   struct TestCase {
794     uint32_t bss_begin;
795     uint32_t string_entry_offset;
796   };
797   static const TestCase test_cases[] = {
798       { 0x12345678u, 0x1234u },
799       { -0x12345678u, 0x4444u },
800       { 0x12345000u, 0x3ffcu },
801       { 0x12345000u, 0x4000u }
802   };
803   for (const TestCase& test_case : test_cases) {
804     Reset();
805     TestNopsAdrpLdr(/*num_nops=*/ 0u, test_case.bss_begin, test_case.string_entry_offset);
806   }
807 }
808 
TEST_F(Arm64RelativePatcherTestDefault,StringReference)809 TEST_F(Arm64RelativePatcherTestDefault, StringReference) {
810   for (uint32_t string_offset : { 0x12345678u, -0x12345678u, 0x12345000u, 0x12345ffcu}) {
811     Reset();
812     TestNopsAdrpAdd(/*num_nops=*/ 0u, string_offset);
813   }
814 }
815 
816 template <typename Test>
TestForAdrpOffsets(Test test,std::initializer_list<uint32_t> args)817 void TestForAdrpOffsets(Test test, std::initializer_list<uint32_t> args) {
818   for (uint32_t adrp_offset : { 0xff4u, 0xff8u, 0xffcu, 0x1000u }) {
819     for (uint32_t arg : args) {
820       test(adrp_offset, arg);
821     }
822   }
823 }
824 
TEST_F(Arm64RelativePatcherTestDefault,StringBssEntryLdur)825 TEST_F(Arm64RelativePatcherTestDefault, StringBssEntryLdur) {
826   TestForAdrpOffsets(
827       [&](uint32_t adrp_offset, uint32_t string_entry_offset) {
828         Reset();
829         bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu);
830         TestAdrpLdurLdr(adrp_offset, has_thunk, /*bss_begin=*/ 0x12345678u, string_entry_offset);
831       },
832       { 0x1234u, 0x1238u });
833 }
834 
835 // LDR <Wt>, <label> is always aligned. We should never have to use a fixup.
TEST_F(Arm64RelativePatcherTestDefault,StringBssEntryWPcRel)836 TEST_F(Arm64RelativePatcherTestDefault, StringBssEntryWPcRel) {
837   TestForAdrpOffsets(
838       [&](uint32_t adrp_offset, uint32_t pcrel_disp) {
839         Reset();
840         TestAdrpLdrPcRelLdr(kLdrWPcRelInsn,
841                             pcrel_disp,
842                             adrp_offset,
843                             /*has_thunk=*/ false,
844                             /*bss_begin=*/ 0x12345678u,
845                             /*string_entry_offset=*/ 0x1234u);
846       },
847       { 0x1234u, 0x1238u });
848 }
849 
850 // LDR <Xt>, <label> is aligned when offset + displacement is a multiple of 8.
TEST_F(Arm64RelativePatcherTestDefault,StringBssEntryXPcRel)851 TEST_F(Arm64RelativePatcherTestDefault, StringBssEntryXPcRel) {
852   TestForAdrpOffsets(
853       [&](uint32_t adrp_offset, uint32_t pcrel_disp) {
854         Reset();
855         bool unaligned = !IsAligned<8u>((adrp_offset) + 4u + static_cast<uint32_t>(pcrel_disp));
856         bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu) && unaligned;
857         TestAdrpLdrPcRelLdr(kLdrXPcRelInsn,
858                             pcrel_disp,
859                             adrp_offset,
860                             has_thunk,
861                             /*bss_begin=*/ 0x12345678u,
862                             /*string_entry_offset=*/ 0x1234u);
863       },
864       { 0x1234u, 0x1238u });
865 }
866 
867 // LDR <Wt>, [SP, #<pimm>] and LDR <Xt>, [SP, #<pimm>] are always aligned. No fixup needed.
TEST_F(Arm64RelativePatcherTestDefault,StringBssEntryWSpRel)868 TEST_F(Arm64RelativePatcherTestDefault, StringBssEntryWSpRel) {
869   TestForAdrpOffsets(
870       [&](uint32_t adrp_offset, uint32_t disp) {
871         Reset();
872         TestAdrpLdrSpRelLdr(kLdrWSpRelInsn,
873                             /*sprel_disp_in_load_units=*/ disp >> 2,
874                             adrp_offset,
875                             /*has_thunk=*/ false,
876                             /*bss_begin=*/ 0x12345678u,
877                             /*string_entry_offset=*/ 0x1234u);
878       },
879       { 0u, 4u });
880 }
881 
TEST_F(Arm64RelativePatcherTestDefault,StringBssEntryXSpRel)882 TEST_F(Arm64RelativePatcherTestDefault, StringBssEntryXSpRel) {
883   TestForAdrpOffsets(
884       [&](uint32_t adrp_offset, uint32_t disp) {
885         Reset();
886         TestAdrpLdrSpRelLdr(kLdrXSpRelInsn,
887                             /*sprel_disp_in_load_units=*/ (disp) >> 3,
888                             adrp_offset,
889                             /*has_thunk=*/ false,
890                             /*bss_begin=*/ 0x12345678u,
891                             /*string_entry_offset=*/ 0x1234u);
892       },
893       { 0u, 8u });
894 }
895 
TEST_F(Arm64RelativePatcherTestDefault,StringReferenceLdur)896 TEST_F(Arm64RelativePatcherTestDefault, StringReferenceLdur) {
897   TestForAdrpOffsets(
898       [&](uint32_t adrp_offset, uint32_t string_offset) {
899         Reset();
900         bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu);
901         TestAdrpLdurAdd(adrp_offset, has_thunk, string_offset);
902       },
903       { 0x12345678u, 0xffffc840u });
904 }
905 
TEST_F(Arm64RelativePatcherTestDefault,StringReferenceSubX3X2)906 TEST_F(Arm64RelativePatcherTestDefault, StringReferenceSubX3X2) {
907   TestForAdrpOffsets(
908       [&](uint32_t adrp_offset, uint32_t string_offset) {
909         Reset();
910         /* SUB unrelated to "ADRP x0, addr". */ \
911         uint32_t sub = kSubXInsn | (100 << 10) | (2u << 5) | 3u;  /* SUB x3, x2, #100 */
912         TestAdrpInsn2Add(sub, adrp_offset, /*has_thunk=*/ false, string_offset);
913       },
914       { 0x12345678u, 0xffffc840u });
915 }
916 
TEST_F(Arm64RelativePatcherTestDefault,StringReferenceSubsX3X0)917 TEST_F(Arm64RelativePatcherTestDefault, StringReferenceSubsX3X0) {
918   TestForAdrpOffsets(
919       [&](uint32_t adrp_offset, uint32_t string_offset) {
920         Reset();
921         /* SUBS that uses the result of "ADRP x0, addr". */ \
922         uint32_t subs = kSubsXInsn | (100 << 10) | (0u << 5) | 3u;  /* SUBS x3, x0, #100 */
923         TestAdrpInsn2Add(subs, adrp_offset, /*has_thunk=*/ false, string_offset);
924       },
925       { 0x12345678u, 0xffffc840u });
926 }
927 
TEST_F(Arm64RelativePatcherTestDefault,StringReferenceAddX0X0)928 TEST_F(Arm64RelativePatcherTestDefault, StringReferenceAddX0X0) {
929   TestForAdrpOffsets(
930       [&](uint32_t adrp_offset, uint32_t string_offset) {
931         Reset();
932         /* ADD that uses the result register of "ADRP x0, addr" as both source and destination. */
933         uint32_t add = kSubXInsn | (100 << 10) | (0u << 5) | 0u;  /* ADD x0, x0, #100 */
934         TestAdrpInsn2Add(add, adrp_offset, /*has_thunk=*/ false, string_offset);
935       },
936       { 0x12345678u, 0xffffc840 });
937 }
938 
TEST_F(Arm64RelativePatcherTestDefault,StringReferenceAddsX0X2)939 TEST_F(Arm64RelativePatcherTestDefault, StringReferenceAddsX0X2) {
940   TestForAdrpOffsets(
941       [&](uint32_t adrp_offset, uint32_t string_offset) {
942         Reset();
943         /* ADDS that does not use the result of "ADRP x0, addr" but overwrites that register. */
944         uint32_t adds = kAddsXInsn | (100 << 10) | (2u << 5) | 0u;  /* ADDS x0, x2, #100 */
945         bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu);
946         TestAdrpInsn2Add(adds, adrp_offset, has_thunk, string_offset);
947       },
948       { 0x12345678u, 0xffffc840u });
949 }
950 
951 // LDR <Wt>, <label> is always aligned. We should never have to use a fixup.
TEST_F(Arm64RelativePatcherTestDefault,StringReferenceWPcRel)952 TEST_F(Arm64RelativePatcherTestDefault, StringReferenceWPcRel) {
953   TestForAdrpOffsets(
954       [&](uint32_t adrp_offset, uint32_t pcrel_disp) {
955         Reset();
956         TestAdrpLdrPcRelAdd(kLdrWPcRelInsn,
957                             pcrel_disp,
958                             adrp_offset,
959                             /*has_thunk=*/ false,
960                             /*string_offset=*/ 0x12345678u);
961       },
962       { 0x1234u, 0x1238u });
963 }
964 
965 // LDR <Xt>, <label> is aligned when offset + displacement is a multiple of 8.
TEST_F(Arm64RelativePatcherTestDefault,StringReferenceXPcRel)966 TEST_F(Arm64RelativePatcherTestDefault, StringReferenceXPcRel) {
967   TestForAdrpOffsets(
968       [&](uint32_t adrp_offset, uint32_t pcrel_disp) {
969         Reset();
970         bool unaligned = !IsAligned<8u>((adrp_offset) + 4u + static_cast<uint32_t>(pcrel_disp));
971         bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu) && unaligned;
972         TestAdrpLdrPcRelAdd(kLdrXPcRelInsn,
973                             pcrel_disp,
974                             adrp_offset,
975                             has_thunk,
976                             /*string_offset=*/ 0x12345678u);
977       },
978       { 0x1234u, 0x1238u });
979 }
980 
981 // LDR <Wt>, [SP, #<pimm>] and LDR <Xt>, [SP, #<pimm>] are always aligned. No fixup needed.
TEST_F(Arm64RelativePatcherTestDefault,StringReferenceWSpRel)982 TEST_F(Arm64RelativePatcherTestDefault, StringReferenceWSpRel) {
983   TestForAdrpOffsets(
984       [&](uint32_t adrp_offset, uint32_t disp) {
985         Reset();
986         TestAdrpLdrSpRelAdd(kLdrWSpRelInsn,
987                             /*sprel_disp_in_load_units=*/ (disp) >> 2,
988                             adrp_offset,
989                             /*has_thunk=*/ false,
990                             /*string_offset=*/ 0x12345678u);
991       },
992       { 0u, 4u });
993 }
994 
TEST_F(Arm64RelativePatcherTestDefault,StringReferenceXSpRel)995 TEST_F(Arm64RelativePatcherTestDefault, StringReferenceXSpRel) {
996   TestForAdrpOffsets(
997       [&](uint32_t adrp_offset, uint32_t disp) {
998         Reset();
999         TestAdrpLdrSpRelAdd(kLdrXSpRelInsn,
1000                             /*sprel_disp_in_load_units=*/ (disp) >> 3,
1001                             adrp_offset,
1002                             /*has_thunk=*/ false,
1003                             /*string_offset=*/ 0x12345678u);
1004       },
1005       { 0u, 8u });
1006 }
1007 
TestBakerField(uint32_t offset,uint32_t ref_reg)1008 void Arm64RelativePatcherTest::TestBakerField(uint32_t offset, uint32_t ref_reg) {
1009   uint32_t valid_regs[] = {
1010       0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
1011       10, 11, 12, 13, 14, 15,         18, 19,  // IP0 and IP1 are reserved.
1012       20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
1013       // LR and SP/ZR are reserved.
1014   };
1015   DCHECK_ALIGNED(offset, 4u);
1016   DCHECK_LT(offset, 16 * KB);
1017   constexpr size_t kMethodCodeSize = 8u;
1018   constexpr size_t kLiteralOffset = 0u;
1019   uint32_t method_idx = 0u;
1020   for (uint32_t base_reg : valid_regs) {
1021     for (uint32_t holder_reg : valid_regs) {
1022       uint32_t ldr = kLdrWInsn | (offset << (10 - 2)) | (base_reg << 5) | ref_reg;
1023       const std::vector<uint8_t> raw_code = RawCode({kCbnzIP1Plus0Insn, ldr});
1024       ASSERT_EQ(kMethodCodeSize, raw_code.size());
1025       ArrayRef<const uint8_t> code(raw_code);
1026       uint32_t encoded_data = EncodeBakerReadBarrierFieldData(base_reg, holder_reg);
1027       const LinkerPatch patches[] = {
1028           LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset, encoded_data),
1029       };
1030       ++method_idx;
1031       AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches));
1032     }
1033   }
1034   Link();
1035 
1036   // All thunks are at the end.
1037   uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64Alignment);
1038   method_idx = 0u;
1039   for (uint32_t base_reg : valid_regs) {
1040     for (uint32_t holder_reg : valid_regs) {
1041       ++method_idx;
1042       uint32_t cbnz_offset = thunk_offset - (GetMethodOffset(method_idx) + kLiteralOffset);
1043       uint32_t cbnz = kCbnzIP1Plus0Insn | (cbnz_offset << (5 - 2));
1044       uint32_t ldr = kLdrWInsn | (offset << (10 - 2)) | (base_reg << 5) | ref_reg;
1045       const std::vector<uint8_t> expected_code = RawCode({cbnz, ldr});
1046       ASSERT_EQ(kMethodCodeSize, expected_code.size());
1047       ASSERT_TRUE(
1048           CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code)));
1049 
1050       std::vector<uint8_t> expected_thunk = CompileBakerOffsetThunk(base_reg, holder_reg);
1051       ASSERT_GT(output_.size(), thunk_offset);
1052       ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size());
1053       ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset,
1054                                              expected_thunk.size());
1055       if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) {
1056         DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk);
1057         ASSERT_TRUE(false);
1058       }
1059 
1060       size_t gray_check_offset = thunk_offset;
1061       if (holder_reg == base_reg) {
1062         // Verify that the null-check CBZ uses the correct register, i.e. holder_reg.
1063         ASSERT_GE(output_.size() - gray_check_offset, 4u);
1064         ASSERT_EQ(0x34000000u | holder_reg, GetOutputInsn(thunk_offset) & 0xff00001fu);
1065         gray_check_offset +=4u;
1066       }
1067       // Verify that the lock word for gray bit check is loaded from the holder address.
1068       static constexpr size_t kGrayCheckInsns = 5;
1069       ASSERT_GE(output_.size() - gray_check_offset, 4u * kGrayCheckInsns);
1070       const uint32_t load_lock_word =
1071           kLdrWInsn |
1072           (mirror::Object::MonitorOffset().Uint32Value() << (10 - 2)) |
1073           (holder_reg << 5) |
1074           /* ip0 */ 16;
1075       EXPECT_EQ(load_lock_word, GetOutputInsn(gray_check_offset));
1076       // Verify the gray bit check.
1077       const uint32_t check_gray_bit_without_offset =
1078           0x37000000u | (LockWord::kReadBarrierStateShift << 19) | /* ip0 */ 16;
1079       EXPECT_EQ(check_gray_bit_without_offset, GetOutputInsn(gray_check_offset + 4u) & 0xfff8001fu);
1080       // Verify the fake dependency.
1081       const uint32_t fake_dependency =
1082           0x8b408000u |             // ADD Xd, Xn, Xm, LSR 32
1083           (/* ip0 */ 16 << 16) |    // Xm = ip0
1084           (base_reg << 5) |         // Xn = base_reg
1085           base_reg;                 // Xd = base_reg
1086       EXPECT_EQ(fake_dependency, GetOutputInsn(gray_check_offset + 12u));
1087       // Do not check the rest of the implementation.
1088 
1089       // The next thunk follows on the next aligned offset.
1090       thunk_offset += RoundUp(expected_thunk.size(), kArm64Alignment);
1091     }
1092   }
1093 }
1094 
TEST_F(Arm64RelativePatcherTestDefault,BakerOffset)1095 TEST_F(Arm64RelativePatcherTestDefault, BakerOffset) {
1096   struct TestCase {
1097     uint32_t offset;
1098     uint32_t ref_reg;
1099   };
1100   static const TestCase test_cases[] = {
1101       { 0u, 0u },
1102       { 8u, 15u},
1103       { 0x3ffcu, 29u },
1104   };
1105   for (const TestCase& test_case : test_cases) {
1106     Reset();
1107     TestBakerField(test_case.offset, test_case.ref_reg);
1108   }
1109 }
1110 
1111 
TEST_F(Arm64RelativePatcherTestDefault,BakerOffsetThunkInTheMiddle)1112 TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddle) {
1113   // One thunk in the middle with maximum distance branches to it from both sides.
1114   // Use offset = 0, base_reg = 0, ref_reg = 0, the LDR is simply `kLdrWInsn`.
1115   constexpr uint32_t kLiteralOffset1 = 4;
1116   const std::vector<uint8_t> raw_code1 = RawCode({kNopInsn, kCbnzIP1Plus0Insn, kLdrWInsn});
1117   ArrayRef<const uint8_t> code1(raw_code1);
1118   uint32_t encoded_data = EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0);
1119   const LinkerPatch patches1[] = {
1120       LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data),
1121   };
1122   AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(patches1));
1123 
1124   // Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4
1125   // allows the branch to reach that thunk.
1126   size_t filler1_size =
1127       1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment);
1128   std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u);
1129   ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
1130   AddCompiledMethod(MethodRef(2u), filler1_code);
1131 
1132   // Enforce thunk reservation with a tiny method.
1133   AddCompiledMethod(MethodRef(3u), kNopCode);
1134 
1135   // Allow reaching the thunk from the very beginning of a method 1MiB away. Backward branch
1136   // reaches the full 1MiB. Things to subtract:
1137   //   - thunk size and method 3 pre-header, rounded up (padding in between if needed)
1138   //   - method 3 code and method 4 pre-header, rounded up (padding in between if needed)
1139   //   - method 4 header (let there be no padding between method 4 code and method 5 pre-header).
1140   size_t thunk_size = CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0).size();
1141   size_t filler2_size =
1142       1 * MB - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArm64Alignment)
1143              - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArm64Alignment)
1144              - sizeof(OatQuickMethodHeader);
1145   std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 4u);
1146   ArrayRef<const uint8_t> filler2_code(raw_filler2_code);
1147   AddCompiledMethod(MethodRef(4u), filler2_code);
1148 
1149   constexpr uint32_t kLiteralOffset2 = 0;
1150   const std::vector<uint8_t> raw_code2 = RawCode({kCbnzIP1Plus0Insn, kLdrWInsn});
1151   ArrayRef<const uint8_t> code2(raw_code2);
1152   const LinkerPatch patches2[] = {
1153       LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset2, encoded_data),
1154   };
1155   AddCompiledMethod(MethodRef(5u), code2, ArrayRef<const LinkerPatch>(patches2));
1156 
1157   Link();
1158 
1159   uint32_t first_method_offset = GetMethodOffset(1u);
1160   uint32_t last_method_offset = GetMethodOffset(5u);
1161   EXPECT_EQ(2 * MB, last_method_offset - first_method_offset);
1162 
1163   const uint32_t cbnz_max_forward = kCbnzIP1Plus0Insn | 0x007fffe0;
1164   const uint32_t cbnz_max_backward = kCbnzIP1Plus0Insn | 0x00800000;
1165   const std::vector<uint8_t> expected_code1 = RawCode({kNopInsn, cbnz_max_forward, kLdrWInsn});
1166   const std::vector<uint8_t> expected_code2 = RawCode({cbnz_max_backward, kLdrWInsn});
1167   ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1)));
1168   ASSERT_TRUE(CheckLinkedMethod(MethodRef(5), ArrayRef<const uint8_t>(expected_code2)));
1169 }
1170 
TEST_F(Arm64RelativePatcherTestDefault,BakerOffsetThunkBeforeFiller)1171 TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkBeforeFiller) {
1172   // Based on the first part of BakerOffsetThunkInTheMiddle but the CBNZ is one instruction
1173   // earlier, so the thunk is emitted before the filler.
1174   // Use offset = 0, base_reg = 0, ref_reg = 0, the LDR is simply `kLdrWInsn`.
1175   constexpr uint32_t kLiteralOffset1 = 0;
1176   const std::vector<uint8_t> raw_code1 = RawCode({kCbnzIP1Plus0Insn, kLdrWInsn, kNopInsn});
1177   ArrayRef<const uint8_t> code1(raw_code1);
1178   uint32_t encoded_data = EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0);
1179   const LinkerPatch patches1[] = {
1180       LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data),
1181   };
1182   AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(patches1));
1183 
1184   // Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4
1185   // allows the branch to reach that thunk.
1186   size_t filler1_size =
1187       1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment);
1188   std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u);
1189   ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
1190   AddCompiledMethod(MethodRef(2u), filler1_code);
1191 
1192   Link();
1193 
1194   const uint32_t cbnz_offset = RoundUp(raw_code1.size(), kArm64Alignment) - kLiteralOffset1;
1195   const uint32_t cbnz = kCbnzIP1Plus0Insn | (cbnz_offset << (5 - 2));
1196   const std::vector<uint8_t> expected_code1 = RawCode({cbnz, kLdrWInsn, kNopInsn});
1197   ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1)));
1198 }
1199 
TEST_F(Arm64RelativePatcherTestDefault,BakerOffsetThunkInTheMiddleUnreachableFromLast)1200 TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddleUnreachableFromLast) {
1201   // Based on the BakerOffsetThunkInTheMiddle but the CBNZ in the last method is preceded
1202   // by NOP and cannot reach the thunk in the middle, so we emit an extra thunk at the end.
1203   // Use offset = 0, base_reg = 0, ref_reg = 0, the LDR is simply `kLdrWInsn`.
1204   constexpr uint32_t kLiteralOffset1 = 4;
1205   const std::vector<uint8_t> raw_code1 = RawCode({kNopInsn, kCbnzIP1Plus0Insn, kLdrWInsn});
1206   ArrayRef<const uint8_t> code1(raw_code1);
1207   uint32_t encoded_data = EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0);
1208   const LinkerPatch patches1[] = {
1209       LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data),
1210   };
1211   AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(patches1));
1212 
1213   // Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4
1214   // allows the branch to reach that thunk.
1215   size_t filler1_size =
1216       1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment);
1217   std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u);
1218   ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
1219   AddCompiledMethod(MethodRef(2u), filler1_code);
1220 
1221   // Enforce thunk reservation with a tiny method.
1222   AddCompiledMethod(MethodRef(3u), kNopCode);
1223 
1224   // If not for the extra NOP, this would allow reaching the thunk from the very beginning
1225   // of a method 1MiB away. Backward branch reaches the full 1MiB. Things to subtract:
1226   //   - thunk size and method 3 pre-header, rounded up (padding in between if needed)
1227   //   - method 3 code and method 4 pre-header, rounded up (padding in between if needed)
1228   //   - method 4 header (let there be no padding between method 4 code and method 5 pre-header).
1229   size_t thunk_size = CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0).size();
1230   size_t filler2_size =
1231       1 * MB - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArm64Alignment)
1232              - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArm64Alignment)
1233              - sizeof(OatQuickMethodHeader);
1234   std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 4u);
1235   ArrayRef<const uint8_t> filler2_code(raw_filler2_code);
1236   AddCompiledMethod(MethodRef(4u), filler2_code);
1237 
1238   // Extra NOP compared to BakerOffsetThunkInTheMiddle.
1239   constexpr uint32_t kLiteralOffset2 = 4;
1240   const std::vector<uint8_t> raw_code2 = RawCode({kNopInsn, kCbnzIP1Plus0Insn, kLdrWInsn});
1241   ArrayRef<const uint8_t> code2(raw_code2);
1242   const LinkerPatch patches2[] = {
1243       LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset2, encoded_data),
1244   };
1245   AddCompiledMethod(MethodRef(5u), code2, ArrayRef<const LinkerPatch>(patches2));
1246 
1247   Link();
1248 
1249   const uint32_t cbnz_max_forward = kCbnzIP1Plus0Insn | 0x007fffe0;
1250   const uint32_t cbnz_last_offset = RoundUp(raw_code2.size(), kArm64Alignment) - kLiteralOffset2;
1251   const uint32_t cbnz_last = kCbnzIP1Plus0Insn | (cbnz_last_offset << (5 - 2));
1252   const std::vector<uint8_t> expected_code1 = RawCode({kNopInsn, cbnz_max_forward, kLdrWInsn});
1253   const std::vector<uint8_t> expected_code2 = RawCode({kNopInsn, cbnz_last, kLdrWInsn});
1254   ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1)));
1255   ASSERT_TRUE(CheckLinkedMethod(MethodRef(5), ArrayRef<const uint8_t>(expected_code2)));
1256 }
1257 
TEST_F(Arm64RelativePatcherTestDefault,BakerArray)1258 TEST_F(Arm64RelativePatcherTestDefault, BakerArray) {
1259   uint32_t valid_regs[] = {
1260       0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
1261       10, 11, 12, 13, 14, 15,         18, 19,  // IP0 and IP1 are reserved.
1262       20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
1263       // LR and SP/ZR are reserved.
1264   };
1265   auto ldr = [](uint32_t base_reg) {
1266     uint32_t index_reg = (base_reg == 0u) ? 1u : 0u;
1267     uint32_t ref_reg = (base_reg == 2) ? 3u : 2u;
1268     return kLdrWLsl2Insn | (index_reg << 16) | (base_reg << 5) | ref_reg;
1269   };
1270   constexpr size_t kMethodCodeSize = 8u;
1271   constexpr size_t kLiteralOffset = 0u;
1272   uint32_t method_idx = 0u;
1273   for (uint32_t base_reg : valid_regs) {
1274     ++method_idx;
1275     const std::vector<uint8_t> raw_code = RawCode({kCbnzIP1Plus0Insn, ldr(base_reg)});
1276     ASSERT_EQ(kMethodCodeSize, raw_code.size());
1277     ArrayRef<const uint8_t> code(raw_code);
1278     const LinkerPatch patches[] = {
1279         LinkerPatch::BakerReadBarrierBranchPatch(
1280             kLiteralOffset, EncodeBakerReadBarrierArrayData(base_reg)),
1281     };
1282     AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches));
1283   }
1284   Link();
1285 
1286   // All thunks are at the end.
1287   uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64Alignment);
1288   method_idx = 0u;
1289   for (uint32_t base_reg : valid_regs) {
1290     ++method_idx;
1291     uint32_t cbnz_offset = thunk_offset - (GetMethodOffset(method_idx) + kLiteralOffset);
1292     uint32_t cbnz = kCbnzIP1Plus0Insn | (cbnz_offset << (5 - 2));
1293     const std::vector<uint8_t> expected_code = RawCode({cbnz, ldr(base_reg)});
1294     ASSERT_EQ(kMethodCodeSize, expected_code.size());
1295     EXPECT_TRUE(CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code)));
1296 
1297     std::vector<uint8_t> expected_thunk = CompileBakerArrayThunk(base_reg);
1298     ASSERT_GT(output_.size(), thunk_offset);
1299     ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size());
1300     ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset,
1301                                            expected_thunk.size());
1302     if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) {
1303       DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk);
1304       ASSERT_TRUE(false);
1305     }
1306 
1307     // Verify that the lock word for gray bit check is loaded from the correct address
1308     // before the base_reg which points to the array data.
1309     static constexpr size_t kGrayCheckInsns = 5;
1310     ASSERT_GE(output_.size() - thunk_offset, 4u * kGrayCheckInsns);
1311     int32_t data_offset =
1312         mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value();
1313     int32_t offset = mirror::Object::MonitorOffset().Int32Value() - data_offset;
1314     ASSERT_LT(offset, 0);
1315     const uint32_t load_lock_word =
1316         kLdurWInsn |
1317         ((offset & 0x1ffu) << 12) |
1318         (base_reg << 5) |
1319         /* ip0 */ 16;
1320     EXPECT_EQ(load_lock_word, GetOutputInsn(thunk_offset));
1321     // Verify the gray bit check.
1322     const uint32_t check_gray_bit_without_offset =
1323         0x37000000u | (LockWord::kReadBarrierStateShift << 19) | /* ip0 */ 16;
1324     EXPECT_EQ(check_gray_bit_without_offset, GetOutputInsn(thunk_offset + 4u) & 0xfff8001fu);
1325     // Verify the fake dependency.
1326     const uint32_t fake_dependency =
1327         0x8b408000u |             // ADD Xd, Xn, Xm, LSR 32
1328         (/* ip0 */ 16 << 16) |    // Xm = ip0
1329         (base_reg << 5) |         // Xn = base_reg
1330         base_reg;                 // Xd = base_reg
1331     EXPECT_EQ(fake_dependency, GetOutputInsn(thunk_offset + 12u));
1332     // Do not check the rest of the implementation.
1333 
1334     // The next thunk follows on the next aligned offset.
1335     thunk_offset += RoundUp(expected_thunk.size(), kArm64Alignment);
1336   }
1337 }
1338 
TEST_F(Arm64RelativePatcherTestDefault,BakerGcRoot)1339 TEST_F(Arm64RelativePatcherTestDefault, BakerGcRoot) {
1340   uint32_t valid_regs[] = {
1341       0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
1342       10, 11, 12, 13, 14, 15,         18, 19,  // IP0 and IP1 are reserved.
1343       20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
1344       // LR and SP/ZR are reserved.
1345   };
1346   constexpr size_t kMethodCodeSize = 8u;
1347   constexpr size_t kLiteralOffset = 4u;
1348   uint32_t method_idx = 0u;
1349   for (uint32_t root_reg : valid_regs) {
1350     ++method_idx;
1351     uint32_t ldr = kLdrWInsn | (/* offset */ 8 << (10 - 2)) | (/* base_reg */ 0 << 5) | root_reg;
1352     const std::vector<uint8_t> raw_code = RawCode({ldr, kCbnzIP1Plus0Insn});
1353     ASSERT_EQ(kMethodCodeSize, raw_code.size());
1354     ArrayRef<const uint8_t> code(raw_code);
1355     const LinkerPatch patches[] = {
1356         LinkerPatch::BakerReadBarrierBranchPatch(
1357             kLiteralOffset, EncodeBakerReadBarrierGcRootData(root_reg)),
1358     };
1359     AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches));
1360   }
1361   Link();
1362 
1363   // All thunks are at the end.
1364   uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64Alignment);
1365   method_idx = 0u;
1366   for (uint32_t root_reg : valid_regs) {
1367     ++method_idx;
1368     uint32_t cbnz_offset = thunk_offset - (GetMethodOffset(method_idx) + kLiteralOffset);
1369     uint32_t cbnz = kCbnzIP1Plus0Insn | (cbnz_offset << (5 - 2));
1370     uint32_t ldr = kLdrWInsn | (/* offset */ 8 << (10 - 2)) | (/* base_reg */ 0 << 5) | root_reg;
1371     const std::vector<uint8_t> expected_code = RawCode({ldr, cbnz});
1372     ASSERT_EQ(kMethodCodeSize, expected_code.size());
1373     EXPECT_TRUE(CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code)));
1374 
1375     std::vector<uint8_t> expected_thunk = CompileBakerGcRootThunk(root_reg);
1376     ASSERT_GT(output_.size(), thunk_offset);
1377     ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size());
1378     ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset,
1379                                            expected_thunk.size());
1380     if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) {
1381       DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk);
1382       ASSERT_TRUE(false);
1383     }
1384 
1385     // Verify that the fast-path null-check CBZ uses the correct register, i.e. root_reg.
1386     ASSERT_GE(output_.size() - thunk_offset, 4u);
1387     ASSERT_EQ(0x34000000u | root_reg, GetOutputInsn(thunk_offset) & 0xff00001fu);
1388     // Do not check the rest of the implementation.
1389 
1390     // The next thunk follows on the next aligned offset.
1391     thunk_offset += RoundUp(expected_thunk.size(), kArm64Alignment);
1392   }
1393 }
1394 
TEST_F(Arm64RelativePatcherTestDefault,BakerAndMethodCallInteraction)1395 TEST_F(Arm64RelativePatcherTestDefault, BakerAndMethodCallInteraction) {
1396   // During development, there was a `DCHECK_LE(MaxNextOffset(), next_thunk.MaxNextOffset());`
1397   // in `ArmBaseRelativePatcher::ThunkData::MakeSpaceBefore()` which does not necessarily
1398   // hold when we're reserving thunks of different sizes. This test exposes the situation
1399   // by using Baker thunks and a method call thunk.
1400 
1401   // Add a method call patch that can reach to method 1 offset + 128MiB.
1402   uint32_t method_idx = 0u;
1403   constexpr size_t kMethodCallLiteralOffset = 4u;
1404   constexpr uint32_t kMissingMethodIdx = 2u;
1405   const std::vector<uint8_t> raw_code1 = RawCode({kNopInsn, kBlPlus0});
1406   const LinkerPatch method1_patches[] = {
1407       LinkerPatch::RelativeCodePatch(kMethodCallLiteralOffset, nullptr, 2u),
1408   };
1409   ArrayRef<const uint8_t> code1(raw_code1);
1410   ++method_idx;
1411   AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(method1_patches));
1412 
1413   // Skip kMissingMethodIdx.
1414   ++method_idx;
1415   ASSERT_EQ(kMissingMethodIdx, method_idx);
1416   // Add a method with the right size that the method code for the next one starts 1MiB
1417   // after code for method 1.
1418   size_t filler_size =
1419       1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment)
1420              - sizeof(OatQuickMethodHeader);
1421   std::vector<uint8_t> filler_code = GenNops(filler_size / 4u);
1422   ++method_idx;
1423   AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(filler_code));
1424   // Add 126 methods with 1MiB code+header, making the code for the next method start 1MiB
1425   // before the currently scheduled MaxNextOffset() for the method call thunk.
1426   for (uint32_t i = 0; i != 126; ++i) {
1427     filler_size = 1 * MB - sizeof(OatQuickMethodHeader);
1428     filler_code = GenNops(filler_size / 4u);
1429     ++method_idx;
1430     AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(filler_code));
1431   }
1432 
1433   // Add 2 Baker GC root patches to the last method, one that would allow the thunk at
1434   // 1MiB + kArm64Alignment, i.e. kArm64Alignment after the method call thunk, and the
1435   // second that needs it kArm64Alignment after that. Given the size of the GC root thunk
1436   // is more than the space required by the method call thunk plus kArm64Alignment,
1437   // this pushes the first GC root thunk's pending MaxNextOffset() before the method call
1438   // thunk's pending MaxNextOffset() which needs to be adjusted.
1439   ASSERT_LT(RoundUp(CompileMethodCallThunk().size(), kArm64Alignment) + kArm64Alignment,
1440             CompileBakerGcRootThunk(/* root_reg */ 0).size());
1441   static_assert(kArm64Alignment == 16, "Code below assumes kArm64Alignment == 16");
1442   constexpr size_t kBakerLiteralOffset1 = 4u + kArm64Alignment;
1443   constexpr size_t kBakerLiteralOffset2 = 4u + 2 * kArm64Alignment;
1444   // Use offset = 0, base_reg = 0, the LDR is simply `kLdrWInsn | root_reg`.
1445   const uint32_t ldr1 = kLdrWInsn | /* root_reg */ 1;
1446   const uint32_t ldr2 = kLdrWInsn | /* root_reg */ 2;
1447   const std::vector<uint8_t> last_method_raw_code = RawCode({
1448       kNopInsn, kNopInsn, kNopInsn, kNopInsn,   // Padding before first GC root read barrier.
1449       ldr1, kCbnzIP1Plus0Insn,                  // First GC root LDR with read barrier.
1450       kNopInsn, kNopInsn,                       // Padding before second GC root read barrier.
1451       ldr2, kCbnzIP1Plus0Insn,                  // Second GC root LDR with read barrier.
1452   });
1453   uint32_t encoded_data1 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 1);
1454   uint32_t encoded_data2 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 2);
1455   const LinkerPatch last_method_patches[] = {
1456       LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset1, encoded_data1),
1457       LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset2, encoded_data2),
1458   };
1459   ++method_idx;
1460   AddCompiledMethod(MethodRef(method_idx),
1461                     ArrayRef<const uint8_t>(last_method_raw_code),
1462                     ArrayRef<const LinkerPatch>(last_method_patches));
1463 
1464   // The main purpose of the test is to check that Link() does not cause a crash.
1465   Link();
1466 
1467   ASSERT_EQ(127 * MB, GetMethodOffset(method_idx) - GetMethodOffset(1u));
1468 }
1469 
1470 }  // namespace linker
1471 }  // namespace art
1472