1/*
2 * Copyright (C) 2013 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 "trampoline_compiler.h"
18
19#include "base/arena_allocator.h"
20#include "jni_env_ext.h"
21
22#ifdef ART_ENABLE_CODEGEN_arm
23#include "utils/arm/assembler_arm_vixl.h"
24#endif
25
26#ifdef ART_ENABLE_CODEGEN_arm64
27#include "utils/arm64/assembler_arm64.h"
28#endif
29
30#ifdef ART_ENABLE_CODEGEN_mips
31#include "utils/mips/assembler_mips.h"
32#endif
33
34#ifdef ART_ENABLE_CODEGEN_mips64
35#include "utils/mips64/assembler_mips64.h"
36#endif
37
38#ifdef ART_ENABLE_CODEGEN_x86
39#include "utils/x86/assembler_x86.h"
40#endif
41
42#ifdef ART_ENABLE_CODEGEN_x86_64
43#include "utils/x86_64/assembler_x86_64.h"
44#endif
45
46#define __ assembler.
47
48namespace art {
49
50#ifdef ART_ENABLE_CODEGEN_arm
51namespace arm {
52
53#ifdef ___
54#error "ARM Assembler macro already defined."
55#else
56#define ___ assembler.GetVIXLAssembler()->
57#endif
58
59static std::unique_ptr<const std::vector<uint8_t>> CreateTrampoline(
60    ArenaAllocator* arena, EntryPointCallingConvention abi, ThreadOffset32 offset) {
61  using vixl::aarch32::MemOperand;
62  using vixl::aarch32::pc;
63  using vixl::aarch32::r0;
64  ArmVIXLAssembler assembler(arena);
65
66  switch (abi) {
67    case kInterpreterAbi:  // Thread* is first argument (R0) in interpreter ABI.
68      ___ Ldr(pc, MemOperand(r0, offset.Int32Value()));
69      break;
70    case kJniAbi: {  // Load via Thread* held in JNIEnv* in first argument (R0).
71      vixl::aarch32::UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
72      const vixl::aarch32::Register temp_reg = temps.Acquire();
73
74      // VIXL will use the destination as a scratch register if
75      // the offset is not encodable as an immediate operand.
76      ___ Ldr(temp_reg, MemOperand(r0, JNIEnvExt::SelfOffset(4).Int32Value()));
77      ___ Ldr(pc, MemOperand(temp_reg, offset.Int32Value()));
78      break;
79    }
80    case kQuickAbi:  // TR holds Thread*.
81      ___ Ldr(pc, MemOperand(tr, offset.Int32Value()));
82  }
83
84  __ FinalizeCode();
85  size_t cs = __ CodeSize();
86  std::unique_ptr<std::vector<uint8_t>> entry_stub(new std::vector<uint8_t>(cs));
87  MemoryRegion code(entry_stub->data(), entry_stub->size());
88  __ FinalizeInstructions(code);
89
90  return std::move(entry_stub);
91}
92
93#undef ___
94
95}  // namespace arm
96#endif  // ART_ENABLE_CODEGEN_arm
97
98#ifdef ART_ENABLE_CODEGEN_arm64
99namespace arm64 {
100static std::unique_ptr<const std::vector<uint8_t>> CreateTrampoline(
101    ArenaAllocator* arena, EntryPointCallingConvention abi, ThreadOffset64 offset) {
102  Arm64Assembler assembler(arena);
103
104  switch (abi) {
105    case kInterpreterAbi:  // Thread* is first argument (X0) in interpreter ABI.
106      __ JumpTo(Arm64ManagedRegister::FromXRegister(X0), Offset(offset.Int32Value()),
107          Arm64ManagedRegister::FromXRegister(IP1));
108
109      break;
110    case kJniAbi:  // Load via Thread* held in JNIEnv* in first argument (X0).
111      __ LoadRawPtr(Arm64ManagedRegister::FromXRegister(IP1),
112                      Arm64ManagedRegister::FromXRegister(X0),
113                      Offset(JNIEnvExt::SelfOffset(8).Int32Value()));
114
115      __ JumpTo(Arm64ManagedRegister::FromXRegister(IP1), Offset(offset.Int32Value()),
116                Arm64ManagedRegister::FromXRegister(IP0));
117
118      break;
119    case kQuickAbi:  // X18 holds Thread*.
120      __ JumpTo(Arm64ManagedRegister::FromXRegister(TR), Offset(offset.Int32Value()),
121                Arm64ManagedRegister::FromXRegister(IP0));
122
123      break;
124  }
125
126  __ FinalizeCode();
127  size_t cs = __ CodeSize();
128  std::unique_ptr<std::vector<uint8_t>> entry_stub(new std::vector<uint8_t>(cs));
129  MemoryRegion code(entry_stub->data(), entry_stub->size());
130  __ FinalizeInstructions(code);
131
132  return std::move(entry_stub);
133}
134}  // namespace arm64
135#endif  // ART_ENABLE_CODEGEN_arm64
136
137#ifdef ART_ENABLE_CODEGEN_mips
138namespace mips {
139static std::unique_ptr<const std::vector<uint8_t>> CreateTrampoline(
140    ArenaAllocator* arena, EntryPointCallingConvention abi, ThreadOffset32 offset) {
141  MipsAssembler assembler(arena);
142
143  switch (abi) {
144    case kInterpreterAbi:  // Thread* is first argument (A0) in interpreter ABI.
145      __ LoadFromOffset(kLoadWord, T9, A0, offset.Int32Value());
146      break;
147    case kJniAbi:  // Load via Thread* held in JNIEnv* in first argument (A0).
148      __ LoadFromOffset(kLoadWord, T9, A0, JNIEnvExt::SelfOffset(4).Int32Value());
149      __ LoadFromOffset(kLoadWord, T9, T9, offset.Int32Value());
150      break;
151    case kQuickAbi:  // S1 holds Thread*.
152      __ LoadFromOffset(kLoadWord, T9, S1, offset.Int32Value());
153  }
154  __ Jr(T9);
155  __ NopIfNoReordering();
156  __ Break();
157
158  __ FinalizeCode();
159  size_t cs = __ CodeSize();
160  std::unique_ptr<std::vector<uint8_t>> entry_stub(new std::vector<uint8_t>(cs));
161  MemoryRegion code(entry_stub->data(), entry_stub->size());
162  __ FinalizeInstructions(code);
163
164  return std::move(entry_stub);
165}
166}  // namespace mips
167#endif  // ART_ENABLE_CODEGEN_mips
168
169#ifdef ART_ENABLE_CODEGEN_mips64
170namespace mips64 {
171static std::unique_ptr<const std::vector<uint8_t>> CreateTrampoline(
172    ArenaAllocator* arena, EntryPointCallingConvention abi, ThreadOffset64 offset) {
173  Mips64Assembler assembler(arena);
174
175  switch (abi) {
176    case kInterpreterAbi:  // Thread* is first argument (A0) in interpreter ABI.
177      __ LoadFromOffset(kLoadDoubleword, T9, A0, offset.Int32Value());
178      break;
179    case kJniAbi:  // Load via Thread* held in JNIEnv* in first argument (A0).
180      __ LoadFromOffset(kLoadDoubleword, T9, A0, JNIEnvExt::SelfOffset(8).Int32Value());
181      __ LoadFromOffset(kLoadDoubleword, T9, T9, offset.Int32Value());
182      break;
183    case kQuickAbi:  // Fall-through.
184      __ LoadFromOffset(kLoadDoubleword, T9, S1, offset.Int32Value());
185  }
186  __ Jr(T9);
187  __ Nop();
188  __ Break();
189
190  __ FinalizeCode();
191  size_t cs = __ CodeSize();
192  std::unique_ptr<std::vector<uint8_t>> entry_stub(new std::vector<uint8_t>(cs));
193  MemoryRegion code(entry_stub->data(), entry_stub->size());
194  __ FinalizeInstructions(code);
195
196  return std::move(entry_stub);
197}
198}  // namespace mips64
199#endif  // ART_ENABLE_CODEGEN_mips
200
201#ifdef ART_ENABLE_CODEGEN_x86
202namespace x86 {
203static std::unique_ptr<const std::vector<uint8_t>> CreateTrampoline(ArenaAllocator* arena,
204                                                                    ThreadOffset32 offset) {
205  X86Assembler assembler(arena);
206
207  // All x86 trampolines call via the Thread* held in fs.
208  __ fs()->jmp(Address::Absolute(offset));
209  __ int3();
210
211  __ FinalizeCode();
212  size_t cs = __ CodeSize();
213  std::unique_ptr<std::vector<uint8_t>> entry_stub(new std::vector<uint8_t>(cs));
214  MemoryRegion code(entry_stub->data(), entry_stub->size());
215  __ FinalizeInstructions(code);
216
217  return std::move(entry_stub);
218}
219}  // namespace x86
220#endif  // ART_ENABLE_CODEGEN_x86
221
222#ifdef ART_ENABLE_CODEGEN_x86_64
223namespace x86_64 {
224static std::unique_ptr<const std::vector<uint8_t>> CreateTrampoline(ArenaAllocator* arena,
225                                                                    ThreadOffset64 offset) {
226  x86_64::X86_64Assembler assembler(arena);
227
228  // All x86 trampolines call via the Thread* held in gs.
229  __ gs()->jmp(x86_64::Address::Absolute(offset, true));
230  __ int3();
231
232  __ FinalizeCode();
233  size_t cs = __ CodeSize();
234  std::unique_ptr<std::vector<uint8_t>> entry_stub(new std::vector<uint8_t>(cs));
235  MemoryRegion code(entry_stub->data(), entry_stub->size());
236  __ FinalizeInstructions(code);
237
238  return std::move(entry_stub);
239}
240}  // namespace x86_64
241#endif  // ART_ENABLE_CODEGEN_x86_64
242
243std::unique_ptr<const std::vector<uint8_t>> CreateTrampoline64(InstructionSet isa,
244                                                               EntryPointCallingConvention abi,
245                                                               ThreadOffset64 offset) {
246  ArenaPool pool;
247  ArenaAllocator arena(&pool);
248  switch (isa) {
249#ifdef ART_ENABLE_CODEGEN_arm64
250    case kArm64:
251      return arm64::CreateTrampoline(&arena, abi, offset);
252#endif
253#ifdef ART_ENABLE_CODEGEN_mips64
254    case kMips64:
255      return mips64::CreateTrampoline(&arena, abi, offset);
256#endif
257#ifdef ART_ENABLE_CODEGEN_x86_64
258    case kX86_64:
259      return x86_64::CreateTrampoline(&arena, offset);
260#endif
261    default:
262      UNUSED(abi);
263      UNUSED(offset);
264      LOG(FATAL) << "Unexpected InstructionSet: " << isa;
265      UNREACHABLE();
266  }
267}
268
269std::unique_ptr<const std::vector<uint8_t>> CreateTrampoline32(InstructionSet isa,
270                                                               EntryPointCallingConvention abi,
271                                                               ThreadOffset32 offset) {
272  ArenaPool pool;
273  ArenaAllocator arena(&pool);
274  switch (isa) {
275#ifdef ART_ENABLE_CODEGEN_arm
276    case kArm:
277    case kThumb2:
278      return arm::CreateTrampoline(&arena, abi, offset);
279#endif
280#ifdef ART_ENABLE_CODEGEN_mips
281    case kMips:
282      return mips::CreateTrampoline(&arena, abi, offset);
283#endif
284#ifdef ART_ENABLE_CODEGEN_x86
285    case kX86:
286      UNUSED(abi);
287      return x86::CreateTrampoline(&arena, offset);
288#endif
289    default:
290      LOG(FATAL) << "Unexpected InstructionSet: " << isa;
291      UNREACHABLE();
292  }
293}
294
295}  // namespace art
296