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