| /* | |
| * Copyright (c) 2018, 2025, 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 | |
| * 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. | |
| * | |
| */ | |
| #include "code/codeCache.hpp" | |
| #include "code/nativeInst.hpp" | |
| #include "gc/shared/barrierSetNMethod.hpp" | |
| #include "logging/log.hpp" | |
| #include "memory/resourceArea.hpp" | |
| #include "runtime/javaThread.hpp" | |
| #include "runtime/sharedRuntime.hpp" | |
| #include "utilities/align.hpp" | |
| #include "utilities/debug.hpp" | |
| #include "utilities/formatBuffer.hpp" | |
| #include "utilities/macros.hpp" | |
| #if INCLUDE_JVMCI | |
| #include "jvmci/jvmciRuntime.hpp" | |
| #endif | |
| class NativeNMethodCmpBarrier: public NativeInstruction { | |
| public: | |
| enum Intel_specific_constants { | |
| instruction_code = 0x81, | |
| instruction_size = 8, | |
| imm_offset = 4, | |
| instruction_rex_prefix = Assembler::REX | Assembler::REX_B, | |
| instruction_modrm = 0x7f // [r15 + offset] | |
| }; | |
| address instruction_address() const { return addr_at(0); } | |
| address immediate_address() const { return addr_at(imm_offset); } | |
| jint get_immediate() const { return int_at(imm_offset); } | |
| void set_immediate(jint imm) { set_int_at(imm_offset, imm); } | |
| bool check_barrier(err_msg& msg) const; | |
| void verify() const { | |
| #ifdef ASSERT | |
| err_msg msg("%s", ""); | |
| assert(check_barrier(msg), "%s", msg.buffer()); | |
| #endif | |
| } | |
| }; | |
| bool NativeNMethodCmpBarrier::check_barrier(err_msg& msg) const { | |
| // Only require 4 byte alignment | |
| if (((uintptr_t) instruction_address()) & 0x3) { | |
| msg.print("Addr: " INTPTR_FORMAT " not properly aligned", p2i(instruction_address())); | |
| return false; | |
| } | |
| int prefix = ubyte_at(0); | |
| if (prefix != instruction_rex_prefix) { | |
| msg.print("Addr: " INTPTR_FORMAT " Code: 0x%x expected 0x%x", p2i(instruction_address()), prefix, instruction_rex_prefix); | |
| return false; | |
| } | |
| int inst = ubyte_at(1); | |
| if (inst != instruction_code) { | |
| msg.print("Addr: " INTPTR_FORMAT " Code: 0x%x expected 0x%x", p2i(instruction_address()), inst, instruction_code); | |
| return false; | |
| } | |
| int modrm = ubyte_at(2); | |
| if (modrm != instruction_modrm) { | |
| msg.print("Addr: " INTPTR_FORMAT " Code: 0x%x expected mod/rm 0x%x", p2i(instruction_address()), modrm, instruction_modrm); | |
| return false; | |
| } | |
| return true; | |
| } | |
| void BarrierSetNMethod::deoptimize(nmethod* nm, address* return_address_ptr) { | |
| /* | |
| * [ callers frame ] | |
| * [ callers return address ] <- callers rsp | |
| * [ callers rbp ] <- callers rbp | |
| * [ callers frame slots ] | |
| * [ return_address ] <- return_address_ptr | |
| * [ cookie ] <- used to write the new rsp (callers rsp) | |
| * [ stub rbp ] | |
| * [ stub stuff ] | |
| */ | |
| address* stub_rbp = return_address_ptr - 2; | |
| address* callers_rsp = return_address_ptr + nm->frame_size(); /* points to callers return_address now */ | |
| address* callers_rbp = callers_rsp - 1; // 1 to move to the callers return address, 1 more to move to the rbp | |
| address* cookie = return_address_ptr - 1; | |
| LogTarget(Trace, nmethod, barrier) out; | |
| if (out.is_enabled()) { | |
| JavaThread* jth = JavaThread::current(); | |
| ResourceMark mark; | |
| log_trace(nmethod, barrier)("deoptimize(nmethod: %p, return_addr: %p, osr: %d, thread: %p(%s), making rsp: %p) -> %p", | |
| nm, (address *) return_address_ptr, nm->is_osr_method(), jth, | |
| jth->name(), callers_rsp, nm->verified_entry_point()); | |
| } | |
| assert(nm->frame_size() >= 3, "invariant"); | |
| assert(*cookie == (address) -1, "invariant"); | |
| // Preserve caller rbp. | |
| *stub_rbp = *callers_rbp; | |
| // At the cookie address put the callers rsp. | |
| *cookie = (address) callers_rsp; // should point to the return address | |
| // In the slot that used to be the callers rbp we put the address that our stub needs to jump to at the end. | |
| // Overwriting the caller rbp should be okay since our stub rbp has the same value. | |
| address* jmp_addr_ptr = callers_rbp; | |
| *jmp_addr_ptr = SharedRuntime::get_handle_wrong_method_stub(); | |
| } | |
| // This is the offset of the entry barrier from where the frame is completed. | |
| // If any code changes between the end of the verified entry where the entry | |
| // barrier resides, and the completion of the frame, then | |
| // NativeNMethodCmpBarrier::verify() will immediately complain when it does | |
| // not find the expected native instruction at this offset, which needs updating. | |
| // Note that this offset is invariant of PreserveFramePointer. | |
| static int entry_barrier_offset(nmethod* nm) { | |
| if (nm->is_compiled_by_c2()) { | |
| return -14; | |
| } else { | |
| return -15; | |
| } | |
| } | |
| static NativeNMethodCmpBarrier* native_nmethod_barrier(nmethod* nm) { | |
| address barrier_address; | |
| #if INCLUDE_JVMCI | |
| if (nm->is_compiled_by_jvmci()) { | |
| barrier_address = nm->code_begin() + nm->jvmci_nmethod_data()->nmethod_entry_patch_offset(); | |
| } else | |
| #endif | |
| { | |
| barrier_address = nm->code_begin() + nm->frame_complete_offset() + entry_barrier_offset(nm); | |
| } | |
| NativeNMethodCmpBarrier* barrier = reinterpret_cast<NativeNMethodCmpBarrier*>(barrier_address); | |
| barrier->verify(); | |
| return barrier; | |
| } | |
| void BarrierSetNMethod::set_guard_value(nmethod* nm, int value) { | |
| if (!supports_entry_barrier(nm)) { | |
| return; | |
| } | |
| NativeNMethodCmpBarrier* cmp = native_nmethod_barrier(nm); | |
| cmp->set_immediate(value); | |
| } | |
| int BarrierSetNMethod::guard_value(nmethod* nm) { | |
| if (!supports_entry_barrier(nm)) { | |
| return disarmed_guard_value(); | |
| } | |
| NativeNMethodCmpBarrier* cmp = native_nmethod_barrier(nm); | |
| return cmp->get_immediate(); | |
| } | |
| #if INCLUDE_JVMCI | |
| bool BarrierSetNMethod::verify_barrier(nmethod* nm, err_msg& msg) { | |
| NativeNMethodCmpBarrier* barrier = native_nmethod_barrier(nm); | |
| return barrier->check_barrier(msg); | |
| } | |
| #endif |