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