• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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