/* * Copyright (c) 2007, 2018, 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. */ package nsk.jvmti.SetNativeMethodPrefix; import java.io.*; import nsk.share.Consts; /* ============================================================================== */ /* Quote from JVMTI specification v.1.1.102: * * Since native methods cannot be directly instrumented (they have no * bytecodes), they must be wrapped with a non-native method which can be * instrumented. For example, if we had: * native boolean foo(int x); * * We could transform the class file (with the ClassFileLoadHook event) so * that this becomes: * boolean foo(int x) { * ... record entry to foo ... * return wrapped_foo(x); * } * * native boolean wrapped_foo(int x); * * Where foo becomes a wrapper for the actual native method with the appended * prefix "wrapped_". * * The wrapper will allow data to be collected on the native method call, but * now the problem becomes linking up the wrapped method with the native * implementation. That is, the method wrapped_foo needs to be resolved to the * native implementation of foo, which might be: * * Java_somePackage_someClass_foo(JNIEnv* env, jint x) * * This function allows the prefix to be specified and the proper resolution * to occur. Specifically, when the standard resolution fails, the resolution * is retried taking the prefix into consideration. There are two ways that * resolution occurs, explicit resolution with the JNI function * RegisterNatives and the normal automatic resolution. For RegisterNatives, * the VM will attempt this association: * * method(foo) -> nativeImplementation(foo) * * When this fails, the resolution will be retried with the specified prefix * prepended to the method name, yielding the correct resolution: * * method(wrapped_foo) -> nativeImplementation(foo) * * For automatic resolution, the VM will attempt: * * method(wrapped_foo) -> nativeImplementation(wrapped_foo) * * When this fails, the resolution will be retried with the specified prefix * deleted from the implementation name, yielding the correct resolution: * * method(wrapped_foo) -> nativeImplementation(foo) * * Note that since the prefix is only used when standard resolution fails, * native methods can be wrapped selectively. */ /* ============================================================================== */ /* ======================================================= */ // Auxiliary classes for explicit method resolution /* ======================================================= */ class ExplicitResolution1 { static public int foo() { return wrapped_foo(); } native static public int wrapped_foo(); } /* ======================================================= */ class ExplicitResolution2 { native static public int wrapped_foo(); } /* ======================================================= */ // Auxiliary classes for automatic resolution /* ======================================================= */ class AutomaticResolution1 { static public int foo() { return wrapped_foo(); } native static public int wrapped_foo(); } /* ======================================================= */ class AutomaticResolution2 { static public int foo() { return wrapped_foo(); } native static public int wrapped_foo(); } /* ======================================================= */ class AutomaticResolution3 { static public int foo() { return wrapped_foo(); } native static public int wrapped_foo(); } /* ======================================================= */ public class SetNativeMethodPrefix001 { PrintStream out = System.out; static public final String prefix = "wrapped_"; /* ============================================================ */ // // For RegisterNatives, the VM will attempt this association: // // method(foo) -> nativeImplementation(foo) // // When this fails, the resolution will be retried with the specified // prefix prepended to the method name, yielding the correct resolution: // // method(wrapped_foo) -> nativeImplementation(foo) // // The prefix is only used when standard resolution fails. // /* ============================================================ */ public boolean checkExplicitResolution1(boolean isPrefixSet) { // Initiate class A loading and initialization // See CR 6493522 for details new ExplicitResolution1(); // Bind ExplicitResolution1.foo() to a native function. // If the prefix isn't set, this code should fail since ExplicitResolution1.foo() isn't native. if (Binder.registerMethod(ExplicitResolution1.class, "foo", "()I", Binder.FUNCTION_FOO) != isPrefixSet) { out.println("ERROR: unexpected RegisterNatives() behavior."); return false; } if (isPrefixSet) { if (ExplicitResolution1.foo() != Binder.FOO_RETURN) { out.println("ERROR: native method wrapped_foo() wasn't correctly bound."); return false; } } // Bind ExplicitResolution1.wrapped_foo() to a native fucntion. // This code should succeed since ExplicitResolution1.wrapped_foo() is native. if (!Binder.registerMethod(ExplicitResolution1.class, "wrapped_foo", "()I", Binder.FUNCTION_WRAPPED_FOO)) { out.println("ERROR: RegisterNative() failed for native method ExplicitResolution1.wrapped_foo()."); return false; } if (ExplicitResolution1.foo() != Binder.WRAPPED_FOO_RETURN) { out.println("ERROR: native method wrapped_foo() wasn't correctly bound."); return false; } return true; } /* ============================================================ */ public boolean checkExplicitResolution2() { // Initiate class ExplicitResolution2 loading and initialization // See CR 6493522 for details new ExplicitResolution2(); // This binding should fail whether the prefix is set or not since: // - there's no Java_nsk_jvmti_SetNativeMethodPrefix_ExplicitResolution2_wrapped_1foo defined // - class ExplicitResolution2 doesn't have java method foo() if (Binder.registerMethod(ExplicitResolution2.class, "foo", "()I", Binder.FUNCTION_WRAPPED_FOO)) { out.println("ERROR: unexpected RegisterNatives() behavior."); return false; } return true; } /* ============================================================ */ public boolean checkExplicitResolution (boolean isMultiplePrefixes) { // Setting method prefix out.println("\tSetting prefix: "+prefix); if (isMultiplePrefixes) { if (!Binder.setMultiplePrefixes(prefix)) { return false; } } else { if (!Binder.setMethodPrefix(prefix)) { return false; } } // Check the behavior out.println("\t\tChecking resolution for ExplicitResolution1"); if (!checkExplicitResolution1(true)) { return false; } out.println("\t\tChecking resolution for ExplicitResolution2"); if (!checkExplicitResolution2()) { return false; } // Resetting method prefix out.println("\tResetting prefix"); if (isMultiplePrefixes) { if (!Binder.setMultiplePrefixes(null)) { return false; } } else { if (!Binder.setMethodPrefix(null)) { return false; } } // Check the behavior out.println("\t\tChecking resolution for ExplicitResolution1"); if (!checkExplicitResolution1(false)) { return false; } out.println("\t\tChecking resolution for ExplicitResolution2"); if (!checkExplicitResolution2()) { return false; } return true; } /* ============================================================ */ // For automatic resolution, the VM will attempt: // // method(wrapped_foo) -> nativeImplementation(wrapped_foo) // // When this fails, the resolution will be retried with the specified // prefix deleted from the implementation name, yielding the correct // resolution: // // method(wrapped_foo) -> nativeImplementation(foo) // // The prefix is only used when standard resolution fails. /* ============================================================ */ public boolean checkAutomaticResolution1() { if (AutomaticResolution1.foo() != Binder.WRAPPED_FOO_RETURN) { out.println("ERROR: native method AutomaticResolution1.wrapped_foo() wasn't correctly bound."); return false; } return true; } /* ============================================================ */ public boolean checkAutomaticResolution2 (boolean isPrefixSet) { if (isPrefixSet) { if (AutomaticResolution2.foo() != Binder.FOO_RETURN) { out.println("ERROR: native method AutomaticResolution2.wrapped_foo() wasn't correctly bound."); return false; } } else { try { AutomaticResolution3.foo(); out.println("ERROR: native method AutomaticResolution3.wrapped_foo() was bound."); return false; } catch (UnsatisfiedLinkError e) { out.println(String.format("The method isn't bound: %s %s\n", e.getClass().getName(), e.getMessage())); return true; } } return true; } /* ============================================================ */ public boolean checkAutomaticResolution (boolean isMultiplePrefixes) { // Setting method prefix out.println("\tSetting prefix: "+prefix); if (isMultiplePrefixes) { if (!Binder.setMultiplePrefixes(prefix)) { return false; } } else { if (!Binder.setMethodPrefix(prefix)) { return false; } } // Check the behavior out.println("\t\tChecking resolution for AutomaticResolution1"); if (!checkAutomaticResolution1()) { return false; } out.println("\t\tChecking resolution for AutomaticResolution2"); if (!checkAutomaticResolution2(true)) { return false; } // Resetting method prefix out.println("\tResetting prefix"); if (isMultiplePrefixes) { if (!Binder.setMultiplePrefixes(null)) { return false; } } else { if (!Binder.setMethodPrefix(null)) { return false; } } // Check the behavior out.println("\t\tChecking resolution for AutomaticResolution1"); if (!checkAutomaticResolution1()) { return false; } out.println("\t\tChecking resolution for AutomaticResolution2"); if (!checkAutomaticResolution2(false)) { return false; } return true; } /* ============================================================ */ public boolean runIt(String [] args, PrintStream _out) { if (_out != null) { out = _out; } out.println("\nAutomatic resolution; SetMethodPrefix is used"); if (!checkAutomaticResolution(true)) { return false; } out.println("\nAutomatic resolution; SetMultiplePrefixes is used"); if (!checkAutomaticResolution(false)) { return false; } out.println("\nExplicit resolution; SetMethodPrefix is used"); if (!checkExplicitResolution(true)) { return false; } out.println("\nExplicit resolution; SetMultiplePrefixes is used"); if (!checkExplicitResolution(false)) { return false; } return true; } /* ============================================================ */ public static int run(String argv[], PrintStream out) { if ((new SetNativeMethodPrefix001()).runIt(argv, out)) { out.println("TEST PASSED"); return Consts.TEST_PASSED; } else { out.println("TEST FAILED"); return Consts.TEST_FAILED; } } /* ============================================================ */ public static void main(String[] argv) { argv = nsk.share.jvmti.JVMTITest.commonInit(argv); System.exit(run(argv, System.out) + Consts.JCK_STATUS_BASE); } }