• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/relative_patcher_test.h"
18 #include "linker/arm64/relative_patcher_arm64.h"
19 #include "oat_quick_method_header.h"
20 
21 namespace art {
22 namespace linker {
23 
24 class Arm64RelativePatcherTest : public RelativePatcherTest {
25  public:
Arm64RelativePatcherTest(const std::string & variant)26   explicit Arm64RelativePatcherTest(const std::string& variant)
27       : RelativePatcherTest(kArm64, variant) { }
28 
29  protected:
30   static const uint8_t kCallRawCode[];
31   static const ArrayRef<const uint8_t> kCallCode;
32   static const uint8_t kNopRawCode[];
33   static const ArrayRef<const uint8_t> kNopCode;
34 
35   // All branches can be created from kBlPlus0 or kBPlus0 by adding the low 26 bits.
36   static constexpr uint32_t kBlPlus0 = 0x94000000u;
37   static constexpr uint32_t kBPlus0 = 0x14000000u;
38 
39   // Special BL values.
40   static constexpr uint32_t kBlPlusMax = 0x95ffffffu;
41   static constexpr uint32_t kBlMinusMax = 0x96000000u;
42 
43   // LDR immediate, 32-bit.
44   static constexpr uint32_t kLdrWInsn = 0xb9400000u;
45 
46   // ADD/ADDS/SUB/SUBS immediate, 64-bit.
47   static constexpr uint32_t kAddXInsn = 0x91000000u;
48   static constexpr uint32_t kAddsXInsn = 0xb1000000u;
49   static constexpr uint32_t kSubXInsn = 0xd1000000u;
50   static constexpr uint32_t kSubsXInsn = 0xf1000000u;
51 
52   // LDUR x2, [sp, #4], i.e. unaligned load crossing 64-bit boundary (assuming aligned sp).
53   static constexpr uint32_t kLdurInsn = 0xf840405fu;
54 
55   // LDR w12, <label> and LDR x12, <label>. Bits 5-23 contain label displacement in 4-byte units.
56   static constexpr uint32_t kLdrWPcRelInsn = 0x1800000cu;
57   static constexpr uint32_t kLdrXPcRelInsn = 0x5800000cu;
58 
59   // LDR w13, [SP, #<pimm>] and LDR x13, [SP, #<pimm>]. Bits 10-21 contain displacement from SP
60   // in units of 4-bytes (for 32-bit load) or 8-bytes (for 64-bit load).
61   static constexpr uint32_t kLdrWSpRelInsn = 0xb94003edu;
62   static constexpr uint32_t kLdrXSpRelInsn = 0xf94003edu;
63 
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)64   uint32_t Create2MethodsWithGap(const ArrayRef<const uint8_t>& method1_code,
65                                  const ArrayRef<const LinkerPatch>& method1_patches,
66                                  const ArrayRef<const uint8_t>& last_method_code,
67                                  const ArrayRef<const LinkerPatch>& last_method_patches,
68                                  uint32_t distance_without_thunks) {
69     CHECK_EQ(distance_without_thunks % kArm64Alignment, 0u);
70     const uint32_t method1_offset =
71         CompiledCode::AlignCode(kTrampolineSize, kArm64) + sizeof(OatQuickMethodHeader);
72     AddCompiledMethod(MethodRef(1u), method1_code, method1_patches);
73     const uint32_t gap_start =
74         CompiledCode::AlignCode(method1_offset + method1_code.size(), kArm64);
75 
76     // We want to put the method3 at a very precise offset.
77     const uint32_t last_method_offset = method1_offset + distance_without_thunks;
78     const uint32_t gap_end = last_method_offset - sizeof(OatQuickMethodHeader);
79     CHECK_ALIGNED(gap_end, kArm64Alignment);
80 
81     // Fill the gap with intermediate methods in chunks of 2MiB and the last in [2MiB, 4MiB).
82     // (This allows deduplicating the small chunks to avoid using 256MiB of memory for +-128MiB
83     // offsets by this test.)
84     uint32_t method_idx = 2u;
85     constexpr uint32_t kSmallChunkSize = 2 * MB;
86     std::vector<uint8_t> gap_code;
87     size_t gap_size = gap_end - gap_start;
88     for (; gap_size >= 2u * kSmallChunkSize; gap_size -= kSmallChunkSize) {
89       uint32_t chunk_code_size = kSmallChunkSize - sizeof(OatQuickMethodHeader);
90       gap_code.resize(chunk_code_size, 0u);
91       AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(gap_code),
92                         ArrayRef<const LinkerPatch>());
93       method_idx += 1u;
94     }
95     uint32_t chunk_code_size = gap_size - sizeof(OatQuickMethodHeader);
96     gap_code.resize(chunk_code_size, 0u);
97     AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(gap_code),
98                       ArrayRef<const LinkerPatch>());
99     method_idx += 1u;
100 
101     // Add the last method and link
102     AddCompiledMethod(MethodRef(method_idx), last_method_code, last_method_patches);
103     Link();
104 
105     // Check assumptions.
106     CHECK_EQ(GetMethodOffset(1), method1_offset);
107     auto last_result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
108     CHECK(last_result.first);
109     // There may be a thunk before method2.
110     if (last_result.second != last_method_offset) {
111       // Thunk present. Check that there's only one.
112       uint32_t aligned_thunk_size = CompiledCode::AlignCode(ThunkSize(), kArm64);
113       CHECK_EQ(last_result.second, last_method_offset + aligned_thunk_size);
114     }
115     return method_idx;
116   }
117 
GetMethodOffset(uint32_t method_idx)118   uint32_t GetMethodOffset(uint32_t method_idx) {
119     auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
120     CHECK(result.first);
121     CHECK_ALIGNED(result.second, 4u);
122     return result.second;
123   }
124 
ThunkSize()125   uint32_t ThunkSize() {
126     return static_cast<Arm64RelativePatcher*>(patcher_.get())->thunk_code_.size();
127   }
128 
CheckThunk(uint32_t thunk_offset)129   bool CheckThunk(uint32_t thunk_offset) {
130     Arm64RelativePatcher* patcher = static_cast<Arm64RelativePatcher*>(patcher_.get());
131     ArrayRef<const uint8_t> expected_code(patcher->thunk_code_);
132     if (output_.size() < thunk_offset + expected_code.size()) {
133       LOG(ERROR) << "output_.size() == " << output_.size() << " < "
134           << "thunk_offset + expected_code.size() == " << (thunk_offset + expected_code.size());
135       return false;
136     }
137     ArrayRef<const uint8_t> linked_code(&output_[thunk_offset], expected_code.size());
138     if (linked_code == expected_code) {
139       return true;
140     }
141     // Log failure info.
142     DumpDiff(expected_code, linked_code);
143     return false;
144   }
145 
GenNopsAndBl(size_t num_nops,uint32_t bl)146   std::vector<uint8_t> GenNopsAndBl(size_t num_nops, uint32_t bl) {
147     std::vector<uint8_t> result;
148     result.reserve(num_nops * 4u + 4u);
149     for (size_t i = 0; i != num_nops; ++i) {
150       result.insert(result.end(), kNopCode.begin(), kNopCode.end());
151     }
152     result.push_back(static_cast<uint8_t>(bl));
153     result.push_back(static_cast<uint8_t>(bl >> 8));
154     result.push_back(static_cast<uint8_t>(bl >> 16));
155     result.push_back(static_cast<uint8_t>(bl >> 24));
156     return result;
157   }
158 
GenNopsAndAdrpAndUse(size_t num_nops,uint32_t method_offset,uint32_t target_offset,uint32_t use_insn)159   std::vector<uint8_t> GenNopsAndAdrpAndUse(size_t num_nops,
160                                             uint32_t method_offset,
161                                             uint32_t target_offset,
162                                             uint32_t use_insn) {
163     std::vector<uint8_t> result;
164     result.reserve(num_nops * 4u + 8u);
165     for (size_t i = 0; i != num_nops; ++i) {
166       result.insert(result.end(), kNopCode.begin(), kNopCode.end());
167     }
168     CHECK_ALIGNED(method_offset, 4u);
169     CHECK_ALIGNED(target_offset, 4u);
170     uint32_t adrp_offset = method_offset + num_nops * 4u;
171     uint32_t disp = target_offset - (adrp_offset & ~0xfffu);
172     if (use_insn == kLdrWInsn) {
173       DCHECK_ALIGNED(disp, 1u << 2);
174       use_insn |= 1 |                         // LDR x1, [x0, #(imm12 << 2)]
175           ((disp & 0xfffu) << (10 - 2));      // imm12 = ((disp & 0xfffu) >> 2) is at bit 10.
176     } else if (use_insn == kAddXInsn) {
177       use_insn |= 1 |                         // ADD x1, x0, #imm
178           (disp & 0xfffu) << 10;              // imm12 = (disp & 0xfffu) is at bit 10.
179     } else {
180       LOG(FATAL) << "Unexpected instruction: 0x" << std::hex << use_insn;
181     }
182     uint32_t adrp = 0x90000000 |              // ADRP x0, +SignExtend(immhi:immlo:Zeros(12), 64)
183         ((disp & 0x3000u) << (29 - 12)) |     // immlo = ((disp & 0x3000u) >> 12) is at bit 29,
184         ((disp & 0xffffc000) >> (14 - 5)) |   // immhi = (disp >> 14) is at bit 5,
185         // We take the sign bit from the disp, limiting disp to +- 2GiB.
186         ((disp & 0x80000000) >> (31 - 23));   // sign bit in immhi is at bit 23.
187     result.push_back(static_cast<uint8_t>(adrp));
188     result.push_back(static_cast<uint8_t>(adrp >> 8));
189     result.push_back(static_cast<uint8_t>(adrp >> 16));
190     result.push_back(static_cast<uint8_t>(adrp >> 24));
191     result.push_back(static_cast<uint8_t>(use_insn));
192     result.push_back(static_cast<uint8_t>(use_insn >> 8));
193     result.push_back(static_cast<uint8_t>(use_insn >> 16));
194     result.push_back(static_cast<uint8_t>(use_insn >> 24));
195     return result;
196   }
197 
GenNopsAndAdrpLdr(size_t num_nops,uint32_t method_offset,uint32_t target_offset)198   std::vector<uint8_t> GenNopsAndAdrpLdr(size_t num_nops,
199                                          uint32_t method_offset,
200                                          uint32_t target_offset) {
201     return GenNopsAndAdrpAndUse(num_nops, method_offset, target_offset, kLdrWInsn);
202   }
203 
TestNopsAdrpLdr(size_t num_nops,uint32_t dex_cache_arrays_begin,uint32_t element_offset)204   void TestNopsAdrpLdr(size_t num_nops, uint32_t dex_cache_arrays_begin, uint32_t element_offset) {
205     dex_cache_arrays_begin_ = dex_cache_arrays_begin;
206     auto code = GenNopsAndAdrpLdr(num_nops, 0u, 0u);  // Unpatched.
207     LinkerPatch patches[] = {
208         LinkerPatch::DexCacheArrayPatch(num_nops * 4u     , nullptr, num_nops * 4u, element_offset),
209         LinkerPatch::DexCacheArrayPatch(num_nops * 4u + 4u, nullptr, num_nops * 4u, element_offset),
210     };
211     AddCompiledMethod(MethodRef(1u),
212                       ArrayRef<const uint8_t>(code),
213                       ArrayRef<const LinkerPatch>(patches));
214     Link();
215 
216     uint32_t method1_offset = GetMethodOffset(1u);
217     uint32_t target_offset = dex_cache_arrays_begin_ + element_offset;
218     auto expected_code = GenNopsAndAdrpLdr(num_nops, method1_offset, target_offset);
219     EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
220   }
221 
GenNopsAndAdrpAdd(size_t num_nops,uint32_t method_offset,uint32_t target_offset)222   std::vector<uint8_t> GenNopsAndAdrpAdd(size_t num_nops,
223                                          uint32_t method_offset,
224                                          uint32_t target_offset) {
225     return GenNopsAndAdrpAndUse(num_nops, method_offset, target_offset, kAddXInsn);
226   }
227 
TestNopsAdrpAdd(size_t num_nops,uint32_t string_offset)228   void TestNopsAdrpAdd(size_t num_nops, uint32_t string_offset) {
229     constexpr uint32_t kStringIndex = 1u;
230     string_index_to_offset_map_.Put(kStringIndex, string_offset);
231     auto code = GenNopsAndAdrpAdd(num_nops, 0u, 0u);  // Unpatched.
232     LinkerPatch patches[] = {
233         LinkerPatch::RelativeStringPatch(num_nops * 4u     , nullptr, num_nops * 4u, kStringIndex),
234         LinkerPatch::RelativeStringPatch(num_nops * 4u + 4u, nullptr, num_nops * 4u, kStringIndex),
235     };
236     AddCompiledMethod(MethodRef(1u),
237                       ArrayRef<const uint8_t>(code),
238                       ArrayRef<const LinkerPatch>(patches));
239     Link();
240 
241     uint32_t method1_offset = GetMethodOffset(1u);
242     auto expected_code = GenNopsAndAdrpAdd(num_nops, method1_offset, string_offset);
243     EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
244   }
245 
InsertInsn(std::vector<uint8_t> * code,size_t pos,uint32_t insn)246   void InsertInsn(std::vector<uint8_t>* code, size_t pos, uint32_t insn) {
247     CHECK_LE(pos, code->size());
248     const uint8_t insn_code[] = {
249         static_cast<uint8_t>(insn), static_cast<uint8_t>(insn >> 8),
250         static_cast<uint8_t>(insn >> 16), static_cast<uint8_t>(insn >> 24),
251     };
252     static_assert(sizeof(insn_code) == 4u, "Invalid sizeof(insn_code).");
253     code->insert(code->begin() + pos, insn_code, insn_code + sizeof(insn_code));
254   }
255 
PrepareNopsAdrpInsn2Ldr(size_t num_nops,uint32_t insn2,uint32_t dex_cache_arrays_begin,uint32_t element_offset)256   void PrepareNopsAdrpInsn2Ldr(size_t num_nops,
257                                uint32_t insn2,
258                                uint32_t dex_cache_arrays_begin,
259                                uint32_t element_offset) {
260     dex_cache_arrays_begin_ = dex_cache_arrays_begin;
261     auto code = GenNopsAndAdrpLdr(num_nops, 0u, 0u);  // Unpatched.
262     InsertInsn(&code, num_nops * 4u + 4u, insn2);
263     LinkerPatch patches[] = {
264         LinkerPatch::DexCacheArrayPatch(num_nops * 4u     , nullptr, num_nops * 4u, element_offset),
265         LinkerPatch::DexCacheArrayPatch(num_nops * 4u + 8u, nullptr, num_nops * 4u, element_offset),
266     };
267     AddCompiledMethod(MethodRef(1u),
268                       ArrayRef<const uint8_t>(code),
269                       ArrayRef<const LinkerPatch>(patches));
270     Link();
271   }
272 
PrepareNopsAdrpInsn2Add(size_t num_nops,uint32_t insn2,uint32_t string_offset)273   void PrepareNopsAdrpInsn2Add(size_t num_nops, uint32_t insn2, uint32_t string_offset) {
274     constexpr uint32_t kStringIndex = 1u;
275     string_index_to_offset_map_.Put(kStringIndex, string_offset);
276     auto code = GenNopsAndAdrpAdd(num_nops, 0u, 0u);  // Unpatched.
277     InsertInsn(&code, num_nops * 4u + 4u, insn2);
278     LinkerPatch patches[] = {
279         LinkerPatch::RelativeStringPatch(num_nops * 4u     , nullptr, num_nops * 4u, kStringIndex),
280         LinkerPatch::RelativeStringPatch(num_nops * 4u + 8u, nullptr, num_nops * 4u, kStringIndex),
281     };
282     AddCompiledMethod(MethodRef(1u),
283                       ArrayRef<const uint8_t>(code),
284                       ArrayRef<const LinkerPatch>(patches));
285     Link();
286   }
287 
TestNopsAdrpInsn2AndUse(size_t num_nops,uint32_t insn2,uint32_t target_offset,uint32_t use_insn)288   void TestNopsAdrpInsn2AndUse(size_t num_nops,
289                                uint32_t insn2,
290                                uint32_t target_offset,
291                                uint32_t use_insn) {
292     uint32_t method1_offset = GetMethodOffset(1u);
293     auto expected_code = GenNopsAndAdrpAndUse(num_nops, method1_offset, target_offset, use_insn);
294     InsertInsn(&expected_code, num_nops * 4u + 4u, insn2);
295     EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
296   }
297 
TestNopsAdrpInsn2AndUseHasThunk(size_t num_nops,uint32_t insn2,uint32_t target_offset,uint32_t use_insn)298   void TestNopsAdrpInsn2AndUseHasThunk(size_t num_nops,
299                                        uint32_t insn2,
300                                        uint32_t target_offset,
301                                        uint32_t use_insn) {
302     uint32_t method1_offset = GetMethodOffset(1u);
303     CHECK(!compiled_method_refs_.empty());
304     CHECK_EQ(compiled_method_refs_[0].dex_method_index, 1u);
305     CHECK_EQ(compiled_method_refs_.size(), compiled_methods_.size());
306     uint32_t method1_size = compiled_methods_[0]->GetQuickCode().size();
307     uint32_t thunk_offset = CompiledCode::AlignCode(method1_offset + method1_size, kArm64);
308     uint32_t b_diff = thunk_offset - (method1_offset + num_nops * 4u);
309     CHECK_ALIGNED(b_diff, 4u);
310     ASSERT_LT(b_diff, 128 * MB);
311     uint32_t b_out = kBPlus0 + ((b_diff >> 2) & 0x03ffffffu);
312     uint32_t b_in = kBPlus0 + ((-b_diff >> 2) & 0x03ffffffu);
313 
314     auto expected_code = GenNopsAndAdrpAndUse(num_nops, method1_offset, target_offset, use_insn);
315     InsertInsn(&expected_code, num_nops * 4u + 4u, insn2);
316     // Replace adrp with bl.
317     expected_code.erase(expected_code.begin() + num_nops * 4u,
318                         expected_code.begin() + num_nops * 4u + 4u);
319     InsertInsn(&expected_code, num_nops * 4u, b_out);
320     EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
321 
322     auto expected_thunk_code = GenNopsAndAdrpLdr(0u, thunk_offset, target_offset);
323     ASSERT_EQ(expected_thunk_code.size(), 8u);
324     expected_thunk_code.erase(expected_thunk_code.begin() + 4u, expected_thunk_code.begin() + 8u);
325     InsertInsn(&expected_thunk_code, 4u, b_in);
326     ASSERT_EQ(expected_thunk_code.size(), 8u);
327 
328     uint32_t thunk_size = ThunkSize();
329     ASSERT_EQ(thunk_offset + thunk_size, output_.size());
330     ASSERT_EQ(thunk_size, expected_thunk_code.size());
331     ArrayRef<const uint8_t> thunk_code(&output_[thunk_offset], thunk_size);
332     if (ArrayRef<const uint8_t>(expected_thunk_code) != thunk_code) {
333       DumpDiff(ArrayRef<const uint8_t>(expected_thunk_code), thunk_code);
334       FAIL();
335     }
336   }
337 
TestAdrpInsn2Ldr(uint32_t insn2,uint32_t adrp_offset,bool has_thunk,uint32_t dex_cache_arrays_begin,uint32_t element_offset)338   void TestAdrpInsn2Ldr(uint32_t insn2,
339                         uint32_t adrp_offset,
340                         bool has_thunk,
341                         uint32_t dex_cache_arrays_begin,
342                         uint32_t element_offset) {
343     uint32_t method1_offset =
344         CompiledCode::AlignCode(kTrampolineSize, kArm64) + sizeof(OatQuickMethodHeader);
345     ASSERT_LT(method1_offset, adrp_offset);
346     CHECK_ALIGNED(adrp_offset, 4u);
347     uint32_t num_nops = (adrp_offset - method1_offset) / 4u;
348     PrepareNopsAdrpInsn2Ldr(num_nops, insn2, dex_cache_arrays_begin, element_offset);
349     uint32_t target_offset = dex_cache_arrays_begin_ + element_offset;
350     if (has_thunk) {
351       TestNopsAdrpInsn2AndUseHasThunk(num_nops, insn2, target_offset, kLdrWInsn);
352     } else {
353       TestNopsAdrpInsn2AndUse(num_nops, insn2, target_offset, kLdrWInsn);
354     }
355     ASSERT_EQ(method1_offset, GetMethodOffset(1u));  // If this fails, num_nops is wrong.
356   }
357 
TestAdrpLdurLdr(uint32_t adrp_offset,bool has_thunk,uint32_t dex_cache_arrays_begin,uint32_t element_offset)358   void TestAdrpLdurLdr(uint32_t adrp_offset,
359                        bool has_thunk,
360                        uint32_t dex_cache_arrays_begin,
361                        uint32_t element_offset) {
362     TestAdrpInsn2Ldr(kLdurInsn, adrp_offset, has_thunk, dex_cache_arrays_begin, element_offset);
363   }
364 
TestAdrpLdrPcRelLdr(uint32_t pcrel_ldr_insn,int32_t pcrel_disp,uint32_t adrp_offset,bool has_thunk,uint32_t dex_cache_arrays_begin,uint32_t element_offset)365   void TestAdrpLdrPcRelLdr(uint32_t pcrel_ldr_insn,
366                            int32_t pcrel_disp,
367                            uint32_t adrp_offset,
368                            bool has_thunk,
369                            uint32_t dex_cache_arrays_begin,
370                            uint32_t element_offset) {
371     ASSERT_LT(pcrel_disp, 0x100000);
372     ASSERT_GE(pcrel_disp, -0x100000);
373     ASSERT_EQ(pcrel_disp & 0x3, 0);
374     uint32_t insn2 = pcrel_ldr_insn | (((static_cast<uint32_t>(pcrel_disp) >> 2) & 0x7ffffu) << 5);
375     TestAdrpInsn2Ldr(insn2, adrp_offset, has_thunk, dex_cache_arrays_begin, element_offset);
376   }
377 
TestAdrpLdrSpRelLdr(uint32_t sprel_ldr_insn,uint32_t sprel_disp_in_load_units,uint32_t adrp_offset,bool has_thunk,uint32_t dex_cache_arrays_begin,uint32_t element_offset)378   void TestAdrpLdrSpRelLdr(uint32_t sprel_ldr_insn,
379                            uint32_t sprel_disp_in_load_units,
380                            uint32_t adrp_offset,
381                            bool has_thunk,
382                            uint32_t dex_cache_arrays_begin,
383                            uint32_t element_offset) {
384     ASSERT_LT(sprel_disp_in_load_units, 0x1000u);
385     uint32_t insn2 = sprel_ldr_insn | ((sprel_disp_in_load_units & 0xfffu) << 10);
386     TestAdrpInsn2Ldr(insn2, adrp_offset, has_thunk, dex_cache_arrays_begin, element_offset);
387   }
388 
TestAdrpInsn2Add(uint32_t insn2,uint32_t adrp_offset,bool has_thunk,uint32_t string_offset)389   void TestAdrpInsn2Add(uint32_t insn2,
390                         uint32_t adrp_offset,
391                         bool has_thunk,
392                         uint32_t string_offset) {
393     uint32_t method1_offset =
394         CompiledCode::AlignCode(kTrampolineSize, kArm64) + sizeof(OatQuickMethodHeader);
395     ASSERT_LT(method1_offset, adrp_offset);
396     CHECK_ALIGNED(adrp_offset, 4u);
397     uint32_t num_nops = (adrp_offset - method1_offset) / 4u;
398     PrepareNopsAdrpInsn2Add(num_nops, insn2, string_offset);
399     if (has_thunk) {
400       TestNopsAdrpInsn2AndUseHasThunk(num_nops, insn2, string_offset, kAddXInsn);
401     } else {
402       TestNopsAdrpInsn2AndUse(num_nops, insn2, string_offset, kAddXInsn);
403     }
404     ASSERT_EQ(method1_offset, GetMethodOffset(1u));  // If this fails, num_nops is wrong.
405   }
406 
TestAdrpLdurAdd(uint32_t adrp_offset,bool has_thunk,uint32_t string_offset)407   void TestAdrpLdurAdd(uint32_t adrp_offset, bool has_thunk, uint32_t string_offset) {
408     TestAdrpInsn2Add(kLdurInsn, adrp_offset, has_thunk, string_offset);
409   }
410 
TestAdrpLdrPcRelAdd(uint32_t pcrel_ldr_insn,int32_t pcrel_disp,uint32_t adrp_offset,bool has_thunk,uint32_t string_offset)411   void TestAdrpLdrPcRelAdd(uint32_t pcrel_ldr_insn,
412                            int32_t pcrel_disp,
413                            uint32_t adrp_offset,
414                            bool has_thunk,
415                            uint32_t string_offset) {
416     ASSERT_LT(pcrel_disp, 0x100000);
417     ASSERT_GE(pcrel_disp, -0x100000);
418     ASSERT_EQ(pcrel_disp & 0x3, 0);
419     uint32_t insn2 = pcrel_ldr_insn | (((static_cast<uint32_t>(pcrel_disp) >> 2) & 0x7ffffu) << 5);
420     TestAdrpInsn2Add(insn2, adrp_offset, has_thunk, string_offset);
421   }
422 
TestAdrpLdrSpRelAdd(uint32_t sprel_ldr_insn,uint32_t sprel_disp_in_load_units,uint32_t adrp_offset,bool has_thunk,uint32_t string_offset)423   void TestAdrpLdrSpRelAdd(uint32_t sprel_ldr_insn,
424                            uint32_t sprel_disp_in_load_units,
425                            uint32_t adrp_offset,
426                            bool has_thunk,
427                            uint32_t string_offset) {
428     ASSERT_LT(sprel_disp_in_load_units, 0x1000u);
429     uint32_t insn2 = sprel_ldr_insn | ((sprel_disp_in_load_units & 0xfffu) << 10);
430     TestAdrpInsn2Add(insn2, adrp_offset, has_thunk, string_offset);
431   }
432 };
433 
434 const uint8_t Arm64RelativePatcherTest::kCallRawCode[] = {
435     0x00, 0x00, 0x00, 0x94
436 };
437 
438 const ArrayRef<const uint8_t> Arm64RelativePatcherTest::kCallCode(kCallRawCode);
439 
440 const uint8_t Arm64RelativePatcherTest::kNopRawCode[] = {
441     0x1f, 0x20, 0x03, 0xd5
442 };
443 
444 const ArrayRef<const uint8_t> Arm64RelativePatcherTest::kNopCode(kNopRawCode);
445 
446 class Arm64RelativePatcherTestDefault : public Arm64RelativePatcherTest {
447  public:
Arm64RelativePatcherTestDefault()448   Arm64RelativePatcherTestDefault() : Arm64RelativePatcherTest("default") { }
449 };
450 
451 class Arm64RelativePatcherTestDenver64 : public Arm64RelativePatcherTest {
452  public:
Arm64RelativePatcherTestDenver64()453   Arm64RelativePatcherTestDenver64() : Arm64RelativePatcherTest("denver64") { }
454 };
455 
TEST_F(Arm64RelativePatcherTestDefault,CallSelf)456 TEST_F(Arm64RelativePatcherTestDefault, CallSelf) {
457   LinkerPatch patches[] = {
458       LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
459   };
460   AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
461   Link();
462 
463   static const uint8_t expected_code[] = {
464       0x00, 0x00, 0x00, 0x94
465   };
466   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
467 }
468 
TEST_F(Arm64RelativePatcherTestDefault,CallOther)469 TEST_F(Arm64RelativePatcherTestDefault, CallOther) {
470   LinkerPatch method1_patches[] = {
471       LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
472   };
473   AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(method1_patches));
474   LinkerPatch method2_patches[] = {
475       LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
476   };
477   AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<const LinkerPatch>(method2_patches));
478   Link();
479 
480   uint32_t method1_offset = GetMethodOffset(1u);
481   uint32_t method2_offset = GetMethodOffset(2u);
482   uint32_t diff_after = method2_offset - method1_offset;
483   CHECK_ALIGNED(diff_after, 4u);
484   ASSERT_LT(diff_after >> 2, 1u << 8);  // Simple encoding, (diff_after >> 2) fits into 8 bits.
485   static const uint8_t method1_expected_code[] = {
486       static_cast<uint8_t>(diff_after >> 2), 0x00, 0x00, 0x94
487   };
488   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code)));
489   uint32_t diff_before = method1_offset - method2_offset;
490   CHECK_ALIGNED(diff_before, 4u);
491   ASSERT_GE(diff_before, -1u << 27);
492   auto method2_expected_code = GenNopsAndBl(0u, kBlPlus0 | ((diff_before >> 2) & 0x03ffffffu));
493   EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code)));
494 }
495 
TEST_F(Arm64RelativePatcherTestDefault,CallTrampoline)496 TEST_F(Arm64RelativePatcherTestDefault, CallTrampoline) {
497   LinkerPatch patches[] = {
498       LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
499   };
500   AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
501   Link();
502 
503   uint32_t method1_offset = GetMethodOffset(1u);
504   uint32_t diff = kTrampolineOffset - method1_offset;
505   ASSERT_EQ(diff & 1u, 0u);
506   ASSERT_GE(diff, -1u << 9);  // Simple encoding, -256 <= (diff >> 1) < 0 (checked as unsigned).
507   auto expected_code = GenNopsAndBl(0u, kBlPlus0 | ((diff >> 2) & 0x03ffffffu));
508   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
509 }
510 
TEST_F(Arm64RelativePatcherTestDefault,CallTrampolineTooFar)511 TEST_F(Arm64RelativePatcherTestDefault, CallTrampolineTooFar) {
512   constexpr uint32_t missing_method_index = 1024u;
513   auto last_method_raw_code = GenNopsAndBl(1u, kBlPlus0);
514   constexpr uint32_t bl_offset_in_last_method = 1u * 4u;  // After NOPs.
515   ArrayRef<const uint8_t> last_method_code(last_method_raw_code);
516   ASSERT_EQ(bl_offset_in_last_method + 4u, last_method_code.size());
517   LinkerPatch last_method_patches[] = {
518       LinkerPatch::RelativeCodePatch(bl_offset_in_last_method, nullptr, missing_method_index),
519   };
520 
521   constexpr uint32_t just_over_max_negative_disp = 128 * MB + 4;
522   uint32_t last_method_idx = Create2MethodsWithGap(
523       kNopCode, ArrayRef<const LinkerPatch>(), last_method_code,
524       ArrayRef<const LinkerPatch>(last_method_patches),
525       just_over_max_negative_disp - bl_offset_in_last_method);
526   uint32_t method1_offset = GetMethodOffset(1u);
527   uint32_t last_method_offset = GetMethodOffset(last_method_idx);
528   ASSERT_EQ(method1_offset,
529             last_method_offset + bl_offset_in_last_method - just_over_max_negative_disp);
530   ASSERT_FALSE(method_offset_map_.FindMethodOffset(MethodRef(missing_method_index)).first);
531 
532   // Check linked code.
533   uint32_t thunk_offset =
534       CompiledCode::AlignCode(last_method_offset + last_method_code.size(), kArm64);
535   uint32_t diff = thunk_offset - (last_method_offset + bl_offset_in_last_method);
536   CHECK_ALIGNED(diff, 4u);
537   ASSERT_LT(diff, 128 * MB);
538   auto expected_code = GenNopsAndBl(1u, kBlPlus0 | (diff >> 2));
539   EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx),
540                                 ArrayRef<const uint8_t>(expected_code)));
541   EXPECT_TRUE(CheckThunk(thunk_offset));
542 }
543 
TEST_F(Arm64RelativePatcherTestDefault,CallOtherAlmostTooFarAfter)544 TEST_F(Arm64RelativePatcherTestDefault, CallOtherAlmostTooFarAfter) {
545   auto method1_raw_code = GenNopsAndBl(1u, kBlPlus0);
546   constexpr uint32_t bl_offset_in_method1 = 1u * 4u;  // After NOPs.
547   ArrayRef<const uint8_t> method1_code(method1_raw_code);
548   ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
549   uint32_t expected_last_method_idx = 65;  // Based on 2MiB chunks in Create2MethodsWithGap().
550   LinkerPatch method1_patches[] = {
551       LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, expected_last_method_idx),
552   };
553 
554   constexpr uint32_t max_positive_disp = 128 * MB - 4u;
555   uint32_t last_method_idx = Create2MethodsWithGap(method1_code,
556                                                    ArrayRef<const LinkerPatch>(method1_patches),
557                                                    kNopCode,
558                                                    ArrayRef<const LinkerPatch>(),
559                                                    bl_offset_in_method1 + max_positive_disp);
560   ASSERT_EQ(expected_last_method_idx, last_method_idx);
561 
562   uint32_t method1_offset = GetMethodOffset(1u);
563   uint32_t last_method_offset = GetMethodOffset(last_method_idx);
564   ASSERT_EQ(method1_offset + bl_offset_in_method1 + max_positive_disp, last_method_offset);
565 
566   // Check linked code.
567   auto expected_code = GenNopsAndBl(1u, kBlPlusMax);
568   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
569 }
570 
TEST_F(Arm64RelativePatcherTestDefault,CallOtherAlmostTooFarBefore)571 TEST_F(Arm64RelativePatcherTestDefault, CallOtherAlmostTooFarBefore) {
572   auto last_method_raw_code = GenNopsAndBl(0u, kBlPlus0);
573   constexpr uint32_t bl_offset_in_last_method = 0u * 4u;  // After NOPs.
574   ArrayRef<const uint8_t> last_method_code(last_method_raw_code);
575   ASSERT_EQ(bl_offset_in_last_method + 4u, last_method_code.size());
576   LinkerPatch last_method_patches[] = {
577       LinkerPatch::RelativeCodePatch(bl_offset_in_last_method, nullptr, 1u),
578   };
579 
580   constexpr uint32_t max_negative_disp = 128 * MB;
581   uint32_t last_method_idx = Create2MethodsWithGap(kNopCode,
582                                                    ArrayRef<const LinkerPatch>(),
583                                                    last_method_code,
584                                                    ArrayRef<const LinkerPatch>(last_method_patches),
585                                                    max_negative_disp - bl_offset_in_last_method);
586   uint32_t method1_offset = GetMethodOffset(1u);
587   uint32_t last_method_offset = GetMethodOffset(last_method_idx);
588   ASSERT_EQ(method1_offset, last_method_offset + bl_offset_in_last_method - max_negative_disp);
589 
590   // Check linked code.
591   auto expected_code = GenNopsAndBl(0u, kBlMinusMax);
592   EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx),
593                                 ArrayRef<const uint8_t>(expected_code)));
594 }
595 
TEST_F(Arm64RelativePatcherTestDefault,CallOtherJustTooFarAfter)596 TEST_F(Arm64RelativePatcherTestDefault, CallOtherJustTooFarAfter) {
597   auto method1_raw_code = GenNopsAndBl(0u, kBlPlus0);
598   constexpr uint32_t bl_offset_in_method1 = 0u * 4u;  // After NOPs.
599   ArrayRef<const uint8_t> method1_code(method1_raw_code);
600   ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
601   uint32_t expected_last_method_idx = 65;  // Based on 2MiB chunks in Create2MethodsWithGap().
602   LinkerPatch method1_patches[] = {
603       LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, expected_last_method_idx),
604   };
605 
606   constexpr uint32_t just_over_max_positive_disp = 128 * MB;
607   uint32_t last_method_idx = Create2MethodsWithGap(
608       method1_code,
609       ArrayRef<const LinkerPatch>(method1_patches),
610       kNopCode,
611       ArrayRef<const LinkerPatch>(),
612       bl_offset_in_method1 + just_over_max_positive_disp);
613   ASSERT_EQ(expected_last_method_idx, last_method_idx);
614 
615   uint32_t method1_offset = GetMethodOffset(1u);
616   uint32_t last_method_offset = GetMethodOffset(last_method_idx);
617   uint32_t last_method_header_offset = last_method_offset - sizeof(OatQuickMethodHeader);
618   ASSERT_TRUE(IsAligned<kArm64Alignment>(last_method_header_offset));
619   uint32_t thunk_offset = last_method_header_offset - CompiledCode::AlignCode(ThunkSize(), kArm64);
620   ASSERT_TRUE(IsAligned<kArm64Alignment>(thunk_offset));
621   uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1);
622   CHECK_ALIGNED(diff, 4u);
623   ASSERT_LT(diff, 128 * MB);
624   auto expected_code = GenNopsAndBl(0u, kBlPlus0 | (diff >> 2));
625   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
626   CheckThunk(thunk_offset);
627 }
628 
TEST_F(Arm64RelativePatcherTestDefault,CallOtherJustTooFarBefore)629 TEST_F(Arm64RelativePatcherTestDefault, CallOtherJustTooFarBefore) {
630   auto last_method_raw_code = GenNopsAndBl(1u, kBlPlus0);
631   constexpr uint32_t bl_offset_in_last_method = 1u * 4u;  // After NOPs.
632   ArrayRef<const uint8_t> last_method_code(last_method_raw_code);
633   ASSERT_EQ(bl_offset_in_last_method + 4u, last_method_code.size());
634   LinkerPatch last_method_patches[] = {
635       LinkerPatch::RelativeCodePatch(bl_offset_in_last_method, nullptr, 1u),
636   };
637 
638   constexpr uint32_t just_over_max_negative_disp = 128 * MB + 4;
639   uint32_t last_method_idx = Create2MethodsWithGap(
640       kNopCode, ArrayRef<const LinkerPatch>(), last_method_code,
641       ArrayRef<const LinkerPatch>(last_method_patches),
642       just_over_max_negative_disp - bl_offset_in_last_method);
643   uint32_t method1_offset = GetMethodOffset(1u);
644   uint32_t last_method_offset = GetMethodOffset(last_method_idx);
645   ASSERT_EQ(method1_offset,
646             last_method_offset + bl_offset_in_last_method - just_over_max_negative_disp);
647 
648   // Check linked code.
649   uint32_t thunk_offset =
650       CompiledCode::AlignCode(last_method_offset + last_method_code.size(), kArm64);
651   uint32_t diff = thunk_offset - (last_method_offset + bl_offset_in_last_method);
652   CHECK_ALIGNED(diff, 4u);
653   ASSERT_LT(diff, 128 * MB);
654   auto expected_code = GenNopsAndBl(1u, kBlPlus0 | (diff >> 2));
655   EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx),
656                                 ArrayRef<const uint8_t>(expected_code)));
657   EXPECT_TRUE(CheckThunk(thunk_offset));
658 }
659 
TEST_F(Arm64RelativePatcherTestDefault,DexCacheReference1)660 TEST_F(Arm64RelativePatcherTestDefault, DexCacheReference1) {
661   TestNopsAdrpLdr(0u, 0x12345678u, 0x1234u);
662 }
663 
TEST_F(Arm64RelativePatcherTestDefault,DexCacheReference2)664 TEST_F(Arm64RelativePatcherTestDefault, DexCacheReference2) {
665   TestNopsAdrpLdr(0u, -0x12345678u, 0x4444u);
666 }
667 
TEST_F(Arm64RelativePatcherTestDefault,DexCacheReference3)668 TEST_F(Arm64RelativePatcherTestDefault, DexCacheReference3) {
669   TestNopsAdrpLdr(0u, 0x12345000u, 0x3ffcu);
670 }
671 
TEST_F(Arm64RelativePatcherTestDefault,DexCacheReference4)672 TEST_F(Arm64RelativePatcherTestDefault, DexCacheReference4) {
673   TestNopsAdrpLdr(0u, 0x12345000u, 0x4000u);
674 }
675 
TEST_F(Arm64RelativePatcherTestDefault,StringReference1)676 TEST_F(Arm64RelativePatcherTestDefault, StringReference1) {
677   TestNopsAdrpAdd(0u, 0x12345678u);
678 }
679 
TEST_F(Arm64RelativePatcherTestDefault,StringReference2)680 TEST_F(Arm64RelativePatcherTestDefault, StringReference2) {
681   TestNopsAdrpAdd(0u, -0x12345678u);
682 }
683 
TEST_F(Arm64RelativePatcherTestDefault,StringReference3)684 TEST_F(Arm64RelativePatcherTestDefault, StringReference3) {
685   TestNopsAdrpAdd(0u, 0x12345000u);
686 }
687 
TEST_F(Arm64RelativePatcherTestDefault,StringReference4)688 TEST_F(Arm64RelativePatcherTestDefault, StringReference4) {
689   TestNopsAdrpAdd(0u, 0x12345ffcu);
690 }
691 
692 #define TEST_FOR_OFFSETS(test, disp1, disp2) \
693   test(0xff4u, disp1) test(0xff8u, disp1) test(0xffcu, disp1) test(0x1000u, disp1) \
694   test(0xff4u, disp2) test(0xff8u, disp2) test(0xffcu, disp2) test(0x1000u, disp2)
695 
696 #define DEFAULT_LDUR_LDR_TEST(adrp_offset, disp) \
697   TEST_F(Arm64RelativePatcherTestDefault, DexCacheReference ## adrp_offset ## Ldur ## disp) { \
698     bool has_thunk = (adrp_offset == 0xff8u || adrp_offset == 0xffcu); \
699     TestAdrpLdurLdr(adrp_offset, has_thunk, 0x12345678u, disp); \
700   }
701 
702 TEST_FOR_OFFSETS(DEFAULT_LDUR_LDR_TEST, 0x1234, 0x1238)
703 
704 #define DENVER64_LDUR_LDR_TEST(adrp_offset, disp) \
705   TEST_F(Arm64RelativePatcherTestDenver64, DexCacheReference ## adrp_offset ## Ldur ## disp) { \
706     TestAdrpLdurLdr(adrp_offset, false, 0x12345678u, disp); \
707   }
708 
709 TEST_FOR_OFFSETS(DENVER64_LDUR_LDR_TEST, 0x1234, 0x1238)
710 
711 // LDR <Wt>, <label> is always aligned. We should never have to use a fixup.
712 #define LDRW_PCREL_LDR_TEST(adrp_offset, disp) \
713   TEST_F(Arm64RelativePatcherTestDefault, DexCacheReference ## adrp_offset ## WPcRel ## disp) { \
714     TestAdrpLdrPcRelLdr(kLdrWPcRelInsn, disp, adrp_offset, false, 0x12345678u, 0x1234u); \
715   }
716 
717 TEST_FOR_OFFSETS(LDRW_PCREL_LDR_TEST, 0x1234, 0x1238)
718 
719 // LDR <Xt>, <label> is aligned when offset + displacement is a multiple of 8.
720 #define LDRX_PCREL_LDR_TEST(adrp_offset, disp) \
721   TEST_F(Arm64RelativePatcherTestDefault, DexCacheReference ## adrp_offset ## XPcRel ## disp) { \
722     bool unaligned = !IsAligned<8u>(adrp_offset + 4u + static_cast<uint32_t>(disp)); \
723     bool has_thunk = (adrp_offset == 0xff8u || adrp_offset == 0xffcu) && unaligned; \
724     TestAdrpLdrPcRelLdr(kLdrXPcRelInsn, disp, adrp_offset, has_thunk, 0x12345678u, 0x1234u); \
725   }
726 
727 TEST_FOR_OFFSETS(LDRX_PCREL_LDR_TEST, 0x1234, 0x1238)
728 
729 // LDR <Wt>, [SP, #<pimm>] and LDR <Xt>, [SP, #<pimm>] are always aligned. No fixup needed.
730 #define LDRW_SPREL_LDR_TEST(adrp_offset, disp) \
731   TEST_F(Arm64RelativePatcherTestDefault, DexCacheReference ## adrp_offset ## WSpRel ## disp) { \
732     TestAdrpLdrSpRelLdr(kLdrWSpRelInsn, disp >> 2, adrp_offset, false, 0x12345678u, 0x1234u); \
733   }
734 
735 TEST_FOR_OFFSETS(LDRW_SPREL_LDR_TEST, 0, 4)
736 
737 #define LDRX_SPREL_LDR_TEST(adrp_offset, disp) \
738   TEST_F(Arm64RelativePatcherTestDefault, DexCacheReference ## adrp_offset ## XSpRel ## disp) { \
739     TestAdrpLdrSpRelLdr(kLdrXSpRelInsn, disp >> 3, adrp_offset, false, 0x12345678u, 0x1234u); \
740   }
741 
742 TEST_FOR_OFFSETS(LDRX_SPREL_LDR_TEST, 0, 8)
743 
744 #define DEFAULT_LDUR_ADD_TEST(adrp_offset, disp) \
745   TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## Ldur ## disp) { \
746     bool has_thunk = (adrp_offset == 0xff8u || adrp_offset == 0xffcu); \
747     TestAdrpLdurAdd(adrp_offset, has_thunk, disp); \
748   }
749 
750 TEST_FOR_OFFSETS(DEFAULT_LDUR_ADD_TEST, 0x12345678, 0xffffc840)
751 
752 #define DENVER64_LDUR_ADD_TEST(adrp_offset, disp) \
753   TEST_F(Arm64RelativePatcherTestDenver64, StringReference ## adrp_offset ## Ldur ## disp) { \
754     TestAdrpLdurAdd(adrp_offset, false, disp); \
755   }
756 
757 TEST_FOR_OFFSETS(DENVER64_LDUR_ADD_TEST, 0x12345678, 0xffffc840)
758 
759 #define DEFAULT_SUBX3X2_ADD_TEST(adrp_offset, disp) \
760   TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## SubX3X2 ## disp) { \
761     /* SUB unrelated to "ADRP x0, addr". */ \
762     uint32_t sub = kSubXInsn | (100 << 10) | (2u << 5) | 3u;  /* SUB x3, x2, #100 */ \
763     TestAdrpInsn2Add(sub, adrp_offset, false, disp); \
764   }
765 
766 TEST_FOR_OFFSETS(DEFAULT_SUBX3X2_ADD_TEST, 0x12345678, 0xffffc840)
767 
768 #define DEFAULT_SUBSX3X0_ADD_TEST(adrp_offset, disp) \
769   TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## SubsX3X0 ## disp) { \
770     /* SUBS that uses the result of "ADRP x0, addr". */ \
771     uint32_t subs = kSubsXInsn | (100 << 10) | (0u << 5) | 3u;  /* SUBS x3, x0, #100 */ \
772     TestAdrpInsn2Add(subs, adrp_offset, false, disp); \
773   }
774 
775 TEST_FOR_OFFSETS(DEFAULT_SUBSX3X0_ADD_TEST, 0x12345678, 0xffffc840)
776 
777 #define DEFAULT_ADDX0X0_ADD_TEST(adrp_offset, disp) \
778   TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## AddX0X0 ## disp) { \
779     /* ADD that uses the result register of "ADRP x0, addr" as both source and destination. */ \
780     uint32_t add = kSubXInsn | (100 << 10) | (0u << 5) | 0u;  /* ADD x0, x0, #100 */ \
781     TestAdrpInsn2Add(add, adrp_offset, false, disp); \
782   }
783 
784 TEST_FOR_OFFSETS(DEFAULT_ADDX0X0_ADD_TEST, 0x12345678, 0xffffc840)
785 
786 #define DEFAULT_ADDSX0X2_ADD_TEST(adrp_offset, disp) \
787   TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## AddsX0X2 ## disp) { \
788     /* ADDS that does not use the result of "ADRP x0, addr" but overwrites that register. */ \
789     uint32_t adds = kAddsXInsn | (100 << 10) | (2u << 5) | 0u;  /* ADDS x0, x2, #100 */ \
790     bool has_thunk = (adrp_offset == 0xff8u || adrp_offset == 0xffcu); \
791     TestAdrpInsn2Add(adds, adrp_offset, has_thunk, disp); \
792   }
793 
794 TEST_FOR_OFFSETS(DEFAULT_ADDSX0X2_ADD_TEST, 0x12345678, 0xffffc840)
795 
796 // LDR <Wt>, <label> is always aligned. We should never have to use a fixup.
797 #define LDRW_PCREL_ADD_TEST(adrp_offset, disp) \
798   TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## WPcRel ## disp) { \
799     TestAdrpLdrPcRelAdd(kLdrWPcRelInsn, disp, adrp_offset, false, 0x12345678u); \
800   }
801 
802 TEST_FOR_OFFSETS(LDRW_PCREL_ADD_TEST, 0x1234, 0x1238)
803 
804 // LDR <Xt>, <label> is aligned when offset + displacement is a multiple of 8.
805 #define LDRX_PCREL_ADD_TEST(adrp_offset, disp) \
806   TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## XPcRel ## disp) { \
807     bool unaligned = !IsAligned<8u>(adrp_offset + 4u + static_cast<uint32_t>(disp)); \
808     bool has_thunk = (adrp_offset == 0xff8u || adrp_offset == 0xffcu) && unaligned; \
809     TestAdrpLdrPcRelAdd(kLdrXPcRelInsn, disp, adrp_offset, has_thunk, 0x12345678u); \
810   }
811 
812 TEST_FOR_OFFSETS(LDRX_PCREL_ADD_TEST, 0x1234, 0x1238)
813 
814 // LDR <Wt>, [SP, #<pimm>] and LDR <Xt>, [SP, #<pimm>] are always aligned. No fixup needed.
815 #define LDRW_SPREL_ADD_TEST(adrp_offset, disp) \
816   TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## WSpRel ## disp) { \
817     TestAdrpLdrSpRelAdd(kLdrWSpRelInsn, disp >> 2, adrp_offset, false, 0x12345678u); \
818   }
819 
820 TEST_FOR_OFFSETS(LDRW_SPREL_ADD_TEST, 0, 4)
821 
822 #define LDRX_SPREL_ADD_TEST(adrp_offset, disp) \
823   TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## XSpRel ## disp) { \
824     TestAdrpLdrSpRelAdd(kLdrXSpRelInsn, disp >> 3, adrp_offset, false, 0x12345678u); \
825   }
826 
827 TEST_FOR_OFFSETS(LDRX_SPREL_ADD_TEST, 0, 8)
828 
829 }  // namespace linker
830 }  // namespace art
831