Add procedure linkage table.
Change-Id: I18bc173c6f7565862e9fcb1b603b391401e7beca
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 1dcea2a..c4f04be 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -235,6 +235,7 @@
LIBART_COMMON_SRC_FILES += \
src/compiler_llvm/elf_loader.cc \
src/compiler_llvm/inferred_reg_category_map.cc \
+ src/compiler_llvm/procedure_linkage_table.cc \
src/compiler_llvm/runtime_support_llvm.cc
endif
diff --git a/src/compiler_llvm/procedure_linkage_table.cc b/src/compiler_llvm/procedure_linkage_table.cc
new file mode 100644
index 0000000..8e03605
--- /dev/null
+++ b/src/compiler_llvm/procedure_linkage_table.cc
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "procedure_linkage_table.h"
+
+#include "compiler_runtime_func_list.h"
+#include "globals.h"
+#include "instruction_set.h"
+#include "logging.h"
+#include "runtime_support_func_list.h"
+#include "runtime_support_llvm.h"
+#include "utils_llvm.h"
+
+#include <algorithm>
+
+#include <UniquePtr.h>
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+
+namespace {
+ const char* const art_runtime_func_name_list[] = {
+#define DEFINE_ENTRY(ID, NAME) #NAME,
+ RUNTIME_SUPPORT_FUNC_LIST(DEFINE_ENTRY)
+#undef DEFINE_ENTRY
+ };
+
+ const char* const compiler_runtime_func_name_list_arm[] = {
+#define DEFINE_ENTRY(NAME) #NAME,
+ COMPILER_RUNTIME_FUNC_LIST_ARM(DEFINE_ENTRY)
+#undef DEFINE_ENTRY
+ };
+
+ const char* const compiler_runtime_func_name_list_mips[] = {
+#define DEFINE_ENTRY(NAME) #NAME,
+ COMPILER_RUNTIME_FUNC_LIST_MIPS(DEFINE_ENTRY)
+#undef DEFINE_ENTRY
+ };
+
+ const char* const compiler_runtime_func_name_list_x86[] = {
+#define DEFINE_ENTRY(NAME) #NAME,
+ COMPILER_RUNTIME_FUNC_LIST_X86(DEFINE_ENTRY)
+#undef DEFINE_ENTRY
+ };
+
+ const size_t art_runtime_func_count =
+ sizeof(art_runtime_func_name_list) / sizeof(const char*);
+
+ const size_t compiler_runtime_func_count_arm =
+ sizeof(compiler_runtime_func_name_list_arm) / sizeof(const char*);
+
+ const size_t compiler_runtime_func_count_mips =
+ sizeof(compiler_runtime_func_name_list_mips) / sizeof(const char*);
+
+ const size_t compiler_runtime_func_count_x86 =
+ sizeof(compiler_runtime_func_name_list_x86) / sizeof(const char*);
+}
+
+
+namespace art {
+namespace compiler_llvm {
+
+
+ProcedureLinkageTable::ProcedureLinkageTable(InstructionSet insn_set)
+ : insn_set_(insn_set) {
+}
+
+
+ProcedureLinkageTable::~ProcedureLinkageTable() {
+}
+
+
+bool ProcedureLinkageTable::AllocateTable() {
+ if (table_mmap_.get()) {
+ return true;
+ }
+
+ // Allocate the PLT
+ byte* suggested_table_addr = reinterpret_cast<byte*>(kTableAddress);
+
+ UniquePtr<MemMap> table_mmap(
+ MemMap::MapAnonymous(".plt", suggested_table_addr,
+ GetTableSizeInBytes(), PROT_READ | PROT_WRITE));
+
+ if (!table_mmap.get()) {
+ return false;
+ }
+
+ if (table_mmap->Begin() != suggested_table_addr) {
+ // Our PLT should be allocated at the FIXED address
+ return false;
+ }
+
+ // Create the stubs in the PLT
+ byte* stub_ptr = table_mmap->Begin();
+ size_t stub_size = GetStubSizeInBytes();
+
+ for (size_t i = 0; i < art_runtime_func_count; ++i, stub_ptr += stub_size) {
+ const char* name = art_runtime_func_name_list[i];
+ void* func = art_find_runtime_support_func(NULL, name);
+ DCHECK(func != NULL);
+ CreateStub(stub_ptr, func);
+ }
+
+ const char* const* crt_name_list = NULL;
+ size_t crt_count = 0u;
+
+ switch (insn_set_) {
+ case kArm:
+ case kThumb2:
+ crt_name_list = compiler_runtime_func_name_list_arm;
+ crt_count = compiler_runtime_func_count_arm;
+ break;
+
+ case kMips:
+ crt_name_list = compiler_runtime_func_name_list_mips;
+ crt_count = compiler_runtime_func_count_mips;
+ break;
+
+ case kX86:
+ crt_name_list = compiler_runtime_func_name_list_x86;
+ crt_count = compiler_runtime_func_count_x86;
+ break;
+
+ default:
+ LOG(FATAL) << "Unknown instruction set: " << insn_set_;
+ return false;
+ }
+
+ for (size_t i = 0; i < crt_count; ++i, stub_ptr += stub_size) {
+ void* func = art_find_runtime_support_func(NULL, crt_name_list[i]);
+ DCHECK(func != NULL);
+ CreateStub(stub_ptr, func);
+ }
+
+ // Protect the procedure linkage table
+ table_mmap->Protect(PROT_READ | PROT_EXEC);
+
+ // Flush the instruction cache on specific architecture
+#if defined(__arm__) || defined(__mips__)
+ cacheflush(reinterpret_cast<long int>(table_mmap->Begin()),
+ reinterpret_cast<long int>(table_mmap->End()), 0);
+#endif
+
+ // Transfer the ownership
+ table_mmap_.reset(table_mmap.release());
+
+ return true;
+}
+
+
+uintptr_t ProcedureLinkageTable::GetEntryAddress(const char* name) const {
+ int func_idx = IndexOfRuntimeFunc(name);
+ if (func_idx == -1) {
+ return 0u;
+ }
+
+ return (kTableAddress + func_idx * GetStubSizeInBytes());
+}
+
+
+
+int ProcedureLinkageTable::IndexOfRuntimeFunc(const char* name) const {
+ int result = IndexOfCompilerRuntimeFunc(name);
+ if (result != -1) {
+ return art_runtime_func_count + result;
+ }
+
+ return IndexOfArtRuntimeFunc(name);
+}
+
+
+int ProcedureLinkageTable::IndexOfArtRuntimeFunc(const char* name) {
+ for (size_t i = 0; i < art_runtime_func_count; ++i) {
+ if (strcmp(name, art_runtime_func_name_list[i]) == 0) {
+ return static_cast<int>(i);
+ }
+ }
+ return -1;
+}
+
+int ProcedureLinkageTable::IndexOfCompilerRuntimeFunc(InstructionSet insn_set,
+ const char* name) {
+ const char* const* rt_begin = NULL;
+ const char* const* rt_end = NULL;
+
+ switch (insn_set) {
+ case kArm:
+ case kThumb2:
+ rt_begin = compiler_runtime_func_name_list_arm;
+ rt_end = compiler_runtime_func_name_list_arm +
+ compiler_runtime_func_count_arm;
+ break;
+
+ case kMips:
+ rt_begin = compiler_runtime_func_name_list_mips;
+ rt_end = compiler_runtime_func_name_list_mips +
+ compiler_runtime_func_count_mips;
+ break;
+
+ case kX86:
+ rt_begin = compiler_runtime_func_name_list_x86;
+ rt_end = compiler_runtime_func_name_list_x86 +
+ compiler_runtime_func_count_x86;
+ break;
+
+ default:
+ LOG(FATAL) << "Unknown instruction set: " << insn_set;
+ return -1;
+ }
+
+ const char* const* name_lbound_ptr =
+ std::lower_bound(rt_begin, rt_end, name, CStringLessThanComparator());
+
+ if (name_lbound_ptr < rt_end && strcmp(*name_lbound_ptr, name) == 0) {
+ return (name_lbound_ptr - rt_begin);
+ } else {
+ return -1;
+ }
+}
+
+
+size_t ProcedureLinkageTable::GetStubCount(InstructionSet insn_set) {
+ switch (insn_set) {
+ case kArm:
+ case kThumb2:
+ return art_runtime_func_count + compiler_runtime_func_count_arm;
+
+ case kMips:
+ return art_runtime_func_count + compiler_runtime_func_count_mips;
+
+ case kX86:
+ return art_runtime_func_count + compiler_runtime_func_count_x86;
+
+ default:
+ LOG(FATAL) << "Unknown instruction set: " << insn_set;
+ return 0u;
+ }
+}
+
+
+size_t ProcedureLinkageTable::GetStubSizeInBytes(InstructionSet insn_set) {
+ switch (insn_set) {
+ case kArm:
+ case kThumb2:
+ return 8u;
+
+ case kMips:
+ return 16u;
+
+ case kX86:
+ return 8u;
+
+ default:
+ LOG(FATAL) << "Unknown instruction set: " << insn_set;
+ return 0u;
+ }
+}
+
+
+void ProcedureLinkageTable::CreateStub(InstructionSet insn_set,
+ byte* stub, void* dest_) {
+ switch (insn_set) {
+ case kArm:
+ case kThumb2:
+ {
+ uint32_t dest = static_cast<uint32_t>(
+ reinterpret_cast<uintptr_t>(dest_) & 0xfffffffful);
+ uint32_t* stub_w = reinterpret_cast<uint32_t*>(stub);
+
+ stub_w[0] = 0xe51ff004ul; // ldr pc, [pc #-4]
+ stub_w[1] = dest;
+ }
+ break;
+
+ case kMips:
+ {
+ uint32_t dest = static_cast<uint32_t>(
+ reinterpret_cast<uintptr_t>(dest_) & 0xfffffffful);
+ uint32_t* stub_w = reinterpret_cast<uint32_t*>(stub);
+
+ stub_w[0] = 0x3c190000ul | ((dest >> 16) & 0xfffful); // lui
+ stub_w[1] = 0x37390000ul | (dest & 0xfffful);; // ori
+ stub_w[2] = 0x03200008ul; // jr (jump register)
+ stub_w[3] = 0x00000000ul; // nop
+ }
+ break;
+
+ case kX86:
+ {
+ uint32_t off = static_cast<uint32_t>(
+ reinterpret_cast<uintptr_t>(dest_) -
+ reinterpret_cast<uintptr_t>(stub + 1) - 4);
+ // jmp (32-bit offset)
+ stub[0] = 0xe9u;
+ stub[1] = off & 0xffu;
+ stub[2] = (off >> 8) & 0xffu;
+ stub[2] = (off >> 16) & 0xffu;
+ stub[2] = (off >> 24) & 0xffu;
+ }
+ break;
+
+ default:
+ LOG(FATAL) << "Unknown instruction set: " << insn_set;
+ }
+}
+
+
+} // namespace compiler_llvm
+} // namespace art
diff --git a/src/compiler_llvm/procedure_linkage_table.h b/src/compiler_llvm/procedure_linkage_table.h
new file mode 100644
index 0000000..26c31ae
--- /dev/null
+++ b/src/compiler_llvm/procedure_linkage_table.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_SRC_COMPILER_LLVM_PROCEDURE_LINKAGE_TABLE_H_
+#define ART_SRC_COMPILER_LLVM_PROCEDURE_LINKAGE_TABLE_H_
+
+#include "globals.h"
+#include "instruction_set.h"
+#include "mem_map.h"
+
+#include <UniquePtr.h>
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace art {
+namespace compiler_llvm {
+
+
+class ProcedureLinkageTable {
+ public:
+ ProcedureLinkageTable(InstructionSet insn_set);
+
+ ~ProcedureLinkageTable();
+
+ bool AllocateTable();
+
+ uintptr_t GetEntryAddress(const char* func_name) const;
+
+ private:
+ static size_t GetStubCount(InstructionSet insn_set);
+ static size_t GetStubSizeInBytes(InstructionSet insn_set);
+ static void CreateStub(InstructionSet insn_set,
+ byte* stub, void* branch_dest);
+
+ int IndexOfRuntimeFunc(const char* name) const;
+ static int IndexOfArtRuntimeFunc(const char* name);
+ static int IndexOfCompilerRuntimeFunc(InstructionSet insn_set,
+ const char* name);
+
+ size_t GetStubCount() const {
+ return GetStubCount(insn_set_);
+ }
+
+ size_t GetStubSizeInBytes() const {
+ return GetStubSizeInBytes(insn_set_);
+ }
+
+ size_t GetTableSizeInBytes() const {
+ return GetStubSizeInBytes() * GetStubCount();
+ }
+
+ void CreateStub(byte* stub, void* branch_dest) {
+ return CreateStub(insn_set_, stub, branch_dest);
+ }
+
+ int IndexOfCompilerRuntimeFunc(const char* name) const {
+ return IndexOfCompilerRuntimeFunc(insn_set_, name);
+ }
+
+ InstructionSet insn_set_;
+ UniquePtr<MemMap> table_mmap_;
+
+ static const size_t kTableSizeInBytes = 1024u;
+ static const uintptr_t kTableAddress = 0x5fffc000u;
+};
+
+
+} // namespace compiler_llvm
+} // namespace art
+
+#endif // ART_SRC_COMPILER_LLVM_PROCEDURE_LINKAGE_TABLE_H_