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