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