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