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