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