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