# HG changeset patch # User redestad # Date 1571742336 -7200 # Tue Oct 22 13:05:36 2019 +0200 # Node ID 79e8a7316cb68a60ab37c9021389fb5055f22ad4 # Parent 2e0462f4cf4e4332cdfe60ca178db50771284db8 8232613: Move Object.registerNatives into HotSpot Reviewed-by: alanb, coleenp, lfoltan diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -1976,6 +1976,20 @@ resolve_wk_klasses_through(WK_KLASS_ENUM_NAME(Class_klass), scan, CHECK); } + assert(WK_KLASS(Object_klass) != NULL, "well-known classes should now be initialized"); + + // Register native methods of Object + Method::register_native(Object_klass(), vmSymbols::hashCode_name(), + vmSymbols::void_int_signature(), (address)&JVM_IHashCode, THREAD); + Method::register_native(Object_klass(), vmSymbols::wait_name(), + vmSymbols::long_void_signature(), (address)&JVM_MonitorWait, THREAD); + Method::register_native(Object_klass(), vmSymbols::notify_name(), + vmSymbols::void_method_signature(), (address)&JVM_MonitorNotify, THREAD); + Method::register_native(Object_klass(), vmSymbols::notifyAll_name(), + vmSymbols::void_method_signature(), (address)&JVM_MonitorNotifyAll, THREAD); + Method::register_native(Object_klass(), vmSymbols::clone_name(), + vmSymbols::void_object_signature(), (address)&JVM_Clone, THREAD); + // Calculate offsets for String and Class classes since they are loaded and // can be used after this point. java_lang_String::compute_offsets(); diff --git a/src/hotspot/share/classfile/systemDictionary.hpp b/src/hotspot/share/classfile/systemDictionary.hpp --- a/src/hotspot/share/classfile/systemDictionary.hpp +++ b/src/hotspot/share/classfile/systemDictionary.hpp @@ -219,7 +219,6 @@ \ /*end*/ - class SystemDictionary : AllStatic { friend class BootstrapInfo; friend class VMStructs; @@ -383,7 +382,6 @@ int limit = (int)end_id + 1; resolve_wk_klasses_until((WKID) limit, start_id, THREAD); } - public: #define WK_KLASS_DECLARE(name, symbol) \ static InstanceKlass* name() { return check_klass(_well_known_klasses[WK_KLASS_ENUM_NAME(name)]); } \ @@ -631,21 +629,6 @@ // Basic find on classes in the midst of being loaded static Symbol* find_placeholder(Symbol* name, ClassLoaderData* loader_data); - // Add a placeholder for a class being loaded - static void add_placeholder(int index, - Symbol* class_name, - ClassLoaderData* loader_data); - static void remove_placeholder(int index, - Symbol* class_name, - ClassLoaderData* loader_data); - - // Performs cleanups after resolve_super_or_fail. This typically needs - // to be called on failure. - // Won't throw, but can block. - static void resolution_cleanups(Symbol* class_name, - ClassLoaderData* loader_data, - TRAPS); - // Resolve well-known classes so they can be used like SystemDictionary::String_klass() static void resolve_well_known_classes(TRAPS); diff --git a/src/hotspot/share/oops/method.cpp b/src/hotspot/share/oops/method.cpp --- a/src/hotspot/share/oops/method.cpp +++ b/src/hotspot/share/oops/method.cpp @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "classfile/classLoaderDataGraph.hpp" #include "classfile/metadataOnStackMark.hpp" +#include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" #include "code/codeCache.hpp" #include "code/debugInfoRec.hpp" @@ -378,7 +379,83 @@ assert(valid_itable_index(), ""); } +// The RegisterNatives call being attempted tried to register with a method that +// is not native. Ask JVM TI what prefixes have been specified. Then check +// to see if the native method is now wrapped with the prefixes. See the +// SetNativeMethodPrefix(es) functions in the JVM TI Spec for details. +static Method* find_prefixed_native(Klass* k, Symbol* name, Symbol* signature, TRAPS) { +#if INCLUDE_JVMTI + ResourceMark rm(THREAD); + Method* method; + int name_len = name->utf8_length(); + char* name_str = name->as_utf8(); + int prefix_count; + char** prefixes = JvmtiExport::get_all_native_method_prefixes(&prefix_count); + for (int i = 0; i < prefix_count; i++) { + char* prefix = prefixes[i]; + int prefix_len = (int)strlen(prefix); + // try adding this prefix to the method name and see if it matches another method name + int trial_len = name_len + prefix_len; + char* trial_name_str = NEW_RESOURCE_ARRAY(char, trial_len + 1); + strcpy(trial_name_str, prefix); + strcat(trial_name_str, name_str); + TempNewSymbol trial_name = SymbolTable::probe(trial_name_str, trial_len); + if (trial_name == NULL) { + continue; // no such symbol, so this prefix wasn't used, try the next prefix + } + method = k->lookup_method(trial_name, signature); + if (method == NULL) { + continue; // signature doesn't match, try the next prefix + } + if (method->is_native()) { + method->set_is_prefixed_native(); + return method; // wahoo, we found a prefixed version of the method, return it + } + // found as non-native, so prefix is good, add it, probably just need more prefixes + name_len = trial_len; + name_str = trial_name_str; + } +#endif // INCLUDE_JVMTI + return NULL; // not found +} + +void Method::register_native(Klass* k, Symbol* name, Symbol* signature, address entry, TRAPS) { + Method* method = k->lookup_method(name, signature); + if (method == NULL) { + ResourceMark rm; + stringStream st; + st.print("Method '"); + Method::print_external_name(&st, k, name, signature); + st.print("' name or signature does not match"); + THROW_MSG(vmSymbols::java_lang_NoSuchMethodError(), st.as_string()); + } + if (!method->is_native()) { + // trying to register to a non-native method, see if a JVM TI agent has added prefix(es) + method = find_prefixed_native(k, name, signature, THREAD); + if (method == NULL) { + ResourceMark rm; + stringStream st; + st.print("Method '"); + Method::print_external_name(&st, k, name, signature); + st.print("' is not declared as native"); + THROW_MSG(vmSymbols::java_lang_NoSuchMethodError(), st.as_string()); + } + } + + if (entry != NULL) { + method->set_native_function(entry, + Method::native_bind_event_is_interesting); + } else { + method->clear_native_function(); + } + if (PrintJNIResolving) { + ResourceMark rm(THREAD); + tty->print_cr("[Registering JNI native method %s.%s]", + method->method_holder()->external_name(), + method->name()->as_C_string()); + } +} bool Method::was_executed_more_than(int n) { // Invocation counter is reset when the Method* is compiled. diff --git a/src/hotspot/share/oops/method.hpp b/src/hotspot/share/oops/method.hpp --- a/src/hotspot/share/oops/method.hpp +++ b/src/hotspot/share/oops/method.hpp @@ -346,6 +346,12 @@ // InterpreterRuntime::exception_handler_for_exception. static int fast_exception_handler_bci_for(const methodHandle& mh, Klass* ex_klass, int throw_bci, TRAPS); + static void register_native(Klass* k, + Symbol* name, + Symbol* signature, + address entry, + TRAPS); + // method data access MethodData* method_data() const { return _method_data; diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp --- a/src/hotspot/share/prims/jni.cpp +++ b/src/hotspot/share/prims/jni.cpp @@ -2909,89 +2909,6 @@ HOTSPOT_JNI_SETDOUBLEARRAYREGION_RETURN()) -// -// Interception of natives -// - -// The RegisterNatives call being attempted tried to register with a method that -// is not native. Ask JVM TI what prefixes have been specified. Then check -// to see if the native method is now wrapped with the prefixes. See the -// SetNativeMethodPrefix(es) functions in the JVM TI Spec for details. -static Method* find_prefixed_native(Klass* k, Symbol* name, Symbol* signature, TRAPS) { -#if INCLUDE_JVMTI - ResourceMark rm(THREAD); - Method* method; - int name_len = name->utf8_length(); - char* name_str = name->as_utf8(); - int prefix_count; - char** prefixes = JvmtiExport::get_all_native_method_prefixes(&prefix_count); - for (int i = 0; i < prefix_count; i++) { - char* prefix = prefixes[i]; - int prefix_len = (int)strlen(prefix); - - // try adding this prefix to the method name and see if it matches another method name - int trial_len = name_len + prefix_len; - char* trial_name_str = NEW_RESOURCE_ARRAY(char, trial_len + 1); - strcpy(trial_name_str, prefix); - strcat(trial_name_str, name_str); - TempNewSymbol trial_name = SymbolTable::probe(trial_name_str, trial_len); - if (trial_name == NULL) { - continue; // no such symbol, so this prefix wasn't used, try the next prefix - } - method = k->lookup_method(trial_name, signature); - if (method == NULL) { - continue; // signature doesn't match, try the next prefix - } - if (method->is_native()) { - method->set_is_prefixed_native(); - return method; // wahoo, we found a prefixed version of the method, return it - } - // found as non-native, so prefix is good, add it, probably just need more prefixes - name_len = trial_len; - name_str = trial_name_str; - } -#endif // INCLUDE_JVMTI - return NULL; // not found -} - -static bool register_native(Klass* k, Symbol* name, Symbol* signature, address entry, TRAPS) { - Method* method = k->lookup_method(name, signature); - if (method == NULL) { - ResourceMark rm; - stringStream st; - st.print("Method '"); - Method::print_external_name(&st, k, name, signature); - st.print("' name or signature does not match"); - THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), false); - } - if (!method->is_native()) { - // trying to register to a non-native method, see if a JVM TI agent has added prefix(es) - method = find_prefixed_native(k, name, signature, THREAD); - if (method == NULL) { - ResourceMark rm; - stringStream st; - st.print("Method '"); - Method::print_external_name(&st, k, name, signature); - st.print("' is not declared as native"); - THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), false); - } - } - - if (entry != NULL) { - method->set_native_function(entry, - Method::native_bind_event_is_interesting); - } else { - method->clear_native_function(); - } - if (PrintJNIResolving) { - ResourceMark rm(THREAD); - tty->print_cr("[Registering JNI native method %s.%s]", - method->method_holder()->external_name(), - method->name()->as_C_string()); - } - return true; -} - DT_RETURN_MARK_DECL(RegisterNatives, jint , HOTSPOT_JNI_REGISTERNATIVES_RETURN(_ret_ref)); @@ -3024,8 +2941,8 @@ THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), -1); } - bool res = register_native(k, name, signature, - (address) methods[index].fnPtr, THREAD); + bool res = Method::register_native(k, name, signature, + (address) methods[index].fnPtr, THREAD); if (!res) { ret = -1; break; diff --git a/src/java.base/share/classes/java/lang/Object.java b/src/java.base/share/classes/java/lang/Object.java --- a/src/java.base/share/classes/java/lang/Object.java +++ b/src/java.base/share/classes/java/lang/Object.java @@ -38,11 +38,6 @@ */ public class Object { - private static native void registerNatives(); - static { - registerNatives(); - } - /** * Constructs a new object. */ diff --git a/src/java.base/share/native/libjava/Object.c b/src/java.base/share/native/libjava/Object.c --- a/src/java.base/share/native/libjava/Object.c +++ b/src/java.base/share/native/libjava/Object.c @@ -39,21 +39,6 @@ #include "java_lang_Object.h" -static JNINativeMethod methods[] = { - {"hashCode", "()I", (void *)&JVM_IHashCode}, - {"wait", "(J)V", (void *)&JVM_MonitorWait}, - {"notify", "()V", (void *)&JVM_MonitorNotify}, - {"notifyAll", "()V", (void *)&JVM_MonitorNotifyAll}, - {"clone", "()Ljava/lang/Object;", (void *)&JVM_Clone}, -}; - -JNIEXPORT void JNICALL -Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls) -{ - (*env)->RegisterNatives(env, cls, - methods, sizeof(methods)/sizeof(methods[0])); -} - JNIEXPORT jclass JNICALL Java_java_lang_Object_getClass(JNIEnv *env, jobject this) { diff --git a/test/hotspot/jtreg/compiler/dependencies/MonomorphicObjectCall/java.base/java/lang/Object.java b/test/hotspot/jtreg/compiler/dependencies/MonomorphicObjectCall/java.base/java/lang/Object.java --- a/test/hotspot/jtreg/compiler/dependencies/MonomorphicObjectCall/java.base/java/lang/Object.java +++ b/test/hotspot/jtreg/compiler/dependencies/MonomorphicObjectCall/java.base/java/lang/Object.java @@ -34,11 +34,6 @@ @HotSpotIntrinsicCandidate public Object() {} - private static native void registerNatives(); - static { - registerNatives(); - } - @HotSpotIntrinsicCandidate public final native Class getClass(); diff --git a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineObject.java b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineObject.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineObject.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2019, 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. + */ + +/* + * @test + * @bug 8232613 + * @summary Ensure Object natives stay registered after redefinition + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.base/jdk.internal.org.objectweb.asm + * java.compiler + * java.instrument + * jdk.jartool/sun.tools.jar + * @run main RedefineObject buildagent + * @run main/othervm -javaagent:redefineagent.jar RedefineObject + */ + +import static jdk.test.lib.Asserts.assertTrue; +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.lang.RuntimeException; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.IllegalClassFormatException; +import java.lang.instrument.Instrumentation; +import java.lang.instrument.UnmodifiableClassException; +import java.security.ProtectionDomain; +import java.util.Arrays; + +import jdk.internal.org.objectweb.asm.ClassReader; +import jdk.internal.org.objectweb.asm.ClassVisitor; +import jdk.internal.org.objectweb.asm.ClassWriter; + +import static jdk.internal.org.objectweb.asm.Opcodes.ASM6; +import static jdk.internal.org.objectweb.asm.Opcodes.V1_8; + +public class RedefineObject { + + static Instrumentation inst; + + public static void premain(String agentArgs, Instrumentation inst) { + RedefineObject.inst = inst; + } + + static class Transformer implements ClassFileTransformer { + + public byte[] asm(ClassLoader loader, String className, + Class classBeingRedefined, + ProtectionDomain protectionDomain, byte[] classfileBuffer) + throws IllegalClassFormatException { + ClassWriter cw = new ClassWriter(0); + // Force an older ASM to force a bytecode update + ClassVisitor cv = new DummyClassVisitor(ASM6, cw) { }; + ClassReader cr = new ClassReader(classfileBuffer); + cr.accept(cv, 0); + byte[] bytes = cw.toByteArray(); + return bytes; + } + + public class DummyClassVisitor extends ClassVisitor { + + public DummyClassVisitor(int api, ClassVisitor cv) { + super(api, cv); + } + + public void visit( + final int version, + final int access, + final String name, + final String signature, + final String superName, + final String[] interfaces) { + // Artificially lower to JDK 8 version to force a redefine + cv.visit(V1_8, access, name, signature, superName, interfaces); + } + } + + @Override public byte[] transform(ClassLoader loader, String className, + Class classBeingRedefined, + ProtectionDomain protectionDomain, byte[] classfileBuffer) + throws IllegalClassFormatException { + + if (className.contains("java/lang/Object")) { + try { + // Here we remove and re-add the dummy fields. This shuffles the constant pool + return asm(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); + } catch (Throwable e) { + // The retransform native code that called this method does not propagate + // exceptions. Instead of getting an uninformative generic error, catch + // problems here and print it, then exit. + e.printStackTrace(); + System.exit(1); + } + } + return null; + } + } + + private static void buildAgent() { + try { + ClassFileInstaller.main("RedefineObject"); + } catch (Exception e) { + throw new RuntimeException("Could not write agent classfile", e); + } + + try { + PrintWriter pw = new PrintWriter("MANIFEST.MF"); + pw.println("Premain-Class: RedefineObject"); + pw.println("Agent-Class: RedefineObject"); + pw.println("Can-Retransform-Classes: true"); + pw.close(); + } catch (FileNotFoundException e) { + throw new RuntimeException("Could not write manifest file for the agent", e); + } + + sun.tools.jar.Main jarTool = new sun.tools.jar.Main(System.out, System.err, "jar"); + if (!jarTool.run(new String[] { "-cmf", "MANIFEST.MF", "redefineagent.jar", "RedefineObject.class" })) { + throw new RuntimeException("Could not write the agent jar file"); + } + } + + public static void main(String[] args) throws Exception { + + int objHash = System.identityHashCode(Object.class); + System.out.println("Object hashCode: " + objHash); + if (args.length == 1 && args[0].equals("buildagent")) { + buildAgent(); + return; + } + + if (inst == null) { + throw new RuntimeException("Instrumentation object was null"); + } + + try { + inst.addTransformer(new RedefineObject.Transformer(), true); + inst.retransformClasses(Object.class); + } catch (UnmodifiableClassException e) { + throw new RuntimeException(e); + } + + // Exercise native methods on Object after transform + Object b = new Object(); + b.hashCode(); + + C c = new C(); + assertTrue(c.hashCode() != c.clone().hashCode() || c != c.clone()); + assertTrue(c.clone() instanceof C); + c = (C)c.clone(); // native method on new Object + } + + private static class C implements Cloneable { + @Override + protected Object clone() throws CloneNotSupportedException { + return super.clone(); + } + } +} diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/bcinstr/BI04/bi04t002/newclass02/java.base/java/lang/Object.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/bcinstr/BI04/bi04t002/newclass02/java.base/java/lang/Object.java --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/bcinstr/BI04/bi04t002/newclass02/java.base/java/lang/Object.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/bcinstr/BI04/bi04t002/newclass02/java.base/java/lang/Object.java @@ -37,11 +37,6 @@ */ public class Object { - private static native void registerNatives(); - static { - registerNatives(); - } - /** * Returns the runtime class of an object. That Class * object is the object that is locked by static synchronized