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/relative_patcher_test.h" 18 #include "linker/x86_64/relative_patcher_x86_64.h" 19 20 namespace art { 21 namespace linker { 22 23 class X86_64RelativePatcherTest : public RelativePatcherTest { 24 public: 25 X86_64RelativePatcherTest() : RelativePatcherTest(kX86_64, "default") { } 26 27 protected: 28 static const uint8_t kCallRawCode[]; 29 static const ArrayRef<const uint8_t> kCallCode; 30 static const uint8_t kDexCacheLoadRawCode[]; 31 static const ArrayRef<const uint8_t> kDexCacheLoadCode; 32 static const uint8_t kStringReferenceRawCode[]; 33 static const ArrayRef<const uint8_t> kStringReferenceCode; 34 35 uint32_t GetMethodOffset(uint32_t method_idx) { 36 auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx)); 37 CHECK(result.first); 38 return result.second; 39 } 40 }; 41 42 const uint8_t X86_64RelativePatcherTest::kCallRawCode[] = { 43 0xe8, 0x00, 0x01, 0x00, 0x00 44 }; 45 46 const ArrayRef<const uint8_t> X86_64RelativePatcherTest::kCallCode(kCallRawCode); 47 48 const uint8_t X86_64RelativePatcherTest::kDexCacheLoadRawCode[] = { 49 0x8b, 0x05, // mov eax, [rip + <offset>] 50 0x00, 0x01, 0x00, 0x00 51 }; 52 53 const ArrayRef<const uint8_t> X86_64RelativePatcherTest::kDexCacheLoadCode( 54 kDexCacheLoadRawCode); 55 56 const uint8_t X86_64RelativePatcherTest::kStringReferenceRawCode[] = { 57 0x8d, 0x05, // lea eax, [rip + <offset>] 58 0x00, 0x01, 0x00, 0x00 59 }; 60 61 const ArrayRef<const uint8_t> X86_64RelativePatcherTest::kStringReferenceCode( 62 kStringReferenceRawCode); 63 64 TEST_F(X86_64RelativePatcherTest, CallSelf) { 65 LinkerPatch patches[] = { 66 LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 1u), 67 }; 68 AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches)); 69 Link(); 70 71 static const uint8_t expected_code[] = { 72 0xe8, 0xfb, 0xff, 0xff, 0xff 73 }; 74 EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); 75 } 76 77 TEST_F(X86_64RelativePatcherTest, CallOther) { 78 LinkerPatch method1_patches[] = { 79 LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 2u), 80 }; 81 AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(method1_patches)); 82 LinkerPatch method2_patches[] = { 83 LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 1u), 84 }; 85 AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<const LinkerPatch>(method2_patches)); 86 Link(); 87 88 uint32_t method1_offset = GetMethodOffset(1u); 89 uint32_t method2_offset = GetMethodOffset(2u); 90 uint32_t diff_after = method2_offset - (method1_offset + kCallCode.size() /* PC adjustment */); 91 static const uint8_t method1_expected_code[] = { 92 0xe8, 93 static_cast<uint8_t>(diff_after), 94 static_cast<uint8_t>(diff_after >> 8), 95 static_cast<uint8_t>(diff_after >> 16), 96 static_cast<uint8_t>(diff_after >> 24) 97 }; 98 EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code))); 99 uint32_t diff_before = method1_offset - (method2_offset + kCallCode.size() /* PC adjustment */); 100 static const uint8_t method2_expected_code[] = { 101 0xe8, 102 static_cast<uint8_t>(diff_before), 103 static_cast<uint8_t>(diff_before >> 8), 104 static_cast<uint8_t>(diff_before >> 16), 105 static_cast<uint8_t>(diff_before >> 24) 106 }; 107 EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code))); 108 } 109 110 TEST_F(X86_64RelativePatcherTest, CallTrampoline) { 111 LinkerPatch patches[] = { 112 LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 2u), 113 }; 114 AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches)); 115 Link(); 116 117 auto result = method_offset_map_.FindMethodOffset(MethodRef(1u)); 118 ASSERT_TRUE(result.first); 119 uint32_t diff = kTrampolineOffset - (result.second + kCallCode.size()); 120 static const uint8_t expected_code[] = { 121 0xe8, 122 static_cast<uint8_t>(diff), 123 static_cast<uint8_t>(diff >> 8), 124 static_cast<uint8_t>(diff >> 16), 125 static_cast<uint8_t>(diff >> 24) 126 }; 127 EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); 128 } 129 130 TEST_F(X86_64RelativePatcherTest, DexCacheReference) { 131 dex_cache_arrays_begin_ = 0x12345678; 132 constexpr size_t kElementOffset = 0x1234; 133 LinkerPatch patches[] = { 134 LinkerPatch::DexCacheArrayPatch(kDexCacheLoadCode.size() - 4u, nullptr, 0u, kElementOffset), 135 }; 136 AddCompiledMethod(MethodRef(1u), kDexCacheLoadCode, ArrayRef<const LinkerPatch>(patches)); 137 Link(); 138 139 auto result = method_offset_map_.FindMethodOffset(MethodRef(1u)); 140 ASSERT_TRUE(result.first); 141 uint32_t diff = 142 dex_cache_arrays_begin_ + kElementOffset - (result.second + kDexCacheLoadCode.size()); 143 static const uint8_t expected_code[] = { 144 0x8b, 0x05, 145 static_cast<uint8_t>(diff), 146 static_cast<uint8_t>(diff >> 8), 147 static_cast<uint8_t>(diff >> 16), 148 static_cast<uint8_t>(diff >> 24) 149 }; 150 EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); 151 } 152 153 TEST_F(X86_64RelativePatcherTest, StringReference) { 154 constexpr uint32_t kStringIndex = 1u; 155 constexpr uint32_t kStringOffset = 0x12345678; 156 string_index_to_offset_map_.Put(kStringIndex, kStringOffset); 157 LinkerPatch patches[] = { 158 LinkerPatch::RelativeStringPatch( 159 kStringReferenceCode.size() - 4u, nullptr, 0u, kStringIndex), 160 }; 161 AddCompiledMethod(MethodRef(1u), kStringReferenceCode, ArrayRef<const LinkerPatch>(patches)); 162 Link(); 163 164 auto result = method_offset_map_.FindMethodOffset(MethodRef(1u)); 165 ASSERT_TRUE(result.first); 166 uint32_t diff = kStringOffset - (result.second + kStringReferenceCode.size()); 167 static const uint8_t expected_code[] = { 168 0x8d, 0x05, 169 static_cast<uint8_t>(diff), 170 static_cast<uint8_t>(diff >> 8), 171 static_cast<uint8_t>(diff >> 16), 172 static_cast<uint8_t>(diff >> 24) 173 }; 174 EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); 175 } 176 177 } // namespace linker 178 } // namespace art 179