Home | History | Annotate | Download | only in mips64
      1 /*
      2  * Copyright (C) 2016 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/mips64/relative_patcher_mips64.h"
     18 
     19 #include "compiled_method.h"
     20 
     21 namespace art {
     22 namespace linker {
     23 
     24 uint32_t Mips64RelativePatcher::ReserveSpace(
     25     uint32_t offset,
     26     const CompiledMethod* compiled_method ATTRIBUTE_UNUSED,
     27     MethodReference method_ref ATTRIBUTE_UNUSED) {
     28   return offset;  // No space reserved; no limit on relative call distance.
     29 }
     30 
     31 uint32_t Mips64RelativePatcher::ReserveSpaceEnd(uint32_t offset) {
     32   return offset;  // No space reserved; no limit on relative call distance.
     33 }
     34 
     35 uint32_t Mips64RelativePatcher::WriteThunks(OutputStream* out ATTRIBUTE_UNUSED, uint32_t offset) {
     36   return offset;  // No thunks added; no limit on relative call distance.
     37 }
     38 
     39 void Mips64RelativePatcher::PatchCall(std::vector<uint8_t>* code,
     40                                       uint32_t literal_offset,
     41                                       uint32_t patch_offset,
     42                                       uint32_t target_offset) {
     43   // Basic sanity checks.
     44   DCHECK_GE(code->size(), 8u);
     45   DCHECK_LE(literal_offset, code->size() - 8u);
     46   // auipc reg, offset_high
     47   DCHECK_EQ((*code)[literal_offset + 0], 0x34);
     48   DCHECK_EQ((*code)[literal_offset + 1], 0x12);
     49   DCHECK_EQ(((*code)[literal_offset + 2] & 0x1F), 0x1E);
     50   DCHECK_EQ(((*code)[literal_offset + 3] & 0xFC), 0xEC);
     51   // jialc reg, offset_low
     52   DCHECK_EQ((*code)[literal_offset + 4], 0x78);
     53   DCHECK_EQ((*code)[literal_offset + 5], 0x56);
     54   DCHECK_EQ(((*code)[literal_offset + 6] & 0xE0), 0x00);
     55   DCHECK_EQ((*code)[literal_offset + 7], 0xF8);
     56 
     57   // Apply patch.
     58   uint32_t diff = target_offset - patch_offset;
     59   // Note that a combination of auipc with an instruction that adds a sign-extended
     60   // 16-bit immediate operand (e.g. jialc) provides a PC-relative range of
     61   // PC-0x80000000 to PC+0x7FFF7FFF on MIPS64, that is, short of 2GB on one end
     62   // by 32KB.
     63   diff += (diff & 0x8000) << 1;  // Account for sign extension in jialc.
     64 
     65   // auipc reg, offset_high
     66   (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16);
     67   (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24);
     68   // jialc reg, offset_low
     69   (*code)[literal_offset + 4] = static_cast<uint8_t>(diff >> 0);
     70   (*code)[literal_offset + 5] = static_cast<uint8_t>(diff >> 8);
     71 }
     72 
     73 void Mips64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
     74                                                      const LinkerPatch& patch,
     75                                                      uint32_t patch_offset,
     76                                                      uint32_t target_offset) {
     77   uint32_t anchor_literal_offset = patch.PcInsnOffset();
     78   uint32_t literal_offset = patch.LiteralOffset();
     79 
     80   // Basic sanity checks.
     81   DCHECK_GE(code->size(), 8u);
     82   DCHECK_LE(literal_offset, code->size() - 8u);
     83   DCHECK_EQ(literal_offset, anchor_literal_offset);
     84   // auipc reg, offset_high
     85   DCHECK_EQ((*code)[literal_offset + 0], 0x34);
     86   DCHECK_EQ((*code)[literal_offset + 1], 0x12);
     87   DCHECK_EQ(((*code)[literal_offset + 2] & 0x1F), 0x1E);
     88   DCHECK_EQ(((*code)[literal_offset + 3] & 0xFC), 0xEC);
     89   // instr reg(s), offset_low
     90   DCHECK_EQ((*code)[literal_offset + 4], 0x78);
     91   DCHECK_EQ((*code)[literal_offset + 5], 0x56);
     92 
     93   // Apply patch.
     94   uint32_t anchor_offset = patch_offset - literal_offset + anchor_literal_offset;
     95   uint32_t diff = target_offset - anchor_offset;
     96   // Note that a combination of auipc with an instruction that adds a sign-extended
     97   // 16-bit immediate operand (e.g. ld) provides a PC-relative range of
     98   // PC-0x80000000 to PC+0x7FFF7FFF on MIPS64, that is, short of 2GB on one end
     99   // by 32KB.
    100   diff += (diff & 0x8000) << 1;  // Account for sign extension in instruction following auipc.
    101 
    102   // auipc reg, offset_high
    103   (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16);
    104   (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24);
    105   // instr reg(s), offset_low
    106   (*code)[literal_offset + 4] = static_cast<uint8_t>(diff >> 0);
    107   (*code)[literal_offset + 5] = static_cast<uint8_t>(diff >> 8);
    108 }
    109 
    110 void Mips64RelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
    111                                                         const LinkerPatch& patch ATTRIBUTE_UNUSED,
    112                                                         uint32_t patch_offset ATTRIBUTE_UNUSED) {
    113   LOG(FATAL) << "UNIMPLEMENTED";
    114 }
    115 
    116 }  // namespace linker
    117 }  // namespace art
    118