diff options
author | Andreas Gampe <agampe@google.com> | 2015-05-22 18:31:50 -0700 |
---|---|---|
committer | Andreas Gampe <agampe@google.com> | 2015-05-31 16:59:11 -0700 |
commit | 8ee2145d65ed14b267ae4c6396f2333115611a97 (patch) | |
tree | 77c4ffa7f2b21e35e9e09bbce26306ca486f64e2 | |
parent | 93de952df65ca5033bda313712b68b6272b4975f (diff) | |
download | art-8ee2145d65ed14b267ae4c6396f2333115611a97.zip art-8ee2145d65ed14b267ae4c6396f2333115611a97.tar.gz art-8ee2145d65ed14b267ae4c6396f2333115611a97.tar.bz2 |
ART: Add support for special handlers in sigchainlib
Add support for a special managed handler in sigchainlib that
will be called as the first user handler.
Use this support for native bridge v2. Extend test 115-native-bridge
to test the functionality.
Bug: 20217701
(cherry picked from commit 03c2cc89428914adf52229d6a3867eef6127911a)
Change-Id: I78cc01fbdabe169154ff6b94c9f3ddb95b5c7448
-rw-r--r-- | runtime/native_bridge_art_interface.cc | 17 | ||||
-rw-r--r-- | sigchainlib/sigchain.cc | 53 | ||||
-rw-r--r-- | sigchainlib/sigchain.h | 3 | ||||
-rw-r--r-- | sigchainlib/sigchain_dummy.cc | 6 | ||||
-rw-r--r-- | test/115-native-bridge/expected.txt | 5 | ||||
-rw-r--r-- | test/115-native-bridge/nativebridge.cc | 97 | ||||
-rw-r--r-- | test/115-native-bridge/src/NativeBridgeMain.java | 13 |
7 files changed, 185 insertions, 9 deletions
diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc index b7f31f2..0ad560e 100644 --- a/runtime/native_bridge_art_interface.cc +++ b/runtime/native_bridge_art_interface.cc @@ -16,6 +16,8 @@ #include "native_bridge_art_interface.h" +#include <signal.h> + #include "nativebridge/native_bridge.h" #include "base/logging.h" @@ -24,6 +26,7 @@ #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" #include "scoped_thread_state_change.h" +#include "sigchain.h" namespace art { @@ -127,7 +130,19 @@ void PreInitializeNativeBridge(std::string dir) { } void InitializeNativeBridge(JNIEnv* env, const char* instruction_set) { - android::InitializeNativeBridge(env, instruction_set); + if (android::InitializeNativeBridge(env, instruction_set)) { + if (android::NativeBridgeGetVersion() >= 2U) { +#ifdef _NSIG // Undefined on Apple, but we don't support running on Mac, anyways. + // Managed signal handling support added in version 2. + for (int signal = 0; signal < _NSIG; ++signal) { + android::NativeBridgeSignalHandlerFn fn = android::NativeBridgeGetSignalHandler(signal); + if (fn != nullptr) { + SetSpecialSignalHandlerFn(signal, fn); + } + } +#endif + } + } } void UnloadNativeBridge() { diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc index 0359ed3..1391d14 100644 --- a/sigchainlib/sigchain.cc +++ b/sigchainlib/sigchain.cc @@ -39,7 +39,7 @@ typedef int (*SigActionFnPtr)(int, const struct sigaction*, struct sigaction*); class SignalAction { public: - SignalAction() : claimed_(false), uses_old_style_(false) { + SignalAction() : claimed_(false), uses_old_style_(false), special_handler_(nullptr) { } // Claim the signal and keep the action specified. @@ -77,10 +77,19 @@ class SignalAction { return uses_old_style_; } + void SetSpecialHandler(SpecialSignalHandlerFn fn) { + special_handler_ = fn; + } + + SpecialSignalHandlerFn GetSpecialHandler() { + return special_handler_; + } + private: - struct sigaction action_; // Action to be performed. - bool claimed_; // Whether signal is claimed or not. - bool uses_old_style_; // Action is created using signal(). Use sa_handler. + struct sigaction action_; // Action to be performed. + bool claimed_; // Whether signal is claimed or not. + bool uses_old_style_; // Action is created using signal(). Use sa_handler. + SpecialSignalHandlerFn special_handler_; // A special handler executed before user handlers. }; // User's signal handlers @@ -109,9 +118,16 @@ static void CheckSignalValid(int signal) { } } +// Sigchainlib's own handler so we can ensure a managed handler is called first even if nobody +// claimed a chain. Simply forward to InvokeUserSignalHandler. +static void sigchainlib_managed_handler_sigaction(int sig, siginfo_t* info, void* context) { + InvokeUserSignalHandler(sig, info, context); +} + // Claim a signal chain for a particular signal. extern "C" void ClaimSignalChain(int signal, struct sigaction* oldaction) { CheckSignalValid(signal); + user_sigactions[signal].Claim(*oldaction); } @@ -131,6 +147,15 @@ extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) abort(); } + // Do we have a managed handler? If so, run it first. + SpecialSignalHandlerFn managed = user_sigactions[sig].GetSpecialHandler(); + if (managed != nullptr) { + // Call the handler. If it succeeds, we're done. + if (managed(sig, info, context)) { + return; + } + } + const struct sigaction& action = user_sigactions[sig].GetAction(); if (user_sigactions[sig].OldStyle()) { if (action.sa_handler != nullptr) { @@ -303,5 +328,25 @@ extern "C" void InitializeSignalChain() { initialized = true; } +extern "C" void SetSpecialSignalHandlerFn(int signal, SpecialSignalHandlerFn fn) { + CheckSignalValid(signal); + + // Set the managed_handler. + user_sigactions[signal].SetSpecialHandler(fn); + + // In case the chain isn't claimed, claim it for ourself so we can ensure the managed handler + // goes first. + if (!user_sigactions[signal].IsClaimed()) { + struct sigaction tmp; + tmp.sa_sigaction = sigchainlib_managed_handler_sigaction; + sigemptyset(&tmp.sa_mask); + tmp.sa_flags = SA_SIGINFO | SA_ONSTACK; +#if !defined(__APPLE__) && !defined(__mips__) + tmp.sa_restorer = nullptr; +#endif + user_sigactions[signal].Claim(tmp); + } +} + } // namespace art diff --git a/sigchainlib/sigchain.h b/sigchainlib/sigchain.h index 79b76a7..01ccedf 100644 --- a/sigchainlib/sigchain.h +++ b/sigchainlib/sigchain.h @@ -27,6 +27,9 @@ extern "C" void ClaimSignalChain(int signal, struct sigaction* oldaction); extern "C" void UnclaimSignalChain(int signal); +typedef bool (*SpecialSignalHandlerFn)(int, siginfo_t*, void*); +extern "C" void SetSpecialSignalHandlerFn(int signal, SpecialSignalHandlerFn fn); + extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context); extern "C" void EnsureFrontOfChain(int signal, struct sigaction* expected_action); diff --git a/sigchainlib/sigchain_dummy.cc b/sigchainlib/sigchain_dummy.cc index 70a4f71..8495a54 100644 --- a/sigchainlib/sigchain_dummy.cc +++ b/sigchainlib/sigchain_dummy.cc @@ -78,6 +78,12 @@ extern "C" void EnsureFrontOfChain(int signal ATTRIBUTE_UNUSED, abort(); } +extern "C" void SetSpecialSignalHandlerFn(int signal ATTRIBUTE_UNUSED, + SpecialSignalHandlerFn fn ATTRIBUTE_UNUSED) { + log("SetSpecialSignalHandlerFn is not exported by the main executable."); + abort(); +} + #pragma GCC diagnostic pop } // namespace art diff --git a/test/115-native-bridge/expected.txt b/test/115-native-bridge/expected.txt index deb70ba..464d2c8 100644 --- a/test/115-native-bridge/expected.txt +++ b/test/115-native-bridge/expected.txt @@ -4,7 +4,7 @@ Checking for getEnvValues. Ready for native bridge tests. Checking for support. Getting trampoline for JNI_OnLoad with shorty (null). -Test ART callbacks: all JNI function number is 10. +Test ART callbacks: all JNI function number is 11. name:booleanMethod, signature:(ZZZZZZZZZZ)Z, shorty:ZZZZZZZZZZZ. name:byteMethod, signature:(BBBBBBBBBB)B, shorty:BBBBBBBBBBB. name:charMethod, signature:(CCCCCCCCCC)C, shorty:CCCCCCCCCCC. @@ -14,6 +14,7 @@ Test ART callbacks: all JNI function number is 10. name:testFindFieldOnAttachedNativeThreadNative, signature:()V, shorty:V. name:testGetMirandaMethodNative, signature:()Ljava/lang/reflect/Method;, shorty:L. name:testNewStringObject, signature:()V, shorty:V. + name:testSignal, signature:()I, shorty:I. name:testZeroLengthByteBuffers, signature:()V, shorty:V. trampoline_JNI_OnLoad called! Getting trampoline for Java_Main_testFindClassOnAttachedNativeThread with shorty V. @@ -58,3 +59,5 @@ trampoline_Java_Main_charMethod called! trampoline_Java_Main_charMethod called! Getting trampoline for Java_Main_testNewStringObject with shorty V. trampoline_Java_Main_testNewStringObject called! +Getting trampoline for Java_Main_testSignal with shorty I. +NB signal handler with signal 11. diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc index db2fc9b..fd94b23 100644 --- a/test/115-native-bridge/nativebridge.cc +++ b/test/115-native-bridge/nativebridge.cc @@ -19,6 +19,8 @@ #include <algorithm> #include <dlfcn.h> #include <jni.h> +#include <stdlib.h> +#include <signal.h> #include <vector> #include "stdio.h" @@ -179,6 +181,35 @@ static jchar trampoline_Java_Main_charMethod(JNIEnv* env, jclass klass, jchar c1 return fnPtr(env, klass, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10); } +// This code is adapted from 004-SignalTest and causes a segfault. +char *go_away_compiler = nullptr; + +[[ noreturn ]] static void test_sigaction_handler(int sig ATTRIBUTE_UNUSED, + siginfo_t* info ATTRIBUTE_UNUSED, + void* context ATTRIBUTE_UNUSED) { + printf("Should not reach the test sigaction handler."); + abort(); +} + +static jint trampoline_Java_Main_testSignal(JNIEnv*, jclass) { + // Install the sigaction handler above, which should *not* be reached as the native-bridge + // handler should be called first. Note: we won't chain at all, if we ever get here, we'll die. + struct sigaction tmp; + sigemptyset(&tmp.sa_mask); + tmp.sa_sigaction = test_sigaction_handler; + tmp.sa_restorer = nullptr; + sigaction(SIGSEGV, &tmp, nullptr); + +#if defined(__arm__) || defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) + // On supported architectures we cause a real SEGV. + *go_away_compiler = 'a'; +#else + // On other architectures we simulate SEGV. + kill(getpid(), SIGSEGV); +#endif + return 1234; +} + NativeBridgeMethod gNativeBridgeMethods[] = { { "JNI_OnLoad", "", true, nullptr, reinterpret_cast<void*>(trampoline_JNI_OnLoad) }, @@ -202,6 +233,8 @@ NativeBridgeMethod gNativeBridgeMethods[] = { reinterpret_cast<void*>(trampoline_Java_Main_testNewStringObject) }, { "testZeroLengthByteBuffers", "()V", true, nullptr, reinterpret_cast<void*>(trampoline_Java_Main_testZeroLengthByteBuffers) }, + { "testSignal", "()I", true, nullptr, + reinterpret_cast<void*>(trampoline_Java_Main_testSignal) }, }; static NativeBridgeMethod* find_native_bridge_method(const char *name) { @@ -319,15 +352,73 @@ extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge_getApp return &nb_env; } +// v2 parts. + +extern "C" bool nb_is_compatible(uint32_t bridge_version ATTRIBUTE_UNUSED) { + return true; +} + +#if defined(__i386__) || defined(__x86_64__) +#if defined(__APPLE__) +#define ucontext __darwin_ucontext + +#if defined(__x86_64__) +// 64 bit mac build. +#define CTX_EIP uc_mcontext->__ss.__rip +#else +// 32 bit mac build. +#define CTX_EIP uc_mcontext->__ss.__eip +#endif + +#elif defined(__x86_64__) +// 64 bit linux build. +#define CTX_EIP uc_mcontext.gregs[REG_RIP] +#else +// 32 bit linux build. +#define CTX_EIP uc_mcontext.gregs[REG_EIP] +#endif +#endif + +// A dummy special handler, continueing after the faulting location. This code comes from +// 004-SignalTest. +static bool nb_signalhandler(int sig, siginfo_t* info ATTRIBUTE_UNUSED, void* context) { + printf("NB signal handler with signal %d.\n", sig); +#if defined(__arm__) + 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. +#elif defined(__aarch64__) + struct ucontext *uc = reinterpret_cast<struct ucontext*>(context); + struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext); + sc->pc += 4; // Skip instruction causing segv. +#elif defined(__i386__) || defined(__x86_64__) + struct ucontext *uc = reinterpret_cast<struct ucontext*>(context); + uc->CTX_EIP += 3; +#else + UNUSED(context); +#endif + // We handled this... + return true; +} + +static ::android::NativeBridgeSignalHandlerFn native_bridge_get_signal_handler(int signal) { + // Only test segfault handler. + if (signal == SIGSEGV) { + return &nb_signalhandler; + } + return nullptr; +} + + // "NativeBridgeItf" is effectively an API (it is the name of the symbol that will be loaded // by the native bridge library). android::NativeBridgeCallbacks NativeBridgeItf { - .version = 1, + .version = 2, .initialize = &native_bridge_initialize, .loadLibrary = &native_bridge_loadLibrary, .getTrampoline = &native_bridge_getTrampoline, .isSupported = &native_bridge_isSupported, .getAppEnv = &native_bridge_getAppEnv, - .isCompatibleWith = nullptr, - .getSignalHandler = nullptr + .isCompatibleWith = &nb_is_compatible, + .getSignalHandler = &native_bridge_get_signal_handler }; diff --git a/test/115-native-bridge/src/NativeBridgeMain.java b/test/115-native-bridge/src/NativeBridgeMain.java index c843707..25390f7 100644 --- a/test/115-native-bridge/src/NativeBridgeMain.java +++ b/test/115-native-bridge/src/NativeBridgeMain.java @@ -32,6 +32,7 @@ class Main { testCharMethod(); testEnvironment(); testNewStringObject(); + testSignalHandler(); } public static native void testFindClassOnAttachedNativeThread(); @@ -170,6 +171,18 @@ class Main { } private static native void testNewStringObject(); + + // Test v2 special signal handlers. This uses the native code from 004-SignalTest to cause + // a non-managed segfault. + private static void testSignalHandler() { + // This uses code from 004-SignalTest. + int x = testSignal(); + if (x != 1234) { + throw new AssertionError(); + } + } + + private static native int testSignal(); } public class NativeBridgeMain { |