1 /* 2 * Copyright (C) 2015 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 #ifndef ART_DEX2OAT_LINKER_RELATIVE_PATCHER_TEST_H_ 18 #define ART_DEX2OAT_LINKER_RELATIVE_PATCHER_TEST_H_ 19 20 #include <gtest/gtest.h> 21 22 #include "arch/instruction_set.h" 23 #include "arch/instruction_set_features.h" 24 #include "base/array_ref.h" 25 #include "base/globals.h" 26 #include "base/macros.h" 27 #include "dex/method_reference.h" 28 #include "dex/string_reference.h" 29 #include "driver/compiled_method-inl.h" 30 #include "driver/compiled_method_storage.h" 31 #include "linker/relative_patcher.h" 32 #include "oat_quick_method_header.h" 33 #include "stream/vector_output_stream.h" 34 35 namespace art { 36 namespace linker { 37 38 // Base class providing infrastructure for architecture-specific tests. 39 class RelativePatcherTest : public testing::Test { 40 protected: RelativePatcherTest(InstructionSet instruction_set,const std::string & variant)41 RelativePatcherTest(InstructionSet instruction_set, const std::string& variant) 42 : storage_(/*swap_fd=*/ -1), 43 instruction_set_(instruction_set), 44 instruction_set_features_(nullptr), 45 method_offset_map_(), 46 patcher_(nullptr), 47 bss_begin_(0u), 48 compiled_method_refs_(), 49 compiled_methods_(), 50 patched_code_(), 51 output_(), 52 out_(nullptr) { 53 std::string error_msg; 54 instruction_set_features_ = 55 InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg); 56 CHECK(instruction_set_features_ != nullptr) << error_msg; 57 58 patched_code_.reserve(16 * KB); 59 } 60 SetUp()61 void SetUp() override { 62 Reset(); 63 } 64 TearDown()65 void TearDown() override { 66 thunk_provider_.Reset(); 67 compiled_methods_.clear(); 68 patcher_.reset(); 69 bss_begin_ = 0u; 70 string_index_to_offset_map_.clear(); 71 compiled_method_refs_.clear(); 72 compiled_methods_.clear(); 73 patched_code_.clear(); 74 output_.clear(); 75 out_.reset(); 76 } 77 78 // Reset the helper to start another test. Creating and tearing down the Runtime is expensive, 79 // so we merge related tests together. Reset()80 void Reset() { 81 thunk_provider_.Reset(); 82 method_offset_map_.map.clear(); 83 patcher_ = RelativePatcher::Create(instruction_set_, 84 instruction_set_features_.get(), 85 &thunk_provider_, 86 &method_offset_map_); 87 bss_begin_ = 0u; 88 string_index_to_offset_map_.clear(); 89 compiled_method_refs_.clear(); 90 compiled_methods_.clear(); 91 patched_code_.clear(); 92 output_.clear(); 93 out_.reset(new VectorOutputStream("test output stream", &output_)); 94 } 95 MethodRef(uint32_t method_idx)96 MethodReference MethodRef(uint32_t method_idx) { 97 CHECK_NE(method_idx, 0u); 98 return MethodReference(nullptr, method_idx); 99 } 100 101 void AddCompiledMethod( 102 MethodReference method_ref, 103 const ArrayRef<const uint8_t>& code, 104 const ArrayRef<const LinkerPatch>& patches = ArrayRef<const LinkerPatch>()) { 105 compiled_method_refs_.push_back(method_ref); 106 compiled_methods_.emplace_back(new CompiledMethod( 107 &storage_, 108 instruction_set_, 109 code, 110 /* vmap_table */ ArrayRef<const uint8_t>(), 111 /* cfi_info */ ArrayRef<const uint8_t>(), 112 patches)); 113 } 114 CodeAlignmentSize(uint32_t header_offset_to_align)115 uint32_t CodeAlignmentSize(uint32_t header_offset_to_align) { 116 // We want to align the code rather than the preheader. 117 uint32_t unaligned_code_offset = header_offset_to_align + sizeof(OatQuickMethodHeader); 118 uint32_t aligned_code_offset = 119 CompiledMethod::AlignCode(unaligned_code_offset, instruction_set_); 120 return aligned_code_offset - unaligned_code_offset; 121 } 122 Link()123 void Link() { 124 // Reserve space. 125 static_assert(kTrampolineOffset == 0u, "Unexpected trampoline offset."); 126 uint32_t offset = kTrampolineSize; 127 size_t idx = 0u; 128 for (auto& compiled_method : compiled_methods_) { 129 offset = patcher_->ReserveSpace(offset, compiled_method.get(), compiled_method_refs_[idx]); 130 131 uint32_t alignment_size = CodeAlignmentSize(offset); 132 offset += alignment_size; 133 134 offset += sizeof(OatQuickMethodHeader); 135 uint32_t quick_code_offset = offset + compiled_method->GetEntryPointAdjustment(); 136 const auto code = compiled_method->GetQuickCode(); 137 offset += code.size(); 138 139 method_offset_map_.map.Put(compiled_method_refs_[idx], quick_code_offset); 140 ++idx; 141 } 142 offset = patcher_->ReserveSpaceEnd(offset); 143 uint32_t output_size = offset; 144 output_.reserve(output_size); 145 146 // Write data. 147 DCHECK(output_.empty()); 148 uint8_t fake_trampoline[kTrampolineSize]; 149 memset(fake_trampoline, 0, sizeof(fake_trampoline)); 150 out_->WriteFully(fake_trampoline, kTrampolineSize); 151 offset = kTrampolineSize; 152 static const uint8_t kPadding[] = { 153 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u 154 }; 155 uint8_t fake_header[sizeof(OatQuickMethodHeader)]; 156 memset(fake_header, 0, sizeof(fake_header)); 157 for (auto& compiled_method : compiled_methods_) { 158 offset = patcher_->WriteThunks(out_.get(), offset); 159 160 uint32_t alignment_size = CodeAlignmentSize(offset); 161 CHECK_LE(alignment_size, sizeof(kPadding)); 162 out_->WriteFully(kPadding, alignment_size); 163 offset += alignment_size; 164 165 out_->WriteFully(fake_header, sizeof(OatQuickMethodHeader)); 166 offset += sizeof(OatQuickMethodHeader); 167 ArrayRef<const uint8_t> code = compiled_method->GetQuickCode(); 168 if (!compiled_method->GetPatches().empty()) { 169 patched_code_.assign(code.begin(), code.end()); 170 code = ArrayRef<const uint8_t>(patched_code_); 171 for (const LinkerPatch& patch : compiled_method->GetPatches()) { 172 if (patch.GetType() == LinkerPatch::Type::kCallRelative) { 173 auto result = method_offset_map_.FindMethodOffset(patch.TargetMethod()); 174 uint32_t target_offset = 175 result.first ? result.second 176 : kTrampolineOffset + compiled_method->GetEntryPointAdjustment(); 177 patcher_->PatchCall(&patched_code_, 178 patch.LiteralOffset(), 179 offset + patch.LiteralOffset(), 180 target_offset); 181 } else if (patch.GetType() == LinkerPatch::Type::kStringBssEntry) { 182 uint32_t target_offset = 183 bss_begin_ + string_index_to_offset_map_.Get(patch.TargetStringIndex().index_); 184 patcher_->PatchPcRelativeReference(&patched_code_, 185 patch, 186 offset + patch.LiteralOffset(), 187 target_offset); 188 } else if (patch.GetType() == LinkerPatch::Type::kStringRelative) { 189 uint32_t target_offset = 190 string_index_to_offset_map_.Get(patch.TargetStringIndex().index_); 191 patcher_->PatchPcRelativeReference(&patched_code_, 192 patch, 193 offset + patch.LiteralOffset(), 194 target_offset); 195 } else if (patch.GetType() == LinkerPatch::Type::kCallEntrypoint) { 196 patcher_->PatchEntrypointCall(&patched_code_, 197 patch, 198 offset + patch.LiteralOffset()); 199 } else if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch) { 200 patcher_->PatchBakerReadBarrierBranch(&patched_code_, 201 patch, 202 offset + patch.LiteralOffset()); 203 } else { 204 LOG(FATAL) << "Bad patch type. " << patch.GetType(); 205 UNREACHABLE(); 206 } 207 } 208 } 209 out_->WriteFully(&code[0], code.size()); 210 offset += code.size(); 211 } 212 offset = patcher_->WriteThunks(out_.get(), offset); 213 CHECK_EQ(offset, output_size); 214 CHECK_EQ(output_.size(), output_size); 215 } 216 CheckLinkedMethod(MethodReference method_ref,const ArrayRef<const uint8_t> & expected_code)217 bool CheckLinkedMethod(MethodReference method_ref, const ArrayRef<const uint8_t>& expected_code) { 218 // Check that the original code size must match linked_code.size(). 219 size_t idx = 0u; 220 for (auto ref : compiled_method_refs_) { 221 if (ref == method_ref) { 222 break; 223 } 224 ++idx; 225 } 226 CHECK_NE(idx, compiled_method_refs_.size()); 227 CHECK_EQ(compiled_methods_[idx]->GetQuickCode().size(), expected_code.size()); 228 229 auto result = method_offset_map_.FindMethodOffset(method_ref); 230 CHECK(result.first); // Must have been linked. 231 size_t offset = result.second - compiled_methods_[idx]->GetEntryPointAdjustment(); 232 CHECK_LT(offset, output_.size()); 233 CHECK_LE(offset + expected_code.size(), output_.size()); 234 ArrayRef<const uint8_t> linked_code(&output_[offset], expected_code.size()); 235 if (linked_code == expected_code) { 236 return true; 237 } 238 // Log failure info. 239 DumpDiff(expected_code, linked_code); 240 return false; 241 } 242 DumpDiff(const ArrayRef<const uint8_t> & expected_code,const ArrayRef<const uint8_t> & linked_code)243 void DumpDiff(const ArrayRef<const uint8_t>& expected_code, 244 const ArrayRef<const uint8_t>& linked_code) { 245 std::ostringstream expected_hex; 246 std::ostringstream linked_hex; 247 std::ostringstream diff_indicator; 248 static const char digits[] = "0123456789abcdef"; 249 bool found_diff = false; 250 for (size_t i = 0; i != expected_code.size(); ++i) { 251 expected_hex << " " << digits[expected_code[i] >> 4] << digits[expected_code[i] & 0xf]; 252 linked_hex << " " << digits[linked_code[i] >> 4] << digits[linked_code[i] & 0xf]; 253 if (!found_diff) { 254 found_diff = (expected_code[i] != linked_code[i]); 255 diff_indicator << (found_diff ? " ^^" : " "); 256 } 257 } 258 CHECK(found_diff); 259 std::string expected_hex_str = expected_hex.str(); 260 std::string linked_hex_str = linked_hex.str(); 261 std::string diff_indicator_str = diff_indicator.str(); 262 if (diff_indicator_str.length() > 60) { 263 CHECK_EQ(diff_indicator_str.length() % 3u, 0u); 264 size_t remove = diff_indicator_str.length() / 3 - 5; 265 std::ostringstream oss; 266 oss << "[stripped " << remove << "]"; 267 std::string replacement = oss.str(); 268 expected_hex_str.replace(0u, remove * 3u, replacement); 269 linked_hex_str.replace(0u, remove * 3u, replacement); 270 diff_indicator_str.replace(0u, remove * 3u, replacement); 271 } 272 LOG(ERROR) << "diff expected_code linked_code"; 273 LOG(ERROR) << "<" << expected_hex_str; 274 LOG(ERROR) << ">" << linked_hex_str; 275 LOG(ERROR) << " " << diff_indicator_str; 276 } 277 278 class ThunkProvider : public RelativePatcherThunkProvider { 279 public: ThunkProvider()280 ThunkProvider() {} 281 SetThunkCode(const LinkerPatch & patch,ArrayRef<const uint8_t> code,const std::string & debug_name)282 void SetThunkCode(const LinkerPatch& patch, 283 ArrayRef<const uint8_t> code, 284 const std::string& debug_name) { 285 thunk_map_.emplace(ThunkKey(patch), ThunkValue(code, debug_name)); 286 } 287 GetThunkCode(const LinkerPatch & patch,ArrayRef<const uint8_t> * code,std::string * debug_name)288 void GetThunkCode(const LinkerPatch& patch, 289 /*out*/ ArrayRef<const uint8_t>* code, 290 /*out*/ std::string* debug_name) override { 291 auto it = thunk_map_.find(ThunkKey(patch)); 292 CHECK(it != thunk_map_.end()); 293 const ThunkValue& value = it->second; 294 CHECK(code != nullptr); 295 *code = value.GetCode(); 296 CHECK(debug_name != nullptr); 297 *debug_name = value.GetDebugName(); 298 } 299 Reset()300 void Reset() { 301 thunk_map_.clear(); 302 } 303 304 private: 305 class ThunkKey { 306 public: ThunkKey(const LinkerPatch & patch)307 explicit ThunkKey(const LinkerPatch& patch) 308 : type_(patch.GetType()), 309 custom_value1_(CustomValue1(patch)), 310 custom_value2_(CustomValue2(patch)) { 311 CHECK(patch.GetType() == LinkerPatch::Type::kCallEntrypoint || 312 patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch || 313 patch.GetType() == LinkerPatch::Type::kCallRelative); 314 } 315 316 bool operator<(const ThunkKey& other) const { 317 if (custom_value1_ != other.custom_value1_) { 318 return custom_value1_ < other.custom_value1_; 319 } 320 if (custom_value2_ != other.custom_value2_) { 321 return custom_value2_ < other.custom_value2_; 322 } 323 return type_ < other.type_; 324 } 325 326 private: CustomValue1(const LinkerPatch & patch)327 static uint32_t CustomValue1(const LinkerPatch& patch) { 328 switch (patch.GetType()) { 329 case LinkerPatch::Type::kCallEntrypoint: 330 return patch.EntrypointOffset(); 331 case LinkerPatch::Type::kBakerReadBarrierBranch: 332 return patch.GetBakerCustomValue1(); 333 default: 334 return 0; 335 } 336 } 337 CustomValue2(const LinkerPatch & patch)338 static uint32_t CustomValue2(const LinkerPatch& patch) { 339 switch (patch.GetType()) { 340 case LinkerPatch::Type::kBakerReadBarrierBranch: 341 return patch.GetBakerCustomValue2(); 342 default: 343 return 0; 344 } 345 } 346 347 const LinkerPatch::Type type_; 348 const uint32_t custom_value1_; 349 const uint32_t custom_value2_; 350 }; 351 352 class ThunkValue { 353 public: ThunkValue(ArrayRef<const uint8_t> code,const std::string & debug_name)354 ThunkValue(ArrayRef<const uint8_t> code, const std::string& debug_name) 355 : code_(code.begin(), code.end()), debug_name_(debug_name) {} GetCode()356 ArrayRef<const uint8_t> GetCode() const { return ArrayRef<const uint8_t>(code_); } GetDebugName()357 const std::string& GetDebugName() const { return debug_name_; } 358 359 private: 360 const std::vector<uint8_t> code_; 361 const std::string debug_name_; 362 }; 363 364 std::map<ThunkKey, ThunkValue> thunk_map_; 365 }; 366 367 // Map method reference to assinged offset. 368 // Wrap the map in a class implementing RelativePatcherTargetProvider. 369 class MethodOffsetMap final : public RelativePatcherTargetProvider { 370 public: FindMethodOffset(MethodReference ref)371 std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) override { 372 auto it = map.find(ref); 373 if (it == map.end()) { 374 return std::pair<bool, uint32_t>(false, 0u); 375 } else { 376 return std::pair<bool, uint32_t>(true, it->second); 377 } 378 } 379 SafeMap<MethodReference, uint32_t> map; 380 }; 381 382 static const uint32_t kTrampolineSize = 4u; 383 static const uint32_t kTrampolineOffset = 0u; 384 385 CompiledMethodStorage storage_; 386 InstructionSet instruction_set_; 387 std::unique_ptr<const InstructionSetFeatures> instruction_set_features_; 388 389 ThunkProvider thunk_provider_; 390 MethodOffsetMap method_offset_map_; 391 std::unique_ptr<RelativePatcher> patcher_; 392 uint32_t bss_begin_; 393 SafeMap<uint32_t, uint32_t> string_index_to_offset_map_; 394 std::vector<MethodReference> compiled_method_refs_; 395 std::vector<std::unique_ptr<CompiledMethod>> compiled_methods_; 396 std::vector<uint8_t> patched_code_; 397 std::vector<uint8_t> output_; 398 std::unique_ptr<VectorOutputStream> out_; 399 }; 400 401 } // namespace linker 402 } // namespace art 403 404 #endif // ART_DEX2OAT_LINKER_RELATIVE_PATCHER_TEST_H_ 405