blob: 4027de2fb3d7efa23efda0af62cc0a5873837797 [file] [edit]
/*
* Copyright (c) 2022, 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 "ci/ciKlass.hpp"
#include "ci/ciMethod.hpp"
#include "classfile/vmSymbols.hpp"
#include "interpreter/linkResolver.hpp"
#include "jfr/recorder/jfrRecorder.hpp"
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdMacros.hpp"
#include "jfr/recorder/stacktrace/jfrStackTrace.hpp"
#include "jfr/support/jfrDeprecationManager.hpp"
#include "jfr/support/jfrResolution.hpp"
#include "memory/resourceArea.inline.hpp"
#include "oops/method.inline.hpp"
#include "runtime/javaThread.inline.hpp"
#include "runtime/vframe.inline.hpp"
#ifdef COMPILER1
#include "c1/c1_GraphBuilder.hpp"
#endif
#ifdef COMPILER2
#include "opto/parse.hpp"
#endif
// for strstr
#include <string.h>
// The following packages are internal implmentation details used by reflection.
// We exclude matching frames on the stack in a manner similar to StackWalker.
static constexpr const int NUM_EXCLUDED_PACKAGES = 4;
static constexpr const char* excluded_packages[NUM_EXCLUDED_PACKAGES] = { "java/lang/invoke/",
"jdk/internal/reflect/",
"java/lang/reflect/",
"sun/invoke/" };
static inline bool match(const char* str, const char* sub_str) {
assert(str != nullptr, "invariant");
assert(sub_str != nullptr, "invariant");
return strstr(str, sub_str) == str;
}
// Caller requires ResourceMark.
static inline bool exclude_frame(const Method* method) {
assert(method != nullptr, "invariant");
// exclude native methods.
if (method->is_native()) {
return true;
}
const Klass* const klass = method->method_holder();
assert(klass != nullptr, "invariant");
const Symbol* const klass_sym = klass->name();
assert(klass_sym != nullptr, "invariant");
const char* const klass_name = klass_sym->as_C_string();
assert(klass_name != nullptr, "invariant");
for (int i = 0; i < NUM_EXCLUDED_PACKAGES; ++i) {
if (match(klass_name,excluded_packages[i])) {
return true;
}
}
return false;
}
static Method* find_real_sender(vframeStream& stream, JavaThread* jt) {
assert(jt != nullptr, "invariant");
assert(stream.method()->is_native(), "invariant");
ResourceMark rm(jt);
while (!stream.at_end()) {
stream.next();
Method* method = stream.method();
if (!exclude_frame(method)) {
return method;
}
}
return nullptr;
}
static inline bool jfr_is_started_on_command_line() {
return JfrRecorder::is_started_on_commandline();
}
static inline Method* frame_context(vframeStream& stream, int& bci, u1& frame_type, JavaThread* jt) {
Method* method = stream.method();
assert(method != nullptr, "invariant");
if (method->is_native()) {
method = find_real_sender(stream, jt);
if (method == nullptr) {
return nullptr;
}
}
assert(method != nullptr, "invariant");
assert(!method->is_native(), "invariant");
bci = stream.bci();
frame_type = stream.is_interpreted_frame() ? JfrStackFrame::FRAME_INTERPRETER : JfrStackFrame::FRAME_JIT;
if (frame_type == JfrStackFrame::FRAME_JIT && !stream.at_end()) {
const intptr_t* const id = stream.frame_id();
stream.next();
if (id == stream.frame_id()) {
frame_type = JfrStackFrame::FRAME_INLINE;
}
}
return method;
}
static inline Method* ljf_sender_method(int& bci, u1& frame_type, JavaThread* jt) {
assert(jt != nullptr, "invariant");
if (!jt->has_last_Java_frame()) {
return nullptr;
}
vframeStream stream(jt, true, false);
return frame_context(stream, bci, frame_type, jt);
}
static inline void on_runtime_deprecated(const Method* method, JavaThread* jt) {
assert(jt != nullptr, "invariant");
assert(method != nullptr, "invariant");
assert(method->deprecated(), "invariant");
if (jfr_is_started_on_command_line()) {
int bci;
u1 frame_type;
Method* const sender = ljf_sender_method(bci, frame_type, jt);
if (sender != nullptr) {
JfrDeprecationManager::on_link(method, sender, bci, frame_type, jt);
}
}
}
// We can circumvent the need to hook into backpatching if ciMethod is made aware
// of the deprecated annotation already as part of parsing bytecodes of the callee method.
static void on_backpatching_deprecated(const Method* deprecated_method, JavaThread* jt) {
assert(deprecated_method != nullptr, "invariant");
assert(deprecated_method->deprecated(), "invariant");
assert(jt->has_last_Java_frame(), "invariant");
assert(jt->last_frame().is_runtime_frame(), "invariant");
if (jfr_is_started_on_command_line()) {
vframeStream stream(jt, true, false);
assert(!stream.at_end(), "invariant");
stream.next(); // now at caller
int bci;
u1 frame_type;
Method* const sender = frame_context(stream, bci, frame_type, jt);
if (sender != nullptr) {
JfrDeprecationManager::on_link(deprecated_method, sender, bci, frame_type, jt);
}
}
}
void JfrResolution::on_backpatching(const Method* callee_method, JavaThread* jt) {
assert(callee_method != nullptr, "invariant");
assert(jt != nullptr, "invariant");
if (callee_method->deprecated()) {
on_backpatching_deprecated(callee_method, jt);
}
}
static inline const Method* ljf_sender_method(JavaThread* jt) {
assert(jt != nullptr, "invariant");
if (!jt->has_last_Java_frame()) {
return nullptr;
}
const vframeStream ljf(jt, true, false);
return ljf.method();
}
static const char* const link_error_msg = "illegal access linking method 'jdk.jfr.internal.event.EventWriter.getEventWriter()'";
void JfrResolution::on_runtime_resolution(const CallInfo & info, TRAPS) {
assert(info.selected_method() != nullptr, "invariant");
assert(info.resolved_klass() != nullptr, "invariant");
static const Symbol* const event_writer_method_name = vmSymbols::getEventWriter_name();
assert(event_writer_method_name != nullptr, "invariant");
Method* const method = info.selected_method();
assert(method != nullptr, "invariant");
if (method->deprecated()) {
on_runtime_deprecated(method, THREAD);
return;
}
// Fast path
if (method->name() != event_writer_method_name) {
return;
}
static const Symbol* const event_writer_klass_name = vmSymbols::jdk_jfr_internal_event_EventWriter();
assert(event_writer_klass_name != nullptr, "invariant");
if (info.resolved_klass()->name() != event_writer_klass_name) {
return;
}
// Attempting to link against jdk.jfr.internal.event.EventWriter.getEventWriter().
// The sender, i.e. the method attempting to link, is in the ljf (if one exists).
const Method* const sender = ljf_sender_method(THREAD);
if (sender == nullptr) {
// A compiler thread is doing linktime resolution but there is no information about the sender available.
// For the compiler threads, the sender is instead found as part of bytecode parsing.
return;
}
// Is the sender method blessed for linkage?
if (IS_METHOD_BLESSED(sender)) {
return;
}
#if INCLUDE_JVMCI
// JVMCI compiler is doing linktime resolution
if (sender->method_holder()->name() == vmSymbols::jdk_vm_ci_hotspot_CompilerToVM()) {
if (sender->name()->equals("lookupMethodInPool")) {
return;
}
}
#endif
THROW_MSG(vmSymbols::java_lang_IllegalAccessError(), link_error_msg);
}
static inline bool is_compiler_linking_event_writer(const Symbol* holder, const Symbol* name) {
static const Symbol* const event_writer_klass_name = vmSymbols::jdk_jfr_internal_event_EventWriter();
assert(event_writer_klass_name != nullptr, "invariant");
if (holder != event_writer_klass_name) {
return false;
}
static const Symbol* const event_writer_method_name = vmSymbols::getEventWriter_name();
assert(event_writer_method_name != nullptr, "invariant");
return name == event_writer_method_name;
}
static inline bool is_compiler_linking_event_writer(const ciKlass * holder, const ciMethod * target) {
assert(holder != nullptr, "invariant");
assert(target != nullptr, "invariant");
return is_compiler_linking_event_writer(holder->name()->get_symbol(), target->name()->get_symbol());
}
static inline void on_compiler_resolve_deprecated(const ciMethod* target, int bci, Method* sender) {
assert(target != nullptr, "invariant");
assert(sender != nullptr, "invariant");
if (jfr_is_started_on_command_line()) {
const Method* const method = target->get_Method();
assert(method != nullptr, "Invariant");
assert(method->deprecated(), "invariant");
JfrDeprecationManager::on_link(method, sender, bci, JfrStackFrame::FRAME_JIT, JavaThread::current());
}
}
#ifdef COMPILER1
// C1
void JfrResolution::on_c1_resolution(const GraphBuilder * builder, const ciKlass * holder, const ciMethod * target) {
Method* const sender = builder->method()->get_Method();
if (is_compiler_linking_event_writer(holder, target) && !IS_METHOD_BLESSED(sender)) {
builder->bailout(link_error_msg);
return;
}
if (target->deprecated()) {
on_compiler_resolve_deprecated(target, builder->bci(), sender);
}
}
#endif
#ifdef COMPILER2
// C2
void JfrResolution::on_c2_resolution(const Parse * parse, const ciKlass * holder, const ciMethod * target) {
Method* const sender = parse->method()->get_Method();
if (is_compiler_linking_event_writer(holder, target) && !IS_METHOD_BLESSED(sender)) {
parse->C->record_failure(link_error_msg);
return;
}
if (target->deprecated()) {
on_compiler_resolve_deprecated(target, parse->bci(), sender);
}
}
#endif
#if INCLUDE_JVMCI
// JVMCI
void JfrResolution::on_jvmci_resolution(const Method* caller, const Method* target, TRAPS) {
if (is_compiler_linking_event_writer(target->method_holder()->name(), target->name())) {
if (caller == nullptr || !IS_METHOD_BLESSED(caller)) {
THROW_MSG(vmSymbols::java_lang_IllegalAccessError(), link_error_msg);
}
}
}
#endif