• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "linker/relative_patcher_test.h"
18 #include "linker/arm/relative_patcher_thumb2.h"
19 #include "oat_quick_method_header.h"
20 
21 namespace art {
22 namespace linker {
23 
24 class Thumb2RelativePatcherTest : public RelativePatcherTest {
25  public:
Thumb2RelativePatcherTest()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;
33   static const uint8_t kUnpatchedPcRelativeRawCode[];
34   static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode;
35   static const uint32_t kPcInsnOffset;
36 
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 
Create2MethodsWithGap(const ArrayRef<const uint8_t> & method1_code,const ArrayRef<const LinkerPatch> & method1_patches,const ArrayRef<const uint8_t> & method3_code,const ArrayRef<const LinkerPatch> & method3_patches,uint32_t distance_without_thunks)45   bool Create2MethodsWithGap(const ArrayRef<const uint8_t>& method1_code,
46                              const ArrayRef<const LinkerPatch>& method1_patches,
47                              const ArrayRef<const uint8_t>& method3_code,
48                              const ArrayRef<const LinkerPatch>& method3_patches,
49                              uint32_t distance_without_thunks) {
50     CHECK_EQ(distance_without_thunks % kArmAlignment, 0u);
51     const uint32_t method1_offset =
52         CompiledCode::AlignCode(kTrampolineSize, kThumb2) + sizeof(OatQuickMethodHeader);
53     AddCompiledMethod(MethodRef(1u), method1_code, method1_patches);
54 
55     // We want to put the method3 at a very precise offset.
56     const uint32_t method3_offset = method1_offset + distance_without_thunks;
57     CHECK_ALIGNED(method3_offset - sizeof(OatQuickMethodHeader), kArmAlignment);
58 
59     // Calculate size of method2 so that we put method3 at the correct place.
60     const uint32_t method2_offset =
61         CompiledCode::AlignCode(method1_offset + method1_code.size(), kThumb2) +
62         sizeof(OatQuickMethodHeader);
63     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);
66     AddCompiledMethod(MethodRef(2u), method2_code, ArrayRef<const LinkerPatch>());
67 
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 {
81       uint32_t aligned_thunk_size = CompiledCode::AlignCode(ThunkSize(), kThumb2);
82       CHECK_EQ(result3.second, method3_offset + aligned_thunk_size + 1 /* thumb mode */);
83       return true;   // Thunk present.
84     }
85   }
86 
GetMethodOffset(uint32_t method_idx)87   uint32_t GetMethodOffset(uint32_t method_idx) {
88     auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
89     CHECK(result.first);
90     CHECK_NE(result.second & 1u, 0u);
91     return result.second - 1 /* thumb mode */;
92   }
93 
ThunkSize()94   uint32_t ThunkSize() {
95     return static_cast<Thumb2RelativePatcher*>(patcher_.get())->thunk_code_.size();
96   }
97 
CheckThunk(uint32_t thunk_offset)98   bool CheckThunk(uint32_t thunk_offset) {
99     Thumb2RelativePatcher* patcher = static_cast<Thumb2RelativePatcher*>(patcher_.get());
100     ArrayRef<const uint8_t> expected_code(patcher->thunk_code_);
101     if (output_.size() < thunk_offset + expected_code.size()) {
102       LOG(ERROR) << "output_.size() == " << output_.size() << " < "
103           << "thunk_offset + expected_code.size() == " << (thunk_offset + expected_code.size());
104       return false;
105     }
106     ArrayRef<const uint8_t> linked_code(&output_[thunk_offset], expected_code.size());
107     if (linked_code == expected_code) {
108       return true;
109     }
110     // Log failure info.
111     DumpDiff(expected_code, linked_code);
112     return false;
113   }
114 
GenNopsAndBl(size_t num_nops,uint32_t bl)115   std::vector<uint8_t> GenNopsAndBl(size_t num_nops, uint32_t bl) {
116     std::vector<uint8_t> result;
117     result.reserve(num_nops * 2u + 4u);
118     for (size_t i = 0; i != num_nops; ++i) {
119       result.push_back(0x00);
120       result.push_back(0xbf);
121     }
122     result.push_back(static_cast<uint8_t>(bl >> 16));
123     result.push_back(static_cast<uint8_t>(bl >> 24));
124     result.push_back(static_cast<uint8_t>(bl));
125     result.push_back(static_cast<uint8_t>(bl >> 8));
126     return result;
127   }
128 
129   void TestDexCacheReference(uint32_t dex_cache_arrays_begin, uint32_t element_offset);
130   void TestStringReference(uint32_t string_offset);
131   void CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, uint32_t target_offset);
132 };
133 
134 const uint8_t Thumb2RelativePatcherTest::kCallRawCode[] = {
135     0x00, 0xf0, 0x00, 0xf8
136 };
137 
138 const ArrayRef<const uint8_t> Thumb2RelativePatcherTest::kCallCode(kCallRawCode);
139 
140 const uint8_t Thumb2RelativePatcherTest::kNopRawCode[] = {
141     0x00, 0xbf
142 };
143 
144 const ArrayRef<const uint8_t> Thumb2RelativePatcherTest::kNopCode(kNopRawCode);
145 
146 const uint8_t Thumb2RelativePatcherTest::kUnpatchedPcRelativeRawCode[] = {
147     0x40, 0xf2, 0x00, 0x00,   // MOVW r0, #0 (placeholder)
148     0xc0, 0xf2, 0x00, 0x00,   // MOVT r0, #0 (placeholder)
149     0x78, 0x44,               // ADD r0, pc
150 };
151 const ArrayRef<const uint8_t> Thumb2RelativePatcherTest::kUnpatchedPcRelativeCode(
152     kUnpatchedPcRelativeRawCode);
153 const uint32_t Thumb2RelativePatcherTest::kPcInsnOffset = 8u;
154 
TestDexCacheReference(uint32_t dex_cache_arrays_begin,uint32_t element_offset)155 void Thumb2RelativePatcherTest::TestDexCacheReference(uint32_t dex_cache_arrays_begin,
156                                                       uint32_t element_offset) {
157   dex_cache_arrays_begin_ = dex_cache_arrays_begin;
158   LinkerPatch patches[] = {
159       LinkerPatch::DexCacheArrayPatch(0u, nullptr, kPcInsnOffset, element_offset),
160       LinkerPatch::DexCacheArrayPatch(4u, nullptr, kPcInsnOffset, element_offset),
161   };
162   CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches),
163                        dex_cache_arrays_begin_ + element_offset);
164 }
165 
TestStringReference(uint32_t string_offset)166 void Thumb2RelativePatcherTest::TestStringReference(uint32_t string_offset) {
167   constexpr uint32_t kStringIndex = 1u;
168   string_index_to_offset_map_.Put(kStringIndex, string_offset);
169   LinkerPatch patches[] = {
170       LinkerPatch::RelativeStringPatch(0u, nullptr, kPcInsnOffset, kStringIndex),
171       LinkerPatch::RelativeStringPatch(4u, nullptr, kPcInsnOffset, kStringIndex),
172   };
173   CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset);
174 }
175 
CheckPcRelativePatch(const ArrayRef<const LinkerPatch> & patches,uint32_t target_offset)176 void Thumb2RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches,
177                                                      uint32_t target_offset) {
178   AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches));
179   Link();
180 
181   uint32_t method1_offset = GetMethodOffset(1u);
182   uint32_t pc_base_offset = method1_offset + kPcInsnOffset + 4u /* PC adjustment */;
183   uint32_t diff = target_offset - pc_base_offset;
184   // Distribute the bits of the diff between the MOVW and MOVT:
185   uint32_t diffw = diff & 0xffffu;
186   uint32_t difft = diff >> 16;
187   uint32_t movw = 0xf2400000u |           // MOVW r0, #0 (placeholder),
188       ((diffw & 0xf000u) << (16 - 12)) |  // move imm4 from bits 12-15 to bits 16-19,
189       ((diffw & 0x0800u) << (26 - 11)) |  // move imm from bit 11 to bit 26,
190       ((diffw & 0x0700u) << (12 - 8)) |   // move imm3 from bits 8-10 to bits 12-14,
191       ((diffw & 0x00ffu));                // keep imm8 at bits 0-7.
192   uint32_t movt = 0xf2c00000u |           // MOVT r0, #0 (placeholder),
193       ((difft & 0xf000u) << (16 - 12)) |  // move imm4 from bits 12-15 to bits 16-19,
194       ((difft & 0x0800u) << (26 - 11)) |  // move imm from bit 11 to bit 26,
195       ((difft & 0x0700u) << (12 - 8)) |   // move imm3 from bits 8-10 to bits 12-14,
196       ((difft & 0x00ffu));                // keep imm8 at bits 0-7.
197   const uint8_t expected_code[] = {
198       static_cast<uint8_t>(movw >> 16), static_cast<uint8_t>(movw >> 24),
199       static_cast<uint8_t>(movw >> 0), static_cast<uint8_t>(movw >> 8),
200       static_cast<uint8_t>(movt >> 16), static_cast<uint8_t>(movt >> 24),
201       static_cast<uint8_t>(movt >> 0), static_cast<uint8_t>(movt >> 8),
202       0x78, 0x44,
203   };
204   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
205 }
206 
TEST_F(Thumb2RelativePatcherTest,CallSelf)207 TEST_F(Thumb2RelativePatcherTest, CallSelf) {
208   LinkerPatch patches[] = {
209       LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
210   };
211   AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
212   Link();
213 
214   static const uint8_t expected_code[] = {
215       0xff, 0xf7, 0xfe, 0xff
216   };
217   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
218 }
219 
TEST_F(Thumb2RelativePatcherTest,CallOther)220 TEST_F(Thumb2RelativePatcherTest, CallOther) {
221   LinkerPatch method1_patches[] = {
222       LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
223   };
224   AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(method1_patches));
225   LinkerPatch method2_patches[] = {
226       LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
227   };
228   AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<const LinkerPatch>(method2_patches));
229   Link();
230 
231   uint32_t method1_offset = GetMethodOffset(1u);
232   uint32_t method2_offset = GetMethodOffset(2u);
233   uint32_t diff_after = method2_offset - (method1_offset + 4u /* PC adjustment */);
234   ASSERT_EQ(diff_after & 1u, 0u);
235   ASSERT_LT(diff_after >> 1, 1u << 8);  // Simple encoding, (diff_after >> 1) fits into 8 bits.
236   static const uint8_t method1_expected_code[] = {
237       0x00, 0xf0, static_cast<uint8_t>(diff_after >> 1), 0xf8
238   };
239   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code)));
240   uint32_t diff_before = method1_offset - (method2_offset + 4u /* PC adjustment */);
241   ASSERT_EQ(diff_before & 1u, 0u);
242   ASSERT_GE(diff_before, -1u << 9);  // Simple encoding, -256 <= (diff >> 1) < 0.
243   auto method2_expected_code = GenNopsAndBl(0u, kBlMinus256 | ((diff_before >> 1) & 0xffu));
244   EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code)));
245 }
246 
TEST_F(Thumb2RelativePatcherTest,CallTrampoline)247 TEST_F(Thumb2RelativePatcherTest, CallTrampoline) {
248   LinkerPatch patches[] = {
249       LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
250   };
251   AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
252   Link();
253 
254   uint32_t method1_offset = GetMethodOffset(1u);
255   uint32_t diff = kTrampolineOffset - (method1_offset + 4u);
256   ASSERT_EQ(diff & 1u, 0u);
257   ASSERT_GE(diff, -1u << 9);  // Simple encoding, -256 <= (diff >> 1) < 0 (checked as unsigned).
258   auto expected_code = GenNopsAndBl(0u, kBlMinus256 | ((diff >> 1) & 0xffu));
259   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
260 }
261 
TEST_F(Thumb2RelativePatcherTest,CallTrampolineTooFar)262 TEST_F(Thumb2RelativePatcherTest, CallTrampolineTooFar) {
263   constexpr uint32_t missing_method_index = 1024u;
264   auto method3_raw_code = GenNopsAndBl(3u, kBlPlus0);
265   constexpr uint32_t bl_offset_in_method3 = 3u * 2u;  // After NOPs.
266   ArrayRef<const uint8_t> method3_code(method3_raw_code);
267   ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size());
268   LinkerPatch method3_patches[] = {
269       LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, missing_method_index),
270   };
271 
272   constexpr uint32_t just_over_max_negative_disp = 16 * MB + 2 - 4u /* PC adjustment */;
273   bool thunk_in_gap = Create2MethodsWithGap(kNopCode,
274                                             ArrayRef<const LinkerPatch>(),
275                                             method3_code,
276                                             ArrayRef<const LinkerPatch>(method3_patches),
277                                             just_over_max_negative_disp - bl_offset_in_method3);
278   ASSERT_FALSE(thunk_in_gap);  // There should be a thunk but it should be after the method2.
279   ASSERT_FALSE(method_offset_map_.FindMethodOffset(MethodRef(missing_method_index)).first);
280 
281   // Check linked code.
282   uint32_t method3_offset = GetMethodOffset(3u);
283   uint32_t thunk_offset = CompiledCode::AlignCode(method3_offset + method3_code.size(), kThumb2);
284   uint32_t diff = thunk_offset - (method3_offset + bl_offset_in_method3 + 4u /* PC adjustment */);
285   ASSERT_EQ(diff & 1u, 0u);
286   ASSERT_LT(diff >> 1, 1u << 8);  // Simple encoding, (diff >> 1) fits into 8 bits.
287   auto expected_code = GenNopsAndBl(3u, kBlPlus0 | ((diff >> 1) & 0xffu));
288   EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code)));
289   EXPECT_TRUE(CheckThunk(thunk_offset));
290 }
291 
TEST_F(Thumb2RelativePatcherTest,CallOtherAlmostTooFarAfter)292 TEST_F(Thumb2RelativePatcherTest, CallOtherAlmostTooFarAfter) {
293   auto method1_raw_code = GenNopsAndBl(3u, kBlPlus0);
294   constexpr uint32_t bl_offset_in_method1 = 3u * 2u;  // After NOPs.
295   ArrayRef<const uint8_t> method1_code(method1_raw_code);
296   ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
297   LinkerPatch method1_patches[] = {
298       LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, 3u),
299   };
300 
301   constexpr uint32_t max_positive_disp = 16 * MB - 2u + 4u /* PC adjustment */;
302   bool thunk_in_gap = Create2MethodsWithGap(method1_code,
303                                             ArrayRef<const LinkerPatch>(method1_patches),
304                                             kNopCode,
305                                             ArrayRef<const LinkerPatch>(),
306                                             bl_offset_in_method1 + max_positive_disp);
307   ASSERT_FALSE(thunk_in_gap);  // There should be no thunk.
308 
309   // Check linked code.
310   auto expected_code = GenNopsAndBl(3u, kBlPlusMax);
311   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
312 }
313 
TEST_F(Thumb2RelativePatcherTest,CallOtherAlmostTooFarBefore)314 TEST_F(Thumb2RelativePatcherTest, CallOtherAlmostTooFarBefore) {
315   auto method3_raw_code = GenNopsAndBl(2u, kBlPlus0);
316   constexpr uint32_t bl_offset_in_method3 = 2u * 2u;  // After NOPs.
317   ArrayRef<const uint8_t> method3_code(method3_raw_code);
318   ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size());
319   LinkerPatch method3_patches[] = {
320       LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, 1u),
321   };
322 
323   constexpr uint32_t just_over_max_negative_disp = 16 * MB - 4u /* PC adjustment */;
324   bool thunk_in_gap = Create2MethodsWithGap(kNopCode,
325                                             ArrayRef<const LinkerPatch>(),
326                                             method3_code,
327                                             ArrayRef<const LinkerPatch>(method3_patches),
328                                             just_over_max_negative_disp - bl_offset_in_method3);
329   ASSERT_FALSE(thunk_in_gap);  // There should be no thunk.
330 
331   // Check linked code.
332   auto expected_code = GenNopsAndBl(2u, kBlMinusMax);
333   EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code)));
334 }
335 
TEST_F(Thumb2RelativePatcherTest,CallOtherJustTooFarAfter)336 TEST_F(Thumb2RelativePatcherTest, CallOtherJustTooFarAfter) {
337   auto method1_raw_code = GenNopsAndBl(2u, kBlPlus0);
338   constexpr uint32_t bl_offset_in_method1 = 2u * 2u;  // After NOPs.
339   ArrayRef<const uint8_t> method1_code(method1_raw_code);
340   ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
341   LinkerPatch method1_patches[] = {
342       LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, 3u),
343   };
344 
345   constexpr uint32_t just_over_max_positive_disp = 16 * MB + 4u /* PC adjustment */;
346   bool thunk_in_gap = Create2MethodsWithGap(method1_code,
347                                             ArrayRef<const LinkerPatch>(method1_patches),
348                                             kNopCode,
349                                             ArrayRef<const LinkerPatch>(),
350                                             bl_offset_in_method1 + just_over_max_positive_disp);
351   ASSERT_TRUE(thunk_in_gap);
352 
353   uint32_t method1_offset = GetMethodOffset(1u);
354   uint32_t method3_offset = GetMethodOffset(3u);
355   uint32_t method3_header_offset = method3_offset - sizeof(OatQuickMethodHeader);
356   ASSERT_TRUE(IsAligned<kArmAlignment>(method3_header_offset));
357   uint32_t thunk_offset = method3_header_offset - CompiledCode::AlignCode(ThunkSize(), kThumb2);
358   ASSERT_TRUE(IsAligned<kArmAlignment>(thunk_offset));
359   uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1 + 4u /* PC adjustment */);
360   ASSERT_EQ(diff & 1u, 0u);
361   ASSERT_GE(diff, 16 * MB - (1u << 9));  // Simple encoding, unknown bits fit into the low 8 bits.
362   auto expected_code = GenNopsAndBl(2u, 0xf3ffd700 | ((diff >> 1) & 0xffu));
363   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
364   CheckThunk(thunk_offset);
365 }
366 
TEST_F(Thumb2RelativePatcherTest,CallOtherJustTooFarBefore)367 TEST_F(Thumb2RelativePatcherTest, CallOtherJustTooFarBefore) {
368   auto method3_raw_code = GenNopsAndBl(3u, kBlPlus0);
369   constexpr uint32_t bl_offset_in_method3 = 3u * 2u;  // After NOPs.
370   ArrayRef<const uint8_t> method3_code(method3_raw_code);
371   ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size());
372   LinkerPatch method3_patches[] = {
373       LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, 1u),
374   };
375 
376   constexpr uint32_t just_over_max_negative_disp = 16 * MB + 2 - 4u /* PC adjustment */;
377   bool thunk_in_gap = Create2MethodsWithGap(kNopCode,
378                                             ArrayRef<const LinkerPatch>(),
379                                             method3_code,
380                                             ArrayRef<const LinkerPatch>(method3_patches),
381                                             just_over_max_negative_disp - bl_offset_in_method3);
382   ASSERT_FALSE(thunk_in_gap);  // There should be a thunk but it should be after the method2.
383 
384   // Check linked code.
385   uint32_t method3_offset = GetMethodOffset(3u);
386   uint32_t thunk_offset = CompiledCode::AlignCode(method3_offset + method3_code.size(), kThumb2);
387   uint32_t diff = thunk_offset - (method3_offset + bl_offset_in_method3 + 4u /* PC adjustment */);
388   ASSERT_EQ(diff & 1u, 0u);
389   ASSERT_LT(diff >> 1, 1u << 8);  // Simple encoding, (diff >> 1) fits into 8 bits.
390   auto expected_code = GenNopsAndBl(3u, kBlPlus0 | ((diff >> 1) & 0xffu));
391   EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code)));
392   EXPECT_TRUE(CheckThunk(thunk_offset));
393 }
394 
TEST_F(Thumb2RelativePatcherTest,DexCacheReference1)395 TEST_F(Thumb2RelativePatcherTest, DexCacheReference1) {
396   TestDexCacheReference(0x00ff0000u, 0x00fcu);
397   ASSERT_LT(GetMethodOffset(1u), 0xfcu);
398 }
399 
TEST_F(Thumb2RelativePatcherTest,DexCacheReference2)400 TEST_F(Thumb2RelativePatcherTest, DexCacheReference2) {
401   TestDexCacheReference(0x02ff0000u, 0x05fcu);
402   ASSERT_LT(GetMethodOffset(1u), 0xfcu);
403 }
404 
TEST_F(Thumb2RelativePatcherTest,DexCacheReference3)405 TEST_F(Thumb2RelativePatcherTest, DexCacheReference3) {
406   TestDexCacheReference(0x08ff0000u, 0x08fcu);
407   ASSERT_LT(GetMethodOffset(1u), 0xfcu);
408 }
409 
TEST_F(Thumb2RelativePatcherTest,DexCacheReference4)410 TEST_F(Thumb2RelativePatcherTest, DexCacheReference4) {
411   TestDexCacheReference(0xd0ff0000u, 0x60fcu);
412   ASSERT_LT(GetMethodOffset(1u), 0xfcu);
413 }
414 
TEST_F(Thumb2RelativePatcherTest,StringReference1)415 TEST_F(Thumb2RelativePatcherTest, StringReference1) {
416   TestStringReference(0x00ff00fcu);
417   ASSERT_LT(GetMethodOffset(1u), 0xfcu);
418 }
419 
TEST_F(Thumb2RelativePatcherTest,StringReference2)420 TEST_F(Thumb2RelativePatcherTest, StringReference2) {
421   TestStringReference(0x02ff05fcu);
422   ASSERT_LT(GetMethodOffset(1u), 0xfcu);
423 }
424 
TEST_F(Thumb2RelativePatcherTest,StringReference3)425 TEST_F(Thumb2RelativePatcherTest, StringReference3) {
426   TestStringReference(0x08ff08fcu);
427   ASSERT_LT(GetMethodOffset(1u), 0xfcu);
428 }
429 
TEST_F(Thumb2RelativePatcherTest,StringReference4)430 TEST_F(Thumb2RelativePatcherTest, StringReference4) {
431   TestStringReference(0xd0ff60fcu);
432   ASSERT_LT(GetMethodOffset(1u), 0xfcu);
433 }
434 
435 }  // namespace linker
436 }  // namespace art
437