8076185: Provide SafeFetchX implementation for zero
Summary: Implement SafeFetchX for zero using setjmp/longjmp and Posix TLS
Reviewed-by: sgehwolf, dholmes
Contributed-by: thomas.stuefe@gmail.com
--- a/hotspot/src/cpu/zero/vm/stubGenerator_zero.cpp Tue Mar 31 02:11:09 2015 +0000
+++ b/hotspot/src/cpu/zero/vm/stubGenerator_zero.cpp Tue Mar 31 05:30:36 2015 -0400
@@ -45,6 +45,18 @@
#include "opto/runtime.hpp"
#endif
+// For SafeFetch we need POSIX tls and setjmp
+#include <setjmp.h>
+#include <pthread.h>
+static pthread_key_t g_jmpbuf_key;
+
+// return the currently active jump buffer for this thread
+// - if there is any, NULL otherwise. Called from
+// zero signal handlers.
+extern sigjmp_buf* get_jmp_buf_for_continuation() {
+ return (sigjmp_buf*) pthread_getspecific(g_jmpbuf_key);
+}
+
// Declaration and definition of StubGenerator (no .hpp file).
// For a more detailed description of the stub routine structure
// see the comment in stubRoutines.hpp
@@ -176,17 +188,55 @@
StubRoutines::_oop_arraycopy;
}
- // NYI: SafeFetch for Zero isn't actually safe.
static int SafeFetch32(int *adr, int errValue) {
+
+ // set up a jump buffer; anchor the pointer to the jump buffer in tls; then
+ // do the pointer access. If pointer is invalid, we crash; in signal
+ // handler, we retrieve pointer to jmp buffer from tls, and jump back.
+ //
+ // Note: the jump buffer itself - which can get pretty large depending on
+ // the architecture - lives on the stack and that is fine, because we will
+ // not rewind the stack: either we crash, in which case signal handler
+ // frame is below us, or we don't crash, in which case it does not matter.
+ sigjmp_buf jb;
+ if (sigsetjmp(jb, 1)) {
+ // we crashed. clean up tls and return default value.
+ pthread_setspecific(g_jmpbuf_key, NULL);
+ return errValue;
+ } else {
+ // preparation phase
+ pthread_setspecific(g_jmpbuf_key, &jb);
+ }
+
int value = errValue;
value = *adr;
+
+ // all went well. clean tls.
+ pthread_setspecific(g_jmpbuf_key, NULL);
+
return value;
}
static intptr_t SafeFetchN(intptr_t *adr, intptr_t errValue) {
+
+ sigjmp_buf jb;
+ if (sigsetjmp(jb, 1)) {
+ // we crashed. clean up tls and return default value.
+ pthread_setspecific(g_jmpbuf_key, NULL);
+ return errValue;
+ } else {
+ // preparation phase
+ pthread_setspecific(g_jmpbuf_key, &jb);
+ }
+
intptr_t value = errValue;
value = *adr;
+
+ // all went well. clean tls.
+ pthread_setspecific(g_jmpbuf_key, NULL);
+
return value;
+
}
void generate_initial() {
@@ -241,6 +291,7 @@
generate_arraycopy_stubs();
// Safefetch stubs.
+ pthread_key_create(&g_jmpbuf_key, NULL);
StubRoutines::_safefetch32_entry = CAST_FROM_FN_PTR(address, StubGenerator::SafeFetch32);
StubRoutines::_safefetch32_fault_pc = NULL;
StubRoutines::_safefetch32_continuation_pc = NULL;
--- a/hotspot/src/os_cpu/bsd_zero/vm/os_bsd_zero.cpp Tue Mar 31 02:11:09 2015 +0000
+++ b/hotspot/src/os_cpu/bsd_zero/vm/os_bsd_zero.cpp Tue Mar 31 05:30:36 2015 -0400
@@ -59,6 +59,10 @@
#include "utilities/events.hpp"
#include "utilities/vmError.hpp"
+// See stubGenerator_zero.cpp
+#include <setjmp.h>
+extern sigjmp_buf* get_jmp_buf_for_continuation();
+
address os::current_stack_pointer() {
address dummy = (address) &dummy;
return dummy;
@@ -134,6 +138,14 @@
SignalHandlerMark shm(t);
+ // handle SafeFetch faults
+ if (sig == SIGSEGV || sig == SIGBUS) {
+ sigjmp_buf* const pjb = get_jmp_buf_for_continuation();
+ if (pjb) {
+ siglongjmp(*pjb, 1);
+ }
+ }
+
// Note: it's not uncommon that JNI code uses signal/sigset to
// install then restore certain signal handler (e.g. to temporarily
// block SIGPIPE, or have a SIGILL handler when detecting CPU
--- a/hotspot/src/os_cpu/linux_zero/vm/os_linux_zero.cpp Tue Mar 31 02:11:09 2015 +0000
+++ b/hotspot/src/os_cpu/linux_zero/vm/os_linux_zero.cpp Tue Mar 31 05:30:36 2015 -0400
@@ -54,6 +54,10 @@
#include "utilities/events.hpp"
#include "utilities/vmError.hpp"
+// See stubGenerator_zero.cpp
+#include <setjmp.h>
+extern sigjmp_buf* get_jmp_buf_for_continuation();
+
address os::current_stack_pointer() {
address dummy = (address) &dummy;
return dummy;
@@ -125,6 +129,14 @@
SignalHandlerMark shm(t);
+ // handle SafeFetch faults
+ if (sig == SIGSEGV || sig == SIGBUS) {
+ sigjmp_buf* const pjb = get_jmp_buf_for_continuation();
+ if (pjb) {
+ siglongjmp(*pjb, 1);
+ }
+ }
+
// Note: it's not uncommon that JNI code uses signal/sigset to
// install then restore certain signal handler (e.g. to temporarily
// block SIGPIPE, or have a SIGILL handler when detecting CPU
--- a/hotspot/src/share/vm/runtime/stubRoutines.hpp Tue Mar 31 02:11:09 2015 +0000
+++ b/hotspot/src/share/vm/runtime/stubRoutines.hpp Tue Mar 31 05:30:36 2015 -0400
@@ -451,14 +451,10 @@
// returns true if SafeFetch32 and SafeFetchN can be used safely (stubroutines are already generated)
inline bool CanUseSafeFetch32() {
- // All platforms have the stub but ZERO isn't safe.
- assert(StubRoutines::SafeFetch32_stub() != NULL, "should have generated stub");
- return NOT_ZERO(true) ZERO_ONLY(false);
+ return StubRoutines::SafeFetch32_stub() ? true : false;
}
-inline bool CanUseSafeFetchN() {
- // All platforms have the stub but ZERO isn't safe.
- assert(StubRoutines::SafeFetchN_stub() != NULL, "should have generated stub");
- return NOT_ZERO(true) ZERO_ONLY(false);
+inline bool CanUseSafeFetchN() {
+ return StubRoutines::SafeFetchN_stub() ? true : false;
}
#endif // SHARE_VM_RUNTIME_STUBROUTINES_HPP