Home | History | Annotate | Download | only in linker
      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 "compiled_method.h"
     18 #include "gtest/gtest.h"
     19 #include "multi_oat_relative_patcher.h"
     20 #include "vector_output_stream.h"
     21 
     22 namespace art {
     23 namespace linker {
     24 
     25 static const MethodReference kNullMethodRef = MethodReference(nullptr, 0u);
     26 
     27 static bool EqualRef(MethodReference lhs, MethodReference rhs) {
     28   return lhs.dex_file == rhs.dex_file && lhs.dex_method_index == rhs.dex_method_index;
     29 }
     30 
     31 class MultiOatRelativePatcherTest : public testing::Test {
     32  protected:
     33   class MockPatcher : public RelativePatcher {
     34    public:
     35     MockPatcher() { }
     36 
     37     uint32_t ReserveSpace(uint32_t offset,
     38                           const CompiledMethod* compiled_method ATTRIBUTE_UNUSED,
     39                           MethodReference method_ref) OVERRIDE {
     40       last_reserve_offset_ = offset;
     41       last_reserve_method_ = method_ref;
     42       offset += next_reserve_adjustment_;
     43       next_reserve_adjustment_ = 0u;
     44       return offset;
     45     }
     46 
     47     uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE {
     48       last_reserve_offset_ = offset;
     49       last_reserve_method_ = kNullMethodRef;
     50       offset += next_reserve_adjustment_;
     51       next_reserve_adjustment_ = 0u;
     52       return offset;
     53     }
     54 
     55     uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE {
     56       last_write_offset_ = offset;
     57       if (next_write_alignment_ != 0u) {
     58         offset += next_write_alignment_;
     59         bool success = WriteCodeAlignment(out, next_write_alignment_);
     60         CHECK(success);
     61         next_write_alignment_ = 0u;
     62       }
     63       if (next_write_call_thunk_ != 0u) {
     64         offset += next_write_call_thunk_;
     65         std::vector<uint8_t> thunk(next_write_call_thunk_, 'c');
     66         bool success = WriteRelCallThunk(out, ArrayRef<const uint8_t>(thunk));
     67         CHECK(success);
     68         next_write_call_thunk_ = 0u;
     69       }
     70       if (next_write_misc_thunk_ != 0u) {
     71         offset += next_write_misc_thunk_;
     72         std::vector<uint8_t> thunk(next_write_misc_thunk_, 'm');
     73         bool success = WriteMiscThunk(out, ArrayRef<const uint8_t>(thunk));
     74         CHECK(success);
     75         next_write_misc_thunk_ = 0u;
     76       }
     77       return offset;
     78     }
     79 
     80     void PatchCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
     81                    uint32_t literal_offset,
     82                    uint32_t patch_offset,
     83                    uint32_t target_offset) OVERRIDE {
     84       last_literal_offset_ = literal_offset;
     85       last_patch_offset_ = patch_offset;
     86       last_target_offset_ = target_offset;
     87     }
     88 
     89     void PatchPcRelativeReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
     90                                   const LinkerPatch& patch,
     91                                   uint32_t patch_offset,
     92                                   uint32_t target_offset) OVERRIDE {
     93       last_literal_offset_ = patch.LiteralOffset();
     94       last_patch_offset_ = patch_offset;
     95       last_target_offset_ = target_offset;
     96     }
     97 
     98     uint32_t last_reserve_offset_ = 0u;
     99     MethodReference last_reserve_method_ = kNullMethodRef;
    100     uint32_t next_reserve_adjustment_ = 0u;
    101 
    102     uint32_t last_write_offset_ = 0u;
    103     uint32_t next_write_alignment_ = 0u;
    104     uint32_t next_write_call_thunk_ = 0u;
    105     uint32_t next_write_misc_thunk_ = 0u;
    106 
    107     uint32_t last_literal_offset_ = 0u;
    108     uint32_t last_patch_offset_ = 0u;
    109     uint32_t last_target_offset_ = 0u;
    110   };
    111 
    112   MultiOatRelativePatcherTest()
    113       : instruction_set_features_(InstructionSetFeatures::FromCppDefines()),
    114         patcher_(kRuntimeISA, instruction_set_features_.get()) {
    115     std::unique_ptr<MockPatcher> mock(new MockPatcher());
    116     mock_ = mock.get();
    117     patcher_.relative_patcher_ = std::move(mock);
    118   }
    119 
    120   std::unique_ptr<const InstructionSetFeatures> instruction_set_features_;
    121   MultiOatRelativePatcher patcher_;
    122   MockPatcher* mock_;
    123 };
    124 
    125 TEST_F(MultiOatRelativePatcherTest, Offsets) {
    126   const DexFile* dex_file = reinterpret_cast<const DexFile*>(1);
    127   MethodReference ref1(dex_file, 1u);
    128   MethodReference ref2(dex_file, 2u);
    129   EXPECT_EQ(0u, patcher_.GetOffset(ref1));
    130   EXPECT_EQ(0u, patcher_.GetOffset(ref2));
    131 
    132   uint32_t adjustment1 = 0x1000;
    133   patcher_.StartOatFile(adjustment1);
    134   EXPECT_EQ(0u, patcher_.GetOffset(ref1));
    135   EXPECT_EQ(0u, patcher_.GetOffset(ref2));
    136 
    137   uint32_t off1 = 0x1234;
    138   patcher_.SetOffset(ref1, off1);
    139   EXPECT_EQ(off1, patcher_.GetOffset(ref1));
    140   EXPECT_EQ(0u, patcher_.GetOffset(ref2));
    141 
    142   uint32_t adjustment2 = 0x30000;
    143   patcher_.StartOatFile(adjustment2);
    144   EXPECT_EQ(off1 + adjustment1 - adjustment2, patcher_.GetOffset(ref1));
    145   EXPECT_EQ(0u, patcher_.GetOffset(ref2));
    146 
    147   uint32_t off2 = 0x4321;
    148   patcher_.SetOffset(ref2, off2);
    149   EXPECT_EQ(off1 + adjustment1 - adjustment2, patcher_.GetOffset(ref1));
    150   EXPECT_EQ(off2, patcher_.GetOffset(ref2));
    151 
    152   uint32_t adjustment3 = 0x78000;
    153   patcher_.StartOatFile(adjustment3);
    154   EXPECT_EQ(off1 + adjustment1 - adjustment3, patcher_.GetOffset(ref1));
    155   EXPECT_EQ(off2 + adjustment2 - adjustment3, patcher_.GetOffset(ref2));
    156 }
    157 
    158 TEST_F(MultiOatRelativePatcherTest, OffsetsInReserve) {
    159   const DexFile* dex_file = reinterpret_cast<const DexFile*>(1);
    160   MethodReference ref1(dex_file, 1u);
    161   MethodReference ref2(dex_file, 2u);
    162   MethodReference ref3(dex_file, 3u);
    163   const CompiledMethod* method = reinterpret_cast<const CompiledMethod*>(-1);
    164 
    165   uint32_t adjustment1 = 0x1000;
    166   patcher_.StartOatFile(adjustment1);
    167 
    168   uint32_t method1_offset = 0x100;
    169   uint32_t method1_offset_check = patcher_.ReserveSpace(method1_offset, method, ref1);
    170   ASSERT_EQ(adjustment1 + method1_offset, mock_->last_reserve_offset_);
    171   ASSERT_TRUE(EqualRef(ref1, mock_->last_reserve_method_));
    172   ASSERT_EQ(method1_offset, method1_offset_check);
    173 
    174   uint32_t method2_offset = 0x1230;
    175   uint32_t method2_reserve_adjustment = 0x10;
    176   mock_->next_reserve_adjustment_ = method2_reserve_adjustment;
    177   uint32_t method2_offset_adjusted = patcher_.ReserveSpace(method2_offset, method, ref2);
    178   ASSERT_EQ(adjustment1 + method2_offset, mock_->last_reserve_offset_);
    179   ASSERT_TRUE(EqualRef(ref2, mock_->last_reserve_method_));
    180   ASSERT_EQ(method2_offset + method2_reserve_adjustment, method2_offset_adjusted);
    181 
    182   uint32_t end1_offset = 0x4320;
    183   uint32_t end1_offset_check = patcher_.ReserveSpaceEnd(end1_offset);
    184   ASSERT_EQ(adjustment1 + end1_offset, mock_->last_reserve_offset_);
    185   ASSERT_TRUE(EqualRef(kNullMethodRef, mock_->last_reserve_method_));
    186   ASSERT_EQ(end1_offset, end1_offset_check);
    187 
    188   uint32_t adjustment2 = 0xd000;
    189   patcher_.StartOatFile(adjustment2);
    190 
    191   uint32_t method3_offset = 0xf00;
    192   uint32_t method3_offset_check = patcher_.ReserveSpace(method3_offset, method, ref3);
    193   ASSERT_EQ(adjustment2 + method3_offset, mock_->last_reserve_offset_);
    194   ASSERT_TRUE(EqualRef(ref3, mock_->last_reserve_method_));
    195   ASSERT_EQ(method3_offset, method3_offset_check);
    196 
    197   uint32_t end2_offset = 0x2400;
    198   uint32_t end2_reserve_adjustment = 0x20;
    199   mock_->next_reserve_adjustment_ = end2_reserve_adjustment;
    200   uint32_t end2_offset_adjusted = patcher_.ReserveSpaceEnd(end2_offset);
    201   ASSERT_EQ(adjustment2 + end2_offset, mock_->last_reserve_offset_);
    202   ASSERT_TRUE(EqualRef(kNullMethodRef, mock_->last_reserve_method_));
    203   ASSERT_EQ(end2_offset + end2_reserve_adjustment, end2_offset_adjusted);
    204 }
    205 
    206 TEST_F(MultiOatRelativePatcherTest, Write) {
    207   std::vector<uint8_t> output;
    208   VectorOutputStream vos("output", &output);
    209 
    210   uint32_t adjustment1 = 0x1000;
    211   patcher_.StartOatFile(adjustment1);
    212 
    213   uint32_t method1_offset = 0x100;
    214   uint32_t method1_offset_check = patcher_.WriteThunks(&vos, method1_offset);
    215   ASSERT_EQ(adjustment1 + method1_offset, mock_->last_write_offset_);
    216   ASSERT_EQ(method1_offset, method1_offset_check);
    217   vos.WriteFully("1", 1);  // Mark method1.
    218 
    219   uint32_t method2_offset = 0x1230;
    220   uint32_t method2_alignment_size = 1;
    221   uint32_t method2_call_thunk_size = 2;
    222   mock_->next_write_alignment_ = method2_alignment_size;
    223   mock_->next_write_call_thunk_ = method2_call_thunk_size;
    224   uint32_t method2_offset_adjusted = patcher_.WriteThunks(&vos, method2_offset);
    225   ASSERT_EQ(adjustment1 + method2_offset, mock_->last_write_offset_);
    226   ASSERT_EQ(method2_offset + method2_alignment_size + method2_call_thunk_size,
    227             method2_offset_adjusted);
    228   vos.WriteFully("2", 1);  // Mark method2.
    229 
    230   EXPECT_EQ(method2_alignment_size, patcher_.CodeAlignmentSize());
    231   EXPECT_EQ(method2_call_thunk_size, patcher_.RelativeCallThunksSize());
    232 
    233   uint32_t adjustment2 = 0xd000;
    234   patcher_.StartOatFile(adjustment2);
    235 
    236   uint32_t method3_offset = 0xf00;
    237   uint32_t method3_alignment_size = 2;
    238   uint32_t method3_misc_thunk_size = 1;
    239   mock_->next_write_alignment_ = method3_alignment_size;
    240   mock_->next_write_misc_thunk_ = method3_misc_thunk_size;
    241   uint32_t method3_offset_adjusted = patcher_.WriteThunks(&vos, method3_offset);
    242   ASSERT_EQ(adjustment2 + method3_offset, mock_->last_write_offset_);
    243   ASSERT_EQ(method3_offset + method3_alignment_size + method3_misc_thunk_size,
    244             method3_offset_adjusted);
    245   vos.WriteFully("3", 1);  // Mark method3.
    246 
    247   EXPECT_EQ(method3_alignment_size, patcher_.CodeAlignmentSize());
    248   EXPECT_EQ(method3_misc_thunk_size, patcher_.MiscThunksSize());
    249 
    250   uint8_t expected_output[] = {
    251       '1',
    252       0, 'c', 'c', '2',
    253       0, 0, 'm', '3',
    254   };
    255   ASSERT_EQ(arraysize(expected_output), output.size());
    256   for (size_t i = 0; i != arraysize(expected_output); ++i) {
    257     ASSERT_EQ(expected_output[i], output[i]) << i;
    258   }
    259 }
    260 
    261 TEST_F(MultiOatRelativePatcherTest, Patch) {
    262   std::vector<uint8_t> code(16);
    263 
    264   uint32_t adjustment1 = 0x1000;
    265   patcher_.StartOatFile(adjustment1);
    266 
    267   uint32_t method1_literal_offset = 4u;
    268   uint32_t method1_patch_offset = 0x1234u;
    269   uint32_t method1_target_offset = 0x8888u;
    270   patcher_.PatchCall(&code, method1_literal_offset, method1_patch_offset, method1_target_offset);
    271   DCHECK_EQ(method1_literal_offset, mock_->last_literal_offset_);
    272   DCHECK_EQ(method1_patch_offset + adjustment1, mock_->last_patch_offset_);
    273   DCHECK_EQ(method1_target_offset + adjustment1, mock_->last_target_offset_);
    274 
    275   uint32_t method2_literal_offset = 12u;
    276   uint32_t method2_patch_offset = 0x7654u;
    277   uint32_t method2_target_offset = 0xccccu;
    278   LinkerPatch method2_patch =
    279       LinkerPatch::DexCacheArrayPatch(method2_literal_offset, nullptr, 0u, 1234u);
    280   patcher_.PatchPcRelativeReference(
    281       &code, method2_patch, method2_patch_offset, method2_target_offset);
    282   DCHECK_EQ(method2_literal_offset, mock_->last_literal_offset_);
    283   DCHECK_EQ(method2_patch_offset + adjustment1, mock_->last_patch_offset_);
    284   DCHECK_EQ(method2_target_offset + adjustment1, mock_->last_target_offset_);
    285 
    286   uint32_t adjustment2 = 0xd000;
    287   patcher_.StartOatFile(adjustment2);
    288 
    289   uint32_t method3_literal_offset = 8u;
    290   uint32_t method3_patch_offset = 0x108u;
    291   uint32_t method3_target_offset = 0x200u;
    292   patcher_.PatchCall(&code, method3_literal_offset, method3_patch_offset, method3_target_offset);
    293   DCHECK_EQ(method3_literal_offset, mock_->last_literal_offset_);
    294   DCHECK_EQ(method3_patch_offset + adjustment2, mock_->last_patch_offset_);
    295   DCHECK_EQ(method3_target_offset + adjustment2, mock_->last_target_offset_);
    296 }
    297 
    298 }  // namespace linker
    299 }  // namespace art
    300