/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
/*
* Copyright 2003 Wily Technology, Inc.
*/
#include <jni.h>
#include <jvmti.h>
#include <stdlib.h>
#include <string.h>
#include "JPLISAgent.h"
#include "JPLISAssert.h"
#include "Utilities.h"
#include "Reentrancy.h"
#include "JavaExceptions.h"
#include "EncodingSupport.h"
#include "FileSystemSupport.h" /* For MAXPATHLEN & uintptr_t */
#include "sun_instrument_InstrumentationImpl.h"
/*
* The JPLISAgent manages the initialization all of the Java programming language Agents.
* It also supports the native method bridge between the JPLIS and the JVMTI.
* It maintains a single JVMTI Env that all JPL agents share.
* It parses command line requests and creates individual Java agents.
*/
/*
* private prototypes
*/
/* Allocates an unformatted JPLIS agent data structure. Returns NULL if allocation fails. */
/* Initializes an already-allocated JPLIS agent data structure. */
/* De-allocates a JPLIS agent data structure. Only used in partial-failure cases at startup;
* in normal usage the JPLIS agent lives forever
*/
void
JPLISAgent * agent);
/* Does one-time work to interrogate the JVM about capabilities and cache the answers. */
void
/* Takes the elements of the command string (agent class name and options string) and
* create java strings for them.
* Returns true if a classname was found. Makes no promises beyond the textual; says nothing about whether
* the class exists or can be loaded.
* If return value is true, sets outputClassname to a non-NULL local JNI reference.
* If return value is true, sets outputOptionsString either to NULL or to a non-NULL local JNI reference.
* If return value is false, neither output parameter is set.
*/
const char * classname,
const char * optionsString,
/* Start one Java agent from the supplied parameters.
* Most of the logic lives in a helper function that lives over in Java code--
* we pass parameters out to Java and use our own Java helper to actually
* load the agent and call the premain.
* with no exceptions, false otherwise.
*/
/* Once we have loaded the Java agent and called the premain,
* we can release the copies we have been keeping of the command line
* data (agent class name and option strings).
*/
void
/*
* Common support for various class list fetchers.
*/
jint * classCount,
/* Fetcher that ignores the class loader parameter, and uses the JVMTI to get a list of all classes.
* Returns a jvmtiError according to the underlying JVMTI service.
*/
jint * classCount,
/* Fetcher that uses the class loader parameter, and uses the JVMTI to get a list of all classes
* for which the supplied loader is the initiating loader.
* Returns a jvmtiError according to the underlying JVMTI service.
*/
jint * classCount,
/*
* Common guts for two native methods, which are the same except for the policy for fetching
* the list of classes.
* Either returns a local JNI reference to an array of references to java.lang.Class.
* Can throw, if it does will alter the JNIEnv with an outstanding exception.
*/
JPLISAgent * agent,
/*
* Misc. utilities.
*/
/* Checked exception mapper used by the redefine classes implementation.
* Allows ClassNotFoundException or UnmodifiableClassException; maps others
* to InternalError. Can return NULL in an error case.
*/
/* Turns a buffer of jclass * into a Java array whose elements are java.lang.Class.
* Can throw, if it does will alter the JNIEnv with an outstanding exception.
*/
(void**)&environment);
/* can be called from any phase */
if (jvmtierror == JVMTI_ERROR_NONE) {
} else {
environment = NULL;
}
return environment;
}
/*
* OnLoad processing code.
*/
/*
* Creates a new JPLISAgent.
* Returns error if the agent cannot be created and initialized.
* The JPLISAgent* pointed to by agent_ptr is set to the new broker,
* or NULL if an error has occurred.
*/
(void **) &jvmtienv,
} else {
} else {
vm,
jvmtienv);
if ( initerror == JPLIS_INIT_ERROR_NONE ) {
} else {
}
}
/* don't leak envs */
if ( initerror != JPLIS_INIT_ERROR_NONE ) {
/* can be called from any phase */
}
}
return initerror;
}
/*
* Allocates a JPLISAgent. Returns NULL if it cannot be allocated
*/
sizeof(JPLISAgent));
}
/* make sure we can recover either handle in either direction.
* the agent has a ref to the jvmti; make it mutual
*/
&(agent->mNormalEnvironment));
/* can be called from any phase */
/* check what capabilities are available */
/* check phase - if live phase then we don't need the VMInit event */
/* can be called from any phase */
if (phase == JVMTI_PHASE_LIVE) {
return JPLIS_INIT_ERROR_NONE;
}
if (phase != JVMTI_PHASE_ONLOAD) {
/* called too early or called too late; either way bail out */
return JPLIS_INIT_ERROR_FAILURE;
}
/* now turn on the VMInit event */
if ( jvmtierror == JVMTI_ERROR_NONE ) {
sizeof(callbacks));
}
if ( jvmtierror == JVMTI_ERROR_NONE ) {
NULL /* all threads */);
}
}
void
}
const char * agentClassName,
const char * optionsString ) {
/* if no actual params, bail out now */
} else {
if (ourCopyOfAgentClassName == NULL) {
} else {
if (optionsString != NULL) {
if (ourCopyOfOptionsString == NULL) {
}
}
}
}
if (initerror == JPLIS_INIT_ERROR_NONE) {
if (optionsString != NULL) {
}
}
return initerror;
}
/*
* VMInit processing code.
*/
/*
* If this call fails, the JVM launch will ultimately be aborted,
* so we don't have to be super-careful to clean up in partial failure
* cases.
*/
/*
* OK, Java is up now. We can start everything that needs Java.
*/
/*
* First make our emergency fallback InternalError throwable.
*/
/*
* Now make the InstrumentationImpl instance.
*/
if ( result ) {
}
/*
* Then turn off the VMInit handler and turn on the ClassFileLoadHook.
* This way it is on before anyone registers a transformer.
*/
if ( result ) {
}
/*
* Load the Java agent, and call the premain.
*/
if ( result ) {
}
/*
* Finally surrender all of the tracking data that we don't need any more.
* If something is wrong, skip it, we will be aborting the JVM anyway.
*/
if ( result ) {
}
return result;
}
const char * classname,
const char * optionsString,
if (success) {
}
return success;
}
void
/* zero things out so it is easier to see what is going on */
}
/*
* Create the java.lang.instrument.Instrumentation instance
* and access information for it (method IDs, etc)
*/
JPLISAgent * agent) {
/* First find the class of our implementation */
if ( !errorOutstanding ) {
}
if ( !errorOutstanding ) {
}
if ( !errorOutstanding ) {
}
/* Now look up the method ID for the pre-main caller (we will need this more than once) */
if ( !errorOutstanding ) {
}
/* Now look up the method ID for the agent-main caller */
if ( !errorOutstanding ) {
}
/* Now look up the method ID for the transform method (we will need this constantly) */
if ( !errorOutstanding ) {
}
if ( !errorOutstanding ) {
}
return !errorOutstanding;
}
const char * classname,
const char * optionsString,
if ( !errorOutstanding ) {
if ( optionsString != NULL) {
}
if ( !errorOutstanding ) {
}
}
return !errorOutstanding;
}
if ( mainCallingMethod != NULL ) {
if ( errorOutstanding ) {
}
}
return !errorOutstanding;
}
/* first swap out the handlers (switch from the VMInit handler, which we do not need,
* to the ClassFileLoadHook handler, which is what the agents need from now on)
*/
sizeof(callbacks));
if ( jvmtierror == JVMTI_ERROR_NONE ) {
/* turn off VMInit */
NULL /* all threads */);
}
if ( jvmtierror == JVMTI_ERROR_NONE ) {
/* turn on ClassFileLoadHook */
NULL /* all threads */);
}
return (jvmtierror == JVMTI_ERROR_NONE);
}
/**
* Check if the can_redefine_classes capability is available.
*/
void
if ( jvmtierror == JVMTI_ERROR_NONE ) {
}
}
}
}
/**
* Enable native method prefix in one JVM TI environment
*/
void
/* can be called from any phase */
}
/**
* Add the can_set_native_method_prefix capability
*/
void
}
}
}
/**
* Add the can_maintain_original_method_order capability (for testing)
*/
void
/* can be called from any phase */
}
/**
* Add the can_redefine_classes capability
*/
void
/* can be called from any phase */
/*
* capability was potentially available in the onload phase but
* subsequently unavailable in the live phase.
*/
if (jvmtierror == JVMTI_ERROR_NONE) {
}
}
}
/*
* Support for the JVMTI callbacks
*/
void
const char* name,
const unsigned char* class_data,
unsigned char** new_class_data,
/* only do this if we aren't already in the middle of processing a class on this thread */
NULL); /* this thread */
if ( shouldRun ) {
/* first marshall all the parameters */
name);
if ( !errorOutstanding ) {
}
if ( !errorOutstanding ) {
/* The sign cast is safe. The const cast is dumb. */
0,
}
/* now call the JPL agents to do the transforming */
/* potential future optimization: may want to skip this if there are none */
if ( !errorOutstanding ) {
}
/* Finally, unmarshall the parameters (if someone touched the buffer, tell the JVM) */
if ( !errorOutstanding ) {
if ( transformedBufferObject != NULL ) {
if ( !errorOutstanding ) {
/* allocate the response buffer with the JVMTI allocate call.
* This is what the JVMTI spec says to do for Class File Load hook responses
*/
&resultBuffer);
}
if ( !errorOutstanding ) {
0,
(jbyte *) resultBuffer);
/* in this case, we will not return the buffer to the JVMTI,
* so we need to deallocate it ourselves
*/
if ( errorOutstanding ) {
(void*)resultBuffer);
}
}
if ( !errorOutstanding ) {
}
}
}
/* release the token */
}
return;
}
/*
* Misc. internal utilities.
*/
/*
* The only checked exceptions we can throw are ClassNotFoundException and
* UnmodifiableClassException. All others map to InternalError.
*/
if ( isInstanceofClassName( jnienv,
"java/lang/ClassNotFoundException") ) {
} else {
if ( isInstanceofClassName( jnienv,
} else {
}
}
return mappedThrowable;
}
/* get the class array class */
if (!errorOccurred) {
/* create the array for the classes */
if (!errorOccurred) {
/* now copy refs to all the classes and put them into the array */
/* put class into array */
if (errorOccurred) {
localArray = NULL;
break;
}
}
}
}
return localArray;
}
/* Return the environment with the retransformation capability.
* Create it if it doesn't exist.
* Return NULL if it can't be created.
*/
jvmtiEnv *
}
(void **) &retransformerEnv,
return NULL;
}
if (agent->mNativeMethodPrefixAdded) {
}
if (jvmtierror != JVMTI_ERROR_NONE) {
/* cannot get the capability, dispose of the retransforming environment */
return NULL;
}
sizeof(callbacks));
if (jvmtierror == JVMTI_ERROR_NONE) {
// install the retransforming environment
// Make it for ClassFileLoadHook handling
&(agent->mRetransformEnvironment));
if (jvmtierror == JVMTI_ERROR_NONE) {
return retransformerEnv;
}
}
return NULL;
}
/*
* Underpinnings for native methods
*/
return is_modifiable;
}
}
void
NULL /* all threads */);
}
void
/* This is supposed to be checked by caller, but just to be sure */
if (retransformerEnv == NULL) {
}
/* This was supposed to be checked by caller too */
}
if (!errorOccurred) {
if (!errorOccurred && numClasses == 0) {
jplis_assert(numClasses != 0);
}
}
if (!errorOccurred) {
numClasses * sizeof(jclass));
if (errorOccurred) {
}
}
if (!errorOccurred) {
if (errorOccurred) {
break;
}
break;
}
}
}
if (!errorOccurred) {
}
/* Give back the buffer if we allocated it. Throw any exceptions after.
*/
if (classArray != NULL) {
}
if (errorCode != JVMTI_ERROR_NONE) {
}
}
/*
* Java code must not call this with a null list or a zero-length list.
*/
void
if (!errorOccurred) {
jplis_assert(numDefs > 0);
/* get method IDs for methods to call on class definitions */
}
if (!errorOccurred) {
"getDefinitionClass",
}
if (!errorOccurred) {
"getDefinitionClassFile",
"()[B");
}
if (!errorOccurred) {
numDefs * sizeof(jvmtiClassDefinition));
if ( errorOccurred ) {
}
else {
/*
* We have to save the targetFile values that we compute so
* that we can release the class_bytes arrays that are
* returned by GetByteArrayElements(). In case of a JNI
* error, we can't (easily) recompute the targetFile values
* and we still want to free any memory we allocated.
*/
numDefs * sizeof(jbyteArray));
if ( errorOccurred ) {
}
else {
jint i, j;
// clear classDefs so we can correctly free memory during errors
for (i = 0; i < numDefs; i++) {
if (errorOccurred) {
break;
}
if (errorOccurred) {
break;
}
if (errorOccurred) {
break;
}
if (errorOccurred) {
break;
}
/*
* Allocate class_bytes last so we don't have to free
* memory on a partial row error.
*/
classDefs[i].class_bytes = (unsigned char*)(*jnienv)->GetByteArrayElements(jnienv, targetFiles[i], NULL);
if (errorOccurred) {
break;
}
}
if (!errorOccurred) {
if (errorCode == JVMTI_ERROR_WRONG_PHASE) {
/* insulate caller from the wrong phase error */
} else {
if ( errorOccurred ) {
}
}
}
/*
* Cleanup memory that we allocated above. If we had a
* tracks how far we got in processing the classDefs
* array. Note: ReleaseByteArrayElements() is safe to
* call with a JNI exception pending.
*/
for (j = 0; j < i; j++) {
0 /* copy back and free */);
/*
* Only check for error if we didn't already have one
* so we don't overwrite errorOccurred.
*/
if (!errorOccurred) {
}
}
}
}
}
}
}
/* Cheesy sharing. ClassLoader may be null. */
JPLISAgent * agent,
/* retrieve the classes from the JVMTI agent */
&classes);
if ( errorOccurred ) {
} else {
/* do this whether or not we saw a problem */
}
return localArray;
}
jint * classCount,
}
return commonGetClassList( jnienv,
NULL,
}
jint * classCount,
}
return commonGetClassList( jnienv,
}
if ( jvmtierror != JVMTI_ERROR_NONE ) {
}
return objectSize;
}
void
appendToClassLoaderSearch(JNIEnv * jnienv, JPLISAgent * agent, jstring jarFile, jboolean isBootLoader)
{
const char* utf8Chars;
int platformLen;
if (!errorOutstanding) {
/*
* JVMTI spec'ed to use modified UTF8. At this time this is not implemented
* the platform encoding is used.
*/
if (platformLen < 0) {
return;
}
if (!errorOutstanding) {
if (isBootLoader) {
} else {
}
if ( jvmtierror != JVMTI_ERROR_NONE ) {
}
}
}
}
}
/*
* Set the prefixes used to wrap native methods (so they can be instrumented).
* Each transform can set a prefix, any that have been set come in as prefixArray.
* Convert them in native strings in a native array then call JVM TI.
* One a given call, this function handles either the prefixes for retransformable
* transforms or for normal transforms.
*/
void
if (isRetransformable) {
} else {
}
if (!errorOccurred) {
/* allocate the native to hold the native prefixes */
arraySize * sizeof(char*));
/* since JNI ReleaseStringUTFChars needs the jstring from which the native
* string was allocated, we store them in a parallel array */
if ( errorOccurred ) {
}
else {
jint i;
for (i = 0; i < arraySize; i++) {
const char* prefix;
prefixArray, i));
if (errorOccurred) {
break;
}
continue;
}
if (errorOccurred) {
break;
}
if (prefixLen > 0) {
++inx;
}
}
}
/* can be called from any phase */
for (i = 0; i < inx; i++) {
}
}
}
}