• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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