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