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 22 namespace art { 23 namespace linker { 24 25 static const MethodReference kNullMethodRef = MethodReference(nullptr, 0u); 26 27 static bool EqualRef(MethodReference lhs, MethodReference rhs) { 28 return lhs.dex_file == rhs.dex_file && lhs.dex_method_index == rhs.dex_method_index; 29 } 30 31 class 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 125 TEST_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 158 TEST_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 206 TEST_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 261 TEST_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