Home | History | Annotate | Download | only in linker
      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_DEX2OAT_LINKER_RELATIVE_PATCHER_TEST_H_
     18 #define ART_DEX2OAT_LINKER_RELATIVE_PATCHER_TEST_H_
     19 
     20 #include <gtest/gtest.h>
     21 
     22 #include "arch/instruction_set.h"
     23 #include "arch/instruction_set_features.h"
     24 #include "base/array_ref.h"
     25 #include "base/globals.h"
     26 #include "base/macros.h"
     27 #include "compiled_method-inl.h"
     28 #include "dex/method_reference.h"
     29 #include "dex/string_reference.h"
     30 #include "driver/compiled_method_storage.h"
     31 #include "linker/relative_patcher.h"
     32 #include "oat.h"
     33 #include "oat_quick_method_header.h"
     34 #include "stream/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:
     42   RelativePatcherTest(InstructionSet instruction_set, const std::string& variant)
     43       : storage_(/*swap_fd=*/ -1),
     44         instruction_set_(instruction_set),
     45         instruction_set_features_(nullptr),
     46         method_offset_map_(),
     47         patcher_(nullptr),
     48         bss_begin_(0u),
     49         compiled_method_refs_(),
     50         compiled_methods_(),
     51         patched_code_(),
     52         output_(),
     53         out_(nullptr) {
     54     std::string error_msg;
     55     instruction_set_features_ =
     56         InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg);
     57     CHECK(instruction_set_features_ != nullptr) << error_msg;
     58 
     59     patched_code_.reserve(16 * KB);
     60   }
     61 
     62   void SetUp() override {
     63     Reset();
     64   }
     65 
     66   void TearDown() override {
     67     thunk_provider_.Reset();
     68     compiled_methods_.clear();
     69     patcher_.reset();
     70     bss_begin_ = 0u;
     71     string_index_to_offset_map_.clear();
     72     compiled_method_refs_.clear();
     73     compiled_methods_.clear();
     74     patched_code_.clear();
     75     output_.clear();
     76     out_.reset();
     77   }
     78 
     79   // Reset the helper to start another test. Creating and tearing down the Runtime is expensive,
     80   // so we merge related tests together.
     81   void Reset() {
     82     thunk_provider_.Reset();
     83     method_offset_map_.map.clear();
     84     patcher_ = RelativePatcher::Create(instruction_set_,
     85                                        instruction_set_features_.get(),
     86                                        &thunk_provider_,
     87                                        &method_offset_map_);
     88     bss_begin_ = 0u;
     89     string_index_to_offset_map_.clear();
     90     compiled_method_refs_.clear();
     91     compiled_methods_.clear();
     92     patched_code_.clear();
     93     output_.clear();
     94     out_.reset(new VectorOutputStream("test output stream", &output_));
     95   }
     96 
     97   MethodReference MethodRef(uint32_t method_idx) {
     98     CHECK_NE(method_idx, 0u);
     99     return MethodReference(nullptr, method_idx);
    100   }
    101 
    102   void AddCompiledMethod(
    103       MethodReference method_ref,
    104       const ArrayRef<const uint8_t>& code,
    105       const ArrayRef<const LinkerPatch>& patches = ArrayRef<const LinkerPatch>()) {
    106     compiled_method_refs_.push_back(method_ref);
    107     compiled_methods_.emplace_back(new CompiledMethod(
    108         &storage_,
    109         instruction_set_,
    110         code,
    111         /* vmap_table */ ArrayRef<const uint8_t>(),
    112         /* cfi_info */ ArrayRef<const uint8_t>(),
    113         patches));
    114   }
    115 
    116   uint32_t CodeAlignmentSize(uint32_t header_offset_to_align) {
    117     // We want to align the code rather than the preheader.
    118     uint32_t unaligned_code_offset = header_offset_to_align + sizeof(OatQuickMethodHeader);
    119     uint32_t aligned_code_offset =
    120         CompiledMethod::AlignCode(unaligned_code_offset, instruction_set_);
    121     return aligned_code_offset - unaligned_code_offset;
    122   }
    123 
    124   void Link() {
    125     // Reserve space.
    126     static_assert(kTrampolineOffset == 0u, "Unexpected trampoline offset.");
    127     uint32_t offset = kTrampolineSize;
    128     size_t idx = 0u;
    129     for (auto& compiled_method : compiled_methods_) {
    130       offset = patcher_->ReserveSpace(offset, compiled_method.get(), compiled_method_refs_[idx]);
    131 
    132       uint32_t alignment_size = CodeAlignmentSize(offset);
    133       offset += alignment_size;
    134 
    135       offset += sizeof(OatQuickMethodHeader);
    136       uint32_t quick_code_offset = offset + compiled_method->CodeDelta();
    137       const auto code = compiled_method->GetQuickCode();
    138       offset += code.size();
    139 
    140       method_offset_map_.map.Put(compiled_method_refs_[idx], quick_code_offset);
    141       ++idx;
    142     }
    143     offset = patcher_->ReserveSpaceEnd(offset);
    144     uint32_t output_size = offset;
    145     output_.reserve(output_size);
    146 
    147     // Write data.
    148     DCHECK(output_.empty());
    149     uint8_t dummy_trampoline[kTrampolineSize];
    150     memset(dummy_trampoline, 0, sizeof(dummy_trampoline));
    151     out_->WriteFully(dummy_trampoline, kTrampolineSize);
    152     offset = kTrampolineSize;
    153     static const uint8_t kPadding[] = {
    154         0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u
    155     };
    156     uint8_t dummy_header[sizeof(OatQuickMethodHeader)];
    157     memset(dummy_header, 0, sizeof(dummy_header));
    158     for (auto& compiled_method : compiled_methods_) {
    159       offset = patcher_->WriteThunks(out_.get(), offset);
    160 
    161       uint32_t alignment_size = CodeAlignmentSize(offset);
    162       CHECK_LE(alignment_size, sizeof(kPadding));
    163       out_->WriteFully(kPadding, alignment_size);
    164       offset += alignment_size;
    165 
    166       out_->WriteFully(dummy_header, sizeof(OatQuickMethodHeader));
    167       offset += sizeof(OatQuickMethodHeader);
    168       ArrayRef<const uint8_t> code = compiled_method->GetQuickCode();
    169       if (!compiled_method->GetPatches().empty()) {
    170         patched_code_.assign(code.begin(), code.end());
    171         code = ArrayRef<const uint8_t>(patched_code_);
    172         for (const LinkerPatch& patch : compiled_method->GetPatches()) {
    173           if (patch.GetType() == LinkerPatch::Type::kCallRelative) {
    174             auto result = method_offset_map_.FindMethodOffset(patch.TargetMethod());
    175             uint32_t target_offset =
    176                 result.first ? result.second : kTrampolineOffset + compiled_method->CodeDelta();
    177             patcher_->PatchCall(&patched_code_, patch.LiteralOffset(),
    178                                 offset + patch.LiteralOffset(), target_offset);
    179           } else if (patch.GetType() == LinkerPatch::Type::kStringBssEntry) {
    180             uint32_t target_offset =
    181                 bss_begin_ + string_index_to_offset_map_.Get(patch.TargetStringIndex().index_);
    182             patcher_->PatchPcRelativeReference(&patched_code_,
    183                                                patch,
    184                                                offset + patch.LiteralOffset(),
    185                                                target_offset);
    186           } else if (patch.GetType() == LinkerPatch::Type::kStringRelative) {
    187             uint32_t target_offset =
    188                 string_index_to_offset_map_.Get(patch.TargetStringIndex().index_);
    189             patcher_->PatchPcRelativeReference(&patched_code_,
    190                                                patch,
    191                                                offset + patch.LiteralOffset(),
    192                                                target_offset);
    193           } else if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch) {
    194             patcher_->PatchBakerReadBarrierBranch(&patched_code_,
    195                                                   patch,
    196                                                   offset + patch.LiteralOffset());
    197           } else {
    198             LOG(FATAL) << "Bad patch type. " << patch.GetType();
    199             UNREACHABLE();
    200           }
    201         }
    202       }
    203       out_->WriteFully(&code[0], code.size());
    204       offset += code.size();
    205     }
    206     offset = patcher_->WriteThunks(out_.get(), offset);
    207     CHECK_EQ(offset, output_size);
    208     CHECK_EQ(output_.size(), output_size);
    209   }
    210 
    211   bool CheckLinkedMethod(MethodReference method_ref, const ArrayRef<const uint8_t>& expected_code) {
    212     // Sanity check: original code size must match linked_code.size().
    213     size_t idx = 0u;
    214     for (auto ref : compiled_method_refs_) {
    215       if (ref == method_ref) {
    216         break;
    217       }
    218       ++idx;
    219     }
    220     CHECK_NE(idx, compiled_method_refs_.size());
    221     CHECK_EQ(compiled_methods_[idx]->GetQuickCode().size(), expected_code.size());
    222 
    223     auto result = method_offset_map_.FindMethodOffset(method_ref);
    224     CHECK(result.first);  // Must have been linked.
    225     size_t offset = result.second - compiled_methods_[idx]->CodeDelta();
    226     CHECK_LT(offset, output_.size());
    227     CHECK_LE(offset + expected_code.size(), output_.size());
    228     ArrayRef<const uint8_t> linked_code(&output_[offset], expected_code.size());
    229     if (linked_code == expected_code) {
    230       return true;
    231     }
    232     // Log failure info.
    233     DumpDiff(expected_code, linked_code);
    234     return false;
    235   }
    236 
    237   void DumpDiff(const ArrayRef<const uint8_t>& expected_code,
    238                 const ArrayRef<const uint8_t>& linked_code) {
    239     std::ostringstream expected_hex;
    240     std::ostringstream linked_hex;
    241     std::ostringstream diff_indicator;
    242     static const char digits[] = "0123456789abcdef";
    243     bool found_diff = false;
    244     for (size_t i = 0; i != expected_code.size(); ++i) {
    245       expected_hex << " " << digits[expected_code[i] >> 4] << digits[expected_code[i] & 0xf];
    246       linked_hex << " " << digits[linked_code[i] >> 4] << digits[linked_code[i] & 0xf];
    247       if (!found_diff) {
    248         found_diff = (expected_code[i] != linked_code[i]);
    249         diff_indicator << (found_diff ? " ^^" : "   ");
    250       }
    251     }
    252     CHECK(found_diff);
    253     std::string expected_hex_str = expected_hex.str();
    254     std::string linked_hex_str = linked_hex.str();
    255     std::string diff_indicator_str = diff_indicator.str();
    256     if (diff_indicator_str.length() > 60) {
    257       CHECK_EQ(diff_indicator_str.length() % 3u, 0u);
    258       size_t remove = diff_indicator_str.length() / 3 - 5;
    259       std::ostringstream oss;
    260       oss << "[stripped " << remove << "]";
    261       std::string replacement = oss.str();
    262       expected_hex_str.replace(0u, remove * 3u, replacement);
    263       linked_hex_str.replace(0u, remove * 3u, replacement);
    264       diff_indicator_str.replace(0u, remove * 3u, replacement);
    265     }
    266     LOG(ERROR) << "diff expected_code linked_code";
    267     LOG(ERROR) << "<" << expected_hex_str;
    268     LOG(ERROR) << ">" << linked_hex_str;
    269     LOG(ERROR) << " " << diff_indicator_str;
    270   }
    271 
    272   class ThunkProvider : public RelativePatcherThunkProvider {
    273    public:
    274     ThunkProvider() {}
    275 
    276     void SetThunkCode(const LinkerPatch& patch,
    277                       ArrayRef<const uint8_t> code,
    278                       const std::string& debug_name) {
    279       thunk_map_.emplace(ThunkKey(patch), ThunkValue(code, debug_name));
    280     }
    281 
    282     void GetThunkCode(const LinkerPatch& patch,
    283                       /*out*/ ArrayRef<const uint8_t>* code,
    284                       /*out*/ std::string* debug_name) override {
    285       auto it = thunk_map_.find(ThunkKey(patch));
    286       CHECK(it != thunk_map_.end());
    287       const ThunkValue& value = it->second;
    288       CHECK(code != nullptr);
    289       *code = value.GetCode();
    290       CHECK(debug_name != nullptr);
    291       *debug_name = value.GetDebugName();
    292     }
    293 
    294     void Reset() {
    295       thunk_map_.clear();
    296     }
    297 
    298    private:
    299     class ThunkKey {
    300      public:
    301       explicit ThunkKey(const LinkerPatch& patch)
    302           : type_(patch.GetType()),
    303             custom_value1_(patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch
    304                                ? patch.GetBakerCustomValue1() : 0u),
    305             custom_value2_(patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch
    306                                ? patch.GetBakerCustomValue2() : 0u) {
    307         CHECK(patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch ||
    308               patch.GetType() == LinkerPatch::Type::kCallRelative);
    309       }
    310 
    311       bool operator<(const ThunkKey& other) const {
    312         if (custom_value1_ != other.custom_value1_) {
    313           return custom_value1_ < other.custom_value1_;
    314         }
    315         if (custom_value2_ != other.custom_value2_) {
    316           return custom_value2_ < other.custom_value2_;
    317         }
    318         return type_ < other.type_;
    319       }
    320 
    321      private:
    322       const LinkerPatch::Type type_;
    323       const uint32_t custom_value1_;
    324       const uint32_t custom_value2_;
    325     };
    326 
    327     class ThunkValue {
    328      public:
    329       ThunkValue(ArrayRef<const uint8_t> code, const std::string& debug_name)
    330           : code_(code.begin(), code.end()), debug_name_(debug_name) {}
    331       ArrayRef<const uint8_t> GetCode() const { return ArrayRef<const uint8_t>(code_); }
    332       const std::string& GetDebugName() const { return debug_name_; }
    333 
    334      private:
    335       const std::vector<uint8_t> code_;
    336       const std::string debug_name_;
    337     };
    338 
    339     std::map<ThunkKey, ThunkValue> thunk_map_;
    340   };
    341 
    342   // Map method reference to assinged offset.
    343   // Wrap the map in a class implementing RelativePatcherTargetProvider.
    344   class MethodOffsetMap final : public RelativePatcherTargetProvider {
    345    public:
    346     std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) override {
    347       auto it = map.find(ref);
    348       if (it == map.end()) {
    349         return std::pair<bool, uint32_t>(false, 0u);
    350       } else {
    351         return std::pair<bool, uint32_t>(true, it->second);
    352       }
    353     }
    354     SafeMap<MethodReference, uint32_t> map;
    355   };
    356 
    357   static const uint32_t kTrampolineSize = 4u;
    358   static const uint32_t kTrampolineOffset = 0u;
    359 
    360   CompiledMethodStorage storage_;
    361   InstructionSet instruction_set_;
    362   std::unique_ptr<const InstructionSetFeatures> instruction_set_features_;
    363 
    364   ThunkProvider thunk_provider_;
    365   MethodOffsetMap method_offset_map_;
    366   std::unique_ptr<RelativePatcher> patcher_;
    367   uint32_t bss_begin_;
    368   SafeMap<uint32_t, uint32_t> string_index_to_offset_map_;
    369   std::vector<MethodReference> compiled_method_refs_;
    370   std::vector<std::unique_ptr<CompiledMethod>> compiled_methods_;
    371   std::vector<uint8_t> patched_code_;
    372   std::vector<uint8_t> output_;
    373   std::unique_ptr<VectorOutputStream> out_;
    374 };
    375 
    376 }  // namespace linker
    377 }  // namespace art
    378 
    379 #endif  // ART_DEX2OAT_LINKER_RELATIVE_PATCHER_TEST_H_
    380