include $(art_path)/dalvikvm/Android.mk
include $(art_path)/tools/Android.mk
include $(art_build_path)/Android.oat.mk
+include $(art_path)/sigchainlib/Android.mk
+
LIBARTTEST_COMMON_SRC_FILES := \
test/JniTest/jni_test.cc \
+ test/SignalTest/signaltest.cc \
test/ReferenceMap/stack_walk_refmap_jni.cc \
test/StackWalk/stack_walk_jni.cc \
test/UnsafeTest/unsafe_test.cc
endif
endif
LOCAL_C_INCLUDES += $(ART_C_INCLUDES)
+ LOCAL_C_INCLUDES += art/sigchainlib
+
LOCAL_SHARED_LIBRARIES += liblog libnativehelper
include external/libcxx/libcxx.mk
LOCAL_SHARED_LIBRARIES += libbacktrace_libc++
ifeq ($$(art_target_or_host),target)
- LOCAL_SHARED_LIBRARIES += libcutils libdl libselinux libutils
+ LOCAL_SHARED_LIBRARIES += libcutils libdl libselinux libutils libsigchain
LOCAL_STATIC_LIBRARIES := libziparchive libz
else # host
LOCAL_STATIC_LIBRARIES += libcutils libziparchive-host libz libutils
ifeq ($(ART_BUILD_TARGET_DEBUG),true)
$(eval $(call build-libart,target,debug,$(ART_TARGET_CLANG)))
endif
+
#include "mirror/object-inl.h"
#include "object_utils.h"
#include "scoped_thread_state_change.h"
+#ifdef HAVE_ANDROID_OS
+#include "sigchain.h"
+#endif
#include "verify_object-inl.h"
namespace art {
// Static fault manger object accessed by signal handler.
FaultManager fault_manager;
+extern "C" {
+void art_sigsegv_fault() {
+ // Set a breakpoint here to be informed when a SIGSEGV is unhandled by ART.
+ VLOG(signals)<< "Caught unknown SIGSEGV in ART fault handler - chaining to next handler.";
+}
+}
+
// 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() {
+#ifdef HAVE_ANDROID_OS
+ UnclaimSignalChain(SIGSEGV);
+#endif
sigaction(SIGSEGV, &oldaction_, nullptr); // Restore old handler.
}
+
void FaultManager::Init() {
struct sigaction action;
action.sa_sigaction = art_fault_handler;
#if !defined(__mips__)
action.sa_restorer = nullptr;
#endif
+
+ // Set our signal handler now.
sigaction(SIGSEGV, &action, &oldaction_);
+#ifdef HAVE_ANDROID_OS
+ // Make sure our signal handler is called before any user handlers.
+ ClaimSignalChain(SIGSEGV, &oldaction_);
+#endif
}
void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) {
return;
}
}
- VLOG(signals)<< "Caught unknown SIGSEGV in ART fault handler";
+ art_sigsegv_fault();
+
+#ifdef HAVE_ANDROID_OS
+ InvokeUserSignalHandler(sig, info, context);
+#else
oldaction_.sa_sigaction(sig, info, context);
+#endif
}
void FaultManager::AddHandler(FaultHandler* handler, bool generated_code) {
GetInstrumentation()->ForceInterpretOnly();
}
- if (options->explicit_checks_ != (ParsedOptions::kExplicitSuspendCheck |
- ParsedOptions::kExplicitNullCheck |
- ParsedOptions::kExplicitStackOverflowCheck) || kEnableJavaStackTraceHandler) {
+ bool implicit_checks_supported = false;
+ switch (kRuntimeISA) {
+ case kArm:
+ case kThumb2:
+ implicit_checks_supported = true;
+ break;
+ default:
+ break;
+ }
+
+ if (implicit_checks_supported &&
+ (options->explicit_checks_ != (ParsedOptions::kExplicitSuspendCheck |
+ ParsedOptions::kExplicitNullCheck |
+ ParsedOptions::kExplicitStackOverflowCheck) || kEnableJavaStackTraceHandler)) {
fault_manager.Init();
// These need to be in a specific order. The null point check handler must be
// of the stack (lowest memory). The higher portion of the memory
// is protected against reads and the lower is available for use while
// throwing the StackOverflow exception.
- static constexpr size_t kStackOverflowProtectedSize = 32 * KB;
+ static constexpr size_t kStackOverflowProtectedSize = 16 * KB;
static constexpr size_t kStackOverflowImplicitCheckSize = kStackOverflowProtectedSize +
kStackOverflowReservedBytes;
--- /dev/null
+#
+# Copyright (C) 2014 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include art/build/Android.common.mk
+
+include $(CLEAR_VARS)
+LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += $(ART_TARGET_CFLAGS)
+LOCAL_SRC_FILES := sigchain.cc
+LOCAL_MODULE:= libsigchain
+LOCAL_SHARED_LIBRARIES += liblog libdl
+LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+include $(BUILD_SHARED_LIBRARY)
--- /dev/null
+/*
+ * Copyright (C) 2014 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 <android/log.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+namespace art {
+
+class SignalAction {
+ public:
+ SignalAction() : claimed_(false) {
+ }
+
+ // Claim the signal and keep the action specified.
+ void Claim(const struct sigaction& action) {
+ action_ = action;
+ claimed_ = true;
+ }
+
+ // Unclaim the signal and restore the old action.
+ void Unclaim(int signal) {
+ claimed_ = false;
+ sigaction(signal, &action_, NULL); // Restore old action.
+ }
+
+ // Get the action associated with this signal.
+ const struct sigaction& GetAction() const {
+ return action_;
+ }
+
+ // Is the signal claimed?
+ bool IsClaimed() const {
+ return claimed_;
+ }
+
+ // Change the recorded action to that specified.
+ void SetAction(const struct sigaction& action) {
+ action_ = action;
+ }
+
+ private:
+ struct sigaction action_; // Action to be performed.
+ bool claimed_; // Whether signal is claimed or not.
+};
+
+// User's signal handlers
+static SignalAction user_sigactions[_NSIG];
+
+static void log(const char* format, ...) {
+ char buf[256];
+ va_list ap;
+ va_start(ap, format);
+ vsnprintf(buf, sizeof(buf), format, ap);
+ __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf);
+ va_end(ap);
+}
+
+static void CheckSignalValid(int signal) {
+ if (signal <= 0 || signal >= _NSIG) {
+ log("Invalid signal %d", signal);
+ abort();
+ }
+}
+
+// Claim a signal chain for a particular signal.
+void ClaimSignalChain(int signal, struct sigaction* oldaction) {
+ CheckSignalValid(signal);
+ user_sigactions[signal].Claim(*oldaction);
+}
+
+void UnclaimSignalChain(int signal) {
+ CheckSignalValid(signal);
+
+ user_sigactions[signal].Unclaim(signal);
+}
+
+// Invoke the user's signal handler.
+void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) {
+ // Check the arguments.
+ CheckSignalValid(sig);
+
+ // The signal must have been claimed in order to get here. Check it.
+ if (!user_sigactions[sig].IsClaimed()) {
+ abort();
+ }
+
+ const struct sigaction& action = user_sigactions[sig].GetAction();
+
+ // Only deliver the signal if the signal was not masked out.
+ if (sigismember(&action.sa_mask, sig)) {
+ return;
+ }
+ if ((action.sa_flags & SA_SIGINFO) == 0) {
+ if (action.sa_handler != NULL) {
+ action.sa_handler(sig);
+ }
+ } else {
+ if (action.sa_sigaction != NULL) {
+ action.sa_sigaction(sig, info, context);
+ }
+ }
+}
+
+extern "C" {
+// These functions are C linkage since they replace the functions in libc.
+
+int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) {
+ // If this signal has been claimed as a signal chain, record the user's
+ // action but don't pass it on to the kernel.
+ // Note that we check that the signal number is in range here. An out of range signal
+ // number should behave exactly as the libc sigaction.
+ if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed()) {
+ if (old_action != NULL) {
+ *old_action = user_sigactions[signal].GetAction();
+ }
+ if (new_action != NULL) {
+ user_sigactions[signal].SetAction(*new_action);
+ }
+ return 0;
+ }
+
+ // Will only get here if the signal chain has not been claimed. We want
+ // to pass the sigaction on to the kernel via the real sigaction in libc.
+
+ void* linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction");
+ if (linked_sigaction_sym == nullptr) {
+ log("Unable to find next sigaction in signal chain");
+ abort();
+ }
+
+ typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*);
+ SigAction linked_sigaction = reinterpret_cast<SigAction>(linked_sigaction_sym);
+ return linked_sigaction(signal, new_action, old_action);
+}
+
+
+int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
+ const sigset_t* new_set_ptr = bionic_new_set;
+ sigset_t tmpset;
+ if (bionic_new_set != NULL) {
+ tmpset = *bionic_new_set;
+
+ if (how == SIG_BLOCK) {
+ // Don't allow claimed signals in the mask. If a signal chain has been claimed
+ // we can't allow the user to block that signal.
+ for (int i = 0 ; i < _NSIG; ++i) {
+ if (user_sigactions[i].IsClaimed() && sigismember(&tmpset, i)) {
+ sigdelset(&tmpset, i);
+ }
+ }
+ }
+ new_set_ptr = &tmpset;
+ }
+
+ void* linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask");
+ if (linked_sigprocmask_sym == nullptr) {
+ log("Unable to find next sigprocmask in signal chain");
+ abort();
+ }
+
+ typedef int (*SigProcMask)(int how, const sigset_t*, sigset_t*);
+ SigProcMask linked_sigprocmask= reinterpret_cast<SigProcMask>(linked_sigprocmask_sym);
+ return linked_sigprocmask(how, new_set_ptr, bionic_old_set);
+}
+} // extern "C"
+} // namespace art
+
--- /dev/null
+/*
+ * Copyright (C) 2014 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_SIGCHAINLIB_SIGCHAIN_H_
+#define ART_SIGCHAINLIB_SIGCHAIN_H_
+
+#include <signal.h>
+namespace art {
+
+void ClaimSignalChain(int signal, struct sigaction* oldaction);
+void UnclaimSignalChain(int signal);
+void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context);
+
+} // namespace art
+
+#endif // ART_SIGCHAINLIB_SIGCHAIN_H_
HelloWorld \
InterfaceTest \
JniTest \
+ SignalTest \
NativeAllocations \
ParallelGC \
ReferenceMap \
--- /dev/null
+/*
+ * Copyright (C) 2014 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.
+ */
+
+class SignalTest {
+ private static native void initSignalTest();
+ private static native void terminateSignalTest();
+ private static native int testSignal();
+
+ private static void stackOverflow() {
+ stackOverflow();
+ }
+
+ public static void main(String[] args) {
+ System.loadLibrary("arttest");
+
+ System.out.println("init signal test");
+ initSignalTest();
+ try {
+ Object o = null;
+ int hash = o.hashCode();
+
+ // Should never get here.
+ System.out.println("hash: " + hash);
+ throw new AssertionError();
+ } catch (NullPointerException e) {
+ System.out.println("Caught NullPointerException");
+ }
+ try {
+ stackOverflow();
+
+ // Should never get here.
+ throw new AssertionError();
+ } catch (StackOverflowError e) {
+ System.out.println("Caught StackOverflowError");
+ }
+
+ // Test that a signal in native code works. This will return
+ // the value 1234 if the signal is caught.
+ int x = testSignal();
+ if (x != 1234) {
+ throw new AssertionError();
+ }
+
+ terminateSignalTest();
+ System.out.println("Signal test OK");
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2014 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 <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "jni.h"
+
+#ifdef __arm__
+#include <sys/ucontext.h>
+#endif
+
+static void signalhandler(int sig, siginfo_t* info, void* context) {
+ printf("signal caught\n");
+#ifdef __arm__
+ // On ARM we do a more exhaustive test to make sure the signal
+ // context is OK.
+ // We can do this because we know that the instruction causing
+ // the signal is 2 bytes long (thumb mov instruction). On
+ // other architectures this is more difficult.
+ // TODO: we could do this on other architectures too if necessary, it's just harder.
+ struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
+ struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+ sc->arm_pc += 2; // Skip instruction causing segv.
+#endif
+}
+
+static struct sigaction oldaction;
+
+extern "C" JNIEXPORT void JNICALL Java_SignalTest_initSignalTest(JNIEnv*, jclass) {
+ struct sigaction action;
+ action.sa_sigaction = signalhandler;
+ sigemptyset(&action.sa_mask);
+ action.sa_flags = SA_SIGINFO | SA_ONSTACK;
+#if !defined(__mips__)
+ action.sa_restorer = nullptr;
+#endif
+
+ sigaction(SIGSEGV, &action, &oldaction);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_SignalTest_terminateSignalTest(JNIEnv*, jclass) {
+ sigaction(SIGSEGV, &oldaction, nullptr);
+}
+
+// Prevent the compiler being a smart-alec and optimizing out the assignment
+// to nullptr.
+char *p = nullptr;
+
+extern "C" JNIEXPORT jint JNICALL Java_SignalTest_testSignal(JNIEnv*, jclass) {
+#ifdef __arm__
+ // On ARM we cause a real SEGV.
+ *p = 'a';
+#else
+ // On other architectures we simulate SEGV.
+ kill(getpid(), SIGSEGV);
+#endif
+ return 1234;
+}
+