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