blob: cb4a9ed64c5a60d369013e2e4762e0fbc10dee86 [file] [log] [blame]
Logan Chien67008a62012-06-06 18:23:12 +08001/*
2 * Copyright (C) 2012 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 "procedure_linkage_table.h"
18
19#include "compiler_runtime_func_list.h"
20#include "globals.h"
21#include "instruction_set.h"
22#include "logging.h"
23#include "runtime_support_func_list.h"
24#include "runtime_support_llvm.h"
25#include "utils_llvm.h"
26
27#include <algorithm>
28
29#include <UniquePtr.h>
30
31#include <stddef.h>
32#include <stdint.h>
33#include <sys/mman.h>
34#include <unistd.h>
35
36
37namespace {
38 const char* const art_runtime_func_name_list[] = {
39#define DEFINE_ENTRY(ID, NAME) #NAME,
40 RUNTIME_SUPPORT_FUNC_LIST(DEFINE_ENTRY)
41#undef DEFINE_ENTRY
42 };
43
44 const char* const compiler_runtime_func_name_list_arm[] = {
Logan Chien2d3bcbb2012-07-06 09:37:26 +080045#define DEFINE_ENTRY(NAME, RETURN_TYPE, ...) #NAME,
Logan Chien67008a62012-06-06 18:23:12 +080046 COMPILER_RUNTIME_FUNC_LIST_ARM(DEFINE_ENTRY)
47#undef DEFINE_ENTRY
48 };
49
50 const char* const compiler_runtime_func_name_list_mips[] = {
Logan Chien2d3bcbb2012-07-06 09:37:26 +080051#define DEFINE_ENTRY(NAME, RETURN_TYPE, ...) #NAME,
Logan Chien67008a62012-06-06 18:23:12 +080052 COMPILER_RUNTIME_FUNC_LIST_MIPS(DEFINE_ENTRY)
53#undef DEFINE_ENTRY
54 };
55
56 const char* const compiler_runtime_func_name_list_x86[] = {
Logan Chien2d3bcbb2012-07-06 09:37:26 +080057#define DEFINE_ENTRY(NAME, RETURN_TYPE, ...) #NAME,
Logan Chien67008a62012-06-06 18:23:12 +080058 COMPILER_RUNTIME_FUNC_LIST_X86(DEFINE_ENTRY)
59#undef DEFINE_ENTRY
60 };
61
62 const size_t art_runtime_func_count =
63 sizeof(art_runtime_func_name_list) / sizeof(const char*);
64
65 const size_t compiler_runtime_func_count_arm =
66 sizeof(compiler_runtime_func_name_list_arm) / sizeof(const char*);
67
68 const size_t compiler_runtime_func_count_mips =
69 sizeof(compiler_runtime_func_name_list_mips) / sizeof(const char*);
70
71 const size_t compiler_runtime_func_count_x86 =
72 sizeof(compiler_runtime_func_name_list_x86) / sizeof(const char*);
73}
74
75
76namespace art {
77namespace compiler_llvm {
78
79
80ProcedureLinkageTable::ProcedureLinkageTable(InstructionSet insn_set)
81 : insn_set_(insn_set) {
82}
83
84
85ProcedureLinkageTable::~ProcedureLinkageTable() {
86}
87
88
89bool ProcedureLinkageTable::AllocateTable() {
90 if (table_mmap_.get()) {
91 return true;
92 }
93
94 // Allocate the PLT
95 byte* suggested_table_addr = reinterpret_cast<byte*>(kTableAddress);
96
97 UniquePtr<MemMap> table_mmap(
98 MemMap::MapAnonymous(".plt", suggested_table_addr,
99 GetTableSizeInBytes(), PROT_READ | PROT_WRITE));
100
101 if (!table_mmap.get()) {
102 return false;
103 }
104
105 if (table_mmap->Begin() != suggested_table_addr) {
106 // Our PLT should be allocated at the FIXED address
107 return false;
108 }
109
110 // Create the stubs in the PLT
111 byte* stub_ptr = table_mmap->Begin();
112 size_t stub_size = GetStubSizeInBytes();
113
114 for (size_t i = 0; i < art_runtime_func_count; ++i, stub_ptr += stub_size) {
115 const char* name = art_runtime_func_name_list[i];
116 void* func = art_find_runtime_support_func(NULL, name);
117 DCHECK(func != NULL);
118 CreateStub(stub_ptr, func);
119 }
120
121 const char* const* crt_name_list = NULL;
122 size_t crt_count = 0u;
123
124 switch (insn_set_) {
125 case kArm:
126 case kThumb2:
127 crt_name_list = compiler_runtime_func_name_list_arm;
128 crt_count = compiler_runtime_func_count_arm;
129 break;
130
131 case kMips:
132 crt_name_list = compiler_runtime_func_name_list_mips;
133 crt_count = compiler_runtime_func_count_mips;
134 break;
135
136 case kX86:
137 crt_name_list = compiler_runtime_func_name_list_x86;
138 crt_count = compiler_runtime_func_count_x86;
139 break;
140
141 default:
142 LOG(FATAL) << "Unknown instruction set: " << insn_set_;
143 return false;
144 }
145
146 for (size_t i = 0; i < crt_count; ++i, stub_ptr += stub_size) {
147 void* func = art_find_runtime_support_func(NULL, crt_name_list[i]);
148 DCHECK(func != NULL);
149 CreateStub(stub_ptr, func);
150 }
151
152 // Protect the procedure linkage table
153 table_mmap->Protect(PROT_READ | PROT_EXEC);
154
155 // Flush the instruction cache on specific architecture
156#if defined(__arm__) || defined(__mips__)
157 cacheflush(reinterpret_cast<long int>(table_mmap->Begin()),
158 reinterpret_cast<long int>(table_mmap->End()), 0);
159#endif
160
161 // Transfer the ownership
162 table_mmap_.reset(table_mmap.release());
163
164 return true;
165}
166
167
168uintptr_t ProcedureLinkageTable::GetEntryAddress(const char* name) const {
169 int func_idx = IndexOfRuntimeFunc(name);
170 if (func_idx == -1) {
171 return 0u;
172 }
173
174 return (kTableAddress + func_idx * GetStubSizeInBytes());
175}
176
177
178
179int ProcedureLinkageTable::IndexOfRuntimeFunc(const char* name) const {
180 int result = IndexOfCompilerRuntimeFunc(name);
181 if (result != -1) {
182 return art_runtime_func_count + result;
183 }
184
185 return IndexOfArtRuntimeFunc(name);
186}
187
188
189int ProcedureLinkageTable::IndexOfArtRuntimeFunc(const char* name) {
190 for (size_t i = 0; i < art_runtime_func_count; ++i) {
191 if (strcmp(name, art_runtime_func_name_list[i]) == 0) {
192 return static_cast<int>(i);
193 }
194 }
195 return -1;
196}
197
198int ProcedureLinkageTable::IndexOfCompilerRuntimeFunc(InstructionSet insn_set,
199 const char* name) {
200 const char* const* rt_begin = NULL;
201 const char* const* rt_end = NULL;
202
203 switch (insn_set) {
204 case kArm:
205 case kThumb2:
206 rt_begin = compiler_runtime_func_name_list_arm;
207 rt_end = compiler_runtime_func_name_list_arm +
208 compiler_runtime_func_count_arm;
209 break;
210
211 case kMips:
212 rt_begin = compiler_runtime_func_name_list_mips;
213 rt_end = compiler_runtime_func_name_list_mips +
214 compiler_runtime_func_count_mips;
215 break;
216
217 case kX86:
218 rt_begin = compiler_runtime_func_name_list_x86;
219 rt_end = compiler_runtime_func_name_list_x86 +
220 compiler_runtime_func_count_x86;
221 break;
222
223 default:
224 LOG(FATAL) << "Unknown instruction set: " << insn_set;
225 return -1;
226 }
227
228 const char* const* name_lbound_ptr =
229 std::lower_bound(rt_begin, rt_end, name, CStringLessThanComparator());
230
231 if (name_lbound_ptr < rt_end && strcmp(*name_lbound_ptr, name) == 0) {
232 return (name_lbound_ptr - rt_begin);
233 } else {
234 return -1;
235 }
236}
237
238
239size_t ProcedureLinkageTable::GetStubCount(InstructionSet insn_set) {
240 switch (insn_set) {
241 case kArm:
242 case kThumb2:
243 return art_runtime_func_count + compiler_runtime_func_count_arm;
244
245 case kMips:
246 return art_runtime_func_count + compiler_runtime_func_count_mips;
247
248 case kX86:
249 return art_runtime_func_count + compiler_runtime_func_count_x86;
250
251 default:
252 LOG(FATAL) << "Unknown instruction set: " << insn_set;
253 return 0u;
254 }
255}
256
257
258size_t ProcedureLinkageTable::GetStubSizeInBytes(InstructionSet insn_set) {
259 switch (insn_set) {
260 case kArm:
261 case kThumb2:
262 return 8u;
263
264 case kMips:
265 return 16u;
266
267 case kX86:
268 return 8u;
269
270 default:
271 LOG(FATAL) << "Unknown instruction set: " << insn_set;
272 return 0u;
273 }
274}
275
276
277void ProcedureLinkageTable::CreateStub(InstructionSet insn_set,
278 byte* stub, void* dest_) {
279 switch (insn_set) {
280 case kArm:
281 case kThumb2:
282 {
283 uint32_t dest = static_cast<uint32_t>(
284 reinterpret_cast<uintptr_t>(dest_) & 0xfffffffful);
285 uint32_t* stub_w = reinterpret_cast<uint32_t*>(stub);
286
287 stub_w[0] = 0xe51ff004ul; // ldr pc, [pc #-4]
288 stub_w[1] = dest;
289 }
290 break;
291
292 case kMips:
293 {
294 uint32_t dest = static_cast<uint32_t>(
295 reinterpret_cast<uintptr_t>(dest_) & 0xfffffffful);
296 uint32_t* stub_w = reinterpret_cast<uint32_t*>(stub);
297
298 stub_w[0] = 0x3c190000ul | ((dest >> 16) & 0xfffful); // lui
299 stub_w[1] = 0x37390000ul | (dest & 0xfffful);; // ori
300 stub_w[2] = 0x03200008ul; // jr (jump register)
301 stub_w[3] = 0x00000000ul; // nop
302 }
303 break;
304
305 case kX86:
306 {
307 uint32_t off = static_cast<uint32_t>(
308 reinterpret_cast<uintptr_t>(dest_) -
309 reinterpret_cast<uintptr_t>(stub + 1) - 4);
310 // jmp (32-bit offset)
311 stub[0] = 0xe9u;
312 stub[1] = off & 0xffu;
313 stub[2] = (off >> 8) & 0xffu;
314 stub[2] = (off >> 16) & 0xffu;
315 stub[2] = (off >> 24) & 0xffu;
316 }
317 break;
318
319 default:
320 LOG(FATAL) << "Unknown instruction set: " << insn_set;
321 }
322}
323
324
325} // namespace compiler_llvm
326} // namespace art