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