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: 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 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 uint32_t method1_offset = 52 kTrampolineSize + CodeAlignmentSize(kTrampolineSize) + 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, kArmAlignment); 58 59 // Calculate size of method2 so that we put method3 at the correct place. 60 const uint32_t method1_end = method1_offset + method1_code.size(); 61 const uint32_t method2_offset = 62 method1_end + CodeAlignmentSize(method1_end) + 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); 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 thunk_end = 82 CompiledCode::AlignCode(method3_offset - sizeof(OatQuickMethodHeader), kThumb2) + 83 MethodCallThunkSize(); 84 uint32_t header_offset = thunk_end + CodeAlignmentSize(thunk_end); 85 CHECK_EQ(result3.second, header_offset + sizeof(OatQuickMethodHeader) + 1 /* thumb mode */); 86 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 std::vector<uint8_t> CompileMethodCallThunk() { 98 ArmBaseRelativePatcher::ThunkKey key( 99 ArmBaseRelativePatcher::ThunkType::kMethodCall, 100 ArmBaseRelativePatcher::ThunkParams{{ 0, 0 }}); // NOLINT(whitespace/braces) 101 return static_cast<Thumb2RelativePatcher*>(patcher_.get())->CompileThunk(key); 102 } 103 104 uint32_t MethodCallThunkSize() { 105 return CompileMethodCallThunk().size(); 106 } 107 108 bool CheckThunk(uint32_t thunk_offset) { 109 const std::vector<uint8_t> expected_code = CompileMethodCallThunk(); 110 if (output_.size() < thunk_offset + expected_code.size()) { 111 LOG(ERROR) << "output_.size() == " << output_.size() << " < " 112 << "thunk_offset + expected_code.size() == " << (thunk_offset + expected_code.size()); 113 return false; 114 } 115 ArrayRef<const uint8_t> linked_code(&output_[thunk_offset], expected_code.size()); 116 if (linked_code == ArrayRef<const uint8_t>(expected_code)) { 117 return true; 118 } 119 // Log failure info. 120 DumpDiff(ArrayRef<const uint8_t>(expected_code), linked_code); 121 return false; 122 } 123 124 std::vector<uint8_t> GenNopsAndBl(size_t num_nops, uint32_t bl) { 125 std::vector<uint8_t> result; 126 result.reserve(num_nops * 2u + 4u); 127 for (size_t i = 0; i != num_nops; ++i) { 128 result.push_back(0x00); 129 result.push_back(0xbf); 130 } 131 result.push_back(static_cast<uint8_t>(bl >> 16)); 132 result.push_back(static_cast<uint8_t>(bl >> 24)); 133 result.push_back(static_cast<uint8_t>(bl)); 134 result.push_back(static_cast<uint8_t>(bl >> 8)); 135 return result; 136 } 137 138 void TestDexCacheReference(uint32_t dex_cache_arrays_begin, uint32_t element_offset); 139 void TestStringReference(uint32_t string_offset); 140 void CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, uint32_t target_offset); 141 }; 142 143 const uint8_t Thumb2RelativePatcherTest::kCallRawCode[] = { 144 0x00, 0xf0, 0x00, 0xf8 145 }; 146 147 const ArrayRef<const uint8_t> Thumb2RelativePatcherTest::kCallCode(kCallRawCode); 148 149 const uint8_t Thumb2RelativePatcherTest::kNopRawCode[] = { 150 0x00, 0xbf 151 }; 152 153 const ArrayRef<const uint8_t> Thumb2RelativePatcherTest::kNopCode(kNopRawCode); 154 155 const uint8_t Thumb2RelativePatcherTest::kUnpatchedPcRelativeRawCode[] = { 156 0x40, 0xf2, 0x00, 0x00, // MOVW r0, #0 (placeholder) 157 0xc0, 0xf2, 0x00, 0x00, // MOVT r0, #0 (placeholder) 158 0x78, 0x44, // ADD r0, pc 159 }; 160 const ArrayRef<const uint8_t> Thumb2RelativePatcherTest::kUnpatchedPcRelativeCode( 161 kUnpatchedPcRelativeRawCode); 162 const uint32_t Thumb2RelativePatcherTest::kPcInsnOffset = 8u; 163 164 void Thumb2RelativePatcherTest::TestDexCacheReference(uint32_t dex_cache_arrays_begin, 165 uint32_t element_offset) { 166 dex_cache_arrays_begin_ = dex_cache_arrays_begin; 167 LinkerPatch patches[] = { 168 LinkerPatch::DexCacheArrayPatch(0u, nullptr, kPcInsnOffset, element_offset), 169 LinkerPatch::DexCacheArrayPatch(4u, nullptr, kPcInsnOffset, element_offset), 170 }; 171 CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), 172 dex_cache_arrays_begin_ + element_offset); 173 } 174 175 void Thumb2RelativePatcherTest::TestStringReference(uint32_t string_offset) { 176 constexpr uint32_t kStringIndex = 1u; 177 string_index_to_offset_map_.Put(kStringIndex, string_offset); 178 LinkerPatch patches[] = { 179 LinkerPatch::RelativeStringPatch(0u, nullptr, kPcInsnOffset, kStringIndex), 180 LinkerPatch::RelativeStringPatch(4u, nullptr, kPcInsnOffset, kStringIndex), 181 }; 182 CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset); 183 } 184 185 void Thumb2RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, 186 uint32_t target_offset) { 187 AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches)); 188 Link(); 189 190 uint32_t method1_offset = GetMethodOffset(1u); 191 uint32_t pc_base_offset = method1_offset + kPcInsnOffset + 4u /* PC adjustment */; 192 uint32_t diff = target_offset - pc_base_offset; 193 // Distribute the bits of the diff between the MOVW and MOVT: 194 uint32_t diffw = diff & 0xffffu; 195 uint32_t difft = diff >> 16; 196 uint32_t movw = 0xf2400000u | // MOVW r0, #0 (placeholder), 197 ((diffw & 0xf000u) << (16 - 12)) | // move imm4 from bits 12-15 to bits 16-19, 198 ((diffw & 0x0800u) << (26 - 11)) | // move imm from bit 11 to bit 26, 199 ((diffw & 0x0700u) << (12 - 8)) | // move imm3 from bits 8-10 to bits 12-14, 200 ((diffw & 0x00ffu)); // keep imm8 at bits 0-7. 201 uint32_t movt = 0xf2c00000u | // MOVT r0, #0 (placeholder), 202 ((difft & 0xf000u) << (16 - 12)) | // move imm4 from bits 12-15 to bits 16-19, 203 ((difft & 0x0800u) << (26 - 11)) | // move imm from bit 11 to bit 26, 204 ((difft & 0x0700u) << (12 - 8)) | // move imm3 from bits 8-10 to bits 12-14, 205 ((difft & 0x00ffu)); // keep imm8 at bits 0-7. 206 const uint8_t expected_code[] = { 207 static_cast<uint8_t>(movw >> 16), static_cast<uint8_t>(movw >> 24), 208 static_cast<uint8_t>(movw >> 0), static_cast<uint8_t>(movw >> 8), 209 static_cast<uint8_t>(movt >> 16), static_cast<uint8_t>(movt >> 24), 210 static_cast<uint8_t>(movt >> 0), static_cast<uint8_t>(movt >> 8), 211 0x78, 0x44, 212 }; 213 EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); 214 } 215 216 TEST_F(Thumb2RelativePatcherTest, CallSelf) { 217 LinkerPatch patches[] = { 218 LinkerPatch::RelativeCodePatch(0u, nullptr, 1u), 219 }; 220 AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches)); 221 Link(); 222 223 static const uint8_t expected_code[] = { 224 0xff, 0xf7, 0xfe, 0xff 225 }; 226 EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); 227 } 228 229 TEST_F(Thumb2RelativePatcherTest, CallOther) { 230 LinkerPatch method1_patches[] = { 231 LinkerPatch::RelativeCodePatch(0u, nullptr, 2u), 232 }; 233 AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(method1_patches)); 234 LinkerPatch method2_patches[] = { 235 LinkerPatch::RelativeCodePatch(0u, nullptr, 1u), 236 }; 237 AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<const LinkerPatch>(method2_patches)); 238 Link(); 239 240 uint32_t method1_offset = GetMethodOffset(1u); 241 uint32_t method2_offset = GetMethodOffset(2u); 242 uint32_t diff_after = method2_offset - (method1_offset + 4u /* PC adjustment */); 243 ASSERT_EQ(diff_after & 1u, 0u); 244 ASSERT_LT(diff_after >> 1, 1u << 8); // Simple encoding, (diff_after >> 1) fits into 8 bits. 245 static const uint8_t method1_expected_code[] = { 246 0x00, 0xf0, static_cast<uint8_t>(diff_after >> 1), 0xf8 247 }; 248 EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code))); 249 uint32_t diff_before = method1_offset - (method2_offset + 4u /* PC adjustment */); 250 ASSERT_EQ(diff_before & 1u, 0u); 251 ASSERT_GE(diff_before, -1u << 9); // Simple encoding, -256 <= (diff >> 1) < 0. 252 auto method2_expected_code = GenNopsAndBl(0u, kBlMinus256 | ((diff_before >> 1) & 0xffu)); 253 EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code))); 254 } 255 256 TEST_F(Thumb2RelativePatcherTest, CallTrampoline) { 257 LinkerPatch patches[] = { 258 LinkerPatch::RelativeCodePatch(0u, nullptr, 2u), 259 }; 260 AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches)); 261 Link(); 262 263 uint32_t method1_offset = GetMethodOffset(1u); 264 uint32_t diff = kTrampolineOffset - (method1_offset + 4u); 265 ASSERT_EQ(diff & 1u, 0u); 266 ASSERT_GE(diff, -1u << 9); // Simple encoding, -256 <= (diff >> 1) < 0 (checked as unsigned). 267 auto expected_code = GenNopsAndBl(0u, kBlMinus256 | ((diff >> 1) & 0xffu)); 268 EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); 269 } 270 271 TEST_F(Thumb2RelativePatcherTest, CallTrampolineTooFar) { 272 constexpr uint32_t missing_method_index = 1024u; 273 auto method3_raw_code = GenNopsAndBl(3u, kBlPlus0); 274 constexpr uint32_t bl_offset_in_method3 = 3u * 2u; // After NOPs. 275 ArrayRef<const uint8_t> method3_code(method3_raw_code); 276 ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size()); 277 LinkerPatch method3_patches[] = { 278 LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, missing_method_index), 279 }; 280 281 constexpr uint32_t just_over_max_negative_disp = 16 * MB + 2 - 4u /* PC adjustment */; 282 bool thunk_in_gap = Create2MethodsWithGap(kNopCode, 283 ArrayRef<const LinkerPatch>(), 284 method3_code, 285 ArrayRef<const LinkerPatch>(method3_patches), 286 just_over_max_negative_disp - bl_offset_in_method3); 287 ASSERT_FALSE(thunk_in_gap); // There should be a thunk but it should be after the method2. 288 ASSERT_FALSE(method_offset_map_.FindMethodOffset(MethodRef(missing_method_index)).first); 289 290 // Check linked code. 291 uint32_t method3_offset = GetMethodOffset(3u); 292 uint32_t thunk_offset = CompiledCode::AlignCode(method3_offset + method3_code.size(), kThumb2); 293 uint32_t diff = thunk_offset - (method3_offset + bl_offset_in_method3 + 4u /* PC adjustment */); 294 ASSERT_EQ(diff & 1u, 0u); 295 ASSERT_LT(diff >> 1, 1u << 8); // Simple encoding, (diff >> 1) fits into 8 bits. 296 auto expected_code = GenNopsAndBl(3u, kBlPlus0 | ((diff >> 1) & 0xffu)); 297 EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code))); 298 EXPECT_TRUE(CheckThunk(thunk_offset)); 299 } 300 301 TEST_F(Thumb2RelativePatcherTest, CallOtherAlmostTooFarAfter) { 302 auto method1_raw_code = GenNopsAndBl(3u, kBlPlus0); 303 constexpr uint32_t bl_offset_in_method1 = 3u * 2u; // After NOPs. 304 ArrayRef<const uint8_t> method1_code(method1_raw_code); 305 ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size()); 306 LinkerPatch method1_patches[] = { 307 LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, 3u), 308 }; 309 310 constexpr uint32_t max_positive_disp = 16 * MB - 2u + 4u /* PC adjustment */; 311 bool thunk_in_gap = Create2MethodsWithGap(method1_code, 312 ArrayRef<const LinkerPatch>(method1_patches), 313 kNopCode, 314 ArrayRef<const LinkerPatch>(), 315 bl_offset_in_method1 + max_positive_disp); 316 ASSERT_FALSE(thunk_in_gap); // There should be no thunk. 317 318 // Check linked code. 319 auto expected_code = GenNopsAndBl(3u, kBlPlusMax); 320 EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); 321 } 322 323 TEST_F(Thumb2RelativePatcherTest, CallOtherAlmostTooFarBefore) { 324 auto method3_raw_code = GenNopsAndBl(2u, kBlPlus0); 325 constexpr uint32_t bl_offset_in_method3 = 2u * 2u; // After NOPs. 326 ArrayRef<const uint8_t> method3_code(method3_raw_code); 327 ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size()); 328 LinkerPatch method3_patches[] = { 329 LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, 1u), 330 }; 331 332 constexpr uint32_t just_over_max_negative_disp = 16 * MB - 4u /* PC adjustment */; 333 bool thunk_in_gap = Create2MethodsWithGap(kNopCode, 334 ArrayRef<const LinkerPatch>(), 335 method3_code, 336 ArrayRef<const LinkerPatch>(method3_patches), 337 just_over_max_negative_disp - bl_offset_in_method3); 338 ASSERT_FALSE(thunk_in_gap); // There should be no thunk. 339 340 // Check linked code. 341 auto expected_code = GenNopsAndBl(2u, kBlMinusMax); 342 EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code))); 343 } 344 345 TEST_F(Thumb2RelativePatcherTest, CallOtherJustTooFarAfter) { 346 auto method1_raw_code = GenNopsAndBl(2u, kBlPlus0); 347 constexpr uint32_t bl_offset_in_method1 = 2u * 2u; // After NOPs. 348 ArrayRef<const uint8_t> method1_code(method1_raw_code); 349 ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size()); 350 LinkerPatch method1_patches[] = { 351 LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, 3u), 352 }; 353 354 constexpr uint32_t just_over_max_positive_disp = 16 * MB + 4u /* PC adjustment */; 355 bool thunk_in_gap = Create2MethodsWithGap(method1_code, 356 ArrayRef<const LinkerPatch>(method1_patches), 357 kNopCode, 358 ArrayRef<const LinkerPatch>(), 359 bl_offset_in_method1 + just_over_max_positive_disp); 360 ASSERT_TRUE(thunk_in_gap); 361 362 uint32_t method1_offset = GetMethodOffset(1u); 363 uint32_t method3_offset = GetMethodOffset(3u); 364 ASSERT_TRUE(IsAligned<kArmAlignment>(method3_offset)); 365 uint32_t method3_header_offset = method3_offset - sizeof(OatQuickMethodHeader); 366 uint32_t thunk_size = MethodCallThunkSize(); 367 uint32_t thunk_offset = 368 RoundDown(method3_header_offset - thunk_size, GetInstructionSetAlignment(kThumb2)); 369 DCHECK_EQ(thunk_offset + thunk_size + CodeAlignmentSize(thunk_offset + thunk_size), 370 method3_header_offset); 371 ASSERT_TRUE(IsAligned<kArmAlignment>(thunk_offset)); 372 uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1 + 4u /* PC adjustment */); 373 ASSERT_EQ(diff & 1u, 0u); 374 ASSERT_GE(diff, 16 * MB - (1u << 9)); // Simple encoding, unknown bits fit into the low 8 bits. 375 auto expected_code = GenNopsAndBl(2u, 0xf3ffd700 | ((diff >> 1) & 0xffu)); 376 EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); 377 CheckThunk(thunk_offset); 378 } 379 380 TEST_F(Thumb2RelativePatcherTest, CallOtherJustTooFarBefore) { 381 auto method3_raw_code = GenNopsAndBl(3u, kBlPlus0); 382 constexpr uint32_t bl_offset_in_method3 = 3u * 2u; // After NOPs. 383 ArrayRef<const uint8_t> method3_code(method3_raw_code); 384 ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size()); 385 LinkerPatch method3_patches[] = { 386 LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, 1u), 387 }; 388 389 constexpr uint32_t just_over_max_negative_disp = 16 * MB + 2 - 4u /* PC adjustment */; 390 bool thunk_in_gap = Create2MethodsWithGap(kNopCode, 391 ArrayRef<const LinkerPatch>(), 392 method3_code, 393 ArrayRef<const LinkerPatch>(method3_patches), 394 just_over_max_negative_disp - bl_offset_in_method3); 395 ASSERT_FALSE(thunk_in_gap); // There should be a thunk but it should be after the method2. 396 397 // Check linked code. 398 uint32_t method3_offset = GetMethodOffset(3u); 399 uint32_t thunk_offset = CompiledCode::AlignCode(method3_offset + method3_code.size(), kThumb2); 400 uint32_t diff = thunk_offset - (method3_offset + bl_offset_in_method3 + 4u /* PC adjustment */); 401 ASSERT_EQ(diff & 1u, 0u); 402 ASSERT_LT(diff >> 1, 1u << 8); // Simple encoding, (diff >> 1) fits into 8 bits. 403 auto expected_code = GenNopsAndBl(3u, kBlPlus0 | ((diff >> 1) & 0xffu)); 404 EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code))); 405 EXPECT_TRUE(CheckThunk(thunk_offset)); 406 } 407 408 TEST_F(Thumb2RelativePatcherTest, DexCacheReference1) { 409 TestDexCacheReference(0x00ff0000u, 0x00fcu); 410 ASSERT_LT(GetMethodOffset(1u), 0xfcu); 411 } 412 413 TEST_F(Thumb2RelativePatcherTest, DexCacheReference2) { 414 TestDexCacheReference(0x02ff0000u, 0x05fcu); 415 ASSERT_LT(GetMethodOffset(1u), 0xfcu); 416 } 417 418 TEST_F(Thumb2RelativePatcherTest, DexCacheReference3) { 419 TestDexCacheReference(0x08ff0000u, 0x08fcu); 420 ASSERT_LT(GetMethodOffset(1u), 0xfcu); 421 } 422 423 TEST_F(Thumb2RelativePatcherTest, DexCacheReference4) { 424 TestDexCacheReference(0xd0ff0000u, 0x60fcu); 425 ASSERT_LT(GetMethodOffset(1u), 0xfcu); 426 } 427 428 TEST_F(Thumb2RelativePatcherTest, StringReference1) { 429 TestStringReference(0x00ff00fcu); 430 ASSERT_LT(GetMethodOffset(1u), 0xfcu); 431 } 432 433 TEST_F(Thumb2RelativePatcherTest, StringReference2) { 434 TestStringReference(0x02ff05fcu); 435 ASSERT_LT(GetMethodOffset(1u), 0xfcu); 436 } 437 438 TEST_F(Thumb2RelativePatcherTest, StringReference3) { 439 TestStringReference(0x08ff08fcu); 440 ASSERT_LT(GetMethodOffset(1u), 0xfcu); 441 } 442 443 TEST_F(Thumb2RelativePatcherTest, StringReference4) { 444 TestStringReference(0xd0ff60fcu); 445 ASSERT_LT(GetMethodOffset(1u), 0xfcu); 446 } 447 448 } // namespace linker 449 } // namespace art 450