xref: /aosp_15_r20/art/dex2oat/linker/arm/relative_patcher_thumb2.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2015 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker #include "linker/arm/relative_patcher_thumb2.h"
18*795d594fSAndroid Build Coastguard Worker 
19*795d594fSAndroid Build Coastguard Worker #include <sstream>
20*795d594fSAndroid Build Coastguard Worker 
21*795d594fSAndroid Build Coastguard Worker #include "arch/arm/asm_support_arm.h"
22*795d594fSAndroid Build Coastguard Worker #include "art_method.h"
23*795d594fSAndroid Build Coastguard Worker #include "base/bit_utils.h"
24*795d594fSAndroid Build Coastguard Worker #include "base/malloc_arena_pool.h"
25*795d594fSAndroid Build Coastguard Worker #include "driver/compiled_method.h"
26*795d594fSAndroid Build Coastguard Worker #include "entrypoints/quick/quick_entrypoints_enum.h"
27*795d594fSAndroid Build Coastguard Worker #include "linker/linker_patch.h"
28*795d594fSAndroid Build Coastguard Worker #include "lock_word.h"
29*795d594fSAndroid Build Coastguard Worker #include "mirror/array-inl.h"
30*795d594fSAndroid Build Coastguard Worker #include "mirror/object.h"
31*795d594fSAndroid Build Coastguard Worker #include "read_barrier.h"
32*795d594fSAndroid Build Coastguard Worker 
33*795d594fSAndroid Build Coastguard Worker namespace art {
34*795d594fSAndroid Build Coastguard Worker namespace linker {
35*795d594fSAndroid Build Coastguard Worker 
36*795d594fSAndroid Build Coastguard Worker // PC displacement from patch location; Thumb2 PC is always at instruction address + 4.
37*795d594fSAndroid Build Coastguard Worker static constexpr int32_t kPcDisplacement = 4;
38*795d594fSAndroid Build Coastguard Worker 
39*795d594fSAndroid Build Coastguard Worker // Maximum positive and negative displacement for method call measured from the patch location.
40*795d594fSAndroid Build Coastguard Worker // (Signed 25 bit displacement with the last bit 0 has range [-2^24, 2^24-2] measured from
41*795d594fSAndroid Build Coastguard Worker // the Thumb2 PC pointing right after the BL, i.e. 4 bytes later than the patch location.)
42*795d594fSAndroid Build Coastguard Worker constexpr uint32_t kMaxMethodCallPositiveDisplacement = (1u << 24) - 2 + kPcDisplacement;
43*795d594fSAndroid Build Coastguard Worker constexpr uint32_t kMaxMethodCallNegativeDisplacement = (1u << 24) - kPcDisplacement;
44*795d594fSAndroid Build Coastguard Worker 
45*795d594fSAndroid Build Coastguard Worker // Maximum positive and negative displacement for a conditional branch measured from the patch
46*795d594fSAndroid Build Coastguard Worker // location. (Signed 21 bit displacement with the last bit 0 has range [-2^20, 2^20-2] measured
47*795d594fSAndroid Build Coastguard Worker // from the Thumb2 PC pointing right after the B.cond, i.e. 4 bytes later than the patch location.)
48*795d594fSAndroid Build Coastguard Worker constexpr uint32_t kMaxBcondPositiveDisplacement = (1u << 20) - 2u + kPcDisplacement;
49*795d594fSAndroid Build Coastguard Worker constexpr uint32_t kMaxBcondNegativeDisplacement = (1u << 20) - kPcDisplacement;
50*795d594fSAndroid Build Coastguard Worker 
Thumb2RelativePatcher(RelativePatcherThunkProvider * thunk_provider,RelativePatcherTargetProvider * target_provider)51*795d594fSAndroid Build Coastguard Worker Thumb2RelativePatcher::Thumb2RelativePatcher(RelativePatcherThunkProvider* thunk_provider,
52*795d594fSAndroid Build Coastguard Worker                                              RelativePatcherTargetProvider* target_provider)
53*795d594fSAndroid Build Coastguard Worker     : ArmBaseRelativePatcher(thunk_provider, target_provider, InstructionSet::kThumb2) {
54*795d594fSAndroid Build Coastguard Worker }
55*795d594fSAndroid Build Coastguard Worker 
PatchCall(std::vector<uint8_t> * code,uint32_t literal_offset,uint32_t patch_offset,uint32_t target_offset)56*795d594fSAndroid Build Coastguard Worker void Thumb2RelativePatcher::PatchCall(std::vector<uint8_t>* code,
57*795d594fSAndroid Build Coastguard Worker                                       uint32_t literal_offset,
58*795d594fSAndroid Build Coastguard Worker                                       uint32_t patch_offset,
59*795d594fSAndroid Build Coastguard Worker                                       uint32_t target_offset) {
60*795d594fSAndroid Build Coastguard Worker   DCHECK_ALIGNED(patch_offset, 2u);
61*795d594fSAndroid Build Coastguard Worker   DCHECK_EQ(target_offset & 1u, 1u);  // Thumb2 mode bit.
62*795d594fSAndroid Build Coastguard Worker   uint32_t displacement = CalculateMethodCallDisplacement(patch_offset, target_offset & ~1u);
63*795d594fSAndroid Build Coastguard Worker   PatchBl(code, literal_offset, displacement);
64*795d594fSAndroid Build Coastguard Worker }
65*795d594fSAndroid Build Coastguard Worker 
PatchPcRelativeReference(std::vector<uint8_t> * code,const LinkerPatch & patch,uint32_t patch_offset,uint32_t target_offset)66*795d594fSAndroid Build Coastguard Worker void Thumb2RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
67*795d594fSAndroid Build Coastguard Worker                                                      const LinkerPatch& patch,
68*795d594fSAndroid Build Coastguard Worker                                                      uint32_t patch_offset,
69*795d594fSAndroid Build Coastguard Worker                                                      uint32_t target_offset) {
70*795d594fSAndroid Build Coastguard Worker   uint32_t literal_offset = patch.LiteralOffset();
71*795d594fSAndroid Build Coastguard Worker   uint32_t pc_literal_offset = patch.PcInsnOffset();
72*795d594fSAndroid Build Coastguard Worker   uint32_t pc_base = patch_offset + (pc_literal_offset - literal_offset) + 4u /* PC adjustment */;
73*795d594fSAndroid Build Coastguard Worker   uint32_t diff = target_offset - pc_base;
74*795d594fSAndroid Build Coastguard Worker 
75*795d594fSAndroid Build Coastguard Worker   uint32_t insn = GetInsn32(code, literal_offset);
76*795d594fSAndroid Build Coastguard Worker   DCHECK_EQ(insn & 0xff7ff0ffu, 0xf2400000u);  // MOVW/MOVT, unpatched (imm16 == 0).
77*795d594fSAndroid Build Coastguard Worker   uint32_t diff16 = ((insn & 0x00800000u) != 0u) ? (diff >> 16) : (diff & 0xffffu);
78*795d594fSAndroid Build Coastguard Worker   uint32_t imm4 = (diff16 >> 12) & 0xfu;
79*795d594fSAndroid Build Coastguard Worker   uint32_t imm = (diff16 >> 11) & 0x1u;
80*795d594fSAndroid Build Coastguard Worker   uint32_t imm3 = (diff16 >> 8) & 0x7u;
81*795d594fSAndroid Build Coastguard Worker   uint32_t imm8 = diff16 & 0xffu;
82*795d594fSAndroid Build Coastguard Worker   insn = (insn & 0xfbf08f00u) | (imm << 26) | (imm4 << 16) | (imm3 << 12) | imm8;
83*795d594fSAndroid Build Coastguard Worker   SetInsn32(code, literal_offset, insn);
84*795d594fSAndroid Build Coastguard Worker }
85*795d594fSAndroid Build Coastguard Worker 
PatchEntrypointCall(std::vector<uint8_t> * code,const LinkerPatch & patch,uint32_t patch_offset)86*795d594fSAndroid Build Coastguard Worker void Thumb2RelativePatcher::PatchEntrypointCall(std::vector<uint8_t>* code,
87*795d594fSAndroid Build Coastguard Worker                                                 const LinkerPatch& patch,
88*795d594fSAndroid Build Coastguard Worker                                                 uint32_t patch_offset) {
89*795d594fSAndroid Build Coastguard Worker   DCHECK_ALIGNED(patch_offset, 2u);
90*795d594fSAndroid Build Coastguard Worker   ThunkKey key = GetEntrypointCallKey(patch);
91*795d594fSAndroid Build Coastguard Worker   uint32_t target_offset = GetThunkTargetOffset(key, patch_offset);
92*795d594fSAndroid Build Coastguard Worker   DCHECK_ALIGNED(target_offset, 4u);
93*795d594fSAndroid Build Coastguard Worker   uint32_t displacement = target_offset - patch_offset;
94*795d594fSAndroid Build Coastguard Worker   PatchBl(code, patch.LiteralOffset(), displacement);
95*795d594fSAndroid Build Coastguard Worker }
96*795d594fSAndroid Build Coastguard Worker 
PatchBakerReadBarrierBranch(std::vector<uint8_t> * code,const LinkerPatch & patch,uint32_t patch_offset)97*795d594fSAndroid Build Coastguard Worker void Thumb2RelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
98*795d594fSAndroid Build Coastguard Worker                                                         const LinkerPatch& patch,
99*795d594fSAndroid Build Coastguard Worker                                                         uint32_t patch_offset) {
100*795d594fSAndroid Build Coastguard Worker   DCHECK_ALIGNED(patch_offset, 2u);
101*795d594fSAndroid Build Coastguard Worker   uint32_t literal_offset = patch.LiteralOffset();
102*795d594fSAndroid Build Coastguard Worker   DCHECK_ALIGNED(literal_offset, 2u);
103*795d594fSAndroid Build Coastguard Worker   DCHECK_LT(literal_offset, code->size());
104*795d594fSAndroid Build Coastguard Worker   uint32_t insn = GetInsn32(code, literal_offset);
105*795d594fSAndroid Build Coastguard Worker   DCHECK_EQ(insn, 0xf0408000);  // BNE +0 (unpatched)
106*795d594fSAndroid Build Coastguard Worker   ThunkKey key = GetBakerThunkKey(patch);
107*795d594fSAndroid Build Coastguard Worker   uint32_t target_offset = GetThunkTargetOffset(key, patch_offset);
108*795d594fSAndroid Build Coastguard Worker   DCHECK_ALIGNED(target_offset, 4u);
109*795d594fSAndroid Build Coastguard Worker   uint32_t disp = target_offset - (patch_offset + kPcDisplacement);
110*795d594fSAndroid Build Coastguard Worker   DCHECK((disp >> 20) == 0u || (disp >> 20) == 0xfffu);   // 21-bit signed.
111*795d594fSAndroid Build Coastguard Worker   insn |= ((disp << (26 - 20)) & 0x04000000u) |           // Shift bit 20 to 26, "S".
112*795d594fSAndroid Build Coastguard Worker           ((disp >> (19 - 11)) & 0x00000800u) |           // Shift bit 19 to 13, "J1".
113*795d594fSAndroid Build Coastguard Worker           ((disp >> (18 - 13)) & 0x00002000u) |           // Shift bit 18 to 11, "J2".
114*795d594fSAndroid Build Coastguard Worker           ((disp << (16 - 12)) & 0x003f0000u) |           // Shift bits 12-17 to 16-25, "imm6".
115*795d594fSAndroid Build Coastguard Worker           ((disp >> (1 - 0)) & 0x000007ffu);              // Shift bits 1-12 to 0-11, "imm11".
116*795d594fSAndroid Build Coastguard Worker   SetInsn32(code, literal_offset, insn);
117*795d594fSAndroid Build Coastguard Worker }
118*795d594fSAndroid Build Coastguard Worker 
MaxPositiveDisplacement(const ThunkKey & key)119*795d594fSAndroid Build Coastguard Worker uint32_t Thumb2RelativePatcher::MaxPositiveDisplacement(const ThunkKey& key) {
120*795d594fSAndroid Build Coastguard Worker   switch (key.GetType()) {
121*795d594fSAndroid Build Coastguard Worker     case ThunkType::kMethodCall:
122*795d594fSAndroid Build Coastguard Worker     case ThunkType::kEntrypointCall:
123*795d594fSAndroid Build Coastguard Worker       return kMaxMethodCallPositiveDisplacement;
124*795d594fSAndroid Build Coastguard Worker     case ThunkType::kBakerReadBarrier:
125*795d594fSAndroid Build Coastguard Worker       return kMaxBcondPositiveDisplacement;
126*795d594fSAndroid Build Coastguard Worker   }
127*795d594fSAndroid Build Coastguard Worker }
128*795d594fSAndroid Build Coastguard Worker 
MaxNegativeDisplacement(const ThunkKey & key)129*795d594fSAndroid Build Coastguard Worker uint32_t Thumb2RelativePatcher::MaxNegativeDisplacement(const ThunkKey& key) {
130*795d594fSAndroid Build Coastguard Worker   switch (key.GetType()) {
131*795d594fSAndroid Build Coastguard Worker     case ThunkType::kMethodCall:
132*795d594fSAndroid Build Coastguard Worker     case ThunkType::kEntrypointCall:
133*795d594fSAndroid Build Coastguard Worker       return kMaxMethodCallNegativeDisplacement;
134*795d594fSAndroid Build Coastguard Worker     case ThunkType::kBakerReadBarrier:
135*795d594fSAndroid Build Coastguard Worker       return kMaxBcondNegativeDisplacement;
136*795d594fSAndroid Build Coastguard Worker   }
137*795d594fSAndroid Build Coastguard Worker }
138*795d594fSAndroid Build Coastguard Worker 
PatchBl(std::vector<uint8_t> * code,uint32_t literal_offset,uint32_t displacement)139*795d594fSAndroid Build Coastguard Worker void Thumb2RelativePatcher::PatchBl(std::vector<uint8_t>* code,
140*795d594fSAndroid Build Coastguard Worker                                     uint32_t literal_offset,
141*795d594fSAndroid Build Coastguard Worker                                     uint32_t displacement) {
142*795d594fSAndroid Build Coastguard Worker   displacement -= kPcDisplacement;  // The base PC is at the end of the 4-byte patch.
143*795d594fSAndroid Build Coastguard Worker   DCHECK_EQ(displacement & 1u, 0u);
144*795d594fSAndroid Build Coastguard Worker   DCHECK((displacement >> 24) == 0u || (displacement >> 24) == 255u);  // 25-bit signed.
145*795d594fSAndroid Build Coastguard Worker   uint32_t signbit = (displacement >> 31) & 0x1;
146*795d594fSAndroid Build Coastguard Worker   uint32_t i1 = (displacement >> 23) & 0x1;
147*795d594fSAndroid Build Coastguard Worker   uint32_t i2 = (displacement >> 22) & 0x1;
148*795d594fSAndroid Build Coastguard Worker   uint32_t imm10 = (displacement >> 12) & 0x03ff;
149*795d594fSAndroid Build Coastguard Worker   uint32_t imm11 = (displacement >> 1) & 0x07ff;
150*795d594fSAndroid Build Coastguard Worker   uint32_t j1 = i1 ^ (signbit ^ 1);
151*795d594fSAndroid Build Coastguard Worker   uint32_t j2 = i2 ^ (signbit ^ 1);
152*795d594fSAndroid Build Coastguard Worker   uint32_t value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm10 << 16) | imm11;
153*795d594fSAndroid Build Coastguard Worker   value |= 0xf000d000;  // BL
154*795d594fSAndroid Build Coastguard Worker 
155*795d594fSAndroid Build Coastguard Worker   // Check that we're just overwriting an existing BL.
156*795d594fSAndroid Build Coastguard Worker   DCHECK_EQ(GetInsn32(code, literal_offset) & 0xf800d000, 0xf000d000);
157*795d594fSAndroid Build Coastguard Worker   // Write the new BL.
158*795d594fSAndroid Build Coastguard Worker   SetInsn32(code, literal_offset, value);
159*795d594fSAndroid Build Coastguard Worker }
160*795d594fSAndroid Build Coastguard Worker 
SetInsn32(std::vector<uint8_t> * code,uint32_t offset,uint32_t value)161*795d594fSAndroid Build Coastguard Worker void Thumb2RelativePatcher::SetInsn32(std::vector<uint8_t>* code, uint32_t offset, uint32_t value) {
162*795d594fSAndroid Build Coastguard Worker   DCHECK_LE(offset + 4u, code->size());
163*795d594fSAndroid Build Coastguard Worker   DCHECK_ALIGNED(offset, 2u);
164*795d594fSAndroid Build Coastguard Worker   uint8_t* addr = &(*code)[offset];
165*795d594fSAndroid Build Coastguard Worker   addr[0] = (value >> 16) & 0xff;
166*795d594fSAndroid Build Coastguard Worker   addr[1] = (value >> 24) & 0xff;
167*795d594fSAndroid Build Coastguard Worker   addr[2] = (value >> 0) & 0xff;
168*795d594fSAndroid Build Coastguard Worker   addr[3] = (value >> 8) & 0xff;
169*795d594fSAndroid Build Coastguard Worker }
170*795d594fSAndroid Build Coastguard Worker 
GetInsn32(ArrayRef<const uint8_t> code,uint32_t offset)171*795d594fSAndroid Build Coastguard Worker uint32_t Thumb2RelativePatcher::GetInsn32(ArrayRef<const uint8_t> code, uint32_t offset) {
172*795d594fSAndroid Build Coastguard Worker   DCHECK_LE(offset + 4u, code.size());
173*795d594fSAndroid Build Coastguard Worker   DCHECK_ALIGNED(offset, 2u);
174*795d594fSAndroid Build Coastguard Worker   const uint8_t* addr = &code[offset];
175*795d594fSAndroid Build Coastguard Worker   return
176*795d594fSAndroid Build Coastguard Worker       (static_cast<uint32_t>(addr[0]) << 16) +
177*795d594fSAndroid Build Coastguard Worker       (static_cast<uint32_t>(addr[1]) << 24) +
178*795d594fSAndroid Build Coastguard Worker       (static_cast<uint32_t>(addr[2]) << 0)+
179*795d594fSAndroid Build Coastguard Worker       (static_cast<uint32_t>(addr[3]) << 8);
180*795d594fSAndroid Build Coastguard Worker }
181*795d594fSAndroid Build Coastguard Worker 
182*795d594fSAndroid Build Coastguard Worker template <typename Vector>
GetInsn32(Vector * code,uint32_t offset)183*795d594fSAndroid Build Coastguard Worker uint32_t Thumb2RelativePatcher::GetInsn32(Vector* code, uint32_t offset) {
184*795d594fSAndroid Build Coastguard Worker   static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
185*795d594fSAndroid Build Coastguard Worker   return GetInsn32(ArrayRef<const uint8_t>(*code), offset);
186*795d594fSAndroid Build Coastguard Worker }
187*795d594fSAndroid Build Coastguard Worker 
GetInsn16(ArrayRef<const uint8_t> code,uint32_t offset)188*795d594fSAndroid Build Coastguard Worker uint32_t Thumb2RelativePatcher::GetInsn16(ArrayRef<const uint8_t> code, uint32_t offset) {
189*795d594fSAndroid Build Coastguard Worker   DCHECK_LE(offset + 2u, code.size());
190*795d594fSAndroid Build Coastguard Worker   DCHECK_ALIGNED(offset, 2u);
191*795d594fSAndroid Build Coastguard Worker   const uint8_t* addr = &code[offset];
192*795d594fSAndroid Build Coastguard Worker   return (static_cast<uint32_t>(addr[0]) << 0) + (static_cast<uint32_t>(addr[1]) << 8);
193*795d594fSAndroid Build Coastguard Worker }
194*795d594fSAndroid Build Coastguard Worker 
195*795d594fSAndroid Build Coastguard Worker template <typename Vector>
GetInsn16(Vector * code,uint32_t offset)196*795d594fSAndroid Build Coastguard Worker uint32_t Thumb2RelativePatcher::GetInsn16(Vector* code, uint32_t offset) {
197*795d594fSAndroid Build Coastguard Worker   static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
198*795d594fSAndroid Build Coastguard Worker   return GetInsn16(ArrayRef<const uint8_t>(*code), offset);
199*795d594fSAndroid Build Coastguard Worker }
200*795d594fSAndroid Build Coastguard Worker 
201*795d594fSAndroid Build Coastguard Worker }  // namespace linker
202*795d594fSAndroid Build Coastguard Worker }  // namespace art
203