1 /*
  2  * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  *
 23  */
 24 
 25 #include "code/codeCache.hpp"
 26 #include "code/nativeInst.hpp"
 27 #include "gc/shared/barrierSet.hpp"
 28 #include "gc/shared/barrierSetAssembler.hpp"
 29 #include "gc/shared/barrierSetNMethod.hpp"
 30 #include "logging/log.hpp"
 31 #include "memory/resourceArea.hpp"
 32 #include "runtime/frame.inline.hpp"
 33 #include "runtime/javaThread.hpp"
 34 #include "runtime/registerMap.hpp"
 35 #include "runtime/sharedRuntime.hpp"
 36 #include "utilities/align.hpp"
 37 #include "utilities/debug.hpp"
 38 #include "utilities/formatBuffer.hpp"
 39 
 40 static int slow_path_size(nmethod* nm) {
 41   // The slow path code is out of line with C2
 42   return nm->is_compiled_by_c2() ? 0 : 6;
 43 }
 44 
 45 // This is the offset of the entry barrier relative to where the frame is completed.
 46 // If any code changes between the end of the verified entry where the entry
 47 // barrier resides, and the completion of the frame, then
 48 // NativeNMethodCmpBarrier::verify() will immediately complain when it does
 49 // not find the expected native instruction at this offset, which needs updating.
 50 // Note that this offset is invariant of PreserveFramePointer.
 51 static int entry_barrier_offset(nmethod* nm) {
 52   BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
 53   switch (bs_asm->nmethod_patching_type()) {
 54   case NMethodPatchingType::stw_instruction_and_data_patch:
 55     return -4 * (4 + slow_path_size(nm));
 56   case NMethodPatchingType::conc_instruction_and_data_patch:
 57     return -4 * (10 + slow_path_size(nm));
 58   }
 59   ShouldNotReachHere();
 60   return 0;
 61 }
 62 
 63 class NativeNMethodBarrier {
 64   address  _instruction_address;
 65   int*     _guard_addr;
 66   nmethod* _nm;
 67 
 68   address instruction_address() const { return _instruction_address; }
 69 
 70   int *guard_addr() {
 71     return _guard_addr;
 72   }
 73 
 74   int local_guard_offset(nmethod* nm) {
 75     // It's the last instruction
 76     return (-entry_barrier_offset(nm)) - 4;
 77   }
 78 
 79 public:
 80   NativeNMethodBarrier(nmethod* nm): _nm(nm) {
 81       _instruction_address = nm->code_begin() + nm->frame_complete_offset() + entry_barrier_offset(nm);
 82       if (nm->is_compiled_by_c2()) {
 83         // With c2 compiled code, the guard is out-of-line in a stub
 84         // We find it using the RelocIterator.
 85         RelocIterator iter(nm);
 86         while (iter.next()) {
 87           if (iter.type() == relocInfo::entry_guard_type) {
 88             entry_guard_Relocation* const reloc = iter.entry_guard_reloc();
 89             _guard_addr = reinterpret_cast<int*>(reloc->addr());
 90             return;
 91           }
 92         }
 93         ShouldNotReachHere();
 94       }
 95       _guard_addr =  reinterpret_cast<int*>(instruction_address() + local_guard_offset(nm));
 96   }
 97 
 98   int get_value() {
 99     return AtomicAccess::load_acquire(guard_addr());
100   }
101 
102   void set_value(int value, int bit_mask) {
103     if (bit_mask == ~0) {
104       AtomicAccess::release_store(guard_addr(), value);
105       return;
106     }
107     assert((value & ~bit_mask) == 0, "trying to set bits outside the mask");
108     value &= bit_mask;
109     int old_value = AtomicAccess::load(guard_addr());
110     while (true) {
111       // Only bits in the mask are changed
112       int new_value = value | (old_value & ~bit_mask);
113       if (new_value == old_value) break;
114       int v = AtomicAccess::cmpxchg(guard_addr(), old_value, new_value, memory_order_release);
115       if (v == old_value) break;
116       old_value = v;
117     }
118   }
119 
120   bool check_barrier(err_msg& msg) const;
121   void verify() const {
122     err_msg msg("%s", "");
123     assert(check_barrier(msg), "%s", msg.buffer());
124   }
125 };
126 
127 // The first instruction of the nmethod entry barrier is an ldr (literal)
128 // instruction. Verify that it's really there, so the offsets are not skewed.
129 bool NativeNMethodBarrier::check_barrier(err_msg& msg) const {
130   uint32_t* addr = (uint32_t*) instruction_address();
131   uint32_t inst = *addr;
132   if ((inst & 0xff000000) != 0x18000000) {
133     msg.print("Nmethod entry barrier did not start with ldr (literal) as expected. "
134               "Addr: " PTR_FORMAT " Code: " UINT32_FORMAT, p2i(addr), inst);
135     return false;
136   }
137   return true;
138 }
139 
140 
141 /* We're called from an nmethod when we need to deoptimize it. We do
142    this by throwing away the nmethod's frame and jumping to the
143    ic_miss stub. This looks like there has been an IC miss at the
144    entry of the nmethod, so we resolve the call, which will fall back
145    to the interpreter if the nmethod has been unloaded. */
146 void BarrierSetNMethod::deoptimize(nmethod* nm, address* return_address_ptr) {
147 
148   typedef struct {
149     intptr_t *sp; intptr_t *fp; address lr; address pc;
150   } frame_pointers_t;
151 
152   frame_pointers_t *new_frame = (frame_pointers_t *)(return_address_ptr - 5);
153 
154   JavaThread *thread = JavaThread::current();
155   RegisterMap reg_map(thread,
156                       RegisterMap::UpdateMap::skip,
157                       RegisterMap::ProcessFrames::include,
158                       RegisterMap::WalkContinuation::skip);
159   frame frame = thread->last_frame();
160 
161   assert(frame.is_compiled_frame() || frame.is_native_frame(), "must be");
162   assert(frame.cb() == nm, "must be");
163   frame = frame.sender(&reg_map);
164 
165   LogTarget(Trace, nmethod, barrier) out;
166   if (out.is_enabled()) {
167     ResourceMark mark;
168     log_trace(nmethod, barrier)("deoptimize(nmethod: %s(%p), return_addr: %p, osr: %d, thread: %p(%s), making rsp: %p) -> %p",
169                                 nm->method()->name_and_sig_as_C_string(),
170                                 nm, *(address *) return_address_ptr, nm->is_osr_method(), thread,
171                                 thread->name(), frame.sp(), nm->verified_entry_point());
172   }
173 
174   new_frame->sp = frame.sp();
175   new_frame->fp = frame.fp();
176   new_frame->lr = frame.pc();
177   new_frame->pc = SharedRuntime::get_handle_wrong_method_stub();
178 }
179 
180 void BarrierSetNMethod::set_guard_value(nmethod* nm, int value, int bit_mask) {
181   if (!supports_entry_barrier(nm)) {
182     return;
183   }
184 
185   if (value == disarmed_guard_value()) {
186     // The patching epoch is incremented before the nmethod is disarmed. Disarming
187     // is performed with a release store. In the nmethod entry barrier, the values
188     // are read in the opposite order, such that the load of the nmethod guard
189     // acquires the patching epoch. This way, the guard is guaranteed to block
190     // entries to the nmethod, until it has safely published the requirement for
191     // further fencing by mutators, before they are allowed to enter.
192     BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
193     bs_asm->increment_patching_epoch();
194   }
195 
196   // Enable WXWrite: the function is called directly from nmethod_entry_barrier
197   // stub.
198   MACOS_AARCH64_ONLY(ThreadWXEnable wx(WXWrite, Thread::current()));
199 
200   NativeNMethodBarrier barrier(nm);
201   barrier.set_value(value, bit_mask);
202 }
203 
204 int BarrierSetNMethod::guard_value(nmethod* nm) {
205   if (!supports_entry_barrier(nm)) {
206     return disarmed_guard_value();
207   }
208 
209   NativeNMethodBarrier barrier(nm);
210   return barrier.get_value();
211 }