mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-12 03:18:37 +00:00
8328306: AArch64: MacOS lazy JIT "write xor execute" switching
Co-authored-by: Dean Long <dlong@openjdk.org> Reviewed-by: dlong, adinn
This commit is contained in:
parent
d1b226dec2
commit
77e8469fb0
@ -4329,6 +4329,7 @@ public:
|
||||
#undef INSN
|
||||
|
||||
Assembler(CodeBuffer* code) : AbstractAssembler(code) {
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
}
|
||||
|
||||
// Stack overflow checking
|
||||
|
||||
@ -209,6 +209,10 @@ void BarrierSetNMethod::set_guard_value(nmethod* nm, int value, int bit_mask) {
|
||||
bs_asm->increment_patching_epoch();
|
||||
}
|
||||
|
||||
// Enable WXWrite: the function is called directly from nmethod_entry_barrier
|
||||
// stub.
|
||||
MACOS_AARCH64_ONLY(ThreadWXEnable wx(WXWrite, Thread::current()));
|
||||
|
||||
NativeNMethodBarrier barrier(nm);
|
||||
barrier.set_value(value, bit_mask);
|
||||
}
|
||||
|
||||
@ -473,6 +473,7 @@ address MacroAssembler::target_addr_for_insn(address insn_addr) {
|
||||
// Patch any kind of instruction; there may be several instructions.
|
||||
// Return the total length (in bytes) of the instructions.
|
||||
int MacroAssembler::pd_patch_instruction_size(address insn_addr, address target) {
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
return RelocActions<Patcher>::run(insn_addr, target);
|
||||
}
|
||||
|
||||
@ -481,6 +482,8 @@ int MacroAssembler::patch_oop(address insn_addr, address o) {
|
||||
unsigned insn = *(unsigned*)insn_addr;
|
||||
assert(nativeInstruction_at(insn_addr+4)->is_movk(), "wrong insns in patch");
|
||||
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
|
||||
// OOPs are either narrow (32 bits) or wide (48 bits). We encode
|
||||
// narrow OOPs by setting the upper 16 bits in the first
|
||||
// instruction.
|
||||
@ -510,6 +513,8 @@ int MacroAssembler::patch_narrow_klass(address insn_addr, narrowKlass n) {
|
||||
assert(Instruction_aarch64::extract(insn->encoding(), 31, 21) == 0b11010010101 &&
|
||||
nativeInstruction_at(insn_addr+4)->is_movk(), "wrong insns in patch");
|
||||
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
|
||||
Instruction_aarch64::patch(insn_addr, 20, 5, n >> 16);
|
||||
Instruction_aarch64::patch(insn_addr+4, 20, 5, n & 0xffff);
|
||||
return 2 * NativeInstruction::instruction_size;
|
||||
|
||||
@ -133,7 +133,6 @@ void NativeMovConstReg::verify() {
|
||||
|
||||
|
||||
intptr_t NativeMovConstReg::data() const {
|
||||
// das(uint64_t(instruction_address()),2);
|
||||
address addr = MacroAssembler::target_addr_for_insn(instruction_address());
|
||||
if (maybe_cpool_ref(instruction_address())) {
|
||||
return *(intptr_t*)addr;
|
||||
@ -144,6 +143,7 @@ intptr_t NativeMovConstReg::data() const {
|
||||
|
||||
void NativeMovConstReg::set_data(intptr_t x) {
|
||||
if (maybe_cpool_ref(instruction_address())) {
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
address addr = MacroAssembler::target_addr_for_insn(instruction_address());
|
||||
*(intptr_t*)addr = x;
|
||||
} else {
|
||||
@ -350,8 +350,6 @@ bool NativeInstruction::is_stop() {
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
void NativeGeneralJump::verify() { }
|
||||
|
||||
// MT-safe patching of a long jump instruction.
|
||||
void NativeGeneralJump::replace_mt_safe(address instr_addr, address code_buffer) {
|
||||
ShouldNotCallThis();
|
||||
|
||||
@ -90,16 +90,18 @@ protected:
|
||||
|
||||
s_char sbyte_at(int offset) const { return *(s_char*)addr_at(offset); }
|
||||
u_char ubyte_at(int offset) const { return *(u_char*)addr_at(offset); }
|
||||
jint int_at(int offset) const { return *(jint*)addr_at(offset); }
|
||||
juint uint_at(int offset) const { return *(juint*)addr_at(offset); }
|
||||
address ptr_at(int offset) const { return *(address*)addr_at(offset); }
|
||||
oop oop_at(int offset) const { return *(oop*)addr_at(offset); }
|
||||
jint int_at(int offset) const { return *(jint*)addr_at(offset); }
|
||||
juint uint_at(int offset) const { return *(juint*)addr_at(offset); }
|
||||
address ptr_at(int offset) const { return *(address*)addr_at(offset); }
|
||||
oop oop_at(int offset) const { return *(oop*)addr_at(offset); }
|
||||
|
||||
void set_char_at(int offset, char c) { *addr_at(offset) = (u_char)c; }
|
||||
void set_int_at(int offset, jint i) { *(jint*)addr_at(offset) = i; }
|
||||
void set_uint_at(int offset, jint i) { *(juint*)addr_at(offset) = i; }
|
||||
void set_ptr_at(int offset, address ptr) { *(address*)addr_at(offset) = ptr; }
|
||||
void set_oop_at(int offset, oop o) { *(oop*)addr_at(offset) = o; }
|
||||
#define MACOS_WX_WRITE MACOS_AARCH64_ONLY(os::thread_wx_enable_write())
|
||||
void set_char_at(int offset, char c) { MACOS_WX_WRITE; *addr_at(offset) = (u_char)c; }
|
||||
void set_int_at(int offset, jint i) { MACOS_WX_WRITE; *(jint*)addr_at(offset) = i; }
|
||||
void set_uint_at(int offset, jint i) { MACOS_WX_WRITE; *(juint*)addr_at(offset) = i; }
|
||||
void set_ptr_at(int offset, address ptr) { MACOS_WX_WRITE; *(address*)addr_at(offset) = ptr; }
|
||||
void set_oop_at(int offset, oop o) { MACOS_WX_WRITE; *(oop*)addr_at(offset) = o; }
|
||||
#undef MACOS_WX_WRITE
|
||||
|
||||
void wrote(int offset);
|
||||
|
||||
@ -380,7 +382,6 @@ public:
|
||||
void set_jump_destination(address dest);
|
||||
|
||||
static void replace_mt_safe(address instr_addr, address code_buffer);
|
||||
static void verify();
|
||||
};
|
||||
|
||||
inline NativeGeneralJump* nativeGeneralJump_at(address address) {
|
||||
|
||||
@ -11742,7 +11742,9 @@ class StubGenerator: public StubCodeGenerator {
|
||||
}
|
||||
#endif
|
||||
|
||||
StubRoutines::_unsafe_setmemory = generate_unsafe_setmemory();
|
||||
if (vmIntrinsics::is_intrinsic_available(vmIntrinsics::_setMemory)) {
|
||||
StubRoutines::_unsafe_setmemory = generate_unsafe_setmemory();
|
||||
}
|
||||
|
||||
StubRoutines::aarch64::set_completed(); // Inidicate that arraycopy and zero_blocks stubs are generated
|
||||
}
|
||||
|
||||
@ -622,6 +622,22 @@ void VM_Version::initialize() {
|
||||
|
||||
check_virtualizations();
|
||||
|
||||
#ifdef __APPLE__
|
||||
DefaultWXWriteMode = UseOldWX ? WXWrite : WXArmedForWrite;
|
||||
|
||||
if (TraceWXHealing) {
|
||||
if (pthread_jit_write_protect_supported_np()) {
|
||||
tty->print_cr("### TraceWXHealing is in use");
|
||||
if (StressWXHealing) {
|
||||
tty->print_cr("### StressWXHealing is in use");
|
||||
}
|
||||
} else {
|
||||
tty->print_cr("WX Healing is not in use because MAP_JIT write protection "
|
||||
"does not work on this system.");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Sync SVE related CPU features with flags
|
||||
if (UseSVE < 2) {
|
||||
clear_feature(CPU_SVE2);
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
//
|
||||
// Declare Bsd specific flags. They are not available on other platforms.
|
||||
//
|
||||
#ifdef AARCH64
|
||||
#define RUNTIME_OS_FLAGS(develop, \
|
||||
develop_pd, \
|
||||
product, \
|
||||
@ -35,9 +36,21 @@
|
||||
range, \
|
||||
constraint) \
|
||||
\
|
||||
AARCH64_ONLY(develop(bool, AssertWXAtThreadSync, true, \
|
||||
"Conservatively check W^X thread state at possible safepoint" \
|
||||
"or handshake"))
|
||||
develop(bool, TraceWXHealing, false, \
|
||||
"track occurrences of W^X mode healing") \
|
||||
develop(bool, UseOldWX, false, \
|
||||
"Choose old W^X implementation.") \
|
||||
product(bool, StressWXHealing, false, DIAGNOSTIC, \
|
||||
"Stress W xor X healing on MacOS")
|
||||
|
||||
#else
|
||||
#define RUNTIME_OS_FLAGS(develop, \
|
||||
develop_pd, \
|
||||
product, \
|
||||
product_pd, \
|
||||
range, \
|
||||
constraint)
|
||||
#endif
|
||||
|
||||
// end of RUNTIME_OS_FLAGS
|
||||
|
||||
|
||||
@ -841,6 +841,7 @@ jlong os::javaTimeNanos() {
|
||||
// We might also condition (c) on the magnitude of the delta between obsv and now.
|
||||
// Avoiding excessive CAS operations to hot RW locations is critical.
|
||||
// See https://blogs.oracle.com/dave/entry/cas_and_cache_trivia_invalidate
|
||||
// https://web.archive.org/web/20131214182431/https://blogs.oracle.com/dave/entry/cas_and_cache_trivia_invalidate
|
||||
return (prev == obsv) ? now : obsv;
|
||||
}
|
||||
|
||||
|
||||
@ -54,8 +54,11 @@
|
||||
#include "signals_posix.hpp"
|
||||
#include "utilities/align.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
#include "utilities/decoder.hpp"
|
||||
#include "utilities/events.hpp"
|
||||
#include "utilities/nativeStackPrinter.hpp"
|
||||
#include "utilities/vmError.hpp"
|
||||
#include "compiler/disassembler.hpp"
|
||||
|
||||
// put OS-includes here
|
||||
# include <sys/types.h>
|
||||
@ -85,6 +88,8 @@
|
||||
#define SPELL_REG_SP "sp"
|
||||
|
||||
#ifdef __APPLE__
|
||||
WXMode DefaultWXWriteMode;
|
||||
|
||||
// see darwin-xnu/osfmk/mach/arm/_structs.h
|
||||
|
||||
// 10.5 UNIX03 member name prefixes
|
||||
@ -233,19 +238,56 @@ NOINLINE frame os::current_frame() {
|
||||
|
||||
bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info,
|
||||
ucontext_t* uc, JavaThread* thread) {
|
||||
// Enable WXWrite: this function is called by the signal handler at arbitrary
|
||||
// point of execution.
|
||||
ThreadWXEnable wx(WXWrite, thread);
|
||||
|
||||
// decide if this trap can be handled by a stub
|
||||
address stub = nullptr;
|
||||
|
||||
address pc = nullptr;
|
||||
address pc = nullptr;
|
||||
|
||||
//%note os_trap_1
|
||||
if (info != nullptr && uc != nullptr && thread != nullptr) {
|
||||
pc = (address) os::Posix::ucontext_get_pc(uc);
|
||||
|
||||
#ifdef MACOS_AARCH64
|
||||
// If we got a SIGBUS because we tried to write into the code
|
||||
// cache, try enabling WXWrite mode.
|
||||
if (sig == SIGBUS
|
||||
&& pc != info->si_addr
|
||||
&& CodeCache::contains(info->si_addr)
|
||||
&& os::address_is_in_vm(pc)) {
|
||||
WXMode *entry_mode = thread->_cur_wx_mode;
|
||||
if (entry_mode != nullptr && *entry_mode == WXArmedForWrite) {
|
||||
if (TraceWXHealing) {
|
||||
static const char *mode_names[3] = {"WXWrite", "WXExec", "WXArmedForWrite"};
|
||||
tty->print("Healing WXMode %s at %p to WXWrite",
|
||||
mode_names[*entry_mode], entry_mode);
|
||||
char name[128];
|
||||
int offset = 0;
|
||||
if (os::dll_address_to_function_name(pc, name, sizeof name, &offset)) {
|
||||
tty->print_cr(" (%s+0x%x)", name, offset);
|
||||
} else {
|
||||
tty->cr();
|
||||
}
|
||||
if (Verbose) {
|
||||
char buf[O_BUFLEN];
|
||||
NativeStackPrinter nsp(thread);
|
||||
nsp.print_stack(tty, buf, sizeof(buf), pc,
|
||||
true /* print_source_info */, -1 /* max stack */);
|
||||
}
|
||||
}
|
||||
#ifndef PRODUCT
|
||||
guarantee(StressWXHealing,
|
||||
"We should not reach here unless StressWXHealing");
|
||||
#endif
|
||||
*(thread->_cur_wx_mode) = WXWrite;
|
||||
return thread->wx_enable_write();
|
||||
}
|
||||
}
|
||||
|
||||
// There may be cases where code after this point that we call
|
||||
// from the signal handler changes WX state, so we protect against
|
||||
// that by saving and restoring the state.
|
||||
ThreadWXEnable wx(thread->get_wx_state(), thread);
|
||||
#endif
|
||||
|
||||
// Handle ALL stack overflow variations here
|
||||
if (sig == SIGSEGV || sig == SIGBUS) {
|
||||
address addr = (address) info->si_addr;
|
||||
@ -515,11 +557,42 @@ int os::extra_bang_size_in_bytes() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
#ifdef MACOS_AARCH64
|
||||
THREAD_LOCAL bool os::_jit_exec_enabled;
|
||||
|
||||
// This is a wrapper around the standard library function
|
||||
// pthread_jit_write_protect_np(3). We keep track of the state of
|
||||
// per-thread write protection on the MAP_JIT region in the
|
||||
// thread-local variable os::_jit_exec_enabled
|
||||
void os::current_thread_enable_wx(WXMode mode) {
|
||||
pthread_jit_write_protect_np(mode == WXExec);
|
||||
bool exec_enabled = mode != WXWrite;
|
||||
if (exec_enabled != _jit_exec_enabled NOT_PRODUCT( || DefaultWXWriteMode == WXWrite)) {
|
||||
permit_forbidden_function::pthread_jit_write_protect_np(exec_enabled);
|
||||
_jit_exec_enabled = exec_enabled;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// If the current thread is in the WX state WXArmedForWrite, change
|
||||
// the state to WXWrite.
|
||||
bool Thread::wx_enable_write() {
|
||||
if (_wx_state == WXArmedForWrite) {
|
||||
_wx_state = WXWrite;
|
||||
os::current_thread_enable_wx(WXWrite);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// A wrapper around wx_enable_write() for when the current thread is
|
||||
// not known.
|
||||
void os::thread_wx_enable_write_impl() {
|
||||
if (!StressWXHealing) {
|
||||
Thread::current()->wx_enable_write();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MACOS_AARCH64
|
||||
|
||||
static inline void atomic_copy64(const volatile void *src, volatile void *dst) {
|
||||
*(jlong *) dst = *(const jlong *) src;
|
||||
|
||||
@ -98,6 +98,8 @@ CodeBuffer::CodeBuffer(const CodeBlob* blob) DEBUG_ONLY(: Scrubber(this, sizeof(
|
||||
}
|
||||
|
||||
void CodeBuffer::initialize(csize_t code_size, csize_t locs_size) {
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
|
||||
// Always allow for empty slop around each section.
|
||||
int slop = (int) CodeSection::end_slop();
|
||||
|
||||
|
||||
@ -541,6 +541,7 @@ extern void vm_exit(int code);
|
||||
// unpack_with_exception entry instead. This makes life for the exception blob easier
|
||||
// because making that same check and diverting is painful from assembly language.
|
||||
JRT_ENTRY_NO_ASYNC(static address, exception_handler_for_pc_helper(JavaThread* current, oopDesc* ex, address pc, nmethod*& nm))
|
||||
MACOS_AARCH64_ONLY(current->wx_enable_write());
|
||||
Handle exception(current, ex);
|
||||
|
||||
// This function is called when we are about to throw an exception. Therefore,
|
||||
|
||||
@ -127,6 +127,7 @@ PerfCounter* ClassLoader::_perf_ik_link_methods_count = nullptr;
|
||||
PerfCounter* ClassLoader::_perf_method_adapters_count = nullptr;
|
||||
PerfCounter* ClassLoader::_unsafe_defineClassCallCounter = nullptr;
|
||||
PerfCounter* ClassLoader::_perf_secondary_hash_time = nullptr;
|
||||
PerfCounter* ClassLoader::_perf_change_wx_time = nullptr;
|
||||
|
||||
PerfCounter* ClassLoader::_perf_resolve_indy_time = nullptr;
|
||||
PerfCounter* ClassLoader::_perf_resolve_invokehandle_time = nullptr;
|
||||
@ -1370,6 +1371,7 @@ void ClassLoader::initialize(TRAPS) {
|
||||
NEWPERFBYTECOUNTER(_perf_sys_classfile_bytes_read, SUN_CLS, "sysClassBytes");
|
||||
NEWPERFEVENTCOUNTER(_unsafe_defineClassCallCounter, SUN_CLS, "unsafeDefineClassCalls");
|
||||
NEWPERFTICKCOUNTER(_perf_secondary_hash_time, SUN_CLS, "secondarySuperHashTime");
|
||||
NEWPERFTICKCOUNTER(_perf_change_wx_time, SUN_CLS, "changeWXTime");
|
||||
|
||||
if (log_is_enabled(Info, perf, class, link)) {
|
||||
NEWPERFTICKCOUNTER(_perf_ik_link_methods_time, SUN_CLS, "linkMethodsTime");
|
||||
|
||||
@ -184,6 +184,7 @@ class ClassLoader: AllStatic {
|
||||
|
||||
// Count the time taken to hash the scondary superclass arrays.
|
||||
static PerfCounter* _perf_secondary_hash_time;
|
||||
static PerfCounter* _perf_change_wx_time;
|
||||
|
||||
// The boot class path consists of 3 ordered pieces:
|
||||
// 1. the module/path pairs specified to --patch-module
|
||||
@ -268,6 +269,9 @@ class ClassLoader: AllStatic {
|
||||
static PerfCounter* perf_secondary_hash_time() {
|
||||
return _perf_secondary_hash_time;
|
||||
}
|
||||
static PerfCounter* perf_change_wx_time() {
|
||||
return _perf_change_wx_time;
|
||||
}
|
||||
static PerfCounter* perf_sys_classload_time() { return _perf_sys_classload_time; }
|
||||
static PerfCounter* perf_app_classload_time() { return _perf_app_classload_time; }
|
||||
static PerfCounter* perf_app_classload_selftime() { return _perf_app_classload_selftime; }
|
||||
|
||||
@ -520,6 +520,8 @@ VtableBlob* VtableBlob::create(const char* name, int buffer_size) {
|
||||
// eventually.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
blob = new (size) VtableBlob(name, size);
|
||||
CodeCache_lock->unlock();
|
||||
}
|
||||
|
||||
@ -2137,6 +2137,9 @@ void nmethod::make_deoptimized() {
|
||||
ResourceMark rm;
|
||||
RelocIterator iter(this, oops_reloc_begin());
|
||||
|
||||
// Assume there will be some calls to make deoptimized.
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
|
||||
while (iter.next()) {
|
||||
|
||||
switch (iter.type()) {
|
||||
@ -2213,6 +2216,7 @@ void nmethod::verify_clean_inline_caches() {
|
||||
}
|
||||
|
||||
void nmethod::mark_as_maybe_on_stack() {
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
AtomicAccess::store(&_gc_epoch, CodeCache::gc_epoch());
|
||||
}
|
||||
|
||||
@ -2305,6 +2309,8 @@ bool nmethod::make_not_entrant(InvalidationReason invalidation_reason) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
|
||||
{
|
||||
// Enter critical section. Does not block for safepoint.
|
||||
ConditionalMutexLocker ml(NMethodState_lock, !NMethodState_lock->owned_by_self(), Mutex::_no_safepoint_check_flag);
|
||||
@ -2740,6 +2746,8 @@ bool nmethod::is_unloading() {
|
||||
state_is_unloading = IsUnloadingBehaviour::is_unloading(this);
|
||||
uint8_t new_state = IsUnloadingState::create(state_is_unloading, state_unloading_cycle);
|
||||
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
|
||||
// Note that if an nmethod has dead oops, everyone will agree that the
|
||||
// nmethod is_unloading. However, the is_cold heuristics can yield
|
||||
// different outcomes, so we guard the computed result with a CAS
|
||||
|
||||
@ -51,6 +51,9 @@ VMReg VtableStub::_receiver_location = VMRegImpl::Bad();
|
||||
void* VtableStub::operator new(size_t size, int code_size) throw() {
|
||||
assert_lock_strong(VtableStubs_lock);
|
||||
assert(size == sizeof(VtableStub), "mismatched size");
|
||||
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
|
||||
// compute real VtableStub size (rounded to nearest word)
|
||||
const int real_size = align_up(code_size + (int)sizeof(VtableStub), wordSize);
|
||||
// malloc them in chunks to minimize header overhead
|
||||
|
||||
@ -111,6 +111,8 @@ bool BarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Enable WXWrite: the function is called directly from nmethod_entry_barrier
|
||||
// stub.
|
||||
MACOS_AARCH64_ONLY(ThreadWXEnable wx(WXWrite, Thread::current()));
|
||||
|
||||
// If the nmethod is the only thing pointing to the oops, and we are using a
|
||||
|
||||
@ -163,6 +163,7 @@ void CodeHeap::mark_segmap_as_used(size_t beg, size_t end, bool is_FreeBlock_joi
|
||||
|
||||
void CodeHeap::invalidate(size_t beg, size_t end, size_t hdr_size) {
|
||||
#ifndef PRODUCT
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
// Fill the given range with some bad value.
|
||||
// length is expected to be in segment_size units.
|
||||
// This prevents inadvertent execution of code leftover from previous use.
|
||||
@ -172,11 +173,13 @@ void CodeHeap::invalidate(size_t beg, size_t end, size_t hdr_size) {
|
||||
}
|
||||
|
||||
void CodeHeap::clear(size_t beg, size_t end) {
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
mark_segmap_as_free(beg, end);
|
||||
invalidate(beg, end, 0);
|
||||
}
|
||||
|
||||
void CodeHeap::clear() {
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
_next_segment = 0;
|
||||
clear(_next_segment, _number_of_committed_segments);
|
||||
}
|
||||
@ -190,6 +193,7 @@ static size_t align_to_page_size(size_t size) {
|
||||
|
||||
|
||||
bool CodeHeap::reserve(ReservedSpace rs, size_t committed_size, size_t segment_size) {
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
assert(rs.size() >= committed_size, "reserved < committed");
|
||||
assert(is_aligned(committed_size, rs.page_size()), "must be page aligned");
|
||||
assert(segment_size >= sizeof(FreeBlock), "segment size is too small");
|
||||
@ -230,6 +234,7 @@ bool CodeHeap::reserve(ReservedSpace rs, size_t committed_size, size_t segment_s
|
||||
|
||||
|
||||
bool CodeHeap::expand_by(size_t size) {
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
assert_locked_or_safepoint(CodeCache_lock);
|
||||
|
||||
// expand _memory space
|
||||
@ -259,6 +264,7 @@ bool CodeHeap::expand_by(size_t size) {
|
||||
|
||||
|
||||
void* CodeHeap::allocate(size_t instance_size) {
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
size_t number_of_segments = size_to_segments(instance_size + header_size());
|
||||
assert(segments_to_size(number_of_segments) >= sizeof(FreeBlock), "not enough room for FreeList");
|
||||
assert_locked_or_safepoint(CodeCache_lock);
|
||||
|
||||
@ -1865,6 +1865,8 @@ JRT_ENTRY_NO_ASYNC(address, OptoRuntime::handle_exception_C_helper(JavaThread* c
|
||||
// has updated oops.
|
||||
StackWatermarkSet::after_unwind(current);
|
||||
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
|
||||
// Do not confuse exception_oop with pending_exception. The exception_oop
|
||||
// is only used to pass arguments into the method. Not for general
|
||||
// exception handling. DO NOT CHANGE IT to use pending_exception, since
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
#include "runtime/interfaceSupport.inline.hpp"
|
||||
|
||||
JVM_ENTRY(static jboolean, UH_FreeUpcallStub0(JNIEnv *env, jobject _unused, jlong addr))
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
// safe to call 'find_blob' without code cache lock, because stub is always alive
|
||||
CodeBlob* cb = CodeCache::find_blob((char*)addr);
|
||||
if (cb == nullptr) {
|
||||
|
||||
@ -132,6 +132,7 @@ void DeoptimizationScope::mark(nmethod* nm, bool inc_recompile_counts) {
|
||||
return;
|
||||
}
|
||||
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
nmethod::DeoptimizationStatus status =
|
||||
inc_recompile_counts ? nmethod::deoptimize : nmethod::deoptimize_noupdate;
|
||||
AtomicAccess::store(&nm->_deoptimization_status, status);
|
||||
|
||||
@ -279,12 +279,14 @@ class VMNativeEntryWrapper {
|
||||
os::verify_stack_alignment(); \
|
||||
/* begin of body */
|
||||
|
||||
|
||||
#define JRT_ENTRY(result_type, header) \
|
||||
result_type header { \
|
||||
assert(current == JavaThread::current(), "Must be"); \
|
||||
MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current)); \
|
||||
ThreadInVMfromJava __tiv(current); \
|
||||
MACOS_AARCH64_ONLY( \
|
||||
static WXMode wx_mode = DefaultWXWriteMode; \
|
||||
ThreadWXEnable __wx(&wx_mode, current); \
|
||||
) \
|
||||
VM_ENTRY_BASE(result_type, header, current) \
|
||||
DEBUG_ONLY(VMEntryWrapper __vew;)
|
||||
|
||||
@ -311,8 +313,11 @@ class VMNativeEntryWrapper {
|
||||
#define JRT_ENTRY_NO_ASYNC(result_type, header) \
|
||||
result_type header { \
|
||||
assert(current == JavaThread::current(), "Must be"); \
|
||||
MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current)); \
|
||||
ThreadInVMfromJava __tiv(current, false /* check asyncs */); \
|
||||
MACOS_AARCH64_ONLY( \
|
||||
static WXMode wx_mode = DefaultWXWriteMode; \
|
||||
ThreadWXEnable __wx(&wx_mode, current); \
|
||||
) \
|
||||
VM_ENTRY_BASE(result_type, header, current) \
|
||||
DEBUG_ONLY(VMEntryWrapper __vew;)
|
||||
|
||||
@ -321,7 +326,10 @@ class VMNativeEntryWrapper {
|
||||
#define JRT_BLOCK_ENTRY(result_type, header) \
|
||||
result_type header { \
|
||||
assert(current == JavaThread::current(), "Must be"); \
|
||||
MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current)); \
|
||||
MACOS_AARCH64_ONLY( \
|
||||
static WXMode wx_mode = DefaultWXWriteMode; \
|
||||
ThreadWXEnable __wx(&wx_mode, current); \
|
||||
) \
|
||||
HandleMarkCleaner __hm(current);
|
||||
|
||||
#define JRT_BLOCK \
|
||||
@ -358,8 +366,11 @@ extern "C" { \
|
||||
result_type JNICALL header { \
|
||||
JavaThread* thread=JavaThread::thread_from_jni_environment(env); \
|
||||
assert(thread == Thread::current(), "JNIEnv is only valid in same thread"); \
|
||||
MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, thread)); \
|
||||
ThreadInVMfromNative __tiv(thread); \
|
||||
MACOS_AARCH64_ONLY( \
|
||||
static WXMode wx_mode = DefaultWXWriteMode; \
|
||||
ThreadWXEnable __wx(&wx_mode, thread); \
|
||||
) \
|
||||
DEBUG_ONLY(VMNativeEntryWrapper __vew;) \
|
||||
VM_ENTRY_BASE(result_type, header, thread)
|
||||
|
||||
@ -383,8 +394,11 @@ extern "C" { \
|
||||
extern "C" { \
|
||||
result_type JNICALL header { \
|
||||
JavaThread* thread=JavaThread::thread_from_jni_environment(env); \
|
||||
MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, thread)); \
|
||||
ThreadInVMfromNative __tiv(thread); \
|
||||
MACOS_AARCH64_ONLY( \
|
||||
static WXMode wx_mode = DefaultWXWriteMode; \
|
||||
ThreadWXEnable __wx(&wx_mode, thread); \
|
||||
) \
|
||||
DEBUG_ONLY(VMNativeEntryWrapper __vew;) \
|
||||
VM_ENTRY_BASE(result_type, header, thread)
|
||||
|
||||
@ -393,8 +407,11 @@ extern "C" { \
|
||||
extern "C" { \
|
||||
result_type JNICALL header { \
|
||||
JavaThread* thread = JavaThread::current(); \
|
||||
MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, thread)); \
|
||||
ThreadInVMfromNative __tiv(thread); \
|
||||
MACOS_AARCH64_ONLY( \
|
||||
static WXMode wx_mode = DefaultWXWriteMode; \
|
||||
ThreadWXEnable __wx(&wx_mode, thread); \
|
||||
) \
|
||||
DEBUG_ONLY(VMNativeEntryWrapper __vew;) \
|
||||
VM_ENTRY_BASE(result_type, header, thread)
|
||||
|
||||
|
||||
@ -94,16 +94,12 @@ JavaCallWrapper::JavaCallWrapper(const methodHandle& callee_method, Handle recei
|
||||
|
||||
DEBUG_ONLY(_thread->inc_java_call_counter());
|
||||
_thread->set_active_handles(new_handles); // install new handle block and reset Java frame linkage
|
||||
|
||||
MACOS_AARCH64_ONLY(_thread->enable_wx(WXExec));
|
||||
}
|
||||
|
||||
|
||||
JavaCallWrapper::~JavaCallWrapper() {
|
||||
assert(_thread == JavaThread::current(), "must still be the same thread");
|
||||
|
||||
MACOS_AARCH64_ONLY(_thread->enable_wx(WXWrite));
|
||||
|
||||
// restore previous handle block & Java frame linkage
|
||||
JNIHandleBlock *_old_handles = _thread->active_handles();
|
||||
_thread->set_active_handles(_handles);
|
||||
@ -413,17 +409,20 @@ void JavaCalls::call_helper(JavaValue* result, const methodHandle& method, JavaC
|
||||
#endif
|
||||
}
|
||||
}
|
||||
StubRoutines::call_stub()(
|
||||
(address)&link,
|
||||
// (intptr_t*)&(result->_value), // see NOTE above (compiler problem)
|
||||
result_val_address, // see NOTE above (compiler problem)
|
||||
result_type,
|
||||
method(),
|
||||
entry_point,
|
||||
parameter_address,
|
||||
args->size_of_parameters(),
|
||||
CHECK
|
||||
);
|
||||
{
|
||||
MACOS_AARCH64_ONLY(ThreadWXEnable wx(WXExec, thread));
|
||||
StubRoutines::call_stub()(
|
||||
(address)&link,
|
||||
// (intptr_t*)&(result->_value), // see NOTE above (compiler problem)
|
||||
result_val_address, // see NOTE above (compiler problem)
|
||||
result_type,
|
||||
method(),
|
||||
entry_point,
|
||||
parameter_address,
|
||||
args->size_of_parameters(),
|
||||
CHECK
|
||||
);
|
||||
}
|
||||
|
||||
result = link.result(); // circumvent MS C++ 5.0 compiler bug (result is clobbered across call)
|
||||
// Preserve oop return value across possible gc points
|
||||
|
||||
@ -377,15 +377,6 @@ void JavaThread::check_possible_safepoint() {
|
||||
// Clear unhandled oops in JavaThreads so we get a crash right away.
|
||||
clear_unhandled_oops();
|
||||
#endif // CHECK_UNHANDLED_OOPS
|
||||
|
||||
// Macos/aarch64 should be in the right state for safepoint (e.g.
|
||||
// deoptimization needs WXWrite). Crashes caused by the wrong state rarely
|
||||
// happens in practice, making such issues hard to find and reproduce.
|
||||
#if defined(__APPLE__) && defined(AARCH64)
|
||||
if (AssertWXAtThreadSync) {
|
||||
assert_wx_state(WXWrite);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void JavaThread::check_for_valid_safepoint_state() {
|
||||
@ -521,6 +512,11 @@ JavaThread::JavaThread(MemTag mem_tag) :
|
||||
_last_freeze_fail_result(freeze_ok),
|
||||
#endif
|
||||
|
||||
#ifdef MACOS_AARCH64
|
||||
_cur_wx_enable(nullptr),
|
||||
_cur_wx_mode(0),
|
||||
#endif
|
||||
|
||||
_lock_stack(this),
|
||||
_om_cache(this) {
|
||||
set_jni_functions(jni_functions());
|
||||
|
||||
@ -81,6 +81,7 @@ class JavaThread;
|
||||
typedef void (*ThreadFunction)(JavaThread*, TRAPS);
|
||||
|
||||
class EventVirtualThreadPinned;
|
||||
class ThreadWXEnable;
|
||||
|
||||
class JavaThread: public Thread {
|
||||
friend class VMStructs;
|
||||
@ -1288,6 +1289,15 @@ public:
|
||||
bool get_and_clear_interrupted();
|
||||
|
||||
private:
|
||||
|
||||
#ifdef MACOS_AARCH64
|
||||
friend class ThreadWXEnable;
|
||||
friend class PosixSignals;
|
||||
|
||||
ThreadWXEnable* _cur_wx_enable;
|
||||
WXMode* _cur_wx_mode;
|
||||
#endif
|
||||
|
||||
LockStack _lock_stack;
|
||||
OMCache _om_cache;
|
||||
|
||||
|
||||
@ -140,11 +140,16 @@ enum ThreadPriority { // JLS 20.20.1-3
|
||||
CriticalPriority = 11 // Critical thread priority
|
||||
};
|
||||
|
||||
#ifdef MACOS_AARCH64
|
||||
enum WXMode {
|
||||
WXWrite,
|
||||
WXExec
|
||||
WXWrite = 0,
|
||||
WXExec = 1,
|
||||
WXArmedForWrite = 2,
|
||||
};
|
||||
|
||||
extern WXMode DefaultWXWriteMode;
|
||||
#endif // MACOS_AARCH64
|
||||
|
||||
// Executable parameter flag for os::commit_memory() and
|
||||
// os::commit_memory_or_exit().
|
||||
const bool ExecMem = true;
|
||||
@ -1128,9 +1133,23 @@ class os: AllStatic {
|
||||
static char* build_agent_function_name(const char *sym, const char *cname,
|
||||
bool is_absolute_path);
|
||||
|
||||
#if defined(__APPLE__) && defined(AARCH64)
|
||||
#ifdef MACOS_AARCH64
|
||||
// Enables write or execute access to writeable and executable pages.
|
||||
static void current_thread_enable_wx(WXMode mode);
|
||||
// Macos-AArch64 only.
|
||||
static void thread_wx_enable_write_impl();
|
||||
|
||||
// Short circuit write enabling if it's already enabled. This
|
||||
// function is executed many times, so it makes sense to inline a
|
||||
// small part of it.
|
||||
private:
|
||||
static THREAD_LOCAL bool _jit_exec_enabled;
|
||||
public:
|
||||
static void thread_wx_enable_write() {
|
||||
if (__builtin_expect(_jit_exec_enabled, false)) {
|
||||
thread_wx_enable_write_impl();
|
||||
}
|
||||
}
|
||||
#endif // __APPLE__ && AARCH64
|
||||
|
||||
protected:
|
||||
|
||||
@ -602,18 +602,21 @@ protected:
|
||||
jint _hashStateY;
|
||||
jint _hashStateZ;
|
||||
|
||||
#if defined(__APPLE__) && defined(AARCH64)
|
||||
#ifdef MACOS_AARCH64
|
||||
private:
|
||||
DEBUG_ONLY(bool _wx_init);
|
||||
WXMode _wx_state;
|
||||
public:
|
||||
void init_wx();
|
||||
WXMode enable_wx(WXMode new_state);
|
||||
|
||||
bool wx_enable_write();
|
||||
void assert_wx_state(WXMode expected) {
|
||||
assert(_wx_state == expected, "wrong state");
|
||||
}
|
||||
#endif // __APPLE__ && AARCH64
|
||||
WXMode get_wx_state() {
|
||||
return _wx_state;
|
||||
}
|
||||
#endif // MACOS_AARCH64
|
||||
|
||||
private:
|
||||
bool _in_asgct = false;
|
||||
|
||||
@ -30,8 +30,9 @@
|
||||
|
||||
#include "gc/shared/tlab_globals.hpp"
|
||||
#include "runtime/atomicAccess.hpp"
|
||||
#include "utilities/permitForbiddenFunctions.hpp"
|
||||
|
||||
#if defined(__APPLE__) && defined(AARCH64)
|
||||
#ifdef MACOS_AARCH64
|
||||
#include "runtime/os.hpp"
|
||||
#endif
|
||||
|
||||
@ -60,11 +61,17 @@ inline void Thread::set_threads_hazard_ptr(ThreadsList* new_list) {
|
||||
}
|
||||
|
||||
#if defined(__APPLE__) && defined(AARCH64)
|
||||
|
||||
static void dummy() { }
|
||||
|
||||
inline void Thread::init_wx() {
|
||||
assert(this == Thread::current(), "should only be called for current thread");
|
||||
assert(!_wx_init, "second init");
|
||||
_wx_state = WXWrite;
|
||||
permit_forbidden_function::pthread_jit_write_protect_np(false);
|
||||
os::current_thread_enable_wx(_wx_state);
|
||||
// Side effect: preload base address of libjvm
|
||||
guarantee(os::address_is_in_vm(CAST_FROM_FN_PTR(address, &dummy)), "must be");
|
||||
DEBUG_ONLY(_wx_init = true);
|
||||
}
|
||||
|
||||
@ -74,10 +81,19 @@ inline WXMode Thread::enable_wx(WXMode new_state) {
|
||||
WXMode old = _wx_state;
|
||||
if (_wx_state != new_state) {
|
||||
_wx_state = new_state;
|
||||
os::current_thread_enable_wx(new_state);
|
||||
switch (new_state) {
|
||||
case WXWrite:
|
||||
case WXExec:
|
||||
os::current_thread_enable_wx(new_state);
|
||||
break;
|
||||
case WXArmedForWrite:
|
||||
break;
|
||||
default: ShouldNotReachHere(); break;
|
||||
}
|
||||
}
|
||||
return old;
|
||||
}
|
||||
|
||||
#endif // __APPLE__ && AARCH64
|
||||
|
||||
#endif // SHARE_RUNTIME_THREAD_INLINE_HPP
|
||||
|
||||
@ -28,25 +28,62 @@
|
||||
|
||||
// No threadWXSetters.hpp
|
||||
|
||||
#if defined(__APPLE__) && defined(AARCH64)
|
||||
#ifdef MACOS_AARCH64
|
||||
|
||||
#include "classfile/classLoader.hpp"
|
||||
#include "runtime/perfData.inline.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
|
||||
class ThreadWXEnable {
|
||||
Thread* _thread;
|
||||
WXMode _old_mode;
|
||||
WXMode *_this_wx_mode;
|
||||
ThreadWXEnable *_prev;
|
||||
public:
|
||||
ThreadWXEnable(WXMode new_mode, Thread* thread) :
|
||||
_thread(thread),
|
||||
_old_mode(_thread ? _thread->enable_wx(new_mode) : WXWrite)
|
||||
{ }
|
||||
~ThreadWXEnable() {
|
||||
if (_thread) {
|
||||
_thread->enable_wx(_old_mode);
|
||||
ThreadWXEnable(WXMode* new_mode, Thread* thread) :
|
||||
_thread(thread), _this_wx_mode(new_mode) {
|
||||
NOT_PRODUCT(PerfTraceTime ptt(ClassLoader::perf_change_wx_time());)
|
||||
JavaThread* javaThread
|
||||
= _thread && _thread->is_Java_thread()
|
||||
? JavaThread::cast(_thread) : nullptr;
|
||||
_prev = javaThread != nullptr ? javaThread->_cur_wx_enable: nullptr;
|
||||
_old_mode = _thread != nullptr ? _thread->enable_wx(*new_mode) : WXWrite;
|
||||
if (javaThread != nullptr) {
|
||||
javaThread->_cur_wx_enable = this;
|
||||
javaThread->_cur_wx_mode = new_mode;
|
||||
}
|
||||
}
|
||||
ThreadWXEnable(WXMode new_mode, Thread* thread) :
|
||||
_thread(thread), _this_wx_mode(nullptr) {
|
||||
NOT_PRODUCT(PerfTraceTime ptt(ClassLoader::perf_change_wx_time());)
|
||||
JavaThread* javaThread
|
||||
= _thread && _thread->is_Java_thread()
|
||||
? JavaThread::cast(_thread) : nullptr;
|
||||
_prev = javaThread != nullptr ? javaThread->_cur_wx_enable: nullptr;
|
||||
_old_mode = _thread != nullptr ? _thread->enable_wx(new_mode) : WXWrite;
|
||||
if (javaThread) {
|
||||
javaThread->_cur_wx_enable = this;
|
||||
javaThread->_cur_wx_mode = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
~ThreadWXEnable() {
|
||||
NOT_PRODUCT(PerfTraceTime ptt(ClassLoader::perf_change_wx_time());)
|
||||
if (_thread) {
|
||||
_thread->enable_wx(_old_mode);
|
||||
JavaThread* javaThread
|
||||
= _thread && _thread->is_Java_thread()
|
||||
? JavaThread::cast(_thread) : nullptr;
|
||||
if (javaThread != nullptr) {
|
||||
javaThread->_cur_wx_enable = _prev;
|
||||
javaThread->_cur_wx_mode = _prev != nullptr ? _prev->_this_wx_mode : nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool test(address p);
|
||||
};
|
||||
#endif // __APPLE__ && AARCH64
|
||||
#endif // MACOS_AARCH64
|
||||
|
||||
#endif // SHARE_RUNTIME_THREADWXSETTERS_INLINE_HPP
|
||||
|
||||
|
||||
@ -63,4 +63,8 @@ PRAGMA_DIAG_POP
|
||||
FORBID_IMPORTED_C_FUNCTION(char* strdup(const char *s), noexcept, "use os::strdup");
|
||||
FORBID_IMPORTED_C_FUNCTION(wchar_t* wcsdup(const wchar_t *s), noexcept, "don't use");
|
||||
|
||||
// Disallow non-wrapped raw library function.
|
||||
MACOS_AARCH64_ONLY(FORBID_C_FUNCTION(void pthread_jit_write_protect_np(int enabled), noexcept, \
|
||||
"use os::current_thread_enable_wx");)
|
||||
|
||||
#endif // SHARE_UTILITIES_FORBIDDENFUNCTIONS_HPP
|
||||
|
||||
@ -548,6 +548,9 @@
|
||||
#endif
|
||||
|
||||
#define MACOS_AARCH64_ONLY(x) MACOS_ONLY(AARCH64_ONLY(x))
|
||||
#if defined(__APPLE__) && defined(AARCH64)
|
||||
#define MACOS_AARCH64 1
|
||||
#endif
|
||||
|
||||
#if defined(RISCV32) || defined(RISCV64)
|
||||
#define RISCV
|
||||
|
||||
@ -70,6 +70,10 @@ inline void* realloc(void* ptr, size_t size) { return ::realloc(ptr, size); }
|
||||
|
||||
inline char* strdup(const char* s) { return ::strdup(s); }
|
||||
|
||||
MACOS_AARCH64_ONLY( \
|
||||
inline void pthread_jit_write_protect_np(int enabled) { return ::pthread_jit_write_protect_np(enabled); } \
|
||||
)
|
||||
|
||||
END_ALLOW_FORBIDDEN_FUNCTIONS
|
||||
} // namespace permit_forbidden_function
|
||||
|
||||
|
||||
58
test/hotspot/jtreg/runtime/os/TestWXHealing.java
Normal file
58
test/hotspot/jtreg/runtime/os/TestWXHealing.java
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2025 IBM Corporation. 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @requires os.family == "mac"
|
||||
* @requires os.arch == "aarch64"
|
||||
* @summary Run shell with -XX:+StressWXHealing. This tests most of
|
||||
* the triggers for WX mode.
|
||||
* @library /test/lib
|
||||
* @compile WXHealing.java
|
||||
* @run main TestWXHealing
|
||||
*/
|
||||
|
||||
import java.util.regex.*;
|
||||
import jdk.test.lib.process.*;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.*;
|
||||
|
||||
public class TestWXHealing {
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
String[] opts = {"-XX:+UnlockDiagnosticVMOptions",
|
||||
"-XX:+TraceWXHealing", "-XX:+StressWXHealing", "WXHealing"};
|
||||
var process = ProcessTools.createTestJavaProcessBuilder(opts).start();
|
||||
String output = new String(process.getInputStream().readAllBytes(), UTF_8);
|
||||
System.out.println(output);
|
||||
if (output.contains("MAP_JIT write protection does not work on this system")) {
|
||||
System.out.println("Test was not run because MAP_JIT write protection does not work on this system");
|
||||
} else {
|
||||
var pattern = Pattern.compile("Healing WXMode WXArmedForWrite at 0x[0-9a-f]* to WXWrite ");
|
||||
var matches = pattern.matcher(output).results().count();
|
||||
if (matches < 10) {
|
||||
throw new RuntimeException("Only " + matches + " healings in\n" + output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
48
test/hotspot/jtreg/runtime/os/WXHealing.java
Normal file
48
test/hotspot/jtreg/runtime/os/WXHealing.java
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2025 IBM Corporation. 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import jdk.jshell.tool.*;
|
||||
|
||||
public class WXHealing {
|
||||
|
||||
// There's nothing special about jshell here: we just need an
|
||||
// application that does a lot of compilation and class loading.
|
||||
public static void main(String[] args) throws Throwable {
|
||||
JavaShellToolBuilder
|
||||
.builder()
|
||||
.in(new ByteArrayInputStream
|
||||
("""
|
||||
void main() {
|
||||
System.out.println("Hello, World!");
|
||||
}
|
||||
main()
|
||||
2+2
|
||||
Math.sqrt(2)
|
||||
4 * Math.atan(1)
|
||||
Math.exp(1)
|
||||
"""
|
||||
.getBytes()), null)
|
||||
.start();
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user