Home | History | Annotate | Download | only in arm
      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