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_COMPILER_LINKER_RELATIVE_PATCHER_TEST_H_ 18 #define ART_COMPILER_LINKER_RELATIVE_PATCHER_TEST_H_ 19 20 #include "arch/instruction_set.h" 21 #include "arch/instruction_set_features.h" 22 #include "base/array_ref.h" 23 #include "base/macros.h" 24 #include "compiled_method.h" 25 #include "dex/verification_results.h" 26 #include "driver/compiler_driver.h" 27 #include "driver/compiler_options.h" 28 #include "globals.h" 29 #include "gtest/gtest.h" 30 #include "linker/relative_patcher.h" 31 #include "method_reference.h" 32 #include "oat.h" 33 #include "oat_quick_method_header.h" 34 #include "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: RelativePatcherTest(InstructionSet instruction_set,const std::string & variant)42 RelativePatcherTest(InstructionSet instruction_set, const std::string& variant) 43 : compiler_options_(), 44 verification_results_(&compiler_options_), 45 driver_(&compiler_options_, 46 &verification_results_, 47 Compiler::kQuick, 48 instruction_set, 49 /* instruction_set_features*/ nullptr, 50 /* image_classes */ nullptr, 51 /* compiled_classes */ nullptr, 52 /* compiled_methods */ nullptr, 53 /* thread_count */ 1u, 54 /* dump_stats */ false, 55 /* dump_passes */ false, 56 /* timer */ nullptr, 57 /* swap_fd */ -1, 58 /* profile_compilation_info */ nullptr), 59 error_msg_(), 60 instruction_set_(instruction_set), 61 features_(InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg_)), 62 method_offset_map_(), 63 patcher_(RelativePatcher::Create(instruction_set, features_.get(), &method_offset_map_)), 64 dex_cache_arrays_begin_(0u), 65 compiled_method_refs_(), 66 compiled_methods_(), 67 patched_code_(), 68 output_(), 69 out_("test output stream", &output_) { 70 CHECK(error_msg_.empty()) << instruction_set << "/" << variant; 71 patched_code_.reserve(16 * KB); 72 } 73 MethodRef(uint32_t method_idx)74 MethodReference MethodRef(uint32_t method_idx) { 75 CHECK_NE(method_idx, 0u); 76 return MethodReference(nullptr, method_idx); 77 } 78 79 void AddCompiledMethod( 80 MethodReference method_ref, 81 const ArrayRef<const uint8_t>& code, 82 const ArrayRef<const LinkerPatch>& patches = ArrayRef<const LinkerPatch>()) { 83 compiled_method_refs_.push_back(method_ref); 84 compiled_methods_.emplace_back(new CompiledMethod( 85 &driver_, 86 instruction_set_, 87 code, 88 /* frame_size_in_bytes */ 0u, 89 /* core_spill_mask */ 0u, 90 /* fp_spill_mask */ 0u, 91 /* method_info */ ArrayRef<const uint8_t>(), 92 /* vmap_table */ ArrayRef<const uint8_t>(), 93 /* cfi_info */ ArrayRef<const uint8_t>(), 94 patches)); 95 } 96 CodeAlignmentSize(uint32_t header_offset_to_align)97 uint32_t CodeAlignmentSize(uint32_t header_offset_to_align) { 98 // We want to align the code rather than the preheader. 99 uint32_t unaligned_code_offset = header_offset_to_align + sizeof(OatQuickMethodHeader); 100 uint32_t aligned_code_offset = 101 CompiledMethod::AlignCode(unaligned_code_offset, instruction_set_); 102 return aligned_code_offset - unaligned_code_offset; 103 } 104 Link()105 void Link() { 106 // Reserve space. 107 static_assert(kTrampolineOffset == 0u, "Unexpected trampoline offset."); 108 uint32_t offset = kTrampolineSize; 109 size_t idx = 0u; 110 for (auto& compiled_method : compiled_methods_) { 111 offset = patcher_->ReserveSpace(offset, compiled_method.get(), compiled_method_refs_[idx]); 112 113 uint32_t alignment_size = CodeAlignmentSize(offset); 114 offset += alignment_size; 115 116 offset += sizeof(OatQuickMethodHeader); 117 uint32_t quick_code_offset = offset + compiled_method->CodeDelta(); 118 const auto code = compiled_method->GetQuickCode(); 119 offset += code.size(); 120 121 method_offset_map_.map.Put(compiled_method_refs_[idx], quick_code_offset); 122 ++idx; 123 } 124 offset = patcher_->ReserveSpaceEnd(offset); 125 uint32_t output_size = offset; 126 output_.reserve(output_size); 127 128 // Write data. 129 DCHECK(output_.empty()); 130 uint8_t dummy_trampoline[kTrampolineSize]; 131 memset(dummy_trampoline, 0, sizeof(dummy_trampoline)); 132 out_.WriteFully(dummy_trampoline, kTrampolineSize); 133 offset = kTrampolineSize; 134 static const uint8_t kPadding[] = { 135 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u 136 }; 137 uint8_t dummy_header[sizeof(OatQuickMethodHeader)]; 138 memset(dummy_header, 0, sizeof(dummy_header)); 139 for (auto& compiled_method : compiled_methods_) { 140 offset = patcher_->WriteThunks(&out_, offset); 141 142 uint32_t alignment_size = CodeAlignmentSize(offset); 143 CHECK_LE(alignment_size, sizeof(kPadding)); 144 out_.WriteFully(kPadding, alignment_size); 145 offset += alignment_size; 146 147 out_.WriteFully(dummy_header, sizeof(OatQuickMethodHeader)); 148 offset += sizeof(OatQuickMethodHeader); 149 ArrayRef<const uint8_t> code = compiled_method->GetQuickCode(); 150 if (!compiled_method->GetPatches().empty()) { 151 patched_code_.assign(code.begin(), code.end()); 152 code = ArrayRef<const uint8_t>(patched_code_); 153 for (const LinkerPatch& patch : compiled_method->GetPatches()) { 154 if (patch.GetType() == LinkerPatch::Type::kCallRelative) { 155 auto result = method_offset_map_.FindMethodOffset(patch.TargetMethod()); 156 uint32_t target_offset = 157 result.first ? result.second : kTrampolineOffset + compiled_method->CodeDelta(); 158 patcher_->PatchCall(&patched_code_, patch.LiteralOffset(), 159 offset + patch.LiteralOffset(), target_offset); 160 } else if (patch.GetType() == LinkerPatch::Type::kDexCacheArray) { 161 uint32_t target_offset = dex_cache_arrays_begin_ + patch.TargetDexCacheElementOffset(); 162 patcher_->PatchPcRelativeReference(&patched_code_, 163 patch, 164 offset + patch.LiteralOffset(), 165 target_offset); 166 } else if (patch.GetType() == LinkerPatch::Type::kStringRelative) { 167 uint32_t target_offset = 168 string_index_to_offset_map_.Get(patch.TargetStringIndex().index_); 169 patcher_->PatchPcRelativeReference(&patched_code_, 170 patch, 171 offset + patch.LiteralOffset(), 172 target_offset); 173 } else if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch) { 174 patcher_->PatchBakerReadBarrierBranch(&patched_code_, 175 patch, 176 offset + patch.LiteralOffset()); 177 } else { 178 LOG(FATAL) << "Bad patch type. " << patch.GetType(); 179 UNREACHABLE(); 180 } 181 } 182 } 183 out_.WriteFully(&code[0], code.size()); 184 offset += code.size(); 185 } 186 offset = patcher_->WriteThunks(&out_, offset); 187 CHECK_EQ(offset, output_size); 188 CHECK_EQ(output_.size(), output_size); 189 } 190 CheckLinkedMethod(MethodReference method_ref,const ArrayRef<const uint8_t> & expected_code)191 bool CheckLinkedMethod(MethodReference method_ref, const ArrayRef<const uint8_t>& expected_code) { 192 // Sanity check: original code size must match linked_code.size(). 193 size_t idx = 0u; 194 for (auto ref : compiled_method_refs_) { 195 if (ref.dex_file == method_ref.dex_file && 196 ref.dex_method_index == method_ref.dex_method_index) { 197 break; 198 } 199 ++idx; 200 } 201 CHECK_NE(idx, compiled_method_refs_.size()); 202 CHECK_EQ(compiled_methods_[idx]->GetQuickCode().size(), expected_code.size()); 203 204 auto result = method_offset_map_.FindMethodOffset(method_ref); 205 CHECK(result.first); // Must have been linked. 206 size_t offset = result.second - compiled_methods_[idx]->CodeDelta(); 207 CHECK_LT(offset, output_.size()); 208 CHECK_LE(offset + expected_code.size(), output_.size()); 209 ArrayRef<const uint8_t> linked_code(&output_[offset], expected_code.size()); 210 if (linked_code == expected_code) { 211 return true; 212 } 213 // Log failure info. 214 DumpDiff(expected_code, linked_code); 215 return false; 216 } 217 DumpDiff(const ArrayRef<const uint8_t> & expected_code,const ArrayRef<const uint8_t> & linked_code)218 void DumpDiff(const ArrayRef<const uint8_t>& expected_code, 219 const ArrayRef<const uint8_t>& linked_code) { 220 std::ostringstream expected_hex; 221 std::ostringstream linked_hex; 222 std::ostringstream diff_indicator; 223 static const char digits[] = "0123456789abcdef"; 224 bool found_diff = false; 225 for (size_t i = 0; i != expected_code.size(); ++i) { 226 expected_hex << " " << digits[expected_code[i] >> 4] << digits[expected_code[i] & 0xf]; 227 linked_hex << " " << digits[linked_code[i] >> 4] << digits[linked_code[i] & 0xf]; 228 if (!found_diff) { 229 found_diff = (expected_code[i] != linked_code[i]); 230 diff_indicator << (found_diff ? " ^^" : " "); 231 } 232 } 233 CHECK(found_diff); 234 std::string expected_hex_str = expected_hex.str(); 235 std::string linked_hex_str = linked_hex.str(); 236 std::string diff_indicator_str = diff_indicator.str(); 237 if (diff_indicator_str.length() > 60) { 238 CHECK_EQ(diff_indicator_str.length() % 3u, 0u); 239 size_t remove = diff_indicator_str.length() / 3 - 5; 240 std::ostringstream oss; 241 oss << "[stripped " << remove << "]"; 242 std::string replacement = oss.str(); 243 expected_hex_str.replace(0u, remove * 3u, replacement); 244 linked_hex_str.replace(0u, remove * 3u, replacement); 245 diff_indicator_str.replace(0u, remove * 3u, replacement); 246 } 247 LOG(ERROR) << "diff expected_code linked_code"; 248 LOG(ERROR) << "<" << expected_hex_str; 249 LOG(ERROR) << ">" << linked_hex_str; 250 LOG(ERROR) << " " << diff_indicator_str; 251 } 252 253 // Map method reference to assinged offset. 254 // Wrap the map in a class implementing linker::RelativePatcherTargetProvider. 255 class MethodOffsetMap FINAL : public linker::RelativePatcherTargetProvider { 256 public: FindMethodOffset(MethodReference ref)257 std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) OVERRIDE { 258 auto it = map.find(ref); 259 if (it == map.end()) { 260 return std::pair<bool, uint32_t>(false, 0u); 261 } else { 262 return std::pair<bool, uint32_t>(true, it->second); 263 } 264 } 265 SafeMap<MethodReference, uint32_t, MethodReferenceComparator> map; 266 }; 267 268 static const uint32_t kTrampolineSize = 4u; 269 static const uint32_t kTrampolineOffset = 0u; 270 271 CompilerOptions compiler_options_; 272 VerificationResults verification_results_; 273 CompilerDriver driver_; // Needed for constructing CompiledMethod. 274 std::string error_msg_; 275 InstructionSet instruction_set_; 276 std::unique_ptr<const InstructionSetFeatures> features_; 277 MethodOffsetMap method_offset_map_; 278 std::unique_ptr<RelativePatcher> patcher_; 279 uint32_t dex_cache_arrays_begin_; 280 SafeMap<uint32_t, uint32_t> string_index_to_offset_map_; 281 std::vector<MethodReference> compiled_method_refs_; 282 std::vector<std::unique_ptr<CompiledMethod>> compiled_methods_; 283 std::vector<uint8_t> patched_code_; 284 std::vector<uint8_t> output_; 285 VectorOutputStream out_; 286 }; 287 288 } // namespace linker 289 } // namespace art 290 291 #endif // ART_COMPILER_LINKER_RELATIVE_PATCHER_TEST_H_ 292