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