This commit is contained in:
Christian Tornqvist 2016-05-04 23:45:10 +00:00
commit e24c1e8bbd
17 changed files with 233 additions and 54 deletions

View File

@ -825,6 +825,17 @@ class StubGenerator: public StubCodeGenerator {
return start;
}
// The following routine generates a subroutine to throw an asynchronous
// UnknownError when an unsafe access gets a fault that could not be
// reasonably prevented by the programmer. (Example: SIGBUS/OBJERR.)
//
address generate_handler_for_unsafe_access() {
StubCodeMark mark(this, "StubRoutines", "handler_for_unsafe_access");
address start = __ function_entry();
__ unimplemented("StubRoutines::handler_for_unsafe_access", 93);
return start;
}
#if !defined(PRODUCT)
// Wrapper which calls oopDesc::is_oop_or_null()
// Only called by MacroAssembler::verify_oop
@ -3100,6 +3111,8 @@ class StubGenerator: public StubCodeGenerator {
StubRoutines::_throw_IncompatibleClassChangeError_entry= generate_throw_exception("IncompatibleClassChangeError throw_exception", CAST_FROM_FN_PTR(address, SharedRuntime::throw_IncompatibleClassChangeError), false);
StubRoutines::_throw_NullPointerException_at_call_entry= generate_throw_exception("NullPointerException at call throw_exception", CAST_FROM_FN_PTR(address, SharedRuntime::throw_NullPointerException_at_call), false);
StubRoutines::_handler_for_unsafe_access_entry = generate_handler_for_unsafe_access();
// support for verify_oop (must happen after universe_init)
StubRoutines::_verify_oop_subroutine_entry = generate_verify_oop();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -64,6 +64,20 @@ static const Register& Lstub_temp = L2;
// -------------------------------------------------------------------------------------------------------------------------
// Stub Code definitions
static address handle_unsafe_access() {
JavaThread* thread = JavaThread::current();
address pc = thread->saved_exception_pc();
address npc = thread->saved_exception_npc();
// pc is the instruction which we must emulate
// doing a no-op is fine: return garbage from the load
// request an async exception
thread->set_pending_unsafe_access_error();
// return address of next instruction to execute
return npc;
}
class StubGenerator: public StubCodeGenerator {
private:
@ -732,6 +746,62 @@ class StubGenerator: public StubCodeGenerator {
Label _atomic_add_stub; // called from other stubs
//------------------------------------------------------------------------------------------------------------------------
// The following routine generates a subroutine to throw an asynchronous
// UnknownError when an unsafe access gets a fault that could not be
// reasonably prevented by the programmer. (Example: SIGBUS/OBJERR.)
//
// Arguments :
//
// trapping PC: O7
//
// Results:
// posts an asynchronous exception, skips the trapping instruction
//
address generate_handler_for_unsafe_access() {
StubCodeMark mark(this, "StubRoutines", "handler_for_unsafe_access");
address start = __ pc();
const int preserve_register_words = (64 * 2);
Address preserve_addr(FP, (-preserve_register_words * wordSize) + STACK_BIAS);
Register Lthread = L7_thread_cache;
int i;
__ save_frame(0);
__ mov(G1, L1);
__ mov(G2, L2);
__ mov(G3, L3);
__ mov(G4, L4);
__ mov(G5, L5);
for (i = 0; i < 64; i += 2) {
__ stf(FloatRegisterImpl::D, as_FloatRegister(i), preserve_addr, i * wordSize);
}
address entry_point = CAST_FROM_FN_PTR(address, handle_unsafe_access);
BLOCK_COMMENT("call handle_unsafe_access");
__ call(entry_point, relocInfo::runtime_call_type);
__ delayed()->nop();
__ mov(L1, G1);
__ mov(L2, G2);
__ mov(L3, G3);
__ mov(L4, G4);
__ mov(L5, G5);
for (i = 0; i < 64; i += 2) {
__ ldf(FloatRegisterImpl::D, preserve_addr, as_FloatRegister(i), i * wordSize);
}
__ verify_thread();
__ jmp(O0, 0);
__ delayed()->restore();
return start;
}
// Support for uint StubRoutine::Sparc::partial_subtype_check( Klass sub, Klass super );
// Arguments :
//
@ -5310,6 +5380,9 @@ class StubGenerator: public StubCodeGenerator {
StubRoutines::_throw_IncompatibleClassChangeError_entry= generate_throw_exception("IncompatibleClassChangeError throw_exception", CAST_FROM_FN_PTR(address, SharedRuntime::throw_IncompatibleClassChangeError));
StubRoutines::_throw_NullPointerException_at_call_entry= generate_throw_exception("NullPointerException at call throw_exception", CAST_FROM_FN_PTR(address, SharedRuntime::throw_NullPointerException_at_call));
StubRoutines::_handler_for_unsafe_access_entry =
generate_handler_for_unsafe_access();
// support for verify_oop (must happen after universe_init)
StubRoutines::_verify_oop_subroutine_entry = generate_verify_oop_subroutine();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -63,6 +63,21 @@ const int FPU_CNTRL_WRD_MASK = 0xFFFF;
// -------------------------------------------------------------------------------------------------------------------------
// Stub Code definitions
static address handle_unsafe_access() {
JavaThread* thread = JavaThread::current();
address pc = thread->saved_exception_pc();
// pc is the instruction which we must emulate
// doing a no-op is fine: return garbage from the load
// therefore, compute npc
address npc = Assembler::locate_next_instruction(pc);
// request an async exception
thread->set_pending_unsafe_access_error();
// return address of next instruction to execute
return npc;
}
class StubGenerator: public StubCodeGenerator {
private:
@ -608,6 +623,27 @@ class StubGenerator: public StubCodeGenerator {
}
//---------------------------------------------------------------------------
// The following routine generates a subroutine to throw an asynchronous
// UnknownError when an unsafe access gets a fault that could not be
// reasonably prevented by the programmer. (Example: SIGBUS/OBJERR.)
address generate_handler_for_unsafe_access() {
StubCodeMark mark(this, "StubRoutines", "handler_for_unsafe_access");
address start = __ pc();
__ push(0); // hole for return address-to-be
__ pusha(); // push registers
Address next_pc(rsp, RegisterImpl::number_of_registers * BytesPerWord);
BLOCK_COMMENT("call handle_unsafe_access");
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, handle_unsafe_access)));
__ movptr(next_pc, rax); // stuff next address
__ popa();
__ ret(0); // jump to next address
return start;
}
//----------------------------------------------------------------------------------------------------
// Non-destructive plausibility checks for oops
@ -3829,6 +3865,9 @@ class StubGenerator: public StubCodeGenerator {
// These are currently used by Solaris/Intel
StubRoutines::_atomic_xchg_entry = generate_atomic_xchg();
StubRoutines::_handler_for_unsafe_access_entry =
generate_handler_for_unsafe_access();
// platform dependent
create_control_words();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -61,6 +61,21 @@ const int MXCSR_MASK = 0xFFC0; // Mask out any pending exceptions
// Stub Code definitions
static address handle_unsafe_access() {
JavaThread* thread = JavaThread::current();
address pc = thread->saved_exception_pc();
// pc is the instruction which we must emulate
// doing a no-op is fine: return garbage from the load
// therefore, compute npc
address npc = Assembler::locate_next_instruction(pc);
// request an async exception
thread->set_pending_unsafe_access_error();
// return address of next instruction to execute
return npc;
}
class StubGenerator: public StubCodeGenerator {
private:
@ -974,6 +989,32 @@ class StubGenerator: public StubCodeGenerator {
return start;
}
// The following routine generates a subroutine to throw an
// asynchronous UnknownError when an unsafe access gets a fault that
// could not be reasonably prevented by the programmer. (Example:
// SIGBUS/OBJERR.)
address generate_handler_for_unsafe_access() {
StubCodeMark mark(this, "StubRoutines", "handler_for_unsafe_access");
address start = __ pc();
__ push(0); // hole for return address-to-be
__ pusha(); // push registers
Address next_pc(rsp, RegisterImpl::number_of_registers * BytesPerWord);
// FIXME: this probably needs alignment logic
__ subptr(rsp, frame::arg_reg_save_area_bytes);
BLOCK_COMMENT("call handle_unsafe_access");
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, handle_unsafe_access)));
__ addptr(rsp, frame::arg_reg_save_area_bytes);
__ movptr(next_pc, rax); // stuff next address
__ popa();
__ ret(0); // jump to next address
return start;
}
// Non-destructive plausibility checks for oops
//
// Arguments:
@ -5095,6 +5136,9 @@ class StubGenerator: public StubCodeGenerator {
StubRoutines::_atomic_add_ptr_entry = generate_atomic_add_ptr();
StubRoutines::_fence_entry = generate_orderaccess_fence();
StubRoutines::_handler_for_unsafe_access_entry =
generate_handler_for_unsafe_access();
// platform dependent
StubRoutines::x86::_get_previous_fp_entry = generate_get_previous_fp();
StubRoutines::x86::_get_previous_sp_entry = generate_get_previous_sp();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright 2007, 2008, 2010, 2015 Red Hat, Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -261,6 +261,10 @@ class StubGenerator: public StubCodeGenerator {
StubRoutines::_atomic_add_entry = ShouldNotCallThisStub();
StubRoutines::_atomic_add_ptr_entry = ShouldNotCallThisStub();
StubRoutines::_fence_entry = ShouldNotCallThisStub();
// amd64 does this here, sparc does it in generate_all()
StubRoutines::_handler_for_unsafe_access_entry =
ShouldNotCallThisStub();
}
void generate_all() {

View File

@ -392,9 +392,11 @@ JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrec
CodeBlob* cb = CodeCache::find_blob_unsafe(pc);
CompiledMethod* nm = cb->as_compiled_method_or_null();
if (nm != NULL && nm->has_unsafe_access()) {
address next_pc = pc + 4;
next_pc = SharedRuntime::handle_unsafe_access(thread, next_pc);
os::Aix::ucontext_set_pc(uc, next_pc);
// We don't really need a stub here! Just set the pending exeption and
// continue at the next instruction after the faulting read. Returning
// garbage from this read is ok.
thread->set_pending_unsafe_access_error();
os::Aix::ucontext_set_pc(uc, pc + 4);
return 1;
}
}
@ -413,9 +415,11 @@ JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrec
}
else if (thread->thread_state() == _thread_in_vm &&
sig == SIGBUS && thread->doing_unsafe_access()) {
address next_pc = pc + 4;
next_pc = SharedRuntime::handle_unsafe_access(thread, next_pc);
os::Aix::ucontext_set_pc(uc, next_pc);
// We don't really need a stub here! Just set the pending exeption and
// continue at the next instruction after the faulting read. Returning
// garbage from this read is ok.
thread->set_pending_unsafe_access_error();
os::Aix::ucontext_set_pc(uc, pc + 4);
return 1;
}
}

View File

@ -584,8 +584,7 @@ JVM_handle_bsd_signal(int sig,
CodeBlob* cb = CodeCache::find_blob_unsafe(pc);
CompiledMethod* nm = (cb != NULL) ? cb->as_compiled_method_or_null() : NULL;
if (nm != NULL && nm->has_unsafe_access()) {
address next_pc = Assembler::locate_next_instruction(pc);
stub = SharedRuntime::handle_unsafe_access(thread, next_pc);
stub = StubRoutines::handler_for_unsafe_access();
}
}
else
@ -656,8 +655,7 @@ JVM_handle_bsd_signal(int sig,
} else if (thread->thread_state() == _thread_in_vm &&
sig == SIGBUS && /* info->si_code == BUS_OBJERR && */
thread->doing_unsafe_access()) {
address next_pc = Assembler::locate_next_instruction(pc);
stub = SharedRuntime::handle_unsafe_access(thread, next_pc);
stub = StubRoutines::handler_for_unsafe_access();
}
// jni_fast_Get<Primitive>Field can trap at certain pc's if a GC kicks in

View File

@ -226,6 +226,23 @@ extern "C" void FetchNPFI () ;
extern "C" void FetchNResume () ;
#endif
// An operation in Unsafe has faulted. We're going to return to the
// instruction after the faulting load or store. We also set
// pending_unsafe_access_error so that at some point in the future our
// user will get a helpful message.
static address handle_unsafe_access(JavaThread* thread, address pc) {
// pc is the instruction which we must emulate
// doing a no-op is fine: return garbage from the load
// therefore, compute npc
address npc = pc + NativeCall::instruction_size;
// request an async exception
thread->set_pending_unsafe_access_error();
// return address of next instruction to execute
return npc;
}
extern "C" JNIEXPORT int
JVM_handle_linux_signal(int sig,
siginfo_t* info,
@ -370,8 +387,7 @@ JVM_handle_linux_signal(int sig,
CodeBlob* cb = CodeCache::find_blob_unsafe(pc);
CompiledMethod* nm = (cb != NULL) ? cb->as_compiled_method_or_null() : NULL;
if (nm != NULL && nm->has_unsafe_access()) {
address next_pc = pc + NativeCall::instruction_size;
stub = SharedRuntime::handle_unsafe_access(thread, next_pc);
stub = handle_unsafe_access(thread, pc);
}
}
else
@ -392,8 +408,7 @@ JVM_handle_linux_signal(int sig,
} else if (thread->thread_state() == _thread_in_vm &&
sig == SIGBUS && /* info->si_code == BUS_OBJERR && */
thread->doing_unsafe_access()) {
address next_pc = pc + NativeCall::instruction_size;
stub = SharedRuntime::handle_unsafe_access(thread, next_pc);
stub = handle_unsafe_access(thread, pc);
}
// jni_fast_Get<Primitive>Field can trap at certain pc's if a GC kicks in

View File

@ -366,9 +366,11 @@ JVM_handle_linux_signal(int sig,
CodeBlob* cb = CodeCache::find_blob_unsafe(pc);
CompiledMethod* nm = (cb != NULL) ? cb->as_compiled_method_or_null() : NULL;
if (nm != NULL && nm->has_unsafe_access()) {
address next_pc = pc + 4;
next_pc = SharedRuntime::handle_unsafe_access(thread, next_pc);
os::Linux::ucontext_set_pc(uc, next_pc);
// We don't really need a stub here! Just set the pending exeption and
// continue at the next instruction after the faulting read. Returning
// garbage from this read is ok.
thread->set_pending_unsafe_access_error();
os::Linux::ucontext_set_pc(uc, pc + 4);
return true;
}
}
@ -383,8 +385,10 @@ JVM_handle_linux_signal(int sig,
}
else if (thread->thread_state() == _thread_in_vm &&
sig == SIGBUS && thread->doing_unsafe_access()) {
address next_pc = pc + 4;
next_pc = SharedRuntime::handle_unsafe_access(thread, next_pc);
// We don't really need a stub here! Just set the pending exeption and
// continue at the next instruction after the faulting read. Returning
// garbage from this read is ok.
thread->set_pending_unsafe_access_error();
os::Linux::ucontext_set_pc(uc, pc + 4);
return true;
}

View File

@ -433,14 +433,14 @@ inline static bool checkPollingPage(address pc, address fault, address* stub) {
return false;
}
inline static bool checkByteBuffer(address pc, address npc, address* stub) {
inline static bool checkByteBuffer(address pc, address* stub) {
// BugId 4454115: A read from a MappedByteBuffer can fault
// here if the underlying file has been truncated.
// Do not crash the VM in such a case.
CodeBlob* cb = CodeCache::find_blob_unsafe(pc);
CompiledMethod* nm = cb->as_compiled_method_or_null();
if (nm != NULL && nm->has_unsafe_access()) {
*stub = SharedRuntime::handle_unsafe_access(thread, npc);
*stub = StubRoutines::handler_for_unsafe_access();
return true;
}
return false;
@ -613,7 +613,7 @@ JVM_handle_linux_signal(int sig,
if (sig == SIGBUS &&
thread->thread_state() == _thread_in_vm &&
thread->doing_unsafe_access()) {
stub = SharedRuntime::handle_unsafe_access(thread, npc);
stub = StubRoutines::handler_for_unsafe_access();
}
if (thread->thread_state() == _thread_in_Java) {
@ -625,7 +625,7 @@ JVM_handle_linux_signal(int sig,
break;
}
if ((sig == SIGBUS) && checkByteBuffer(pc, npc, &stub)) {
if ((sig == SIGBUS) && checkByteBuffer(pc, &stub)) {
break;
}

View File

@ -420,8 +420,7 @@ JVM_handle_linux_signal(int sig,
CodeBlob* cb = CodeCache::find_blob_unsafe(pc);
CompiledMethod* nm = (cb != NULL) ? cb->as_compiled_method_or_null() : NULL;
if (nm != NULL && nm->has_unsafe_access()) {
address next_pc = Assembler::locate_next_instruction(pc);
stub = SharedRuntime::handle_unsafe_access(thread, next_pc);
stub = StubRoutines::handler_for_unsafe_access();
}
}
else
@ -470,8 +469,7 @@ JVM_handle_linux_signal(int sig,
} else if (thread->thread_state() == _thread_in_vm &&
sig == SIGBUS && /* info->si_code == BUS_OBJERR && */
thread->doing_unsafe_access()) {
address next_pc = Assembler::locate_next_instruction(pc);
stub = SharedRuntime::handle_unsafe_access(thread, next_pc);
stub = StubRoutines::handler_for_unsafe_access();
}
// jni_fast_Get<Primitive>Field can trap at certain pc's if a GC kicks in

View File

@ -441,7 +441,7 @@ JVM_handle_solaris_signal(int sig, siginfo_t* info, void* ucVoid,
if (thread->thread_state() == _thread_in_vm) {
if (sig == SIGBUS && info->si_code == BUS_OBJERR && thread->doing_unsafe_access()) {
stub = SharedRuntime::handle_unsafe_access(thread, npc);
stub = StubRoutines::handler_for_unsafe_access();
}
}
@ -480,7 +480,7 @@ JVM_handle_solaris_signal(int sig, siginfo_t* info, void* ucVoid,
CodeBlob* cb = CodeCache::find_blob_unsafe(pc);
CompiledMethod* nm = cb->as_compiled_method_or_null();
if (nm != NULL && nm->has_unsafe_access()) {
stub = SharedRuntime::handle_unsafe_access(thread, npc);
stub = StubRoutines::handler_for_unsafe_access();
}
}

View File

@ -503,8 +503,7 @@ JVM_handle_solaris_signal(int sig, siginfo_t* info, void* ucVoid,
if (thread->thread_state() == _thread_in_vm) {
if (sig == SIGBUS && info->si_code == BUS_OBJERR && thread->doing_unsafe_access()) {
address next_pc = Assembler::locate_next_instruction(pc);
stub = SharedRuntime::handle_unsafe_access(thread, next_pc);
stub = StubRoutines::handler_for_unsafe_access();
}
}
@ -521,8 +520,7 @@ JVM_handle_solaris_signal(int sig, siginfo_t* info, void* ucVoid,
if (cb != NULL) {
CompiledMethod* nm = cb->as_compiled_method_or_null();
if (nm != NULL && nm->has_unsafe_access()) {
address next_pc = Assembler::locate_next_instruction(pc);
stub = SharedRuntime::handle_unsafe_access(thread, next_pc);
stub = StubRoutines::handler_for_unsafe_access();
}
}
}

View File

@ -1762,21 +1762,6 @@ methodHandle SharedRuntime::reresolve_call_site(JavaThread *thread, TRAPS) {
return callee_method;
}
address SharedRuntime::handle_unsafe_access(JavaThread* thread, address next_pc) {
// The faulting unsafe accesses should be changed to throw the error
// synchronously instead. Meanwhile the faulting instruction will be
// skipped over (effectively turning it into a no-op) and an
// asynchronous exception will be raised which the thread will
// handle at a later point. If the instruction is a load it will
// return garbage.
// Request an async exception.
thread->set_pending_unsafe_access_error();
// Return address of next instruction to execute.
return next_pc;
}
#ifdef ASSERT
void SharedRuntime::check_member_name_argument_is_last_argument(const methodHandle& method,
const BasicType* sig_bt,

View File

@ -522,8 +522,6 @@ class SharedRuntime: AllStatic {
static address handle_wrong_method_abstract(JavaThread* thread);
static address handle_wrong_method_ic_miss(JavaThread* thread);
static address handle_unsafe_access(JavaThread* thread, address next_pc);
#ifndef PRODUCT
// Collect and print inline cache miss statistics

View File

@ -55,6 +55,7 @@ address StubRoutines::_throw_IncompatibleClassChangeError_entry = NULL;
address StubRoutines::_throw_NullPointerException_at_call_entry = NULL;
address StubRoutines::_throw_StackOverflowError_entry = NULL;
address StubRoutines::_throw_delayed_StackOverflowError_entry = NULL;
address StubRoutines::_handler_for_unsafe_access_entry = NULL;
jint StubRoutines::_verify_oop_count = 0;
address StubRoutines::_verify_oop_subroutine_entry = NULL;
address StubRoutines::_atomic_xchg_entry = NULL;

View File

@ -111,6 +111,7 @@ class StubRoutines: AllStatic {
static address _throw_NullPointerException_at_call_entry;
static address _throw_StackOverflowError_entry;
static address _throw_delayed_StackOverflowError_entry;
static address _handler_for_unsafe_access_entry;
static address _atomic_xchg_entry;
static address _atomic_xchg_ptr_entry;
@ -287,6 +288,10 @@ class StubRoutines: AllStatic {
static address throw_StackOverflowError_entry() { return _throw_StackOverflowError_entry; }
static address throw_delayed_StackOverflowError_entry() { return _throw_delayed_StackOverflowError_entry; }
// Exceptions during unsafe access - should throw Java exception rather
// than crash.
static address handler_for_unsafe_access() { return _handler_for_unsafe_access_entry; }
static address atomic_xchg_entry() { return _atomic_xchg_entry; }
static address atomic_xchg_ptr_entry() { return _atomic_xchg_ptr_entry; }
static address atomic_store_entry() { return _atomic_store_entry; }