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: 34 MockPatcher() { } 35 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 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 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 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 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 97 void PatchBakerReadBarrierBranch(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 103 std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo( 104 uint32_t executable_offset ATTRIBUTE_UNUSED) override { 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 123 MultiOatRelativePatcherTest() 124 : instruction_set_features_(InstructionSetFeatures::FromCppDefines()), 125 patcher_(kRuntimeISA, instruction_set_features_.get(), /* storage */ nullptr) { 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 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 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 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 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