diff options
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/Android.mk | 15 | ||||
-rw-r--r-- | runtime/arch/arm/fault_handler_arm.cc | 148 | ||||
-rw-r--r-- | runtime/arch/mips/fault_handler_mips.cc | 46 | ||||
-rw-r--r-- | runtime/arch/x86/fault_handler_x86.cc | 46 | ||||
-rw-r--r-- | runtime/arch/x86_64/fault_handler_x86_64.cc | 46 | ||||
-rw-r--r-- | runtime/class_linker.cc | 32 | ||||
-rw-r--r-- | runtime/fault_handler.cc | 181 | ||||
-rw-r--r-- | runtime/fault_handler.h | 91 | ||||
-rw-r--r-- | runtime/gc/space/image_space.cc | 31 | ||||
-rw-r--r-- | runtime/mirror/art_method.cc | 10 | ||||
-rw-r--r-- | runtime/mirror/art_method.h | 3 | ||||
-rw-r--r-- | runtime/oat.cc | 2 | ||||
-rw-r--r-- | runtime/parsed_options.cc | 82 | ||||
-rw-r--r-- | runtime/parsed_options.h | 5 | ||||
-rw-r--r-- | runtime/runtime.cc | 30 | ||||
-rw-r--r-- | runtime/runtime.h | 16 | ||||
-rw-r--r-- | runtime/stack.cc | 4 | ||||
-rw-r--r-- | runtime/stack.h | 2 | ||||
-rw-r--r-- | runtime/thread.cc | 4 | ||||
-rw-r--r-- | runtime/thread.h | 22 | ||||
-rw-r--r-- | runtime/thread_list.cc | 1 |
21 files changed, 803 insertions, 14 deletions
diff --git a/runtime/Android.mk b/runtime/Android.mk index e094850..983b48f 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -139,6 +139,7 @@ LIBART_COMMON_SRC_FILES := \ trace.cc \ transaction.cc \ profiler.cc \ + fault_handler.cc \ utf.cc \ utils.cc \ verifier/dex_gc_map.cc \ @@ -207,7 +208,8 @@ LIBART_TARGET_SRC_FILES_arm := \ arch/arm/portable_entrypoints_arm.S \ arch/arm/quick_entrypoints_arm.S \ arch/arm/arm_sdiv.S \ - arch/arm/thread_arm.cc + arch/arm/thread_arm.cc \ + arch/arm/fault_handler_arm.cc LIBART_TARGET_SRC_FILES_x86 := \ arch/x86/context_x86.cc \ @@ -215,7 +217,8 @@ LIBART_TARGET_SRC_FILES_x86 := \ arch/x86/jni_entrypoints_x86.S \ arch/x86/portable_entrypoints_x86.S \ arch/x86/quick_entrypoints_x86.S \ - arch/x86/thread_x86.cc + arch/x86/thread_x86.cc \ + arch/x86/fault_handler_x86.cc LIBART_TARGET_SRC_FILES_x86_64 := \ arch/x86_64/context_x86_64.cc \ @@ -224,7 +227,8 @@ LIBART_TARGET_SRC_FILES_x86_64 := \ arch/x86_64/portable_entrypoints_x86_64.S \ arch/x86_64/quick_entrypoints_x86_64.S \ arch/x86_64/thread_x86_64.cc \ - monitor_pool.cc + monitor_pool.cc \ + arch/x86_64/fault_handler_x86_64.cc LIBART_TARGET_SRC_FILES_mips := \ @@ -233,7 +237,8 @@ LIBART_TARGET_SRC_FILES_mips := \ arch/mips/jni_entrypoints_mips.S \ arch/mips/portable_entrypoints_mips.S \ arch/mips/quick_entrypoints_mips.S \ - arch/mips/thread_mips.cc + arch/mips/thread_mips.cc \ + arch/mips/fault_mhandlerarm.cc ifeq ($(TARGET_ARCH),arm64) $(info TODOArm64: $(LOCAL_PATH)/Android.mk Add Arm64 specific runtime files) @@ -263,6 +268,7 @@ LIBART_HOST_SRC_FILES += \ arch/x86_64/portable_entrypoints_x86_64.S \ arch/x86_64/quick_entrypoints_x86_64.S \ arch/x86_64/thread_x86_64.cc \ + arch/x86_64/fault_handler_x86_64.cc \ monitor_pool.cc else LIBART_HOST_SRC_FILES += \ @@ -271,6 +277,7 @@ LIBART_HOST_SRC_FILES += \ arch/x86/jni_entrypoints_x86.S \ arch/x86/portable_entrypoints_x86.S \ arch/x86/quick_entrypoints_x86.S \ + arch/x86/fault_handler_x86.cc \ arch/x86/thread_x86.cc endif else # HOST_ARCH != x86 diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc new file mode 100644 index 0000000..c748ce9 --- /dev/null +++ b/runtime/arch/arm/fault_handler_arm.cc @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2008 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 "fault_handler.h" +#include <sys/ucontext.h> +#include "base/macros.h" +#include "globals.h" +#include "base/logging.h" +#include "base/hex_dump.h" +#include "thread.h" +#include "thread-inl.h" + +// +// ARM specific fault handler functions. +// + +namespace art { + +extern "C" void art_quick_throw_null_pointer_exception(); +extern "C" void art_quick_test_suspend(); + +void FaultManager::GetMethodAndReturnPC(void* context, uintptr_t& method, uintptr_t& return_pc) { + struct ucontext *uc = (struct ucontext *)context; + struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext); + uintptr_t* sp = reinterpret_cast<uint32_t*>(sc->arm_sp); + if (sp == nullptr) { + return; + } + + // Work out the return PC. This will be the address of the instruction + // following the faulting ldr/str instruction. This is in thumb mode so + // the instruction might be a 16 or 32 bit one. Also, the GC map always + // has the bottom bit of the PC set so we also need to set that. + + // Need to work out the size of the instruction that caused the exception. + uint8_t* ptr = reinterpret_cast<uint8_t*>(sc->arm_pc); + + uint16_t instr = ptr[0] | ptr[1] << 8; + bool is_32bit = ((instr & 0xF000) == 0xF000) || ((instr & 0xF800) == 0xE800); + uint32_t instr_size = is_32bit ? 4 : 2; + + // The method is at the top of the stack. + method = sp[0]; + + return_pc = (sc->arm_pc + instr_size) | 1; +} + +bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) { + // The code that looks for the catch location needs to know the value of the + // ARM PC at the point of call. For Null checks we insert a GC map that is immediately after + // the load/store instruction that might cause the fault. However the mapping table has + // the low bits set for thumb mode so we need to set the bottom bit for the LR + // register in order to find the mapping. + + // Need to work out the size of the instruction that caused the exception. + struct ucontext *uc = (struct ucontext *)context; + struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext); + uint8_t* ptr = reinterpret_cast<uint8_t*>(sc->arm_pc); + + uint16_t instr = ptr[0] | ptr[1] << 8; + bool is_32bit = ((instr & 0xF000) == 0xF000) || ((instr & 0xF800) == 0xE800); + uint32_t instr_size = is_32bit ? 4 : 2; + sc->arm_lr = (sc->arm_pc + instr_size) | 1; // LR needs to point to gc map location + sc->arm_pc = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception); + LOG(DEBUG) << "Generating null pointer exception"; + return true; +} + +// A suspend check is done using the following instruction sequence: +// 0xf723c0b2: f8d902c0 ldr.w r0, [r9, #704] ; suspend_trigger_ +// .. some intervening instruction +// 0xf723c0b6: 6800 ldr r0, [r0, #0] + +// The offset from r9 is Thread::ThreadSuspendTriggerOffset(). +// To check for a suspend check, we examine the instructions that caused +// the fault (at PC-4 and PC). +bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) { + // These are the instructions to check for. The first one is the ldr r0,[r9,#xxx] + // where xxx is the offset of the suspend trigger. + uint32_t checkinst1 = 0xf8d90000 + Thread::ThreadSuspendTriggerOffset().Int32Value(); + uint16_t checkinst2 = 0x6800; + + struct ucontext *uc = (struct ucontext *)context; + struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext); + uint8_t* ptr2 = reinterpret_cast<uint8_t*>(sc->arm_pc); + uint8_t* ptr1 = ptr2 - 4; + LOG(DEBUG) << "checking suspend"; + + uint16_t inst2 = ptr2[0] | ptr2[1] << 8; + LOG(DEBUG) << "inst2: " << std::hex << inst2 << " checkinst2: " << checkinst2; + if (inst2 != checkinst2) { + // Second instruction is not good, not ours. + return false; + } + + // The first instruction can a little bit up the stream due to load hoisting + // in the compiler. + uint8_t* limit = ptr1 - 40; // Compiler will hoist to a max of 20 instructions. + bool found = false; + while (ptr1 > limit) { + uint32_t inst1 = ((ptr1[0] | ptr1[1] << 8) << 16) | (ptr1[2] | ptr1[3] << 8); + LOG(DEBUG) << "inst1: " << std::hex << inst1 << " checkinst1: " << checkinst1; + if (inst1 == checkinst1) { + found = true; + break; + } + ptr1 -= 2; // Min instruction size is 2 bytes. + } + if (found) { + LOG(DEBUG) << "suspend check match"; + // This is a suspend check. Arrange for the signal handler to return to + // art_quick_test_suspend. Also set LR so that after the suspend check it + // will resume the instruction (current PC + 2). PC points to the + // ldr r0,[r0,#0] instruction (r0 will be 0, set by the trigger). + + // NB: remember that we need to set the bottom bit of the LR register + // to switch to thumb mode. + LOG(DEBUG) << "arm lr: " << std::hex << sc->arm_lr; + LOG(DEBUG) << "arm pc: " << std::hex << sc->arm_pc; + sc->arm_lr = sc->arm_pc + 3; // +2 + 1 (for thumb) + sc->arm_pc = reinterpret_cast<uintptr_t>(art_quick_test_suspend); + + // Now remove the suspend trigger that caused this fault. + Thread::Current()->RemoveSuspendTrigger(); + LOG(DEBUG) << "removed suspend trigger invoking test suspend"; + return true; + } + return false; +} + +bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) { + return false; +} +} // namespace art diff --git a/runtime/arch/mips/fault_handler_mips.cc b/runtime/arch/mips/fault_handler_mips.cc new file mode 100644 index 0000000..8d494c1 --- /dev/null +++ b/runtime/arch/mips/fault_handler_mips.cc @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 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 "fault_handler.h" +#include <sys/ucontext.h> +#include "base/macros.h" +#include "globals.h" +#include "base/logging.h" +#include "base/hex_dump.h" + + +// +// Mips specific fault handler functions. +// + +namespace art { + +void FaultManager::GetMethodAndReturnPC(void* context, uintptr_t& method, uintptr_t& return_pc) { +} + +bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) { + return false; +} + +bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) { + return false; +} + +bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) { + return false; +} +} // namespace art diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc new file mode 100644 index 0000000..171a541 --- /dev/null +++ b/runtime/arch/x86/fault_handler_x86.cc @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 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 "fault_handler.h" +#include <sys/ucontext.h> +#include "base/macros.h" +#include "globals.h" +#include "base/logging.h" +#include "base/hex_dump.h" + + +// +// X86 specific fault handler functions. +// + +namespace art { + +void FaultManager::GetMethodAndReturnPC(void* context, uintptr_t& method, uintptr_t& return_pc) { +} + +bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) { + return false; +} + +bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) { + return false; +} + +bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) { + return false; +} +} // namespace art diff --git a/runtime/arch/x86_64/fault_handler_x86_64.cc b/runtime/arch/x86_64/fault_handler_x86_64.cc new file mode 100644 index 0000000..3ef19fb --- /dev/null +++ b/runtime/arch/x86_64/fault_handler_x86_64.cc @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 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 "fault_handler.h" +#include <sys/ucontext.h> +#include "base/macros.h" +#include "globals.h" +#include "base/logging.h" +#include "base/hex_dump.h" + + +// +// X86_64 specific fault handler functions. +// + +namespace art { + +void FaultManager::GetMethodAndReturnPC(void* context, uintptr_t& method, uintptr_t& return_pc) { +} + +bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) { + return false; +} + +bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) { + return false; +} + +bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) { + return false; +} +} // namespace art diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index b709da3..b8d1493 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -573,6 +573,38 @@ bool ClassLinker::GenerateOatFile(const char* dex_filename, argv.push_back("-classpath"); argv.push_back("--runtime-arg"); argv.push_back(Runtime::Current()->GetClassPathString()); + + argv.push_back("--runtime-arg"); + std::string checkstr = "-implicit-checks"; + + int nchecks = 0; + char checksep = ':'; + + if (!Runtime::Current()->ExplicitNullChecks()) { + checkstr += checksep; + checksep = ','; + checkstr += "null"; + ++nchecks; + } + if (!Runtime::Current()->ExplicitSuspendChecks()) { + checkstr += checksep; + checksep = ','; + checkstr += "suspend"; + ++nchecks; + } + + if (!Runtime::Current()->ExplicitStackOverflowChecks()) { + checkstr += checksep; + checksep = ','; + checkstr += "stack"; + ++nchecks; + } + + if (nchecks == 0) { + checkstr += ":none"; + } + argv.push_back(checkstr); + if (!kIsTargetBuild) { argv.push_back("--host"); } diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc new file mode 100644 index 0000000..6399c0d --- /dev/null +++ b/runtime/fault_handler.cc @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2008 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 "fault_handler.h" +#include <sys/mman.h> +#include <sys/ucontext.h> +#include "base/macros.h" +#include "globals.h" +#include "base/logging.h" +#include "base/hex_dump.h" +#include "thread.h" +#include "mirror/art_method-inl.h" +#include "mirror/class-inl.h" +#include "mirror/dex_cache.h" +#include "mirror/object_array-inl.h" +#include "mirror/object-inl.h" +#include "object_utils.h" +#include "scoped_thread_state_change.h" +#include "verify_object-inl.h" + +namespace art { +// Static fault manger object accessed by signal handler. +FaultManager fault_manager; + +// Signal handler called on SIGSEGV. +static void art_fault_handler(int sig, siginfo_t* info, void* context) { + fault_manager.HandleFault(sig, info, context); +} + +FaultManager::FaultManager() { + sigaction(SIGSEGV, nullptr, &oldaction_); +} + +FaultManager::~FaultManager() { + sigaction(SIGSEGV, &oldaction_, nullptr); // Restore old handler. +} + +void FaultManager::Init() { + struct sigaction action; + action.sa_sigaction = art_fault_handler; + sigemptyset(&action.sa_mask); + action.sa_flags = SA_SIGINFO | SA_ONSTACK; + action.sa_restorer = nullptr; + sigaction(SIGSEGV, &action, &oldaction_); +} + +void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) { + bool handled = false; + if (IsInGeneratedCode(context)) { + for (auto& handler : handlers_) { + handled = handler->Action(sig, info, context); + if (handled) { + return; + } + } + } + + if (!handled) { + LOG(INFO)<< "Caught unknown SIGSEGV in ART fault handler"; + oldaction_.sa_sigaction(sig, info, context); + } +} + +void FaultManager::AddHandler(FaultHandler* handler) { + handlers_.push_back(handler); +} + +void FaultManager::RemoveHandler(FaultHandler* handler) { + for (Handlers::iterator i = handlers_.begin(); i != handlers_.end(); ++i) { + FaultHandler* h = *i; + if (h == handler) { + handlers_.erase(i); + return; + } + } +} + + +// This function is called within the signal handler. It checks that +// the mutator_lock is held (shared). No annotalysis is done. +bool FaultManager::IsInGeneratedCode(void *context) { + // We can only be running Java code in the current thread if it + // is in Runnable state. + Thread* thread = Thread::Current(); + if (thread == nullptr) { + return false; + } + + ThreadState state = thread->GetState(); + if (state != kRunnable) { + return false; + } + + // Current thread is runnable. + // Make sure it has the mutator lock. + if (!Locks::mutator_lock_->IsSharedHeld(thread)) { + return false; + } + + uintptr_t potential_method = 0; + uintptr_t return_pc = 0; + + // Get the architecture specific method address and return address. These + // are in architecture specific files in arch/<arch>/fault_handler_<arch>.cc + GetMethodAndReturnPC(context, /*out*/potential_method, /*out*/return_pc); + + // If we don't have a potential method, we're outta here. + if (potential_method == 0) { + return false; + } + + // Verify that the potential method is indeed a method. + // TODO: check the GC maps to make sure it's an object. + + mirror::Object* method_obj = + reinterpret_cast<mirror::Object*>(potential_method); + + // Check that the class pointer inside the object is not null and is aligned. + mirror::Class* cls = method_obj->GetClass<kVerifyNone>(); + if (cls == nullptr) { + return false; + } + if (!IsAligned<kObjectAlignment>(cls)) { + return false; + } + + + if (!VerifyClassClass(cls)) { + return false; + } + + // Now make sure the class is a mirror::ArtMethod. + if (!cls->IsArtMethodClass()) { + return false; + } + + // We can be certain that this is a method now. Check if we have a GC map + // at the return PC address. + mirror::ArtMethod* method = + reinterpret_cast<mirror::ArtMethod*>(potential_method); + return method->ToDexPc(return_pc, false) != DexFile::kDexNoIndex; +} + +// +// Null pointer fault handler +// + +NullPointerHandler::NullPointerHandler(FaultManager* manager) { + manager->AddHandler(this); +} + +// +// Suspension fault handler +// + +SuspensionHandler::SuspensionHandler(FaultManager* manager) { + manager->AddHandler(this); +} + +// +// Stack overflow fault handler +// + +StackOverflowHandler::StackOverflowHandler(FaultManager* manager) { + manager->AddHandler(this); +} +} // namespace art + diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h new file mode 100644 index 0000000..9fe6e9a --- /dev/null +++ b/runtime/fault_handler.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2008 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_RUNTIME_FAULT_HANDLER_H_ +#define ART_RUNTIME_FAULT_HANDLER_H_ + +#include <signal.h> +#include <vector> +#include <setjmp.h> +#include <stdint.h> + +#include "base/mutex.h" // For annotalysis. + +namespace art { +class FaultHandler; + +class FaultManager { + public: + FaultManager(); + ~FaultManager(); + + void Init(); + + void HandleFault(int sig, siginfo_t* info, void* context); + void AddHandler(FaultHandler* handler); + void RemoveHandler(FaultHandler* handler); + + private: + bool IsInGeneratedCode(void *context) NO_THREAD_SAFETY_ANALYSIS; + void GetMethodAndReturnPC(void* context, uintptr_t& method, uintptr_t& return_pc); + + typedef std::vector<FaultHandler*> Handlers; + Handlers handlers_; + struct sigaction oldaction_; +}; + +class FaultHandler { + public: + FaultHandler() : manager_(nullptr) {} + explicit FaultHandler(FaultManager* manager) : manager_(manager) {} + virtual ~FaultHandler() {} + + virtual bool Action(int sig, siginfo_t* siginfo, void* context) = 0; + protected: + FaultManager* const manager_; +}; + +class NullPointerHandler FINAL : public FaultHandler { + public: + NullPointerHandler() {} + explicit NullPointerHandler(FaultManager* manager); + + bool Action(int sig, siginfo_t* siginfo, void* context) OVERRIDE; +}; + +class SuspensionHandler FINAL : public FaultHandler { + public: + SuspensionHandler() {} + explicit SuspensionHandler(FaultManager* manager); + + bool Action(int sig, siginfo_t* siginfo, void* context) OVERRIDE; +}; + +class StackOverflowHandler FINAL : public FaultHandler { + public: + StackOverflowHandler() {} + explicit StackOverflowHandler(FaultManager* manager); + + bool Action(int sig, siginfo_t* siginfo, void* context) OVERRIDE; +}; + +// Statically allocated so the the signal handler can get access to it. +extern FaultManager fault_manager; + +} // namespace art +#endif // ART_RUNTIME_FAULT_HANDLER_H_ + diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index ca5b5a9..5480639 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -67,6 +67,37 @@ static bool GenerateImage(const std::string& image_file_name, std::string* error arg_vector.push_back("--runtime-arg"); arg_vector.push_back("-Xmx64m"); + arg_vector.push_back("--runtime-arg"); + std::string checkstr = "-implicit-checks"; + int nchecks = 0; + char checksep = ':'; + + if (!Runtime::Current()->ExplicitNullChecks()) { + checkstr += checksep; + checksep = ','; + checkstr += "null"; + ++nchecks; + } + if (!Runtime::Current()->ExplicitSuspendChecks()) { + checkstr += checksep; + checksep = ','; + checkstr += "suspend"; + ++nchecks; + } + + if (!Runtime::Current()->ExplicitStackOverflowChecks()) { + checkstr += checksep; + checksep = ','; + checkstr += "stack"; + ++nchecks; + } + + if (nchecks == 0) { + checkstr += ":none"; + } + + arg_vector.push_back(checkstr); + for (size_t i = 0; i < boot_class_path.size(); i++) { arg_vector.push_back(std::string("--dex-file=") + boot_class_path[i]); } diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index fe27992..e8a0891 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -157,7 +157,7 @@ uintptr_t ArtMethod::NativePcOffset(const uintptr_t pc) { return pc - reinterpret_cast<uintptr_t>(code); } -uint32_t ArtMethod::ToDexPc(const uintptr_t pc) { +uint32_t ArtMethod::ToDexPc(const uintptr_t pc, bool abort_on_failure) { if (IsPortableCompiled()) { // Portable doesn't use the machine pc, we just use dex pc instead. return static_cast<uint32_t>(pc); @@ -183,9 +183,11 @@ uint32_t ArtMethod::ToDexPc(const uintptr_t pc) { return cur.DexPc(); } } - LOG(FATAL) << "Failed to find Dex offset for PC offset " << reinterpret_cast<void*>(sought_offset) - << "(PC " << reinterpret_cast<void*>(pc) << ", code=" << code - << ") in " << PrettyMethod(this); + if (abort_on_failure) { + LOG(FATAL) << "Failed to find Dex offset for PC offset " << reinterpret_cast<void*>(sought_offset) + << "(PC " << reinterpret_cast<void*>(pc) << ", code=" << code + << ") in " << PrettyMethod(this); + } return DexFile::kDexNoIndex; } diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index a9da66c..84a3eb6 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -402,7 +402,8 @@ class MANAGED ArtMethod : public Object { uintptr_t NativePcOffset(const uintptr_t pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Converts a native PC to a dex PC. - uint32_t ToDexPc(const uintptr_t pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + uint32_t ToDexPc(const uintptr_t pc, bool abort_on_failure = true) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Converts a dex PC to a native PC. uintptr_t ToNativePc(const uint32_t dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/oat.cc b/runtime/oat.cc index 454786d..d04514f 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -22,7 +22,7 @@ namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; -const uint8_t OatHeader::kOatVersion[] = { '0', '1', '7', '\0' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '1', '8', '\0' }; OatHeader::OatHeader() { memset(this, 0, sizeof(*this)); diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 37db462..5717689 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -15,6 +15,9 @@ */ #include "parsed_options.h" +#ifdef HAVE_ANDROID_OS +#include "cutils/properties.h" +#endif #include "debugger.h" #include "monitor.h" @@ -191,6 +194,36 @@ bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecogni profile_backoff_coefficient_ = 2.0; profile_clock_source_ = kDefaultProfilerClockSource; + // Default to explicit checks. Switch off with -implicit-checks:. + // or setprop dalvik.vm.implicit_checks check1,check2,... +#ifdef HAVE_ANDROID_OS + { + char buf[PROP_VALUE_MAX]; + property_get("dalvik.vm.implicit_checks", buf, "none"); + std::string checks(buf); + std::vector<std::string> checkvec; + Split(checks, ',', checkvec); + for (auto& str : checkvec) { + std::string val = Trim(str); + if (val == "none") { + explicit_checks_ = kExplicitNullCheck | kExplicitSuspendCheck | + kExplicitStackOverflowCheck; + } else if (val == "null") { + explicit_checks_ &= ~kExplicitNullCheck; + } else if (val == "suspend") { + explicit_checks_ &= ~kExplicitSuspendCheck; + } else if (val == "stack") { + explicit_checks_ &= ~kExplicitStackOverflowCheck; + } else if (val == "all") { + explicit_checks_ = 0; + } + } + } +#else + explicit_checks_ = kExplicitNullCheck | kExplicitSuspendCheck | + kExplicitStackOverflowCheck; +#endif + for (size_t i = 0; i < options.size(); ++i) { if (true && options[0].first == "-Xzygote") { LOG(INFO) << "option[" << i << "]=" << options[i].first; @@ -470,6 +503,54 @@ bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecogni if (!ParseDouble(option, ':', 1.0, 10.0, &profile_backoff_coefficient_)) { return false; } + } else if (StartsWith(option, "-implicit-checks:")) { + std::string checks; + if (!ParseStringAfterChar(option, ':', &checks)) { + return false; + } + std::vector<std::string> checkvec; + Split(checks, ',', checkvec); + for (auto& str : checkvec) { + std::string val = Trim(str); + if (val == "none") { + explicit_checks_ = kExplicitNullCheck | kExplicitSuspendCheck | + kExplicitStackOverflowCheck; + } else if (val == "null") { + explicit_checks_ &= ~kExplicitNullCheck; + } else if (val == "suspend") { + explicit_checks_ &= ~kExplicitSuspendCheck; + } else if (val == "stack") { + explicit_checks_ &= ~kExplicitStackOverflowCheck; + } else if (val == "all") { + explicit_checks_ = 0; + } else { + return false; + } + } + } else if (StartsWith(option, "-explicit-checks:")) { + std::string checks; + if (!ParseStringAfterChar(option, ':', &checks)) { + return false; + } + std::vector<std::string> checkvec; + Split(checks, ',', checkvec); + for (auto& str : checkvec) { + std::string val = Trim(str); + if (val == "none") { + explicit_checks_ = 0; + } else if (val == "null") { + explicit_checks_ |= kExplicitNullCheck; + } else if (val == "suspend") { + explicit_checks_ |= kExplicitSuspendCheck; + } else if (val == "stack") { + explicit_checks_ |= kExplicitStackOverflowCheck; + } else if (val == "all") { + explicit_checks_ = kExplicitNullCheck | kExplicitSuspendCheck | + kExplicitStackOverflowCheck; + } else { + return false; + } + } } else if (option == "-Xcompiler-option") { i++; if (i == options.size()) { @@ -488,6 +569,7 @@ bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecogni StartsWith(option, "-da:") || StartsWith(option, "-enableassertions:") || StartsWith(option, "-disableassertions:") || + (option == "--runtime-arg") || (option == "-esa") || (option == "-dsa") || (option == "-enablesystemassertions") || diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h index f07bba1..d6516a8 100644 --- a/runtime/parsed_options.h +++ b/runtime/parsed_options.h @@ -81,6 +81,11 @@ class ParsedOptions { double profile_backoff_coefficient_; ProfilerClockSource profile_clock_source_; + static constexpr uint32_t kExplicitNullCheck = 1; + static constexpr uint32_t kExplicitSuspendCheck = 2; + static constexpr uint32_t kExplicitStackOverflowCheck = 4; + uint32_t explicit_checks_; + private: ParsedOptions() {} diff --git a/runtime/runtime.cc b/runtime/runtime.cc index d1c8370..1555bf2 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -123,7 +123,10 @@ Runtime::Runtime() system_thread_group_(nullptr), system_class_loader_(nullptr), dump_gc_performance_on_shutdown_(false), - preinitialization_transaction(nullptr) { + preinitialization_transaction(nullptr), + null_pointer_handler_(nullptr), + suspend_handler_(nullptr), + stack_overflow_handler_(nullptr) { for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { callee_save_methods_[i] = nullptr; } @@ -170,6 +173,10 @@ Runtime::~Runtime() { // TODO: acquire a static mutex on Runtime to avoid racing. CHECK(instance_ == nullptr || instance_ == this); instance_ = nullptr; + + delete null_pointer_handler_; + delete suspend_handler_; + delete stack_overflow_handler_; } struct AbortState { @@ -515,6 +522,27 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { GetInstrumentation()->ForceInterpretOnly(); } + if (options->explicit_checks_ != (ParsedOptions::kExplicitSuspendCheck | + ParsedOptions::kExplicitNullCheck | + ParsedOptions::kExplicitStackOverflowCheck)) { + // Initialize the fault manager. + fault_manager.Init(); + + // These need to be in a specific order. The null point check must be + // the last in the list. + if ((options->explicit_checks_ & ParsedOptions::kExplicitSuspendCheck) == 0) { + suspend_handler_ = new SuspensionHandler(&fault_manager); + } + + if ((options->explicit_checks_ & ParsedOptions::kExplicitStackOverflowCheck) == 0) { + stack_overflow_handler_ = new StackOverflowHandler(&fault_manager); + } + + if ((options->explicit_checks_ & ParsedOptions::kExplicitNullCheck) == 0) { + null_pointer_handler_ = new NullPointerHandler(&fault_manager); + } + } + heap_ = new gc::Heap(options->heap_initial_size_, options->heap_growth_limit_, options->heap_min_free_, diff --git a/runtime/runtime.h b/runtime/runtime.h index 109f031..eeaaa2b 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -36,6 +36,7 @@ #include "object_callbacks.h" #include "runtime_stats.h" #include "safe_map.h" +#include "fault_handler.h" namespace art { @@ -404,6 +405,18 @@ class Runtime { return fault_message_; } + bool ExplicitNullChecks() const { + return null_pointer_handler_ == nullptr; + } + + bool ExplicitSuspendChecks() const { + return suspend_handler_ == nullptr; + } + + bool ExplicitStackOverflowChecks() const { + return stack_overflow_handler_ == nullptr; + } + private: static void InitPlatformSignalHandlers(); @@ -536,6 +549,9 @@ class Runtime { // Transaction used for pre-initializing classes at compilation time. Transaction* preinitialization_transaction; + NullPointerHandler* null_pointer_handler_; + SuspensionHandler* suspend_handler_; + StackOverflowHandler* stack_overflow_handler_; DISALLOW_COPY_AND_ASSIGN(Runtime); }; diff --git a/runtime/stack.cc b/runtime/stack.cc index 26b4de3..f397afa 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -92,11 +92,11 @@ StackVisitor::StackVisitor(Thread* thread, Context* context) DCHECK(thread == Thread::Current() || thread->IsSuspended()) << *thread; } -uint32_t StackVisitor::GetDexPc() const { +uint32_t StackVisitor::GetDexPc(bool abort_on_failure) const { if (cur_shadow_frame_ != NULL) { return cur_shadow_frame_->GetDexPC(); } else if (cur_quick_frame_ != NULL) { - return GetMethod()->ToDexPc(cur_quick_frame_pc_); + return GetMethod()->ToDexPc(cur_quick_frame_pc_, abort_on_failure); } else { return 0; } diff --git a/runtime/stack.h b/runtime/stack.h index f840f67..4ee5de1 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -520,7 +520,7 @@ class StackVisitor { return cur_shadow_frame_ != nullptr; } - uint32_t GetDexPc() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + uint32_t GetDexPc(bool abort_on_failure = true) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); mirror::Object* GetThisObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/thread.cc b/runtime/thread.cc index fbdf95f..f4b9d9a 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -302,6 +302,7 @@ void Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm) { SetUpAlternateSignalStack(); InitCpu(); InitTlsEntryPoints(); + RemoveSuspendTrigger(); InitCardTable(); InitTid(); // Set pthread_self_ ahead of pthread_setspecific, that makes Thread::Current function, this @@ -576,6 +577,7 @@ void Thread::ModifySuspendCount(Thread* self, int delta, bool for_debugger) { AtomicClearFlag(kSuspendRequest); } else { AtomicSetFlag(kSuspendRequest); + TriggerSuspend(); } } @@ -643,6 +645,7 @@ bool Thread::RequestCheckpoint(Closure* function) { checkpoint_functions_[available_checkpoint] = nullptr; } else { CHECK_EQ(ReadFlag(kCheckpointRequest), true); + TriggerSuspend(); } return succeeded == 0; } @@ -1774,6 +1777,7 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset, size_t size_of_ // DO_THREAD_OFFSET(top_of_managed_stack_); // DO_THREAD_OFFSET(top_of_managed_stack_pc_); DO_THREAD_OFFSET(top_sirt_); + DO_THREAD_OFFSET(suspend_trigger_); #undef DO_THREAD_OFFSET size_t entry_point_count = arraysize(gThreadEntryPointInfo); diff --git a/runtime/thread.h b/runtime/thread.h index eaffc3e..264a927 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -433,6 +433,10 @@ class PACKED(4) Thread { return ThreadOffset(OFFSETOF_MEMBER(Thread, state_and_flags_)); } + static ThreadOffset ThreadSuspendTriggerOffset() { + return ThreadOffset(OFFSETOF_MEMBER(Thread, suspend_trigger_)); + } + // Size of stack less any space reserved for stack overflow size_t GetStackSize() const { return stack_size_ - (stack_end_ - stack_begin_); @@ -824,6 +828,10 @@ class PACKED(4) Thread { PortableEntryPoints portable_entrypoints_; QuickEntryPoints quick_entrypoints_; + // Setting this to 0 will trigger a SEGV and thus a suspend check. It is normally + // set to the address of itself. + uintptr_t* suspend_trigger_; + // How many times has our pthread key's destructor been called? uint32_t thread_exit_check_count_; @@ -838,6 +846,20 @@ class PACKED(4) Thread { mirror::Object* AllocTlab(size_t bytes); void SetTlab(byte* start, byte* end); + // Remove the suspend trigger for this thread by making the suspend_trigger_ TLS value + // equal to a valid pointer. + // TODO: does this need to atomic? I don't think so. + void RemoveSuspendTrigger() { + suspend_trigger_ = reinterpret_cast<uintptr_t*>(&suspend_trigger_); + } + + // Trigger a suspend check by making the suspend_trigger_ TLS value an invalid pointer. + // The next time a suspend check is done, it will load from the value at this address + // and trigger a SIGSEGV. + void TriggerSuspend() { + suspend_trigger_ = nullptr; + } + // Thread-local rosalloc runs. There are 34 size brackets in rosalloc // runs (RosAlloc::kNumOfSizeBrackets). We can't refer to the // RosAlloc class due to a header file circular dependency issue. diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index ac5750b..ec610e1 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -753,6 +753,7 @@ void ThreadList::Register(Thread* self) { self->debug_suspend_count_ = debug_suspend_all_count_; if (self->suspend_count_ > 0) { self->AtomicSetFlag(kSuspendRequest); + self->TriggerSuspend(); } CHECK(!Contains(self)); list_.push_back(self); |