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 "linker/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
PatchBakerReadBarrierBranch(std::vector<uint8_t> * code ATTRIBUTE_UNUSED,const LinkerPatch & patch ATTRIBUTE_UNUSED,uint32_t patch_offset ATTRIBUTE_UNUSED)97 void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
98 const LinkerPatch& patch ATTRIBUTE_UNUSED,
99 uint32_t patch_offset ATTRIBUTE_UNUSED) {
100 LOG(FATAL) << "UNIMPLEMENTED";
101 }
102
GenerateThunkDebugInfo(uint32_t executable_offset ATTRIBUTE_UNUSED)103 std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(
104 uint32_t executable_offset ATTRIBUTE_UNUSED) {
105 LOG(FATAL) << "UNIMPLEMENTED";
106 UNREACHABLE();
107 }
108
109 uint32_t last_reserve_offset_ = 0u;
110 MethodReference last_reserve_method_ = kNullMethodRef;
111 uint32_t next_reserve_adjustment_ = 0u;
112
113 uint32_t last_write_offset_ = 0u;
114 uint32_t next_write_alignment_ = 0u;
115 uint32_t next_write_call_thunk_ = 0u;
116 uint32_t next_write_misc_thunk_ = 0u;
117
118 uint32_t last_literal_offset_ = 0u;
119 uint32_t last_patch_offset_ = 0u;
120 uint32_t last_target_offset_ = 0u;
121 };
122
MultiOatRelativePatcherTest()123 MultiOatRelativePatcherTest()
124 : instruction_set_features_(InstructionSetFeatures::FromCppDefines()),
125 patcher_(kRuntimeISA, instruction_set_features_.get()) {
126 std::unique_ptr<MockPatcher> mock(new MockPatcher());
127 mock_ = mock.get();
128 patcher_.relative_patcher_ = std::move(mock);
129 }
130
131 std::unique_ptr<const InstructionSetFeatures> instruction_set_features_;
132 MultiOatRelativePatcher patcher_;
133 MockPatcher* mock_;
134 };
135
TEST_F(MultiOatRelativePatcherTest,Offsets)136 TEST_F(MultiOatRelativePatcherTest, Offsets) {
137 const DexFile* dex_file = reinterpret_cast<const DexFile*>(1);
138 MethodReference ref1(dex_file, 1u);
139 MethodReference ref2(dex_file, 2u);
140 EXPECT_EQ(0u, patcher_.GetOffset(ref1));
141 EXPECT_EQ(0u, patcher_.GetOffset(ref2));
142
143 uint32_t adjustment1 = 0x1000;
144 patcher_.StartOatFile(adjustment1);
145 EXPECT_EQ(0u, patcher_.GetOffset(ref1));
146 EXPECT_EQ(0u, patcher_.GetOffset(ref2));
147
148 uint32_t off1 = 0x1234;
149 patcher_.SetOffset(ref1, off1);
150 EXPECT_EQ(off1, patcher_.GetOffset(ref1));
151 EXPECT_EQ(0u, patcher_.GetOffset(ref2));
152
153 uint32_t adjustment2 = 0x30000;
154 patcher_.StartOatFile(adjustment2);
155 EXPECT_EQ(off1 + adjustment1 - adjustment2, patcher_.GetOffset(ref1));
156 EXPECT_EQ(0u, patcher_.GetOffset(ref2));
157
158 uint32_t off2 = 0x4321;
159 patcher_.SetOffset(ref2, off2);
160 EXPECT_EQ(off1 + adjustment1 - adjustment2, patcher_.GetOffset(ref1));
161 EXPECT_EQ(off2, patcher_.GetOffset(ref2));
162
163 uint32_t adjustment3 = 0x78000;
164 patcher_.StartOatFile(adjustment3);
165 EXPECT_EQ(off1 + adjustment1 - adjustment3, patcher_.GetOffset(ref1));
166 EXPECT_EQ(off2 + adjustment2 - adjustment3, patcher_.GetOffset(ref2));
167 }
168
TEST_F(MultiOatRelativePatcherTest,OffsetsInReserve)169 TEST_F(MultiOatRelativePatcherTest, OffsetsInReserve) {
170 const DexFile* dex_file = reinterpret_cast<const DexFile*>(1);
171 MethodReference ref1(dex_file, 1u);
172 MethodReference ref2(dex_file, 2u);
173 MethodReference ref3(dex_file, 3u);
174 const CompiledMethod* method = reinterpret_cast<const CompiledMethod*>(-1);
175
176 uint32_t adjustment1 = 0x1000;
177 patcher_.StartOatFile(adjustment1);
178
179 uint32_t method1_offset = 0x100;
180 uint32_t method1_offset_check = patcher_.ReserveSpace(method1_offset, method, ref1);
181 ASSERT_EQ(adjustment1 + method1_offset, mock_->last_reserve_offset_);
182 ASSERT_TRUE(ref1 == mock_->last_reserve_method_);
183 ASSERT_EQ(method1_offset, method1_offset_check);
184
185 uint32_t method2_offset = 0x1230;
186 uint32_t method2_reserve_adjustment = 0x10;
187 mock_->next_reserve_adjustment_ = method2_reserve_adjustment;
188 uint32_t method2_offset_adjusted = patcher_.ReserveSpace(method2_offset, method, ref2);
189 ASSERT_EQ(adjustment1 + method2_offset, mock_->last_reserve_offset_);
190 ASSERT_TRUE(ref2 == mock_->last_reserve_method_);
191 ASSERT_EQ(method2_offset + method2_reserve_adjustment, method2_offset_adjusted);
192
193 uint32_t end1_offset = 0x4320;
194 uint32_t end1_offset_check = patcher_.ReserveSpaceEnd(end1_offset);
195 ASSERT_EQ(adjustment1 + end1_offset, mock_->last_reserve_offset_);
196 ASSERT_TRUE(kNullMethodRef == mock_->last_reserve_method_);
197 ASSERT_EQ(end1_offset, end1_offset_check);
198
199 uint32_t adjustment2 = 0xd000;
200 patcher_.StartOatFile(adjustment2);
201
202 uint32_t method3_offset = 0xf00;
203 uint32_t method3_offset_check = patcher_.ReserveSpace(method3_offset, method, ref3);
204 ASSERT_EQ(adjustment2 + method3_offset, mock_->last_reserve_offset_);
205 ASSERT_TRUE(ref3 == mock_->last_reserve_method_);
206 ASSERT_EQ(method3_offset, method3_offset_check);
207
208 uint32_t end2_offset = 0x2400;
209 uint32_t end2_reserve_adjustment = 0x20;
210 mock_->next_reserve_adjustment_ = end2_reserve_adjustment;
211 uint32_t end2_offset_adjusted = patcher_.ReserveSpaceEnd(end2_offset);
212 ASSERT_EQ(adjustment2 + end2_offset, mock_->last_reserve_offset_);
213 ASSERT_TRUE(kNullMethodRef == mock_->last_reserve_method_);
214 ASSERT_EQ(end2_offset + end2_reserve_adjustment, end2_offset_adjusted);
215 }
216
TEST_F(MultiOatRelativePatcherTest,Write)217 TEST_F(MultiOatRelativePatcherTest, Write) {
218 std::vector<uint8_t> output;
219 VectorOutputStream vos("output", &output);
220
221 uint32_t adjustment1 = 0x1000;
222 patcher_.StartOatFile(adjustment1);
223
224 uint32_t method1_offset = 0x100;
225 uint32_t method1_offset_check = patcher_.WriteThunks(&vos, method1_offset);
226 ASSERT_EQ(adjustment1 + method1_offset, mock_->last_write_offset_);
227 ASSERT_EQ(method1_offset, method1_offset_check);
228 vos.WriteFully("1", 1); // Mark method1.
229
230 uint32_t method2_offset = 0x1230;
231 uint32_t method2_alignment_size = 1;
232 uint32_t method2_call_thunk_size = 2;
233 mock_->next_write_alignment_ = method2_alignment_size;
234 mock_->next_write_call_thunk_ = method2_call_thunk_size;
235 uint32_t method2_offset_adjusted = patcher_.WriteThunks(&vos, method2_offset);
236 ASSERT_EQ(adjustment1 + method2_offset, mock_->last_write_offset_);
237 ASSERT_EQ(method2_offset + method2_alignment_size + method2_call_thunk_size,
238 method2_offset_adjusted);
239 vos.WriteFully("2", 1); // Mark method2.
240
241 EXPECT_EQ(method2_alignment_size, patcher_.CodeAlignmentSize());
242 EXPECT_EQ(method2_call_thunk_size, patcher_.RelativeCallThunksSize());
243
244 uint32_t adjustment2 = 0xd000;
245 patcher_.StartOatFile(adjustment2);
246
247 uint32_t method3_offset = 0xf00;
248 uint32_t method3_alignment_size = 2;
249 uint32_t method3_misc_thunk_size = 1;
250 mock_->next_write_alignment_ = method3_alignment_size;
251 mock_->next_write_misc_thunk_ = method3_misc_thunk_size;
252 uint32_t method3_offset_adjusted = patcher_.WriteThunks(&vos, method3_offset);
253 ASSERT_EQ(adjustment2 + method3_offset, mock_->last_write_offset_);
254 ASSERT_EQ(method3_offset + method3_alignment_size + method3_misc_thunk_size,
255 method3_offset_adjusted);
256 vos.WriteFully("3", 1); // Mark method3.
257
258 EXPECT_EQ(method3_alignment_size, patcher_.CodeAlignmentSize());
259 EXPECT_EQ(method3_misc_thunk_size, patcher_.MiscThunksSize());
260
261 uint8_t expected_output[] = {
262 '1',
263 0, 'c', 'c', '2',
264 0, 0, 'm', '3',
265 };
266 ASSERT_EQ(arraysize(expected_output), output.size());
267 for (size_t i = 0; i != arraysize(expected_output); ++i) {
268 ASSERT_EQ(expected_output[i], output[i]) << i;
269 }
270 }
271
TEST_F(MultiOatRelativePatcherTest,Patch)272 TEST_F(MultiOatRelativePatcherTest, Patch) {
273 std::vector<uint8_t> code(16);
274
275 uint32_t adjustment1 = 0x1000;
276 patcher_.StartOatFile(adjustment1);
277
278 uint32_t method1_literal_offset = 4u;
279 uint32_t method1_patch_offset = 0x1234u;
280 uint32_t method1_target_offset = 0x8888u;
281 patcher_.PatchCall(&code, method1_literal_offset, method1_patch_offset, method1_target_offset);
282 DCHECK_EQ(method1_literal_offset, mock_->last_literal_offset_);
283 DCHECK_EQ(method1_patch_offset + adjustment1, mock_->last_patch_offset_);
284 DCHECK_EQ(method1_target_offset + adjustment1, mock_->last_target_offset_);
285
286 uint32_t method2_literal_offset = 12u;
287 uint32_t method2_patch_offset = 0x7654u;
288 uint32_t method2_target_offset = 0xccccu;
289 LinkerPatch method2_patch =
290 LinkerPatch::StringBssEntryPatch(method2_literal_offset, nullptr, 0u, 1u);
291 patcher_.PatchPcRelativeReference(
292 &code, method2_patch, method2_patch_offset, method2_target_offset);
293 DCHECK_EQ(method2_literal_offset, mock_->last_literal_offset_);
294 DCHECK_EQ(method2_patch_offset + adjustment1, mock_->last_patch_offset_);
295 DCHECK_EQ(method2_target_offset + adjustment1, mock_->last_target_offset_);
296
297 uint32_t adjustment2 = 0xd000;
298 patcher_.StartOatFile(adjustment2);
299
300 uint32_t method3_literal_offset = 8u;
301 uint32_t method3_patch_offset = 0x108u;
302 uint32_t method3_target_offset = 0x200u;
303 patcher_.PatchCall(&code, method3_literal_offset, method3_patch_offset, method3_target_offset);
304 DCHECK_EQ(method3_literal_offset, mock_->last_literal_offset_);
305 DCHECK_EQ(method3_patch_offset + adjustment2, mock_->last_patch_offset_);
306 DCHECK_EQ(method3_target_offset + adjustment2, mock_->last_target_offset_);
307 }
308
309 } // namespace linker
310 } // namespace art
311