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 
20 namespace art {
21 namespace linker {
22 
23 class Thumb2RelativePatcherTest : public RelativePatcherTest {
24  public:
Thumb2RelativePatcherTest()25   Thumb2RelativePatcherTest() : RelativePatcherTest(kThumb2, "default") { }
26 
27  protected:
28   static const uint8_t kCallRawCode[];
29   static const ArrayRef<const uint8_t> kCallCode;
30   static const uint8_t kNopRawCode[];
31   static const ArrayRef<const uint8_t> kNopCode;
32 
33   // Branches within range [-256, 256) can be created from these by adding the low 8 bits.
34   static constexpr uint32_t kBlPlus0 = 0xf000f800;
35   static constexpr uint32_t kBlMinus256 = 0xf7ffff00;
36 
37   // Special BL values.
38   static constexpr uint32_t kBlPlusMax = 0xf3ffd7ff;
39   static constexpr uint32_t kBlMinusMax = 0xf400d000;
40 
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)41   bool Create2MethodsWithGap(const ArrayRef<const uint8_t>& method1_code,
42                              const ArrayRef<const LinkerPatch>& method1_patches,
43                              const ArrayRef<const uint8_t>& method3_code,
44                              const ArrayRef<const LinkerPatch>& method3_patches,
45                              uint32_t distance_without_thunks) {
46     CHECK_EQ(distance_without_thunks % kArmAlignment, 0u);
47     const uint32_t method1_offset =
48         CompiledCode::AlignCode(kTrampolineSize, kThumb2) + sizeof(OatQuickMethodHeader);
49     AddCompiledMethod(MethodRef(1u), method1_code, method1_patches);
50 
51     // We want to put the method3 at a very precise offset.
52     const uint32_t method3_offset = method1_offset + distance_without_thunks;
53     CHECK(IsAligned<kArmAlignment>(method3_offset - sizeof(OatQuickMethodHeader)));
54 
55     // Calculate size of method2 so that we put method3 at the correct place.
56     const uint32_t method2_offset =
57         CompiledCode::AlignCode(method1_offset + method1_code.size(), kThumb2) +
58         sizeof(OatQuickMethodHeader);
59     const uint32_t method2_size = (method3_offset - sizeof(OatQuickMethodHeader) - method2_offset);
60     std::vector<uint8_t> method2_raw_code(method2_size);
61     ArrayRef<const uint8_t> method2_code(method2_raw_code);
62     AddCompiledMethod(MethodRef(2u), method2_code, ArrayRef<const LinkerPatch>());
63 
64     AddCompiledMethod(MethodRef(3u), method3_code, method3_patches);
65 
66     Link();
67 
68     // Check assumptions.
69     CHECK_EQ(GetMethodOffset(1), method1_offset);
70     CHECK_EQ(GetMethodOffset(2), method2_offset);
71     auto result3 = method_offset_map_.FindMethodOffset(MethodRef(3));
72     CHECK(result3.first);
73     // There may be a thunk before method2.
74     if (result3.second == method3_offset + 1 /* thumb mode */) {
75       return false;  // No thunk.
76     } else {
77       uint32_t aligned_thunk_size = CompiledCode::AlignCode(ThunkSize(), kThumb2);
78       CHECK_EQ(result3.second, method3_offset + aligned_thunk_size + 1 /* thumb mode */);
79       return true;   // Thunk present.
80     }
81   }
82 
GetMethodOffset(uint32_t method_idx)83   uint32_t GetMethodOffset(uint32_t method_idx) {
84     auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
85     CHECK(result.first);
86     CHECK_NE(result.second & 1u, 0u);
87     return result.second - 1 /* thumb mode */;
88   }
89 
ThunkSize()90   uint32_t ThunkSize() {
91     return static_cast<Thumb2RelativePatcher*>(patcher_.get())->thunk_code_.size();
92   }
93 
CheckThunk(uint32_t thunk_offset)94   bool CheckThunk(uint32_t thunk_offset) {
95     Thumb2RelativePatcher* patcher = static_cast<Thumb2RelativePatcher*>(patcher_.get());
96     ArrayRef<const uint8_t> expected_code(patcher->thunk_code_);
97     if (output_.size() < thunk_offset + expected_code.size()) {
98       LOG(ERROR) << "output_.size() == " << output_.size() << " < "
99           << "thunk_offset + expected_code.size() == " << (thunk_offset + expected_code.size());
100       return false;
101     }
102     ArrayRef<const uint8_t> linked_code(&output_[thunk_offset], expected_code.size());
103     if (linked_code == expected_code) {
104       return true;
105     }
106     // Log failure info.
107     DumpDiff(expected_code, linked_code);
108     return false;
109   }
110 
GenNopsAndBl(size_t num_nops,uint32_t bl)111   std::vector<uint8_t> GenNopsAndBl(size_t num_nops, uint32_t bl) {
112     std::vector<uint8_t> result;
113     result.reserve(num_nops * 2u + 4u);
114     for (size_t i = 0; i != num_nops; ++i) {
115       result.push_back(0x00);
116       result.push_back(0xbf);
117     }
118     result.push_back(static_cast<uint8_t>(bl >> 16));
119     result.push_back(static_cast<uint8_t>(bl >> 24));
120     result.push_back(static_cast<uint8_t>(bl));
121     result.push_back(static_cast<uint8_t>(bl >> 8));
122     return result;
123   }
124 
TestDexCachereference(uint32_t dex_cache_arrays_begin,uint32_t element_offset)125   void TestDexCachereference(uint32_t dex_cache_arrays_begin, uint32_t element_offset) {
126     dex_cache_arrays_begin_ = dex_cache_arrays_begin;
127     static const uint8_t raw_code[] = {
128         0x40, 0xf2, 0x00, 0x00,   // MOVW r0, #0 (placeholder)
129         0xc0, 0xf2, 0x00, 0x00,   // MOVT r0, #0 (placeholder)
130         0x78, 0x44,               // ADD r0, pc
131     };
132     constexpr uint32_t pc_insn_offset = 8u;
133     const ArrayRef<const uint8_t> code(raw_code);
134     LinkerPatch patches[] = {
135         LinkerPatch::DexCacheArrayPatch(0u, nullptr, pc_insn_offset, element_offset),
136         LinkerPatch::DexCacheArrayPatch(4u, nullptr, pc_insn_offset, element_offset),
137     };
138     AddCompiledMethod(MethodRef(1u), code, ArrayRef<const LinkerPatch>(patches));
139     Link();
140 
141     uint32_t method1_offset = GetMethodOffset(1u);
142     uint32_t pc_base_offset = method1_offset + pc_insn_offset + 4u /* PC adjustment */;
143     uint32_t diff = dex_cache_arrays_begin_ + element_offset - pc_base_offset;
144     // Distribute the bits of the diff between the MOVW and MOVT:
145     uint32_t diffw = diff & 0xffffu;
146     uint32_t difft = diff >> 16;
147     uint32_t movw = 0xf2400000u |           // MOVW r0, #0 (placeholder),
148         ((diffw & 0xf000u) << (16 - 12)) |  // move imm4 from bits 12-15 to bits 16-19,
149         ((diffw & 0x0800u) << (26 - 11)) |  // move imm from bit 11 to bit 26,
150         ((diffw & 0x0700u) << (12 - 8)) |   // move imm3 from bits 8-10 to bits 12-14,
151         ((diffw & 0x00ffu));                // keep imm8 at bits 0-7.
152     uint32_t movt = 0xf2c00000u |           // MOVT r0, #0 (placeholder),
153         ((difft & 0xf000u) << (16 - 12)) |  // move imm4 from bits 12-15 to bits 16-19,
154         ((difft & 0x0800u) << (26 - 11)) |  // move imm from bit 11 to bit 26,
155         ((difft & 0x0700u) << (12 - 8)) |   // move imm3 from bits 8-10 to bits 12-14,
156         ((difft & 0x00ffu));                // keep imm8 at bits 0-7.
157     const uint8_t expected_code[] = {
158         static_cast<uint8_t>(movw >> 16), static_cast<uint8_t>(movw >> 24),
159         static_cast<uint8_t>(movw >> 0), static_cast<uint8_t>(movw >> 8),
160         static_cast<uint8_t>(movt >> 16), static_cast<uint8_t>(movt >> 24),
161         static_cast<uint8_t>(movt >> 0), static_cast<uint8_t>(movt >> 8),
162         0x78, 0x44,
163     };
164     EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
165   }
166 };
167 
168 const uint8_t Thumb2RelativePatcherTest::kCallRawCode[] = {
169     0x00, 0xf0, 0x00, 0xf8
170 };
171 
172 const ArrayRef<const uint8_t> Thumb2RelativePatcherTest::kCallCode(kCallRawCode);
173 
174 const uint8_t Thumb2RelativePatcherTest::kNopRawCode[] = {
175     0x00, 0xbf
176 };
177 
178 const ArrayRef<const uint8_t> Thumb2RelativePatcherTest::kNopCode(kNopRawCode);
179 
TEST_F(Thumb2RelativePatcherTest,CallSelf)180 TEST_F(Thumb2RelativePatcherTest, CallSelf) {
181   LinkerPatch patches[] = {
182       LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
183   };
184   AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
185   Link();
186 
187   static const uint8_t expected_code[] = {
188       0xff, 0xf7, 0xfe, 0xff
189   };
190   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
191 }
192 
TEST_F(Thumb2RelativePatcherTest,CallOther)193 TEST_F(Thumb2RelativePatcherTest, CallOther) {
194   LinkerPatch method1_patches[] = {
195       LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
196   };
197   AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(method1_patches));
198   LinkerPatch method2_patches[] = {
199       LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
200   };
201   AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<const LinkerPatch>(method2_patches));
202   Link();
203 
204   uint32_t method1_offset = GetMethodOffset(1u);
205   uint32_t method2_offset = GetMethodOffset(2u);
206   uint32_t diff_after = method2_offset - (method1_offset + 4u /* PC adjustment */);
207   ASSERT_EQ(diff_after & 1u, 0u);
208   ASSERT_LT(diff_after >> 1, 1u << 8);  // Simple encoding, (diff_after >> 1) fits into 8 bits.
209   static const uint8_t method1_expected_code[] = {
210       0x00, 0xf0, static_cast<uint8_t>(diff_after >> 1), 0xf8
211   };
212   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code)));
213   uint32_t diff_before = method1_offset - (method2_offset + 4u /* PC adjustment */);
214   ASSERT_EQ(diff_before & 1u, 0u);
215   ASSERT_GE(diff_before, -1u << 9);  // Simple encoding, -256 <= (diff >> 1) < 0.
216   auto method2_expected_code = GenNopsAndBl(0u, kBlMinus256 | ((diff_before >> 1) & 0xffu));
217   EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code)));
218 }
219 
TEST_F(Thumb2RelativePatcherTest,CallTrampoline)220 TEST_F(Thumb2RelativePatcherTest, CallTrampoline) {
221   LinkerPatch patches[] = {
222       LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
223   };
224   AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
225   Link();
226 
227   uint32_t method1_offset = GetMethodOffset(1u);
228   uint32_t diff = kTrampolineOffset - (method1_offset + 4u);
229   ASSERT_EQ(diff & 1u, 0u);
230   ASSERT_GE(diff, -1u << 9);  // Simple encoding, -256 <= (diff >> 1) < 0 (checked as unsigned).
231   auto expected_code = GenNopsAndBl(0u, kBlMinus256 | ((diff >> 1) & 0xffu));
232   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
233 }
234 
TEST_F(Thumb2RelativePatcherTest,CallOtherAlmostTooFarAfter)235 TEST_F(Thumb2RelativePatcherTest, CallOtherAlmostTooFarAfter) {
236   auto method1_raw_code = GenNopsAndBl(3u, kBlPlus0);
237   constexpr uint32_t bl_offset_in_method1 = 3u * 2u;  // After NOPs.
238   ArrayRef<const uint8_t> method1_code(method1_raw_code);
239   ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
240   LinkerPatch method1_patches[] = {
241       LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, 3u),
242   };
243 
244   constexpr uint32_t max_positive_disp = 16 * MB - 2u + 4u /* PC adjustment */;
245   bool thunk_in_gap = Create2MethodsWithGap(method1_code, method1_patches,
246                                             kNopCode, ArrayRef<const LinkerPatch>(),
247                                             bl_offset_in_method1 + max_positive_disp);
248   ASSERT_FALSE(thunk_in_gap);  // There should be no thunk.
249 
250   // Check linked code.
251   auto expected_code = GenNopsAndBl(3u, kBlPlusMax);
252   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
253 }
254 
TEST_F(Thumb2RelativePatcherTest,CallOtherAlmostTooFarBefore)255 TEST_F(Thumb2RelativePatcherTest, CallOtherAlmostTooFarBefore) {
256   auto method3_raw_code = GenNopsAndBl(2u, kBlPlus0);
257   constexpr uint32_t bl_offset_in_method3 = 2u * 2u;  // After NOPs.
258   ArrayRef<const uint8_t> method3_code(method3_raw_code);
259   ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size());
260   LinkerPatch method3_patches[] = {
261       LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, 1u),
262   };
263 
264   constexpr uint32_t just_over_max_negative_disp = 16 * MB - 4u /* PC adjustment */;
265   bool thunk_in_gap = Create2MethodsWithGap(kNopCode, ArrayRef<const LinkerPatch>(),
266                                             method3_code, method3_patches,
267                                             just_over_max_negative_disp - bl_offset_in_method3);
268   ASSERT_FALSE(thunk_in_gap);  // There should be no thunk.
269 
270   // Check linked code.
271   auto expected_code = GenNopsAndBl(2u, kBlMinusMax);
272   EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code)));
273 }
274 
TEST_F(Thumb2RelativePatcherTest,CallOtherJustTooFarAfter)275 TEST_F(Thumb2RelativePatcherTest, CallOtherJustTooFarAfter) {
276   auto method1_raw_code = GenNopsAndBl(2u, kBlPlus0);
277   constexpr uint32_t bl_offset_in_method1 = 2u * 2u;  // After NOPs.
278   ArrayRef<const uint8_t> method1_code(method1_raw_code);
279   ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
280   LinkerPatch method1_patches[] = {
281       LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, 3u),
282   };
283 
284   constexpr uint32_t just_over_max_positive_disp = 16 * MB + 4u /* PC adjustment */;
285   bool thunk_in_gap = Create2MethodsWithGap(method1_code, method1_patches,
286                                             kNopCode, ArrayRef<const LinkerPatch>(),
287                                             bl_offset_in_method1 + just_over_max_positive_disp);
288   ASSERT_TRUE(thunk_in_gap);
289 
290   uint32_t method1_offset = GetMethodOffset(1u);
291   uint32_t method3_offset = GetMethodOffset(3u);
292   uint32_t method3_header_offset = method3_offset - sizeof(OatQuickMethodHeader);
293   ASSERT_TRUE(IsAligned<kArmAlignment>(method3_header_offset));
294   uint32_t thunk_offset = method3_header_offset - CompiledCode::AlignCode(ThunkSize(), kThumb2);
295   ASSERT_TRUE(IsAligned<kArmAlignment>(thunk_offset));
296   uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1 + 4u /* PC adjustment */);
297   ASSERT_EQ(diff & 1u, 0u);
298   ASSERT_GE(diff, 16 * MB - (1u << 9));  // Simple encoding, unknown bits fit into the low 8 bits.
299   auto expected_code = GenNopsAndBl(2u, 0xf3ffd700 | ((diff >> 1) & 0xffu));
300   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
301   CheckThunk(thunk_offset);
302 }
303 
TEST_F(Thumb2RelativePatcherTest,CallOtherJustTooFarBefore)304 TEST_F(Thumb2RelativePatcherTest, CallOtherJustTooFarBefore) {
305   auto method3_raw_code = GenNopsAndBl(3u, kBlPlus0);
306   constexpr uint32_t bl_offset_in_method3 = 3u * 2u;  // After NOPs.
307   ArrayRef<const uint8_t> method3_code(method3_raw_code);
308   ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size());
309   LinkerPatch method3_patches[] = {
310       LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, 1u),
311   };
312 
313   constexpr uint32_t just_over_max_negative_disp = 16 * MB + 2 - 4u /* PC adjustment */;
314   bool thunk_in_gap = Create2MethodsWithGap(kNopCode, ArrayRef<const LinkerPatch>(),
315                                             method3_code, method3_patches,
316                                             just_over_max_negative_disp - bl_offset_in_method3);
317   ASSERT_FALSE(thunk_in_gap);  // There should be a thunk but it should be after the method2.
318 
319   // Check linked code.
320   uint32_t method3_offset = GetMethodOffset(3u);
321   uint32_t thunk_offset = CompiledCode::AlignCode(method3_offset + method3_code.size(), kThumb2);
322   uint32_t diff = thunk_offset - (method3_offset + bl_offset_in_method3 + 4u /* PC adjustment */);
323   ASSERT_EQ(diff & 1u, 0u);
324   ASSERT_LT(diff >> 1, 1u << 8);  // Simple encoding, (diff >> 1) fits into 8 bits.
325   auto expected_code = GenNopsAndBl(3u, kBlPlus0 | ((diff >> 1) & 0xffu));
326   EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code)));
327   EXPECT_TRUE(CheckThunk(thunk_offset));
328 }
329 
TEST_F(Thumb2RelativePatcherTest,DexCacheReferenceImm8)330 TEST_F(Thumb2RelativePatcherTest, DexCacheReferenceImm8) {
331   TestDexCachereference(0x00ff0000u, 0x00fcu);
332   ASSERT_LT(GetMethodOffset(1u), 0xfcu);
333 }
334 
TEST_F(Thumb2RelativePatcherTest,DexCacheReferenceImm3)335 TEST_F(Thumb2RelativePatcherTest, DexCacheReferenceImm3) {
336   TestDexCachereference(0x02ff0000u, 0x05fcu);
337   ASSERT_LT(GetMethodOffset(1u), 0xfcu);
338 }
339 
TEST_F(Thumb2RelativePatcherTest,DexCacheReferenceImm)340 TEST_F(Thumb2RelativePatcherTest, DexCacheReferenceImm) {
341   TestDexCachereference(0x08ff0000u, 0x08fcu);
342   ASSERT_LT(GetMethodOffset(1u), 0xfcu);
343 }
344 
TEST_F(Thumb2RelativePatcherTest,DexCacheReferenceimm4)345 TEST_F(Thumb2RelativePatcherTest, DexCacheReferenceimm4) {
346   TestDexCachereference(0xd0ff0000u, 0x60fcu);
347   ASSERT_LT(GetMethodOffset(1u), 0xfcu);
348 }
349 
350 }  // namespace linker
351 }  // namespace art
352