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 "compiled_method-inl.h" 28 #include "dex/method_reference.h" 29 #include "dex/string_reference.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->CodeDelta(); 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 : kTrampolineOffset + compiled_method->CodeDelta(); 176 patcher_->PatchCall(&patched_code_, 177 patch.LiteralOffset(), 178 offset + patch.LiteralOffset(), 179 target_offset); 180 } else if (patch.GetType() == LinkerPatch::Type::kStringBssEntry) { 181 uint32_t target_offset = 182 bss_begin_ + string_index_to_offset_map_.Get(patch.TargetStringIndex().index_); 183 patcher_->PatchPcRelativeReference(&patched_code_, 184 patch, 185 offset + patch.LiteralOffset(), 186 target_offset); 187 } else if (patch.GetType() == LinkerPatch::Type::kStringRelative) { 188 uint32_t target_offset = 189 string_index_to_offset_map_.Get(patch.TargetStringIndex().index_); 190 patcher_->PatchPcRelativeReference(&patched_code_, 191 patch, 192 offset + patch.LiteralOffset(), 193 target_offset); 194 } else if (patch.GetType() == LinkerPatch::Type::kCallEntrypoint) { 195 patcher_->PatchEntrypointCall(&patched_code_, 196 patch, 197 offset + patch.LiteralOffset()); 198 } else if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch) { 199 patcher_->PatchBakerReadBarrierBranch(&patched_code_, 200 patch, 201 offset + patch.LiteralOffset()); 202 } else { 203 LOG(FATAL) << "Bad patch type. " << patch.GetType(); 204 UNREACHABLE(); 205 } 206 } 207 } 208 out_->WriteFully(&code[0], code.size()); 209 offset += code.size(); 210 } 211 offset = patcher_->WriteThunks(out_.get(), offset); 212 CHECK_EQ(offset, output_size); 213 CHECK_EQ(output_.size(), output_size); 214 } 215 CheckLinkedMethod(MethodReference method_ref,const ArrayRef<const uint8_t> & expected_code)216 bool CheckLinkedMethod(MethodReference method_ref, const ArrayRef<const uint8_t>& expected_code) { 217 // Check that the original code size must match linked_code.size(). 218 size_t idx = 0u; 219 for (auto ref : compiled_method_refs_) { 220 if (ref == method_ref) { 221 break; 222 } 223 ++idx; 224 } 225 CHECK_NE(idx, compiled_method_refs_.size()); 226 CHECK_EQ(compiled_methods_[idx]->GetQuickCode().size(), expected_code.size()); 227 228 auto result = method_offset_map_.FindMethodOffset(method_ref); 229 CHECK(result.first); // Must have been linked. 230 size_t offset = result.second - compiled_methods_[idx]->CodeDelta(); 231 CHECK_LT(offset, output_.size()); 232 CHECK_LE(offset + expected_code.size(), output_.size()); 233 ArrayRef<const uint8_t> linked_code(&output_[offset], expected_code.size()); 234 if (linked_code == expected_code) { 235 return true; 236 } 237 // Log failure info. 238 DumpDiff(expected_code, linked_code); 239 return false; 240 } 241 DumpDiff(const ArrayRef<const uint8_t> & expected_code,const ArrayRef<const uint8_t> & linked_code)242 void DumpDiff(const ArrayRef<const uint8_t>& expected_code, 243 const ArrayRef<const uint8_t>& linked_code) { 244 std::ostringstream expected_hex; 245 std::ostringstream linked_hex; 246 std::ostringstream diff_indicator; 247 static const char digits[] = "0123456789abcdef"; 248 bool found_diff = false; 249 for (size_t i = 0; i != expected_code.size(); ++i) { 250 expected_hex << " " << digits[expected_code[i] >> 4] << digits[expected_code[i] & 0xf]; 251 linked_hex << " " << digits[linked_code[i] >> 4] << digits[linked_code[i] & 0xf]; 252 if (!found_diff) { 253 found_diff = (expected_code[i] != linked_code[i]); 254 diff_indicator << (found_diff ? " ^^" : " "); 255 } 256 } 257 CHECK(found_diff); 258 std::string expected_hex_str = expected_hex.str(); 259 std::string linked_hex_str = linked_hex.str(); 260 std::string diff_indicator_str = diff_indicator.str(); 261 if (diff_indicator_str.length() > 60) { 262 CHECK_EQ(diff_indicator_str.length() % 3u, 0u); 263 size_t remove = diff_indicator_str.length() / 3 - 5; 264 std::ostringstream oss; 265 oss << "[stripped " << remove << "]"; 266 std::string replacement = oss.str(); 267 expected_hex_str.replace(0u, remove * 3u, replacement); 268 linked_hex_str.replace(0u, remove * 3u, replacement); 269 diff_indicator_str.replace(0u, remove * 3u, replacement); 270 } 271 LOG(ERROR) << "diff expected_code linked_code"; 272 LOG(ERROR) << "<" << expected_hex_str; 273 LOG(ERROR) << ">" << linked_hex_str; 274 LOG(ERROR) << " " << diff_indicator_str; 275 } 276 277 class ThunkProvider : public RelativePatcherThunkProvider { 278 public: ThunkProvider()279 ThunkProvider() {} 280 SetThunkCode(const LinkerPatch & patch,ArrayRef<const uint8_t> code,const std::string & debug_name)281 void SetThunkCode(const LinkerPatch& patch, 282 ArrayRef<const uint8_t> code, 283 const std::string& debug_name) { 284 thunk_map_.emplace(ThunkKey(patch), ThunkValue(code, debug_name)); 285 } 286 GetThunkCode(const LinkerPatch & patch,ArrayRef<const uint8_t> * code,std::string * debug_name)287 void GetThunkCode(const LinkerPatch& patch, 288 /*out*/ ArrayRef<const uint8_t>* code, 289 /*out*/ std::string* debug_name) override { 290 auto it = thunk_map_.find(ThunkKey(patch)); 291 CHECK(it != thunk_map_.end()); 292 const ThunkValue& value = it->second; 293 CHECK(code != nullptr); 294 *code = value.GetCode(); 295 CHECK(debug_name != nullptr); 296 *debug_name = value.GetDebugName(); 297 } 298 Reset()299 void Reset() { 300 thunk_map_.clear(); 301 } 302 303 private: 304 class ThunkKey { 305 public: ThunkKey(const LinkerPatch & patch)306 explicit ThunkKey(const LinkerPatch& patch) 307 : type_(patch.GetType()), 308 custom_value1_(CustomValue1(patch)), 309 custom_value2_(CustomValue2(patch)) { 310 CHECK(patch.GetType() == LinkerPatch::Type::kCallEntrypoint || 311 patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch || 312 patch.GetType() == LinkerPatch::Type::kCallRelative); 313 } 314 315 bool operator<(const ThunkKey& other) const { 316 if (custom_value1_ != other.custom_value1_) { 317 return custom_value1_ < other.custom_value1_; 318 } 319 if (custom_value2_ != other.custom_value2_) { 320 return custom_value2_ < other.custom_value2_; 321 } 322 return type_ < other.type_; 323 } 324 325 private: CustomValue1(const LinkerPatch & patch)326 static uint32_t CustomValue1(const LinkerPatch& patch) { 327 switch (patch.GetType()) { 328 case LinkerPatch::Type::kCallEntrypoint: 329 return patch.EntrypointOffset(); 330 case LinkerPatch::Type::kBakerReadBarrierBranch: 331 return patch.GetBakerCustomValue1(); 332 default: 333 return 0; 334 } 335 } 336 CustomValue2(const LinkerPatch & patch)337 static uint32_t CustomValue2(const LinkerPatch& patch) { 338 switch (patch.GetType()) { 339 case LinkerPatch::Type::kBakerReadBarrierBranch: 340 return patch.GetBakerCustomValue2(); 341 default: 342 return 0; 343 } 344 } 345 346 const LinkerPatch::Type type_; 347 const uint32_t custom_value1_; 348 const uint32_t custom_value2_; 349 }; 350 351 class ThunkValue { 352 public: ThunkValue(ArrayRef<const uint8_t> code,const std::string & debug_name)353 ThunkValue(ArrayRef<const uint8_t> code, const std::string& debug_name) 354 : code_(code.begin(), code.end()), debug_name_(debug_name) {} GetCode()355 ArrayRef<const uint8_t> GetCode() const { return ArrayRef<const uint8_t>(code_); } GetDebugName()356 const std::string& GetDebugName() const { return debug_name_; } 357 358 private: 359 const std::vector<uint8_t> code_; 360 const std::string debug_name_; 361 }; 362 363 std::map<ThunkKey, ThunkValue> thunk_map_; 364 }; 365 366 // Map method reference to assinged offset. 367 // Wrap the map in a class implementing RelativePatcherTargetProvider. 368 class MethodOffsetMap final : public RelativePatcherTargetProvider { 369 public: FindMethodOffset(MethodReference ref)370 std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) override { 371 auto it = map.find(ref); 372 if (it == map.end()) { 373 return std::pair<bool, uint32_t>(false, 0u); 374 } else { 375 return std::pair<bool, uint32_t>(true, it->second); 376 } 377 } 378 SafeMap<MethodReference, uint32_t> map; 379 }; 380 381 static const uint32_t kTrampolineSize = 4u; 382 static const uint32_t kTrampolineOffset = 0u; 383 384 CompiledMethodStorage storage_; 385 InstructionSet instruction_set_; 386 std::unique_ptr<const InstructionSetFeatures> instruction_set_features_; 387 388 ThunkProvider thunk_provider_; 389 MethodOffsetMap method_offset_map_; 390 std::unique_ptr<RelativePatcher> patcher_; 391 uint32_t bss_begin_; 392 SafeMap<uint32_t, uint32_t> string_index_to_offset_map_; 393 std::vector<MethodReference> compiled_method_refs_; 394 std::vector<std::unique_ptr<CompiledMethod>> compiled_methods_; 395 std::vector<uint8_t> patched_code_; 396 std::vector<uint8_t> output_; 397 std::unique_ptr<VectorOutputStream> out_; 398 }; 399 400 } // namespace linker 401 } // namespace art 402 403 #endif // ART_DEX2OAT_LINKER_RELATIVE_PATCHER_TEST_H_ 404