src/java.instrument/share/native/libinstrument/InvocationAdapter.c
changeset 47216 71c04702a3d5
parent 45004 ea3137042a61
child 47777 d85284ccd1bd
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2003, 2017, 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.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 /*
       
    27  * Copyright 2003 Wily Technology, Inc.
       
    28  */
       
    29 
       
    30 #include    <string.h>
       
    31 #include    <stdlib.h>
       
    32 
       
    33 #include    "jni.h"
       
    34 
       
    35 #include    "Utilities.h"
       
    36 #include    "JPLISAssert.h"
       
    37 #include    "JPLISAgent.h"
       
    38 #include    "JavaExceptions.h"
       
    39 
       
    40 #include    "EncodingSupport.h"
       
    41 #include    "FileSystemSupport.h"
       
    42 #include    "JarFacade.h"
       
    43 #include    "PathCharsValidator.h"
       
    44 
       
    45 /**
       
    46  * This module contains the direct interface points with the JVMTI.
       
    47  * The OnLoad handler is here, along with the various event handlers.
       
    48  */
       
    49 
       
    50 static int
       
    51 appendClassPath(JPLISAgent* agent,
       
    52                 const char* jarfile);
       
    53 
       
    54 static void
       
    55 appendBootClassPath(JPLISAgent* agent,
       
    56                     const char* jarfile,
       
    57                     const char* pathList);
       
    58 
       
    59 
       
    60 /*
       
    61  * Parse -javaagent tail, of the form name[=options], into name
       
    62  * and options. Returned values are heap allocated and options maybe
       
    63  * NULL. Returns 0 if parse succeeds, -1 if allocation fails.
       
    64  */
       
    65 static int
       
    66 parseArgumentTail(char* tail, char** name, char** options) {
       
    67     int len;
       
    68     char* pos;
       
    69 
       
    70     pos = strchr(tail, '=');
       
    71     len = (pos == NULL) ? (int)strlen(tail) : (int)(pos - tail);
       
    72 
       
    73     *name = (char*)malloc(len+1);
       
    74     if (*name == NULL) {
       
    75         return -1;
       
    76     }
       
    77     memcpy(*name, tail, len);
       
    78     (*name)[len] = '\0';
       
    79 
       
    80     if (pos == NULL) {
       
    81         *options = NULL;
       
    82     } else {
       
    83         char * str = (char*)malloc( (int)strlen(pos + 1) + 1 );
       
    84         if (str == NULL) {
       
    85             free(*name);
       
    86             return -1;
       
    87         }
       
    88         strcpy(str, pos +1);
       
    89         *options = str;
       
    90     }
       
    91     return 0;
       
    92 }
       
    93 
       
    94 /*
       
    95  * Get the value of an attribute in an attribute list. Returns NULL
       
    96  * if attribute not found.
       
    97  */
       
    98 jboolean
       
    99 getBooleanAttribute(const jarAttribute* attributes, const char* name) {
       
   100     char* attributeValue = getAttribute(attributes, name);
       
   101     return attributeValue != NULL && strcasecmp(attributeValue, "true") == 0;
       
   102 }
       
   103 
       
   104 /*
       
   105  * Parse any capability settings in the JAR manifest and
       
   106  * convert them to JVM TI capabilities.
       
   107  */
       
   108 void
       
   109 convertCapabilityAttributes(const jarAttribute* attributes, JPLISAgent* agent) {
       
   110     /* set redefineClasses capability */
       
   111     if (getBooleanAttribute(attributes, "Can-Redefine-Classes")) {
       
   112         addRedefineClassesCapability(agent);
       
   113     }
       
   114 
       
   115     /* create an environment which has the retransformClasses capability */
       
   116     if (getBooleanAttribute(attributes, "Can-Retransform-Classes")) {
       
   117         retransformableEnvironment(agent);
       
   118     }
       
   119 
       
   120     /* set setNativeMethodPrefix capability */
       
   121     if (getBooleanAttribute(attributes, "Can-Set-Native-Method-Prefix")) {
       
   122         addNativeMethodPrefixCapability(agent);
       
   123     }
       
   124 
       
   125     /* for retransformClasses testing, set capability to use original method order */
       
   126     if (getBooleanAttribute(attributes, "Can-Maintain-Original-Method-Order")) {
       
   127         addOriginalMethodOrderCapability(agent);
       
   128     }
       
   129 }
       
   130 
       
   131 /*
       
   132  *  This will be called once for every -javaagent on the command line.
       
   133  *  Each call to Agent_OnLoad will create its own agent and agent data.
       
   134  *
       
   135  *  The argument tail string provided to Agent_OnLoad will be of form
       
   136  *  <jarfile>[=<options>]. The tail string is split into the jarfile and
       
   137  *  options components. The jarfile manifest is parsed and the value of the
       
   138  *  Premain-Class attribute will become the agent's premain class. The jar
       
   139  *  file is then added to the system class path, and if the Boot-Class-Path
       
   140  *  attribute is present then all relative URLs in the value are processed
       
   141  *  to create boot class path segments to append to the boot class path.
       
   142  */
       
   143 JNIEXPORT jint JNICALL
       
   144 DEF_Agent_OnLoad(JavaVM *vm, char *tail, void * reserved) {
       
   145     JPLISInitializationError initerror  = JPLIS_INIT_ERROR_NONE;
       
   146     jint                     result     = JNI_OK;
       
   147     JPLISAgent *             agent      = NULL;
       
   148 
       
   149     initerror = createNewJPLISAgent(vm, &agent);
       
   150     if ( initerror == JPLIS_INIT_ERROR_NONE ) {
       
   151         int             oldLen, newLen;
       
   152         char *          jarfile;
       
   153         char *          options;
       
   154         jarAttribute*   attributes;
       
   155         char *          premainClass;
       
   156         char *          bootClassPath;
       
   157 
       
   158         /*
       
   159          * Parse <jarfile>[=options] into jarfile and options
       
   160          */
       
   161         if (parseArgumentTail(tail, &jarfile, &options) != 0) {
       
   162             fprintf(stderr, "-javaagent: memory allocation failure.\n");
       
   163             return JNI_ERR;
       
   164         }
       
   165 
       
   166         /*
       
   167          * Agent_OnLoad is specified to provide the agent options
       
   168          * argument tail in modified UTF8. However for 1.5.0 this is
       
   169          * actually in the platform encoding - see 5049313.
       
   170          *
       
   171          * Open zip/jar file and parse archive. If can't be opened or
       
   172          * not a zip file return error. Also if Premain-Class attribute
       
   173          * isn't present we return an error.
       
   174          */
       
   175         attributes = readAttributes(jarfile);
       
   176         if (attributes == NULL) {
       
   177             fprintf(stderr, "Error opening zip file or JAR manifest missing : %s\n", jarfile);
       
   178             free(jarfile);
       
   179             if (options != NULL) free(options);
       
   180             return JNI_ERR;
       
   181         }
       
   182 
       
   183         premainClass = getAttribute(attributes, "Premain-Class");
       
   184         if (premainClass == NULL) {
       
   185             fprintf(stderr, "Failed to find Premain-Class manifest attribute in %s\n",
       
   186                 jarfile);
       
   187             free(jarfile);
       
   188             if (options != NULL) free(options);
       
   189             freeAttributes(attributes);
       
   190             return JNI_ERR;
       
   191         }
       
   192 
       
   193         /* Save the jarfile name */
       
   194         agent->mJarfile = jarfile;
       
   195 
       
   196         /*
       
   197          * The value of the Premain-Class attribute becomes the agent
       
   198          * class name. The manifest is in UTF8 so need to convert to
       
   199          * modified UTF8 (see JNI spec).
       
   200          */
       
   201         oldLen = (int)strlen(premainClass);
       
   202         newLen = modifiedUtf8LengthOfUtf8(premainClass, oldLen);
       
   203         if (newLen == oldLen) {
       
   204             premainClass = strdup(premainClass);
       
   205         } else {
       
   206             char* str = (char*)malloc( newLen+1 );
       
   207             if (str != NULL) {
       
   208                 convertUtf8ToModifiedUtf8(premainClass, oldLen, str, newLen);
       
   209             }
       
   210             premainClass = str;
       
   211         }
       
   212         if (premainClass == NULL) {
       
   213             fprintf(stderr, "-javaagent: memory allocation failed\n");
       
   214             free(jarfile);
       
   215             if (options != NULL) free(options);
       
   216             freeAttributes(attributes);
       
   217             return JNI_ERR;
       
   218         }
       
   219 
       
   220         /*
       
   221          * If the Boot-Class-Path attribute is specified then we process
       
   222          * each relative URL and add it to the bootclasspath.
       
   223          */
       
   224         bootClassPath = getAttribute(attributes, "Boot-Class-Path");
       
   225         if (bootClassPath != NULL) {
       
   226             appendBootClassPath(agent, jarfile, bootClassPath);
       
   227         }
       
   228 
       
   229         /*
       
   230          * Convert JAR attributes into agent capabilities
       
   231          */
       
   232         convertCapabilityAttributes(attributes, agent);
       
   233 
       
   234         /*
       
   235          * Track (record) the agent class name and options data
       
   236          */
       
   237         initerror = recordCommandLineData(agent, premainClass, options);
       
   238 
       
   239         /*
       
   240          * Clean-up
       
   241          */
       
   242         if (options != NULL) free(options);
       
   243         freeAttributes(attributes);
       
   244         free(premainClass);
       
   245     }
       
   246 
       
   247     switch (initerror) {
       
   248     case JPLIS_INIT_ERROR_NONE:
       
   249       result = JNI_OK;
       
   250       break;
       
   251     case JPLIS_INIT_ERROR_CANNOT_CREATE_NATIVE_AGENT:
       
   252       result = JNI_ERR;
       
   253       fprintf(stderr, "java.lang.instrument/-javaagent: cannot create native agent.\n");
       
   254       break;
       
   255     case JPLIS_INIT_ERROR_FAILURE:
       
   256       result = JNI_ERR;
       
   257       fprintf(stderr, "java.lang.instrument/-javaagent: initialization of native agent failed.\n");
       
   258       break;
       
   259     case JPLIS_INIT_ERROR_ALLOCATION_FAILURE:
       
   260       result = JNI_ERR;
       
   261       fprintf(stderr, "java.lang.instrument/-javaagent: allocation failure.\n");
       
   262       break;
       
   263     case JPLIS_INIT_ERROR_AGENT_CLASS_NOT_SPECIFIED:
       
   264       result = JNI_ERR;
       
   265       fprintf(stderr, "-javaagent: agent class not specified.\n");
       
   266       break;
       
   267     default:
       
   268       result = JNI_ERR;
       
   269       fprintf(stderr, "java.lang.instrument/-javaagent: unknown error\n");
       
   270       break;
       
   271     }
       
   272     return result;
       
   273 }
       
   274 
       
   275 /*
       
   276  * Agent_OnAttach returns a jint. 0/JNI_OK indicates success and non-0
       
   277  * indicates an error. To allow the attach mechanism throw an
       
   278  * AgentInitializationException with a reasonable exception message we define
       
   279  * a few specific errors here.
       
   280  */
       
   281 #define AGENT_ERROR_BADJAR    ((jint)100)  /* Agent JAR not found or no Agent-Class attribute */
       
   282 #define AGENT_ERROR_NOTONCP   ((jint)101)  /* Unable to add JAR file to system class path */
       
   283 #define AGENT_ERROR_STARTFAIL ((jint)102)  /* No agentmain method or agentmain failed */
       
   284 
       
   285 /*
       
   286  *  This will be called once each time a tool attaches to the VM and loads
       
   287  *  the JPLIS library.
       
   288  */
       
   289 JNIEXPORT jint JNICALL
       
   290 DEF_Agent_OnAttach(JavaVM* vm, char *args, void * reserved) {
       
   291     JPLISInitializationError initerror  = JPLIS_INIT_ERROR_NONE;
       
   292     jint                     result     = JNI_OK;
       
   293     JPLISAgent *             agent      = NULL;
       
   294     JNIEnv *                 jni_env    = NULL;
       
   295 
       
   296     /*
       
   297      * Need JNIEnv - guaranteed to be called from thread that is already
       
   298      * attached to VM
       
   299      */
       
   300     result = (*vm)->GetEnv(vm, (void**)&jni_env, JNI_VERSION_1_2);
       
   301     jplis_assert(result==JNI_OK);
       
   302 
       
   303     initerror = createNewJPLISAgent(vm, &agent);
       
   304     if ( initerror == JPLIS_INIT_ERROR_NONE ) {
       
   305         int             oldLen, newLen;
       
   306         char *          jarfile;
       
   307         char *          options;
       
   308         jarAttribute*   attributes;
       
   309         char *          agentClass;
       
   310         char *          bootClassPath;
       
   311         jboolean        success;
       
   312 
       
   313         /*
       
   314          * Parse <jarfile>[=options] into jarfile and options
       
   315          */
       
   316         if (parseArgumentTail(args, &jarfile, &options) != 0) {
       
   317             return JNI_ENOMEM;
       
   318         }
       
   319 
       
   320         /*
       
   321          * Open the JAR file and parse the manifest
       
   322          */
       
   323         attributes = readAttributes( jarfile );
       
   324         if (attributes == NULL) {
       
   325             fprintf(stderr, "Error opening zip file or JAR manifest missing: %s\n", jarfile);
       
   326             free(jarfile);
       
   327             if (options != NULL) free(options);
       
   328             return AGENT_ERROR_BADJAR;
       
   329         }
       
   330 
       
   331         agentClass = getAttribute(attributes, "Agent-Class");
       
   332         if (agentClass == NULL) {
       
   333             fprintf(stderr, "Failed to find Agent-Class manifest attribute from %s\n",
       
   334                 jarfile);
       
   335             free(jarfile);
       
   336             if (options != NULL) free(options);
       
   337             freeAttributes(attributes);
       
   338             return AGENT_ERROR_BADJAR;
       
   339         }
       
   340 
       
   341         /*
       
   342          * Add the jarfile to the system class path
       
   343          */
       
   344         if (appendClassPath(agent, jarfile)) {
       
   345             fprintf(stderr, "Unable to add %s to system class path "
       
   346                 "- not supported by system class loader or configuration error!\n",
       
   347                 jarfile);
       
   348             free(jarfile);
       
   349             if (options != NULL) free(options);
       
   350             freeAttributes(attributes);
       
   351             return AGENT_ERROR_NOTONCP;
       
   352         }
       
   353 
       
   354         /*
       
   355          * The value of the Agent-Class attribute becomes the agent
       
   356          * class name. The manifest is in UTF8 so need to convert to
       
   357          * modified UTF8 (see JNI spec).
       
   358          */
       
   359         oldLen = (int)strlen(agentClass);
       
   360         newLen = modifiedUtf8LengthOfUtf8(agentClass, oldLen);
       
   361         if (newLen == oldLen) {
       
   362             agentClass = strdup(agentClass);
       
   363         } else {
       
   364             char* str = (char*)malloc( newLen+1 );
       
   365             if (str != NULL) {
       
   366                 convertUtf8ToModifiedUtf8(agentClass, oldLen, str, newLen);
       
   367             }
       
   368             agentClass = str;
       
   369         }
       
   370         if (agentClass == NULL) {
       
   371             free(jarfile);
       
   372             if (options != NULL) free(options);
       
   373             freeAttributes(attributes);
       
   374             return JNI_ENOMEM;
       
   375         }
       
   376 
       
   377         /*
       
   378          * If the Boot-Class-Path attribute is specified then we process
       
   379          * each URL - in the live phase only JAR files will be added.
       
   380          */
       
   381         bootClassPath = getAttribute(attributes, "Boot-Class-Path");
       
   382         if (bootClassPath != NULL) {
       
   383             appendBootClassPath(agent, jarfile, bootClassPath);
       
   384         }
       
   385 
       
   386         /*
       
   387          * Convert JAR attributes into agent capabilities
       
   388          */
       
   389         convertCapabilityAttributes(attributes, agent);
       
   390 
       
   391         /*
       
   392          * Create the java.lang.instrument.Instrumentation instance
       
   393          */
       
   394         success = createInstrumentationImpl(jni_env, agent);
       
   395         jplis_assert(success);
       
   396 
       
   397         /*
       
   398          *  Turn on the ClassFileLoadHook.
       
   399          */
       
   400         if (success) {
       
   401             success = setLivePhaseEventHandlers(agent);
       
   402             jplis_assert(success);
       
   403         }
       
   404 
       
   405         /*
       
   406          * Start the agent
       
   407          */
       
   408         if (success) {
       
   409             success = startJavaAgent(agent,
       
   410                                      jni_env,
       
   411                                      agentClass,
       
   412                                      options,
       
   413                                      agent->mAgentmainCaller);
       
   414         }
       
   415 
       
   416         if (!success) {
       
   417             fprintf(stderr, "Agent failed to start!\n");
       
   418             result = AGENT_ERROR_STARTFAIL;
       
   419         }
       
   420 
       
   421         /*
       
   422          * Clean-up
       
   423          */
       
   424         free(jarfile);
       
   425         if (options != NULL) free(options);
       
   426         free(agentClass);
       
   427         freeAttributes(attributes);
       
   428     }
       
   429 
       
   430     return result;
       
   431 }
       
   432 
       
   433 
       
   434 JNIEXPORT void JNICALL
       
   435 DEF_Agent_OnUnload(JavaVM *vm) {
       
   436 }
       
   437 
       
   438 /**
       
   439  * Invoked by the java launcher to load an agent in the main executable JAR.
       
   440  * The Launcher-Agent-Class attribute in the main manifest of the JAR file
       
   441  * is the agent class.
       
   442  *
       
   443  * Returns JNI_OK if the agent is loaded and initialized; JNI_ERR if this
       
   444  * function fails, possibly with a pending exception.
       
   445  */
       
   446 jint loadAgent(JNIEnv* env, jstring path) {
       
   447     JavaVM* vm;
       
   448     JPLISAgent* agent;
       
   449     const char* jarfile = NULL;
       
   450     jarAttribute* attributes = NULL;
       
   451     char* agentClass = NULL;
       
   452     char* bootClassPath;
       
   453     int oldLen, newLen;
       
   454     jint result = JNI_ERR;
       
   455 
       
   456     if ((*env)->GetJavaVM(env, &vm) < 0) {
       
   457         return JNI_ERR;
       
   458     }
       
   459 
       
   460     // create JPLISAgent with JVMTI environment
       
   461     if (createNewJPLISAgent(vm, &agent) != JPLIS_INIT_ERROR_NONE) {
       
   462         return JNI_ERR;
       
   463     }
       
   464 
       
   465     // get path to JAR file as UTF-8 string
       
   466     jarfile = (*env)->GetStringUTFChars(env, path, NULL);
       
   467     if (jarfile == NULL) {
       
   468         return JNI_ERR;
       
   469     }
       
   470 
       
   471     // read the attributes in the main section of JAR manifest
       
   472     attributes = readAttributes(jarfile);
       
   473     if (attributes == NULL) {
       
   474         goto releaseAndReturn;
       
   475     }
       
   476 
       
   477     // Launcher-Agent-Class is required
       
   478     agentClass = getAttribute(attributes, "Launcher-Agent-Class");
       
   479     if (agentClass == NULL) {
       
   480         goto releaseAndReturn;
       
   481     }
       
   482 
       
   483     // The value of Launcher-Agent-Class is in UTF-8, convert it to modified UTF-8
       
   484     oldLen = (int) strlen(agentClass);
       
   485     newLen = modifiedUtf8LengthOfUtf8(agentClass, oldLen);
       
   486     if (newLen == oldLen) {
       
   487         agentClass = strdup(agentClass);
       
   488     } else {
       
   489         char* str = (char*) malloc(newLen + 1);
       
   490         if (str != NULL) {
       
   491             convertUtf8ToModifiedUtf8(agentClass, oldLen, str, newLen);
       
   492         }
       
   493         agentClass = str;
       
   494     }
       
   495     if (agentClass == NULL) {
       
   496          jthrowable oome = createThrowable(env, "java/lang/OutOfMemoryError", NULL);
       
   497          if (oome != NULL) (*env)->Throw(env, oome);
       
   498          goto releaseAndReturn;
       
   499     }
       
   500 
       
   501     // Boot-Class-Path
       
   502     bootClassPath = getAttribute(attributes, "Boot-Class-Path");
       
   503     if (bootClassPath != NULL) {
       
   504         appendBootClassPath(agent, jarfile, bootClassPath);
       
   505     }
       
   506 
       
   507     // Can-XXXX capabilities
       
   508     convertCapabilityAttributes(attributes, agent);
       
   509 
       
   510     // Create the java.lang.instrument.Instrumentation object
       
   511     if (!createInstrumentationImpl(env, agent)) {
       
   512         goto releaseAndReturn;
       
   513     }
       
   514 
       
   515     // Enable the ClassFileLoadHook
       
   516     if (!setLivePhaseEventHandlers(agent)) {
       
   517         goto releaseAndReturn;
       
   518     }
       
   519 
       
   520     // invoke the agentmain method
       
   521     if (!startJavaAgent(agent, env, agentClass, "", agent->mAgentmainCaller)) {
       
   522         goto releaseAndReturn;
       
   523     }
       
   524 
       
   525     // initialization complete
       
   526     result = JNI_OK;
       
   527 
       
   528     releaseAndReturn:
       
   529         if (agentClass != NULL) {
       
   530             free(agentClass);
       
   531         }
       
   532         if (attributes != NULL) {
       
   533             freeAttributes(attributes);
       
   534         }
       
   535         if (jarfile != NULL) {
       
   536             (*env)->ReleaseStringUTFChars(env, path, jarfile);
       
   537         }
       
   538 
       
   539     return result;
       
   540 }
       
   541 
       
   542 /*
       
   543  *  JVMTI callback support
       
   544  *
       
   545  *  We have two "stages" of callback support.
       
   546  *  At OnLoad time, we install a VMInit handler.
       
   547  *  When the VMInit handler runs, we remove the VMInit handler and install a
       
   548  *  ClassFileLoadHook handler.
       
   549  */
       
   550 
       
   551 void JNICALL
       
   552 eventHandlerVMInit( jvmtiEnv *      jvmtienv,
       
   553                     JNIEnv *        jnienv,
       
   554                     jthread         thread) {
       
   555     JPLISEnvironment * environment  = NULL;
       
   556     jboolean           success      = JNI_FALSE;
       
   557 
       
   558     environment = getJPLISEnvironment(jvmtienv);
       
   559 
       
   560     /* process the premain calls on the all the JPL agents */
       
   561     if ( environment != NULL ) {
       
   562         jthrowable outstandingException = NULL;
       
   563         /*
       
   564          * Add the jarfile to the system class path
       
   565          */
       
   566         JPLISAgent * agent = environment->mAgent;
       
   567         if (appendClassPath(agent, agent->mJarfile)) {
       
   568             fprintf(stderr, "Unable to add %s to system class path - "
       
   569                     "the system class loader does not define the "
       
   570                     "appendToClassPathForInstrumentation method or the method failed\n",
       
   571                     agent->mJarfile);
       
   572             free((void *)agent->mJarfile);
       
   573             abortJVM(jnienv, JPLIS_ERRORMESSAGE_CANNOTSTART);
       
   574         }
       
   575         free((void *)agent->mJarfile);
       
   576         agent->mJarfile = NULL;
       
   577 
       
   578         outstandingException = preserveThrowable(jnienv);
       
   579         success = processJavaStart( environment->mAgent,
       
   580                                     jnienv);
       
   581         restoreThrowable(jnienv, outstandingException);
       
   582     }
       
   583 
       
   584     /* if we fail to start cleanly, bring down the JVM */
       
   585     if ( !success ) {
       
   586         abortJVM(jnienv, JPLIS_ERRORMESSAGE_CANNOTSTART);
       
   587     }
       
   588 }
       
   589 
       
   590 void JNICALL
       
   591 eventHandlerClassFileLoadHook(  jvmtiEnv *              jvmtienv,
       
   592                                 JNIEnv *                jnienv,
       
   593                                 jclass                  class_being_redefined,
       
   594                                 jobject                 loader,
       
   595                                 const char*             name,
       
   596                                 jobject                 protectionDomain,
       
   597                                 jint                    class_data_len,
       
   598                                 const unsigned char*    class_data,
       
   599                                 jint*                   new_class_data_len,
       
   600                                 unsigned char**         new_class_data) {
       
   601     JPLISEnvironment * environment  = NULL;
       
   602 
       
   603     environment = getJPLISEnvironment(jvmtienv);
       
   604 
       
   605     /* if something is internally inconsistent (no agent), just silently return without touching the buffer */
       
   606     if ( environment != NULL ) {
       
   607         jthrowable outstandingException = preserveThrowable(jnienv);
       
   608         transformClassFile( environment->mAgent,
       
   609                             jnienv,
       
   610                             loader,
       
   611                             name,
       
   612                             class_being_redefined,
       
   613                             protectionDomain,
       
   614                             class_data_len,
       
   615                             class_data,
       
   616                             new_class_data_len,
       
   617                             new_class_data,
       
   618                             environment->mIsRetransformer);
       
   619         restoreThrowable(jnienv, outstandingException);
       
   620     }
       
   621 }
       
   622 
       
   623 
       
   624 
       
   625 
       
   626 /*
       
   627  * URLs in Boot-Class-Path attributes are separated by one or more spaces.
       
   628  * This function splits the attribute value into a list of path segments.
       
   629  * The attribute value is in UTF8 but cannot contain NUL. Also non US-ASCII
       
   630  * characters must be escaped (URI syntax) so safe to iterate through the
       
   631  * value as a C string.
       
   632  */
       
   633 static void
       
   634 splitPathList(const char* str, int* pathCount, char*** paths) {
       
   635     int count = 0;
       
   636     char** segments = NULL;
       
   637     char** new_segments;
       
   638     char* c = (char*) str;
       
   639     while (*c != '\0') {
       
   640         while (*c == ' ') c++;          /* skip leading spaces */
       
   641         if (*c == '\0') {
       
   642             break;
       
   643         }
       
   644         new_segments = (char**)realloc(segments, (count+1)*sizeof(char*));
       
   645         if (new_segments == NULL) {
       
   646             jplis_assert(0);
       
   647             free(segments);
       
   648             count = 0;
       
   649             segments = NULL;
       
   650             break;
       
   651         }
       
   652         segments = new_segments;
       
   653         segments[count++] = c;
       
   654         c = strchr(c, ' ');
       
   655         if (c == NULL) {
       
   656             break;
       
   657         }
       
   658         *c = '\0';
       
   659         c++;
       
   660     }
       
   661     *pathCount = count;
       
   662     *paths = segments;
       
   663 }
       
   664 
       
   665 
       
   666 /* URI path decoding - ported from src/share/classes/java/net/URI.java */
       
   667 
       
   668 static int
       
   669 decodeNibble(char c) {
       
   670     if ((c >= '0') && (c <= '9'))
       
   671         return c - '0';
       
   672     if ((c >= 'a') && (c <= 'f'))
       
   673         return c - 'a' + 10;
       
   674     if ((c >= 'A') && (c <= 'F'))
       
   675         return c - 'A' + 10;
       
   676     return -1;
       
   677 }
       
   678 
       
   679 static int
       
   680 decodeByte(char c1, char c2) {
       
   681     return (((decodeNibble(c1) & 0xf) << 4) | ((decodeNibble(c2) & 0xf) << 0));
       
   682 }
       
   683 
       
   684 /*
       
   685  * Evaluates all escapes in s.  Assumes that escapes are well-formed
       
   686  * syntactically, i.e., of the form %XX.
       
   687  * If the path does not require decoding the original path is
       
   688  * returned. Otherwise the decoded path (heap allocated) is returned,
       
   689  * along with the length of the decoded path. Note that the return
       
   690  * string will not be null terminated after decoding.
       
   691  */
       
   692 static
       
   693 char *decodePath(const char *s, int* decodedLen) {
       
   694     int n;
       
   695     char *result;
       
   696     char *resultp;
       
   697     int c;
       
   698     int i;
       
   699 
       
   700     n = (int)strlen(s);
       
   701     if (n == 0) {
       
   702         *decodedLen = 0;
       
   703         return (char*)s;
       
   704     }
       
   705     if (strchr(s, '%') == NULL) {
       
   706         *decodedLen = n;
       
   707         return (char*)s; /* no escapes, we are done */
       
   708     }
       
   709 
       
   710     resultp = result = calloc(n+1, 1);
       
   711     c = s[0];
       
   712     for (i = 0; i < n;) {
       
   713         if (c != '%') {
       
   714             *resultp++ = c;
       
   715             if (++i >= n)
       
   716                 break;
       
   717             c = s[i];
       
   718             continue;
       
   719         }
       
   720         for (;;) {
       
   721             char b1 = s[++i];
       
   722             char b2 = s[++i];
       
   723             int decoded = decodeByte(b1, b2);
       
   724             *resultp++ = decoded;
       
   725             if (++i >= n)
       
   726                 break;
       
   727             c = s[i];
       
   728             if (c != '%')
       
   729                 break;
       
   730         }
       
   731     }
       
   732     *decodedLen = (int)(resultp - result);
       
   733     return result; // not null terminated.
       
   734 }
       
   735 
       
   736 /*
       
   737  * Append the given jar file to the system class path. This should succeed in the
       
   738  * onload phase but may fail in the live phase if the system class loader doesn't
       
   739  * support appending to the class path.
       
   740  */
       
   741 static int
       
   742 appendClassPath( JPLISAgent* agent,
       
   743                  const char* jarfile ) {
       
   744     jvmtiEnv* jvmtienv = jvmti(agent);
       
   745     jvmtiError jvmtierr;
       
   746 
       
   747     jvmtierr = (*jvmtienv)->AddToSystemClassLoaderSearch(jvmtienv, jarfile);
       
   748     check_phase_ret_1(jvmtierr);
       
   749 
       
   750     switch (jvmtierr) {
       
   751         case JVMTI_ERROR_NONE :
       
   752             return 0;
       
   753         case JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED :
       
   754             fprintf(stderr, "System class loader does not define "
       
   755                 "the appendToClassPathForInstrumentation method\n");
       
   756             break;
       
   757         default:
       
   758             fprintf(stderr, "Unexpected error (%d) returned by "
       
   759                 "AddToSystemClassLoaderSearch\n", jvmtierr);
       
   760             break;
       
   761     }
       
   762     return -1;
       
   763 }
       
   764 
       
   765 
       
   766 /*
       
   767  * res = func, free'ing the previous value of 'res' if function
       
   768  * returns a new result.
       
   769  */
       
   770 #define TRANSFORM(res,func) {    \
       
   771     char* tmp = func;            \
       
   772     if (tmp != res) {            \
       
   773         free(res);               \
       
   774         res = tmp;               \
       
   775     }                            \
       
   776     jplis_assert((void*)res != (void*)NULL);     \
       
   777 }
       
   778 
       
   779 /**
       
   780  * Convert a pathname to canonical form.
       
   781  * This method is exported from libjava.
       
   782  */
       
   783 extern int
       
   784 Canonicalize(JNIEnv *unused, char *orig, char *out, int len);
       
   785 
       
   786 
       
   787 /*
       
   788  * This function takes the value of the Boot-Class-Path attribute,
       
   789  * splits it into the individual path segments, and then combines it
       
   790  * with the path to the jar file to create the path to be added
       
   791  * to the bootclasspath.
       
   792  *
       
   793  * Each individual path segment starts out as a UTF8 string. Additionally
       
   794  * as the path is specified to use URI path syntax all non US-ASCII
       
   795  * characters are escaped. Once the URI path is decoded we get a UTF8
       
   796  * string which must then be converted to the platform encoding (as it
       
   797  * will be combined with the platform path of the jar file). Once
       
   798  * converted it is then normalized (remove duplicate slashes, etc.).
       
   799  * If the resulting path is an absolute path (starts with a slash for
       
   800  * example) then the path will be added to the bootclasspath. Otherwise
       
   801  * if it's not absolute then we get the canoncial path of the agent jar
       
   802  * file and then resolve the path in the context of the base path of
       
   803  * the agent jar.
       
   804  */
       
   805 static void
       
   806 appendBootClassPath( JPLISAgent* agent,
       
   807                      const char* jarfile,
       
   808                      const char* pathList ) {
       
   809     char canonicalPath[MAXPATHLEN];
       
   810     char *parent = NULL;
       
   811     int haveBasePath = 0;
       
   812 
       
   813     int count, i;
       
   814     char **paths;
       
   815     jvmtiEnv* jvmtienv = jvmti(agent);
       
   816     jvmtiError jvmtierr;
       
   817 
       
   818     /*
       
   819      * Split the attribute value into the individual path segments
       
   820      * and process each in sequence
       
   821      */
       
   822     splitPathList(pathList, &count, &paths);
       
   823 
       
   824     for (i=0; i<count; i++) {
       
   825         int len;
       
   826         char* path;
       
   827         char* pos;
       
   828 
       
   829         /*
       
   830          * The path segment at this point is a pointer into the attribute
       
   831          * value. As it will go through a number of transformation (tossing away
       
   832          * the previous results as we go along) it make it easier if the path
       
   833          * starts out as a heap allocated string.
       
   834          */
       
   835         path = strdup(paths[i]);
       
   836         jplis_assert(path != (char*)NULL);
       
   837 
       
   838         /*
       
   839          * The attribute is specified to be a list of relative URIs so in theory
       
   840          * there could be a query component - if so, get rid of it.
       
   841          */
       
   842         pos = strchr(path, '?');
       
   843         if (pos != NULL) {
       
   844             *pos = '\0';
       
   845         }
       
   846 
       
   847         /*
       
   848          * Check for characters that are not allowed in the path component of
       
   849          * a URI.
       
   850          */
       
   851         if (validatePathChars(path)) {
       
   852             fprintf(stderr, "WARNING: illegal character in Boot-Class-Path value: %s\n",
       
   853                path);
       
   854             free(path);
       
   855             continue;
       
   856         }
       
   857 
       
   858 
       
   859         /*
       
   860          * Next decode any escaped characters. The result is a UTF8 string.
       
   861          */
       
   862         TRANSFORM(path, decodePath(path,&len));
       
   863 
       
   864         /*
       
   865          * Convert to the platform encoding
       
   866          */
       
   867         {
       
   868             char platform[MAXPATHLEN];
       
   869             int new_len = convertUft8ToPlatformString(path, len, platform, MAXPATHLEN);
       
   870             free(path);
       
   871             if (new_len  < 0) {
       
   872                 /* bogus value - exceeds maximum path size or unable to convert */
       
   873                 continue;
       
   874             }
       
   875             path = strdup(platform);
       
   876             jplis_assert(path != (char*)NULL);
       
   877         }
       
   878 
       
   879         /*
       
   880          * Post-process the URI path - needed on Windows to transform
       
   881          * /c:/foo to c:/foo.
       
   882          */
       
   883         TRANSFORM(path, fromURIPath(path));
       
   884 
       
   885         /*
       
   886          * Normalize the path - no duplicate slashes (except UNCs on Windows), trailing
       
   887          * slash removed.
       
   888          */
       
   889         TRANSFORM(path, normalize(path));
       
   890 
       
   891         /*
       
   892          * If the path is an absolute path then add to the bootclassloader
       
   893          * search path. Otherwise we get the canonical path of the agent jar
       
   894          * and then use its base path (directory) to resolve the given path
       
   895          * segment.
       
   896          *
       
   897          * NOTE: JVMTI is specified to use modified UTF8 strings (like JNI).
       
   898          * In 1.5.0 the AddToBootstrapClassLoaderSearch takes a platform string
       
   899          * - see 5049313.
       
   900          */
       
   901         if (isAbsolute(path)) {
       
   902             jvmtierr = (*jvmtienv)->AddToBootstrapClassLoaderSearch(jvmtienv, path);
       
   903         } else {
       
   904             char* resolved;
       
   905 
       
   906             if (!haveBasePath) {
       
   907                 /* Use NULL as the JNIEnv since we know that Canonicalize does not use it. */
       
   908                 if (Canonicalize(NULL, (char*)jarfile, canonicalPath, sizeof(canonicalPath)) != 0) {
       
   909                     fprintf(stderr, "WARNING: unable to canonicalize %s\n", jarfile);
       
   910                     free(path);
       
   911                     continue;
       
   912                 }
       
   913                 parent = basePath(canonicalPath);
       
   914                 jplis_assert(parent != (char*)NULL);
       
   915                 haveBasePath = 1;
       
   916             }
       
   917 
       
   918             resolved = resolve(parent, path);
       
   919             jvmtierr = (*jvmtienv)->AddToBootstrapClassLoaderSearch(jvmtienv, resolved);
       
   920         }
       
   921 
       
   922         /* print warning if boot class path not updated */
       
   923         if (jvmtierr != JVMTI_ERROR_NONE) {
       
   924             check_phase_blob_ret(jvmtierr, free(path));
       
   925 
       
   926             fprintf(stderr, "WARNING: %s not added to bootstrap class loader search: ", path);
       
   927             switch (jvmtierr) {
       
   928                 case JVMTI_ERROR_ILLEGAL_ARGUMENT :
       
   929                     fprintf(stderr, "Illegal argument or not JAR file\n");
       
   930                     break;
       
   931                 default:
       
   932                     fprintf(stderr, "Unexpected error: %d\n", jvmtierr);
       
   933             }
       
   934         }
       
   935 
       
   936         /* finished with the path */
       
   937         free(path);
       
   938     }
       
   939 
       
   940 
       
   941     /* clean-up */
       
   942     if (haveBasePath && parent != canonicalPath) {
       
   943         free(parent);
       
   944     }
       
   945 }