1 /*
2 * Copyright (C) 2016 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 "multi_oat_relative_patcher.h"
18
19 #include "compiled_method.h"
20 #include "debug/method_debug_info.h"
21 #include "gtest/gtest.h"
22 #include "linker/linker_patch.h"
23 #include "stream/vector_output_stream.h"
24
25 namespace art {
26 namespace linker {
27
28 static const MethodReference kNullMethodRef = MethodReference(nullptr, 0u);
29
30 class MultiOatRelativePatcherTest : public testing::Test {
31 protected:
32 class MockPatcher : public RelativePatcher {
33 public:
MockPatcher()34 MockPatcher() { }
35
ReserveSpace(uint32_t offset,const CompiledMethod * compiled_method ATTRIBUTE_UNUSED,MethodReference method_ref)36 uint32_t ReserveSpace(uint32_t offset,
37 const CompiledMethod* compiled_method ATTRIBUTE_UNUSED,
38 MethodReference method_ref) override {
39 last_reserve_offset_ = offset;
40 last_reserve_method_ = method_ref;
41 offset += next_reserve_adjustment_;
42 next_reserve_adjustment_ = 0u;
43 return offset;
44 }
45
ReserveSpaceEnd(uint32_t offset)46 uint32_t ReserveSpaceEnd(uint32_t offset) override {
47 last_reserve_offset_ = offset;
48 last_reserve_method_ = kNullMethodRef;
49 offset += next_reserve_adjustment_;
50 next_reserve_adjustment_ = 0u;
51 return offset;
52 }
53
WriteThunks(OutputStream * out,uint32_t offset)54 uint32_t WriteThunks(OutputStream* out, uint32_t offset) override {
55 last_write_offset_ = offset;
56 if (next_write_alignment_ != 0u) {
57 offset += next_write_alignment_;
58 bool success = WriteCodeAlignment(out, next_write_alignment_);
59 CHECK(success);
60 next_write_alignment_ = 0u;
61 }
62 if (next_write_call_thunk_ != 0u) {
63 offset += next_write_call_thunk_;
64 std::vector<uint8_t> thunk(next_write_call_thunk_, 'c');
65 bool success = WriteThunk(out, ArrayRef<const uint8_t>(thunk));
66 CHECK(success);
67 next_write_call_thunk_ = 0u;
68 }
69 if (next_write_misc_thunk_ != 0u) {
70 offset += next_write_misc_thunk_;
71 std::vector<uint8_t> thunk(next_write_misc_thunk_, 'm');
72 bool success = WriteMiscThunk(out, ArrayRef<const uint8_t>(thunk));
73 CHECK(success);
74 next_write_misc_thunk_ = 0u;
75 }
76 return offset;
77 }
78
PatchCall(std::vector<uint8_t> * code ATTRIBUTE_UNUSED,uint32_t literal_offset,uint32_t patch_offset,uint32_t target_offset)79 void PatchCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
80 uint32_t literal_offset,
81 uint32_t patch_offset,
82 uint32_t target_offset) override {
83 last_literal_offset_ = literal_offset;
84 last_patch_offset_ = patch_offset;
85 last_target_offset_ = target_offset;
86 }
87
PatchPcRelativeReference(std::vector<uint8_t> * code ATTRIBUTE_UNUSED,const LinkerPatch & patch,uint32_t patch_offset,uint32_t target_offset)88 void PatchPcRelativeReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
89 const LinkerPatch& patch,
90 uint32_t patch_offset,
91 uint32_t target_offset) override {
92 last_literal_offset_ = patch.LiteralOffset();
93 last_patch_offset_ = patch_offset;
94 last_target_offset_ = target_offset;
95 }
96
PatchEntrypointCall(std::vector<uint8_t> * code ATTRIBUTE_UNUSED,const LinkerPatch & patch ATTRIBUTE_UNUSED,uint32_t patch_offset ATTRIBUTE_UNUSED)97 void PatchEntrypointCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
98 const LinkerPatch& patch ATTRIBUTE_UNUSED,
99 uint32_t patch_offset ATTRIBUTE_UNUSED) override {
100 LOG(FATAL) << "UNIMPLEMENTED";
101 }
102
PatchBakerReadBarrierBranch(std::vector<uint8_t> * code ATTRIBUTE_UNUSED,const LinkerPatch & patch ATTRIBUTE_UNUSED,uint32_t patch_offset ATTRIBUTE_UNUSED)103 void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
104 const LinkerPatch& patch ATTRIBUTE_UNUSED,
105 uint32_t patch_offset ATTRIBUTE_UNUSED) override {
106 LOG(FATAL) << "UNIMPLEMENTED";
107 }
108
GenerateThunkDebugInfo(uint32_t executable_offset ATTRIBUTE_UNUSED)109 std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(
110 uint32_t executable_offset ATTRIBUTE_UNUSED) override {
111 LOG(FATAL) << "UNIMPLEMENTED";
112 UNREACHABLE();
113 }
114
115 uint32_t last_reserve_offset_ = 0u;
116 MethodReference last_reserve_method_ = kNullMethodRef;
117 uint32_t next_reserve_adjustment_ = 0u;
118
119 uint32_t last_write_offset_ = 0u;
120 uint32_t next_write_alignment_ = 0u;
121 uint32_t next_write_call_thunk_ = 0u;
122 uint32_t next_write_misc_thunk_ = 0u;
123
124 uint32_t last_literal_offset_ = 0u;
125 uint32_t last_patch_offset_ = 0u;
126 uint32_t last_target_offset_ = 0u;
127 };
128
MultiOatRelativePatcherTest()129 MultiOatRelativePatcherTest()
130 : instruction_set_features_(InstructionSetFeatures::FromCppDefines()),
131 patcher_(kRuntimeISA, instruction_set_features_.get(), /* storage */ nullptr) {
132 std::unique_ptr<MockPatcher> mock(new MockPatcher());
133 mock_ = mock.get();
134 patcher_.relative_patcher_ = std::move(mock);
135 }
136
137 std::unique_ptr<const InstructionSetFeatures> instruction_set_features_;
138 MultiOatRelativePatcher patcher_;
139 MockPatcher* mock_;
140 };
141
TEST_F(MultiOatRelativePatcherTest,Offsets)142 TEST_F(MultiOatRelativePatcherTest, Offsets) {
143 const DexFile* dex_file = reinterpret_cast<const DexFile*>(1);
144 MethodReference ref1(dex_file, 1u);
145 MethodReference ref2(dex_file, 2u);
146 EXPECT_EQ(0u, patcher_.GetOffset(ref1));
147 EXPECT_EQ(0u, patcher_.GetOffset(ref2));
148
149 uint32_t adjustment1 = 0x1000;
150 patcher_.StartOatFile(adjustment1);
151 EXPECT_EQ(0u, patcher_.GetOffset(ref1));
152 EXPECT_EQ(0u, patcher_.GetOffset(ref2));
153
154 uint32_t off1 = 0x1234;
155 patcher_.SetOffset(ref1, off1);
156 EXPECT_EQ(off1, patcher_.GetOffset(ref1));
157 EXPECT_EQ(0u, patcher_.GetOffset(ref2));
158
159 uint32_t adjustment2 = 0x30000;
160 patcher_.StartOatFile(adjustment2);
161 EXPECT_EQ(off1 + adjustment1 - adjustment2, patcher_.GetOffset(ref1));
162 EXPECT_EQ(0u, patcher_.GetOffset(ref2));
163
164 uint32_t off2 = 0x4321;
165 patcher_.SetOffset(ref2, off2);
166 EXPECT_EQ(off1 + adjustment1 - adjustment2, patcher_.GetOffset(ref1));
167 EXPECT_EQ(off2, patcher_.GetOffset(ref2));
168
169 uint32_t adjustment3 = 0x78000;
170 patcher_.StartOatFile(adjustment3);
171 EXPECT_EQ(off1 + adjustment1 - adjustment3, patcher_.GetOffset(ref1));
172 EXPECT_EQ(off2 + adjustment2 - adjustment3, patcher_.GetOffset(ref2));
173 }
174
TEST_F(MultiOatRelativePatcherTest,OffsetsInReserve)175 TEST_F(MultiOatRelativePatcherTest, OffsetsInReserve) {
176 const DexFile* dex_file = reinterpret_cast<const DexFile*>(1);
177 MethodReference ref1(dex_file, 1u);
178 MethodReference ref2(dex_file, 2u);
179 MethodReference ref3(dex_file, 3u);
180 const CompiledMethod* method = reinterpret_cast<const CompiledMethod*>(-1);
181
182 uint32_t adjustment1 = 0x1000;
183 patcher_.StartOatFile(adjustment1);
184
185 uint32_t method1_offset = 0x100;
186 uint32_t method1_offset_check = patcher_.ReserveSpace(method1_offset, method, ref1);
187 ASSERT_EQ(adjustment1 + method1_offset, mock_->last_reserve_offset_);
188 ASSERT_TRUE(ref1 == mock_->last_reserve_method_);
189 ASSERT_EQ(method1_offset, method1_offset_check);
190
191 uint32_t method2_offset = 0x1230;
192 uint32_t method2_reserve_adjustment = 0x10;
193 mock_->next_reserve_adjustment_ = method2_reserve_adjustment;
194 uint32_t method2_offset_adjusted = patcher_.ReserveSpace(method2_offset, method, ref2);
195 ASSERT_EQ(adjustment1 + method2_offset, mock_->last_reserve_offset_);
196 ASSERT_TRUE(ref2 == mock_->last_reserve_method_);
197 ASSERT_EQ(method2_offset + method2_reserve_adjustment, method2_offset_adjusted);
198
199 uint32_t end1_offset = 0x4320;
200 uint32_t end1_offset_check = patcher_.ReserveSpaceEnd(end1_offset);
201 ASSERT_EQ(adjustment1 + end1_offset, mock_->last_reserve_offset_);
202 ASSERT_TRUE(kNullMethodRef == mock_->last_reserve_method_);
203 ASSERT_EQ(end1_offset, end1_offset_check);
204
205 uint32_t adjustment2 = 0xd000;
206 patcher_.StartOatFile(adjustment2);
207
208 uint32_t method3_offset = 0xf00;
209 uint32_t method3_offset_check = patcher_.ReserveSpace(method3_offset, method, ref3);
210 ASSERT_EQ(adjustment2 + method3_offset, mock_->last_reserve_offset_);
211 ASSERT_TRUE(ref3 == mock_->last_reserve_method_);
212 ASSERT_EQ(method3_offset, method3_offset_check);
213
214 uint32_t end2_offset = 0x2400;
215 uint32_t end2_reserve_adjustment = 0x20;
216 mock_->next_reserve_adjustment_ = end2_reserve_adjustment;
217 uint32_t end2_offset_adjusted = patcher_.ReserveSpaceEnd(end2_offset);
218 ASSERT_EQ(adjustment2 + end2_offset, mock_->last_reserve_offset_);
219 ASSERT_TRUE(kNullMethodRef == mock_->last_reserve_method_);
220 ASSERT_EQ(end2_offset + end2_reserve_adjustment, end2_offset_adjusted);
221 }
222
TEST_F(MultiOatRelativePatcherTest,Write)223 TEST_F(MultiOatRelativePatcherTest, Write) {
224 std::vector<uint8_t> output;
225 VectorOutputStream vos("output", &output);
226
227 uint32_t adjustment1 = 0x1000;
228 patcher_.StartOatFile(adjustment1);
229
230 uint32_t method1_offset = 0x100;
231 uint32_t method1_offset_check = patcher_.WriteThunks(&vos, method1_offset);
232 ASSERT_EQ(adjustment1 + method1_offset, mock_->last_write_offset_);
233 ASSERT_EQ(method1_offset, method1_offset_check);
234 vos.WriteFully("1", 1); // Mark method1.
235
236 uint32_t method2_offset = 0x1230;
237 uint32_t method2_alignment_size = 1;
238 uint32_t method2_call_thunk_size = 2;
239 mock_->next_write_alignment_ = method2_alignment_size;
240 mock_->next_write_call_thunk_ = method2_call_thunk_size;
241 uint32_t method2_offset_adjusted = patcher_.WriteThunks(&vos, method2_offset);
242 ASSERT_EQ(adjustment1 + method2_offset, mock_->last_write_offset_);
243 ASSERT_EQ(method2_offset + method2_alignment_size + method2_call_thunk_size,
244 method2_offset_adjusted);
245 vos.WriteFully("2", 1); // Mark method2.
246
247 EXPECT_EQ(method2_alignment_size, patcher_.CodeAlignmentSize());
248 EXPECT_EQ(method2_call_thunk_size, patcher_.RelativeCallThunksSize());
249
250 uint32_t adjustment2 = 0xd000;
251 patcher_.StartOatFile(adjustment2);
252
253 uint32_t method3_offset = 0xf00;
254 uint32_t method3_alignment_size = 2;
255 uint32_t method3_misc_thunk_size = 1;
256 mock_->next_write_alignment_ = method3_alignment_size;
257 mock_->next_write_misc_thunk_ = method3_misc_thunk_size;
258 uint32_t method3_offset_adjusted = patcher_.WriteThunks(&vos, method3_offset);
259 ASSERT_EQ(adjustment2 + method3_offset, mock_->last_write_offset_);
260 ASSERT_EQ(method3_offset + method3_alignment_size + method3_misc_thunk_size,
261 method3_offset_adjusted);
262 vos.WriteFully("3", 1); // Mark method3.
263
264 EXPECT_EQ(method3_alignment_size, patcher_.CodeAlignmentSize());
265 EXPECT_EQ(method3_misc_thunk_size, patcher_.MiscThunksSize());
266
267 uint8_t expected_output[] = {
268 '1',
269 0, 'c', 'c', '2',
270 0, 0, 'm', '3',
271 };
272 ASSERT_EQ(arraysize(expected_output), output.size());
273 for (size_t i = 0; i != arraysize(expected_output); ++i) {
274 ASSERT_EQ(expected_output[i], output[i]) << i;
275 }
276 }
277
TEST_F(MultiOatRelativePatcherTest,Patch)278 TEST_F(MultiOatRelativePatcherTest, Patch) {
279 std::vector<uint8_t> code(16);
280
281 uint32_t adjustment1 = 0x1000;
282 patcher_.StartOatFile(adjustment1);
283
284 uint32_t method1_literal_offset = 4u;
285 uint32_t method1_patch_offset = 0x1234u;
286 uint32_t method1_target_offset = 0x8888u;
287 patcher_.PatchCall(&code, method1_literal_offset, method1_patch_offset, method1_target_offset);
288 DCHECK_EQ(method1_literal_offset, mock_->last_literal_offset_);
289 DCHECK_EQ(method1_patch_offset + adjustment1, mock_->last_patch_offset_);
290 DCHECK_EQ(method1_target_offset + adjustment1, mock_->last_target_offset_);
291
292 uint32_t method2_literal_offset = 12u;
293 uint32_t method2_patch_offset = 0x7654u;
294 uint32_t method2_target_offset = 0xccccu;
295 LinkerPatch method2_patch =
296 LinkerPatch::StringBssEntryPatch(method2_literal_offset, nullptr, 0u, 1u);
297 patcher_.PatchPcRelativeReference(
298 &code, method2_patch, method2_patch_offset, method2_target_offset);
299 DCHECK_EQ(method2_literal_offset, mock_->last_literal_offset_);
300 DCHECK_EQ(method2_patch_offset + adjustment1, mock_->last_patch_offset_);
301 DCHECK_EQ(method2_target_offset + adjustment1, mock_->last_target_offset_);
302
303 uint32_t adjustment2 = 0xd000;
304 patcher_.StartOatFile(adjustment2);
305
306 uint32_t method3_literal_offset = 8u;
307 uint32_t method3_patch_offset = 0x108u;
308 uint32_t method3_target_offset = 0x200u;
309 patcher_.PatchCall(&code, method3_literal_offset, method3_patch_offset, method3_target_offset);
310 DCHECK_EQ(method3_literal_offset, mock_->last_literal_offset_);
311 DCHECK_EQ(method3_patch_offset + adjustment2, mock_->last_patch_offset_);
312 DCHECK_EQ(method3_target_offset + adjustment2, mock_->last_target_offset_);
313 }
314
315 } // namespace linker
316 } // namespace art
317