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/x86/relative_patcher_x86.h"
19
20namespace art {
21namespace linker {
22
23class X86RelativePatcherTest : public RelativePatcherTest {
24 public:
25  X86RelativePatcherTest() : RelativePatcherTest(kX86, "default") { }
26
27 protected:
28  static const uint8_t kCallRawCode[];
29  static const ArrayRef<const uint8_t> kCallCode;
30
31  uint32_t GetMethodOffset(uint32_t method_idx) {
32    auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
33    CHECK(result.first);
34    return result.second;
35  }
36};
37
38const uint8_t X86RelativePatcherTest::kCallRawCode[] = {
39    0xe8, 0x00, 0x01, 0x00, 0x00
40};
41
42const ArrayRef<const uint8_t> X86RelativePatcherTest::kCallCode(kCallRawCode);
43
44TEST_F(X86RelativePatcherTest, CallSelf) {
45  LinkerPatch patches[] = {
46      LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 1u),
47  };
48  AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
49  Link();
50
51  static const uint8_t expected_code[] = {
52      0xe8, 0xfb, 0xff, 0xff, 0xff
53  };
54  EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
55}
56
57TEST_F(X86RelativePatcherTest, CallOther) {
58  LinkerPatch method1_patches[] = {
59      LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 2u),
60  };
61  AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(method1_patches));
62  LinkerPatch method2_patches[] = {
63      LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 1u),
64  };
65  AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<const LinkerPatch>(method2_patches));
66  Link();
67
68  uint32_t method1_offset = GetMethodOffset(1u);
69  uint32_t method2_offset = GetMethodOffset(2u);
70  uint32_t diff_after = method2_offset - (method1_offset + kCallCode.size() /* PC adjustment */);
71  static const uint8_t method1_expected_code[] = {
72      0xe8,
73      static_cast<uint8_t>(diff_after),
74      static_cast<uint8_t>(diff_after >> 8),
75      static_cast<uint8_t>(diff_after >> 16),
76      static_cast<uint8_t>(diff_after >> 24)
77  };
78  EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code)));
79  uint32_t diff_before = method1_offset - (method2_offset + kCallCode.size() /* PC adjustment */);
80  static const uint8_t method2_expected_code[] = {
81      0xe8,
82      static_cast<uint8_t>(diff_before),
83      static_cast<uint8_t>(diff_before >> 8),
84      static_cast<uint8_t>(diff_before >> 16),
85      static_cast<uint8_t>(diff_before >> 24)
86  };
87  EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code)));
88}
89
90TEST_F(X86RelativePatcherTest, CallTrampoline) {
91  LinkerPatch patches[] = {
92      LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 2u),
93  };
94  AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
95  Link();
96
97  auto result = method_offset_map_.FindMethodOffset(MethodRef(1));
98  ASSERT_TRUE(result.first);
99  uint32_t diff = kTrampolineOffset - (result.second + kCallCode.size());
100  static const uint8_t expected_code[] = {
101      0xe8,
102      static_cast<uint8_t>(diff),
103      static_cast<uint8_t>(diff >> 8),
104      static_cast<uint8_t>(diff >> 16),
105      static_cast<uint8_t>(diff >> 24)
106  };
107  EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
108}
109
110TEST_F(X86RelativePatcherTest, DexCacheReference) {
111  dex_cache_arrays_begin_ = 0x12345678;
112  constexpr size_t kElementOffset = 0x1234;
113  static const uint8_t raw_code[] = {
114      0xe8, 0x00, 0x00, 0x00, 0x00,         // call +0
115      0x5b,                                 // pop ebx
116      0x8b, 0x83, 0x00, 0x01, 0x00, 0x00,   // mov eax, [ebx + 256 (kDummy32BitValue)]
117  };
118  constexpr uint32_t anchor_offset = 5u;  // After call +0.
119  ArrayRef<const uint8_t> code(raw_code);
120  LinkerPatch patches[] = {
121      LinkerPatch::DexCacheArrayPatch(code.size() - 4u, nullptr, anchor_offset, kElementOffset),
122  };
123  AddCompiledMethod(MethodRef(1u), code, ArrayRef<const LinkerPatch>(patches));
124  Link();
125
126  auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
127  ASSERT_TRUE(result.first);
128  uint32_t diff =
129      dex_cache_arrays_begin_ + kElementOffset - (result.second + anchor_offset);
130  static const uint8_t expected_code[] = {
131      0xe8, 0x00, 0x00, 0x00, 0x00,         // call +0
132      0x5b,                                 // pop ebx
133      0x8b, 0x83,                           // mov eax, [ebx + diff]
134      static_cast<uint8_t>(diff),
135      static_cast<uint8_t>(diff >> 8),
136      static_cast<uint8_t>(diff >> 16),
137      static_cast<uint8_t>(diff >> 24)
138  };
139  EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
140}
141
142TEST_F(X86RelativePatcherTest, StringReference) {
143  constexpr uint32_t kStringIndex = 1u;
144  constexpr uint32_t kStringOffset = 0x12345678;
145  string_index_to_offset_map_.Put(kStringIndex, kStringOffset);
146  static const uint8_t raw_code[] = {
147      0xe8, 0x00, 0x00, 0x00, 0x00,         // call +0
148      0x5b,                                 // pop ebx
149      0x8d, 0x83, 0x00, 0x01, 0x00, 0x00,   // lea eax, [ebx + 256 (kDummy32BitValue)]
150  };
151  constexpr uint32_t anchor_offset = 5u;  // After call +0.
152  ArrayRef<const uint8_t> code(raw_code);
153  LinkerPatch patches[] = {
154      LinkerPatch::RelativeStringPatch(code.size() - 4u, nullptr, anchor_offset, kStringIndex),
155  };
156  AddCompiledMethod(MethodRef(1u), code, ArrayRef<const LinkerPatch>(patches));
157  Link();
158
159  auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
160  ASSERT_TRUE(result.first);
161  uint32_t diff = kStringOffset - (result.second + anchor_offset);
162  static const uint8_t expected_code[] = {
163      0xe8, 0x00, 0x00, 0x00, 0x00,         // call +0
164      0x5b,                                 // pop ebx
165      0x8d, 0x83,                           // lea eax, [ebx + diff]
166      static_cast<uint8_t>(diff),
167      static_cast<uint8_t>(diff >> 8),
168      static_cast<uint8_t>(diff >> 16),
169      static_cast<uint8_t>(diff >> 24)
170  };
171  EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
172}
173
174}  // namespace linker
175}  // namespace art
176