Home | History | Annotate | Download | only in arm
      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 #include "linker/arm/relative_patcher_arm_base.h"
     18 
     19 #include "compiled_method.h"
     20 #include "linker/output_stream.h"
     21 #include "oat.h"
     22 #include "oat_quick_method_header.h"
     23 
     24 namespace art {
     25 namespace linker {
     26 
     27 uint32_t ArmBaseRelativePatcher::ReserveSpace(uint32_t offset,
     28                                               const CompiledMethod* compiled_method,
     29                                               MethodReference method_ref) {
     30   return ReserveSpaceInternal(offset, compiled_method, method_ref, 0u);
     31 }
     32 
     33 uint32_t ArmBaseRelativePatcher::ReserveSpaceEnd(uint32_t offset) {
     34   // NOTE: The final thunk can be reserved from InitCodeMethodVisitor::EndClass() while it
     35   // may be written early by WriteCodeMethodVisitor::VisitMethod() for a deduplicated chunk
     36   // of code. To avoid any alignment discrepancies for the final chunk, we always align the
     37   // offset after reserving of writing any chunk.
     38   uint32_t aligned_offset = CompiledMethod::AlignCode(offset, instruction_set_);
     39   bool needs_thunk = ReserveSpaceProcessPatches(aligned_offset,
     40                                                 MethodReference(nullptr, 0u),
     41                                                 aligned_offset);
     42   if (needs_thunk) {
     43     // All remaining patches will be handled by this thunk.
     44     DCHECK(!unprocessed_patches_.empty());
     45     DCHECK_LE(aligned_offset - unprocessed_patches_.front().second, max_positive_displacement_);
     46     unprocessed_patches_.clear();
     47 
     48     thunk_locations_.push_back(aligned_offset);
     49     offset = CompiledMethod::AlignCode(aligned_offset + thunk_code_.size(), instruction_set_);
     50   }
     51   return offset;
     52 }
     53 
     54 uint32_t ArmBaseRelativePatcher::WriteThunks(OutputStream* out, uint32_t offset) {
     55   if (current_thunk_to_write_ == thunk_locations_.size()) {
     56     return offset;
     57   }
     58   uint32_t aligned_offset = CompiledMethod::AlignCode(offset, instruction_set_);
     59   if (UNLIKELY(aligned_offset == thunk_locations_[current_thunk_to_write_])) {
     60     ++current_thunk_to_write_;
     61     uint32_t aligned_code_delta = aligned_offset - offset;
     62     if (aligned_code_delta != 0u && !WriteCodeAlignment(out, aligned_code_delta)) {
     63       return 0u;
     64     }
     65     if (UNLIKELY(!WriteRelCallThunk(out, ArrayRef<const uint8_t>(thunk_code_)))) {
     66       return 0u;
     67     }
     68     uint32_t thunk_end_offset = aligned_offset + thunk_code_.size();
     69     // Align after writing chunk, see the ReserveSpace() above.
     70     offset = CompiledMethod::AlignCode(thunk_end_offset, instruction_set_);
     71     aligned_code_delta = offset - thunk_end_offset;
     72     if (aligned_code_delta != 0u && !WriteCodeAlignment(out, aligned_code_delta)) {
     73       return 0u;
     74     }
     75   }
     76   return offset;
     77 }
     78 
     79 ArmBaseRelativePatcher::ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider,
     80                                                InstructionSet instruction_set,
     81                                                std::vector<uint8_t> thunk_code,
     82                                                uint32_t max_positive_displacement,
     83                                                uint32_t max_negative_displacement)
     84     : provider_(provider), instruction_set_(instruction_set), thunk_code_(thunk_code),
     85       max_positive_displacement_(max_positive_displacement),
     86       max_negative_displacement_(max_negative_displacement),
     87       thunk_locations_(), current_thunk_to_write_(0u), unprocessed_patches_() {
     88 }
     89 
     90 uint32_t ArmBaseRelativePatcher::ReserveSpaceInternal(uint32_t offset,
     91                                                       const CompiledMethod* compiled_method,
     92                                                       MethodReference method_ref,
     93                                                       uint32_t max_extra_space) {
     94   uint32_t quick_code_size = compiled_method->GetQuickCode().size();
     95   uint32_t quick_code_offset = compiled_method->AlignCode(offset) + sizeof(OatQuickMethodHeader);
     96   uint32_t next_aligned_offset = compiled_method->AlignCode(quick_code_offset + quick_code_size);
     97   // Adjust for extra space required by the subclass.
     98   next_aligned_offset = compiled_method->AlignCode(next_aligned_offset + max_extra_space);
     99   // TODO: ignore unprocessed patches targeting this method if they can reach quick_code_offset.
    100   // We need the MethodReference for that.
    101   if (!unprocessed_patches_.empty() &&
    102       next_aligned_offset - unprocessed_patches_.front().second > max_positive_displacement_) {
    103     bool needs_thunk = ReserveSpaceProcessPatches(quick_code_offset,
    104                                                   method_ref,
    105                                                   next_aligned_offset);
    106     if (needs_thunk) {
    107       // A single thunk will cover all pending patches.
    108       unprocessed_patches_.clear();
    109       uint32_t thunk_location = compiled_method->AlignCode(offset);
    110       thunk_locations_.push_back(thunk_location);
    111       offset = CompiledMethod::AlignCode(thunk_location + thunk_code_.size(), instruction_set_);
    112     }
    113   }
    114   for (const LinkerPatch& patch : compiled_method->GetPatches()) {
    115     if (patch.GetType() == LinkerPatch::Type::kCallRelative) {
    116       unprocessed_patches_.emplace_back(patch.TargetMethod(),
    117                                         quick_code_offset + patch.LiteralOffset());
    118     }
    119   }
    120   return offset;
    121 }
    122 
    123 uint32_t ArmBaseRelativePatcher::CalculateDisplacement(uint32_t patch_offset,
    124                                                        uint32_t target_offset) {
    125   // Unsigned arithmetic with its well-defined overflow behavior is just fine here.
    126   uint32_t displacement = target_offset - patch_offset;
    127   // NOTE: With unsigned arithmetic we do mean to use && rather than || below.
    128   if (displacement > max_positive_displacement_ && displacement < -max_negative_displacement_) {
    129     // Unwritten thunks have higher offsets, check if it's within range.
    130     DCHECK(current_thunk_to_write_ == thunk_locations_.size() ||
    131            thunk_locations_[current_thunk_to_write_] > patch_offset);
    132     if (current_thunk_to_write_ != thunk_locations_.size() &&
    133         thunk_locations_[current_thunk_to_write_] - patch_offset < max_positive_displacement_) {
    134       displacement = thunk_locations_[current_thunk_to_write_] - patch_offset;
    135     } else {
    136       // We must have a previous thunk then.
    137       DCHECK_NE(current_thunk_to_write_, 0u);
    138       DCHECK_LT(thunk_locations_[current_thunk_to_write_ - 1], patch_offset);
    139       displacement = thunk_locations_[current_thunk_to_write_ - 1] - patch_offset;
    140       DCHECK(displacement >= -max_negative_displacement_);
    141     }
    142   }
    143   return displacement;
    144 }
    145 
    146 bool ArmBaseRelativePatcher::ReserveSpaceProcessPatches(uint32_t quick_code_offset,
    147                                                         MethodReference method_ref,
    148                                                         uint32_t next_aligned_offset) {
    149   // Process as many patches as possible, stop only on unresolved targets or calls too far back.
    150   while (!unprocessed_patches_.empty()) {
    151     MethodReference patch_ref = unprocessed_patches_.front().first;
    152     uint32_t patch_offset = unprocessed_patches_.front().second;
    153     DCHECK(thunk_locations_.empty() || thunk_locations_.back() <= patch_offset);
    154     if (patch_ref.dex_file == method_ref.dex_file &&
    155         patch_ref.dex_method_index == method_ref.dex_method_index) {
    156       DCHECK_GT(quick_code_offset, patch_offset);
    157       if (quick_code_offset - patch_offset > max_positive_displacement_) {
    158         return true;
    159       }
    160     } else {
    161       auto result = provider_->FindMethodOffset(patch_ref);
    162       if (!result.first) {
    163         // If still unresolved, check if we have a thunk within range.
    164         if (thunk_locations_.empty() ||
    165             patch_offset - thunk_locations_.back() > max_negative_displacement_) {
    166           // No thunk in range, we need a thunk if the next aligned offset
    167           // is out of range, or if we're at the end of all code.
    168           return (next_aligned_offset - patch_offset > max_positive_displacement_) ||
    169               (quick_code_offset == next_aligned_offset);  // End of code.
    170         }
    171       } else {
    172         uint32_t target_offset = result.second - CompiledCode::CodeDelta(instruction_set_);
    173         if (target_offset >= patch_offset) {
    174           DCHECK_LE(target_offset - patch_offset, max_positive_displacement_);
    175         } else {
    176           // When calling back, check if we have a thunk that's closer than the actual target.
    177           if (!thunk_locations_.empty()) {
    178             target_offset = std::max(target_offset, thunk_locations_.back());
    179           }
    180           if (patch_offset - target_offset > max_negative_displacement_) {
    181             return true;
    182           }
    183         }
    184       }
    185     }
    186     unprocessed_patches_.pop_front();
    187   }
    188   return false;
    189 }
    190 
    191 }  // namespace linker
    192 }  // namespace art
    193