blob: eace3d4326a80b290f402244f62d2cbcbeb3ca54 [file] [log] [blame]
Vladimir Marko4d23c9d2015-04-01 23:03:09 +01001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "linker/relative_patcher_test.h"
18#include "linker/arm/relative_patcher_thumb2.h"
Nicolas Geoffray524e7ea2015-10-16 17:13:34 +010019#include "oat_quick_method_header.h"
Vladimir Marko4d23c9d2015-04-01 23:03:09 +010020
21namespace art {
22namespace linker {
23
24class Thumb2RelativePatcherTest : public RelativePatcherTest {
25 public:
26 Thumb2RelativePatcherTest() : RelativePatcherTest(kThumb2, "default") { }
27
28 protected:
29 static const uint8_t kCallRawCode[];
30 static const ArrayRef<const uint8_t> kCallCode;
31 static const uint8_t kNopRawCode[];
32 static const ArrayRef<const uint8_t> kNopCode;
Vladimir Markocac5a7e2016-02-22 10:39:50 +000033 static const uint8_t kUnpatchedPcRelativeRawCode[];
34 static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode;
35 static const uint32_t kPcInsnOffset;
Vladimir Marko4d23c9d2015-04-01 23:03:09 +010036
37 // Branches within range [-256, 256) can be created from these by adding the low 8 bits.
38 static constexpr uint32_t kBlPlus0 = 0xf000f800;
39 static constexpr uint32_t kBlMinus256 = 0xf7ffff00;
40
41 // Special BL values.
42 static constexpr uint32_t kBlPlusMax = 0xf3ffd7ff;
43 static constexpr uint32_t kBlMinusMax = 0xf400d000;
44
45 bool Create2MethodsWithGap(const ArrayRef<const uint8_t>& method1_code,
Vladimir Markob207e142015-04-02 21:25:21 +010046 const ArrayRef<const LinkerPatch>& method1_patches,
Vladimir Marko4d23c9d2015-04-01 23:03:09 +010047 const ArrayRef<const uint8_t>& method3_code,
Vladimir Markob207e142015-04-02 21:25:21 +010048 const ArrayRef<const LinkerPatch>& method3_patches,
Vladimir Marko4d23c9d2015-04-01 23:03:09 +010049 uint32_t distance_without_thunks) {
50 CHECK_EQ(distance_without_thunks % kArmAlignment, 0u);
Vladimir Marko0c737df2016-08-01 16:33:16 +010051 uint32_t method1_offset =
52 kTrampolineSize + CodeAlignmentSize(kTrampolineSize) + sizeof(OatQuickMethodHeader);
Vladimir Markob207e142015-04-02 21:25:21 +010053 AddCompiledMethod(MethodRef(1u), method1_code, method1_patches);
Vladimir Marko4d23c9d2015-04-01 23:03:09 +010054
55 // We want to put the method3 at a very precise offset.
56 const uint32_t method3_offset = method1_offset + distance_without_thunks;
Vladimir Marko0c737df2016-08-01 16:33:16 +010057 CHECK_ALIGNED(method3_offset, kArmAlignment);
Vladimir Marko4d23c9d2015-04-01 23:03:09 +010058
59 // Calculate size of method2 so that we put method3 at the correct place.
Vladimir Marko0c737df2016-08-01 16:33:16 +010060 const uint32_t method1_end = method1_offset + method1_code.size();
Vladimir Marko4d23c9d2015-04-01 23:03:09 +010061 const uint32_t method2_offset =
Vladimir Marko0c737df2016-08-01 16:33:16 +010062 method1_end + CodeAlignmentSize(method1_end) + sizeof(OatQuickMethodHeader);
Vladimir Marko4d23c9d2015-04-01 23:03:09 +010063 const uint32_t method2_size = (method3_offset - sizeof(OatQuickMethodHeader) - method2_offset);
64 std::vector<uint8_t> method2_raw_code(method2_size);
65 ArrayRef<const uint8_t> method2_code(method2_raw_code);
Vladimir Markob207e142015-04-02 21:25:21 +010066 AddCompiledMethod(MethodRef(2u), method2_code, ArrayRef<const LinkerPatch>());
Vladimir Marko4d23c9d2015-04-01 23:03:09 +010067
68 AddCompiledMethod(MethodRef(3u), method3_code, method3_patches);
69
70 Link();
71
72 // Check assumptions.
73 CHECK_EQ(GetMethodOffset(1), method1_offset);
74 CHECK_EQ(GetMethodOffset(2), method2_offset);
75 auto result3 = method_offset_map_.FindMethodOffset(MethodRef(3));
76 CHECK(result3.first);
77 // There may be a thunk before method2.
78 if (result3.second == method3_offset + 1 /* thumb mode */) {
79 return false; // No thunk.
80 } else {
Vladimir Marko0c737df2016-08-01 16:33:16 +010081 uint32_t thunk_end =
82 CompiledCode::AlignCode(method3_offset - sizeof(OatQuickMethodHeader), kThumb2) +
83 ThunkSize();
84 uint32_t header_offset = thunk_end + CodeAlignmentSize(thunk_end);
85 CHECK_EQ(result3.second, header_offset + sizeof(OatQuickMethodHeader) + 1 /* thumb mode */);
Vladimir Marko4d23c9d2015-04-01 23:03:09 +010086 return true; // Thunk present.
87 }
88 }
89
90 uint32_t GetMethodOffset(uint32_t method_idx) {
91 auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
92 CHECK(result.first);
93 CHECK_NE(result.second & 1u, 0u);
94 return result.second - 1 /* thumb mode */;
95 }
96
97 uint32_t ThunkSize() {
98 return static_cast<Thumb2RelativePatcher*>(patcher_.get())->thunk_code_.size();
99 }
100
101 bool CheckThunk(uint32_t thunk_offset) {
102 Thumb2RelativePatcher* patcher = static_cast<Thumb2RelativePatcher*>(patcher_.get());
103 ArrayRef<const uint8_t> expected_code(patcher->thunk_code_);
104 if (output_.size() < thunk_offset + expected_code.size()) {
105 LOG(ERROR) << "output_.size() == " << output_.size() << " < "
106 << "thunk_offset + expected_code.size() == " << (thunk_offset + expected_code.size());
107 return false;
108 }
109 ArrayRef<const uint8_t> linked_code(&output_[thunk_offset], expected_code.size());
110 if (linked_code == expected_code) {
111 return true;
112 }
113 // Log failure info.
114 DumpDiff(expected_code, linked_code);
115 return false;
116 }
117
118 std::vector<uint8_t> GenNopsAndBl(size_t num_nops, uint32_t bl) {
119 std::vector<uint8_t> result;
120 result.reserve(num_nops * 2u + 4u);
121 for (size_t i = 0; i != num_nops; ++i) {
122 result.push_back(0x00);
123 result.push_back(0xbf);
124 }
125 result.push_back(static_cast<uint8_t>(bl >> 16));
126 result.push_back(static_cast<uint8_t>(bl >> 24));
127 result.push_back(static_cast<uint8_t>(bl));
128 result.push_back(static_cast<uint8_t>(bl >> 8));
129 return result;
130 }
Vladimir Markoe5c76c52015-04-06 12:10:19 +0100131
Vladimir Markocac5a7e2016-02-22 10:39:50 +0000132 void TestDexCacheReference(uint32_t dex_cache_arrays_begin, uint32_t element_offset);
133 void TestStringReference(uint32_t string_offset);
134 void CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, uint32_t target_offset);
Vladimir Marko4d23c9d2015-04-01 23:03:09 +0100135};
136
137const uint8_t Thumb2RelativePatcherTest::kCallRawCode[] = {
138 0x00, 0xf0, 0x00, 0xf8
139};
140
141const ArrayRef<const uint8_t> Thumb2RelativePatcherTest::kCallCode(kCallRawCode);
142
143const uint8_t Thumb2RelativePatcherTest::kNopRawCode[] = {
144 0x00, 0xbf
145};
146
147const ArrayRef<const uint8_t> Thumb2RelativePatcherTest::kNopCode(kNopRawCode);
148
Vladimir Markocac5a7e2016-02-22 10:39:50 +0000149const uint8_t Thumb2RelativePatcherTest::kUnpatchedPcRelativeRawCode[] = {
150 0x40, 0xf2, 0x00, 0x00, // MOVW r0, #0 (placeholder)
151 0xc0, 0xf2, 0x00, 0x00, // MOVT r0, #0 (placeholder)
152 0x78, 0x44, // ADD r0, pc
153};
154const ArrayRef<const uint8_t> Thumb2RelativePatcherTest::kUnpatchedPcRelativeCode(
155 kUnpatchedPcRelativeRawCode);
156const uint32_t Thumb2RelativePatcherTest::kPcInsnOffset = 8u;
157
158void Thumb2RelativePatcherTest::TestDexCacheReference(uint32_t dex_cache_arrays_begin,
159 uint32_t element_offset) {
160 dex_cache_arrays_begin_ = dex_cache_arrays_begin;
161 LinkerPatch patches[] = {
162 LinkerPatch::DexCacheArrayPatch(0u, nullptr, kPcInsnOffset, element_offset),
163 LinkerPatch::DexCacheArrayPatch(4u, nullptr, kPcInsnOffset, element_offset),
164 };
165 CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches),
166 dex_cache_arrays_begin_ + element_offset);
167}
168
169void Thumb2RelativePatcherTest::TestStringReference(uint32_t string_offset) {
170 constexpr uint32_t kStringIndex = 1u;
171 string_index_to_offset_map_.Put(kStringIndex, string_offset);
172 LinkerPatch patches[] = {
173 LinkerPatch::RelativeStringPatch(0u, nullptr, kPcInsnOffset, kStringIndex),
174 LinkerPatch::RelativeStringPatch(4u, nullptr, kPcInsnOffset, kStringIndex),
175 };
176 CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset);
177}
178
179void Thumb2RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches,
180 uint32_t target_offset) {
181 AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches));
182 Link();
183
184 uint32_t method1_offset = GetMethodOffset(1u);
185 uint32_t pc_base_offset = method1_offset + kPcInsnOffset + 4u /* PC adjustment */;
186 uint32_t diff = target_offset - pc_base_offset;
187 // Distribute the bits of the diff between the MOVW and MOVT:
188 uint32_t diffw = diff & 0xffffu;
189 uint32_t difft = diff >> 16;
190 uint32_t movw = 0xf2400000u | // MOVW r0, #0 (placeholder),
191 ((diffw & 0xf000u) << (16 - 12)) | // move imm4 from bits 12-15 to bits 16-19,
192 ((diffw & 0x0800u) << (26 - 11)) | // move imm from bit 11 to bit 26,
193 ((diffw & 0x0700u) << (12 - 8)) | // move imm3 from bits 8-10 to bits 12-14,
194 ((diffw & 0x00ffu)); // keep imm8 at bits 0-7.
195 uint32_t movt = 0xf2c00000u | // MOVT r0, #0 (placeholder),
196 ((difft & 0xf000u) << (16 - 12)) | // move imm4 from bits 12-15 to bits 16-19,
197 ((difft & 0x0800u) << (26 - 11)) | // move imm from bit 11 to bit 26,
198 ((difft & 0x0700u) << (12 - 8)) | // move imm3 from bits 8-10 to bits 12-14,
199 ((difft & 0x00ffu)); // keep imm8 at bits 0-7.
200 const uint8_t expected_code[] = {
201 static_cast<uint8_t>(movw >> 16), static_cast<uint8_t>(movw >> 24),
202 static_cast<uint8_t>(movw >> 0), static_cast<uint8_t>(movw >> 8),
203 static_cast<uint8_t>(movt >> 16), static_cast<uint8_t>(movt >> 24),
204 static_cast<uint8_t>(movt >> 0), static_cast<uint8_t>(movt >> 8),
205 0x78, 0x44,
206 };
207 EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
208}
209
Vladimir Marko4d23c9d2015-04-01 23:03:09 +0100210TEST_F(Thumb2RelativePatcherTest, CallSelf) {
211 LinkerPatch patches[] = {
212 LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
213 };
Vladimir Markob207e142015-04-02 21:25:21 +0100214 AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
Vladimir Marko4d23c9d2015-04-01 23:03:09 +0100215 Link();
216
217 static const uint8_t expected_code[] = {
218 0xff, 0xf7, 0xfe, 0xff
219 };
220 EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
221}
222
223TEST_F(Thumb2RelativePatcherTest, CallOther) {
224 LinkerPatch method1_patches[] = {
225 LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
226 };
Vladimir Markob207e142015-04-02 21:25:21 +0100227 AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(method1_patches));
Vladimir Marko4d23c9d2015-04-01 23:03:09 +0100228 LinkerPatch method2_patches[] = {
229 LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
230 };
Vladimir Markob207e142015-04-02 21:25:21 +0100231 AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<const LinkerPatch>(method2_patches));
Vladimir Marko4d23c9d2015-04-01 23:03:09 +0100232 Link();
233
234 uint32_t method1_offset = GetMethodOffset(1u);
235 uint32_t method2_offset = GetMethodOffset(2u);
236 uint32_t diff_after = method2_offset - (method1_offset + 4u /* PC adjustment */);
237 ASSERT_EQ(diff_after & 1u, 0u);
238 ASSERT_LT(diff_after >> 1, 1u << 8); // Simple encoding, (diff_after >> 1) fits into 8 bits.
239 static const uint8_t method1_expected_code[] = {
240 0x00, 0xf0, static_cast<uint8_t>(diff_after >> 1), 0xf8
241 };
242 EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code)));
243 uint32_t diff_before = method1_offset - (method2_offset + 4u /* PC adjustment */);
244 ASSERT_EQ(diff_before & 1u, 0u);
245 ASSERT_GE(diff_before, -1u << 9); // Simple encoding, -256 <= (diff >> 1) < 0.
246 auto method2_expected_code = GenNopsAndBl(0u, kBlMinus256 | ((diff_before >> 1) & 0xffu));
247 EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code)));
248}
249
250TEST_F(Thumb2RelativePatcherTest, CallTrampoline) {
251 LinkerPatch patches[] = {
252 LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
253 };
Vladimir Markob207e142015-04-02 21:25:21 +0100254 AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
Vladimir Marko4d23c9d2015-04-01 23:03:09 +0100255 Link();
256
257 uint32_t method1_offset = GetMethodOffset(1u);
258 uint32_t diff = kTrampolineOffset - (method1_offset + 4u);
259 ASSERT_EQ(diff & 1u, 0u);
260 ASSERT_GE(diff, -1u << 9); // Simple encoding, -256 <= (diff >> 1) < 0 (checked as unsigned).
261 auto expected_code = GenNopsAndBl(0u, kBlMinus256 | ((diff >> 1) & 0xffu));
262 EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
263}
264
Vladimir Markod1eaf0d2015-10-29 12:18:29 +0000265TEST_F(Thumb2RelativePatcherTest, CallTrampolineTooFar) {
266 constexpr uint32_t missing_method_index = 1024u;
267 auto method3_raw_code = GenNopsAndBl(3u, kBlPlus0);
268 constexpr uint32_t bl_offset_in_method3 = 3u * 2u; // After NOPs.
269 ArrayRef<const uint8_t> method3_code(method3_raw_code);
270 ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size());
271 LinkerPatch method3_patches[] = {
272 LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, missing_method_index),
273 };
274
275 constexpr uint32_t just_over_max_negative_disp = 16 * MB + 2 - 4u /* PC adjustment */;
276 bool thunk_in_gap = Create2MethodsWithGap(kNopCode,
277 ArrayRef<const LinkerPatch>(),
278 method3_code,
279 ArrayRef<const LinkerPatch>(method3_patches),
280 just_over_max_negative_disp - bl_offset_in_method3);
281 ASSERT_FALSE(thunk_in_gap); // There should be a thunk but it should be after the method2.
282 ASSERT_FALSE(method_offset_map_.FindMethodOffset(MethodRef(missing_method_index)).first);
283
284 // Check linked code.
285 uint32_t method3_offset = GetMethodOffset(3u);
286 uint32_t thunk_offset = CompiledCode::AlignCode(method3_offset + method3_code.size(), kThumb2);
287 uint32_t diff = thunk_offset - (method3_offset + bl_offset_in_method3 + 4u /* PC adjustment */);
288 ASSERT_EQ(diff & 1u, 0u);
289 ASSERT_LT(diff >> 1, 1u << 8); // Simple encoding, (diff >> 1) fits into 8 bits.
290 auto expected_code = GenNopsAndBl(3u, kBlPlus0 | ((diff >> 1) & 0xffu));
291 EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code)));
292 EXPECT_TRUE(CheckThunk(thunk_offset));
293}
294
Vladimir Marko4d23c9d2015-04-01 23:03:09 +0100295TEST_F(Thumb2RelativePatcherTest, CallOtherAlmostTooFarAfter) {
296 auto method1_raw_code = GenNopsAndBl(3u, kBlPlus0);
297 constexpr uint32_t bl_offset_in_method1 = 3u * 2u; // After NOPs.
298 ArrayRef<const uint8_t> method1_code(method1_raw_code);
299 ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
300 LinkerPatch method1_patches[] = {
301 LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, 3u),
302 };
303
304 constexpr uint32_t max_positive_disp = 16 * MB - 2u + 4u /* PC adjustment */;
Vladimir Marko345f93e2015-07-14 18:58:59 +0100305 bool thunk_in_gap = Create2MethodsWithGap(method1_code,
306 ArrayRef<const LinkerPatch>(method1_patches),
307 kNopCode,
308 ArrayRef<const LinkerPatch>(),
Vladimir Marko4d23c9d2015-04-01 23:03:09 +0100309 bl_offset_in_method1 + max_positive_disp);
310 ASSERT_FALSE(thunk_in_gap); // There should be no thunk.
311
312 // Check linked code.
313 auto expected_code = GenNopsAndBl(3u, kBlPlusMax);
314 EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
315}
316
317TEST_F(Thumb2RelativePatcherTest, CallOtherAlmostTooFarBefore) {
318 auto method3_raw_code = GenNopsAndBl(2u, kBlPlus0);
319 constexpr uint32_t bl_offset_in_method3 = 2u * 2u; // After NOPs.
320 ArrayRef<const uint8_t> method3_code(method3_raw_code);
321 ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size());
322 LinkerPatch method3_patches[] = {
323 LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, 1u),
324 };
325
326 constexpr uint32_t just_over_max_negative_disp = 16 * MB - 4u /* PC adjustment */;
Vladimir Marko345f93e2015-07-14 18:58:59 +0100327 bool thunk_in_gap = Create2MethodsWithGap(kNopCode,
328 ArrayRef<const LinkerPatch>(),
329 method3_code,
330 ArrayRef<const LinkerPatch>(method3_patches),
Vladimir Marko4d23c9d2015-04-01 23:03:09 +0100331 just_over_max_negative_disp - bl_offset_in_method3);
332 ASSERT_FALSE(thunk_in_gap); // There should be no thunk.
333
334 // Check linked code.
335 auto expected_code = GenNopsAndBl(2u, kBlMinusMax);
336 EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code)));
337}
338
339TEST_F(Thumb2RelativePatcherTest, CallOtherJustTooFarAfter) {
340 auto method1_raw_code = GenNopsAndBl(2u, kBlPlus0);
341 constexpr uint32_t bl_offset_in_method1 = 2u * 2u; // After NOPs.
342 ArrayRef<const uint8_t> method1_code(method1_raw_code);
343 ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
344 LinkerPatch method1_patches[] = {
345 LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, 3u),
346 };
347
348 constexpr uint32_t just_over_max_positive_disp = 16 * MB + 4u /* PC adjustment */;
Vladimir Marko345f93e2015-07-14 18:58:59 +0100349 bool thunk_in_gap = Create2MethodsWithGap(method1_code,
350 ArrayRef<const LinkerPatch>(method1_patches),
351 kNopCode,
352 ArrayRef<const LinkerPatch>(),
Vladimir Marko4d23c9d2015-04-01 23:03:09 +0100353 bl_offset_in_method1 + just_over_max_positive_disp);
354 ASSERT_TRUE(thunk_in_gap);
355
356 uint32_t method1_offset = GetMethodOffset(1u);
357 uint32_t method3_offset = GetMethodOffset(3u);
Vladimir Marko0c737df2016-08-01 16:33:16 +0100358 ASSERT_TRUE(IsAligned<kArmAlignment>(method3_offset));
Vladimir Marko4d23c9d2015-04-01 23:03:09 +0100359 uint32_t method3_header_offset = method3_offset - sizeof(OatQuickMethodHeader);
Vladimir Marko0c737df2016-08-01 16:33:16 +0100360 uint32_t thunk_offset =
361 RoundDown(method3_header_offset - ThunkSize(), GetInstructionSetAlignment(kThumb2));
362 DCHECK_EQ(thunk_offset + ThunkSize() + CodeAlignmentSize(thunk_offset + ThunkSize()),
363 method3_header_offset);
Vladimir Marko4d23c9d2015-04-01 23:03:09 +0100364 ASSERT_TRUE(IsAligned<kArmAlignment>(thunk_offset));
365 uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1 + 4u /* PC adjustment */);
366 ASSERT_EQ(diff & 1u, 0u);
367 ASSERT_GE(diff, 16 * MB - (1u << 9)); // Simple encoding, unknown bits fit into the low 8 bits.
368 auto expected_code = GenNopsAndBl(2u, 0xf3ffd700 | ((diff >> 1) & 0xffu));
369 EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
370 CheckThunk(thunk_offset);
371}
372
373TEST_F(Thumb2RelativePatcherTest, CallOtherJustTooFarBefore) {
374 auto method3_raw_code = GenNopsAndBl(3u, kBlPlus0);
375 constexpr uint32_t bl_offset_in_method3 = 3u * 2u; // After NOPs.
376 ArrayRef<const uint8_t> method3_code(method3_raw_code);
377 ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size());
378 LinkerPatch method3_patches[] = {
379 LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, 1u),
380 };
381
382 constexpr uint32_t just_over_max_negative_disp = 16 * MB + 2 - 4u /* PC adjustment */;
Vladimir Marko345f93e2015-07-14 18:58:59 +0100383 bool thunk_in_gap = Create2MethodsWithGap(kNopCode,
384 ArrayRef<const LinkerPatch>(),
385 method3_code,
386 ArrayRef<const LinkerPatch>(method3_patches),
Vladimir Marko4d23c9d2015-04-01 23:03:09 +0100387 just_over_max_negative_disp - bl_offset_in_method3);
388 ASSERT_FALSE(thunk_in_gap); // There should be a thunk but it should be after the method2.
389
390 // Check linked code.
391 uint32_t method3_offset = GetMethodOffset(3u);
392 uint32_t thunk_offset = CompiledCode::AlignCode(method3_offset + method3_code.size(), kThumb2);
393 uint32_t diff = thunk_offset - (method3_offset + bl_offset_in_method3 + 4u /* PC adjustment */);
394 ASSERT_EQ(diff & 1u, 0u);
395 ASSERT_LT(diff >> 1, 1u << 8); // Simple encoding, (diff >> 1) fits into 8 bits.
396 auto expected_code = GenNopsAndBl(3u, kBlPlus0 | ((diff >> 1) & 0xffu));
397 EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code)));
398 EXPECT_TRUE(CheckThunk(thunk_offset));
399}
400
Vladimir Markocac5a7e2016-02-22 10:39:50 +0000401TEST_F(Thumb2RelativePatcherTest, DexCacheReference1) {
402 TestDexCacheReference(0x00ff0000u, 0x00fcu);
Vladimir Markoe5c76c52015-04-06 12:10:19 +0100403 ASSERT_LT(GetMethodOffset(1u), 0xfcu);
404}
405
Vladimir Markocac5a7e2016-02-22 10:39:50 +0000406TEST_F(Thumb2RelativePatcherTest, DexCacheReference2) {
407 TestDexCacheReference(0x02ff0000u, 0x05fcu);
Vladimir Markoe5c76c52015-04-06 12:10:19 +0100408 ASSERT_LT(GetMethodOffset(1u), 0xfcu);
409}
410
Vladimir Markocac5a7e2016-02-22 10:39:50 +0000411TEST_F(Thumb2RelativePatcherTest, DexCacheReference3) {
412 TestDexCacheReference(0x08ff0000u, 0x08fcu);
Vladimir Markoe5c76c52015-04-06 12:10:19 +0100413 ASSERT_LT(GetMethodOffset(1u), 0xfcu);
414}
415
Vladimir Markocac5a7e2016-02-22 10:39:50 +0000416TEST_F(Thumb2RelativePatcherTest, DexCacheReference4) {
417 TestDexCacheReference(0xd0ff0000u, 0x60fcu);
418 ASSERT_LT(GetMethodOffset(1u), 0xfcu);
419}
420
421TEST_F(Thumb2RelativePatcherTest, StringReference1) {
422 TestStringReference(0x00ff00fcu);
423 ASSERT_LT(GetMethodOffset(1u), 0xfcu);
424}
425
426TEST_F(Thumb2RelativePatcherTest, StringReference2) {
427 TestStringReference(0x02ff05fcu);
428 ASSERT_LT(GetMethodOffset(1u), 0xfcu);
429}
430
431TEST_F(Thumb2RelativePatcherTest, StringReference3) {
432 TestStringReference(0x08ff08fcu);
433 ASSERT_LT(GetMethodOffset(1u), 0xfcu);
434}
435
436TEST_F(Thumb2RelativePatcherTest, StringReference4) {
437 TestStringReference(0xd0ff60fcu);
Vladimir Markoe5c76c52015-04-06 12:10:19 +0100438 ASSERT_LT(GetMethodOffset(1u), 0xfcu);
439}
440
Vladimir Marko4d23c9d2015-04-01 23:03:09 +0100441} // namespace linker
442} // namespace art