| 1 | /* | 
| 2 |  * Copyright (c) 2003, 2019, 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 |          * Setup ClassFileLoadHook handler. | 
| 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 |         abortJVM(jnienv, JPLIS_ERRORMESSAGE_CANNOTSTART ", getting JPLIS environment failed" ); | 
| 563 |     } | 
| 564 |     jthrowable outstandingException = NULL; | 
| 565 |     /* | 
| 566 |      * Add the jarfile to the system class path | 
| 567 |      */ | 
| 568 |     JPLISAgent * agent = environment->mAgent; | 
| 569 |     if (appendClassPath(agent, agent->mJarfile)) { | 
| 570 |         fprintf(stderr, "Unable to add %s to system class path - "  | 
| 571 |                 "the system class loader does not define the "  | 
| 572 |                 "appendToClassPathForInstrumentation method or the method failed\n" , | 
| 573 |                 agent->mJarfile); | 
| 574 |         free((void *)agent->mJarfile); | 
| 575 |         abortJVM(jnienv, JPLIS_ERRORMESSAGE_CANNOTSTART ", appending to system class path failed" ); | 
| 576 |     } | 
| 577 |     free((void *)agent->mJarfile); | 
| 578 |     agent->mJarfile = NULL; | 
| 579 |  | 
| 580 |     outstandingException = preserveThrowable(jnienv); | 
| 581 |     success = processJavaStart( environment->mAgent, jnienv); | 
| 582 |     restoreThrowable(jnienv, outstandingException); | 
| 583 |  | 
| 584 |     /* if we fail to start cleanly, bring down the JVM */ | 
| 585 |     if ( !success ) { | 
| 586 |         abortJVM(jnienv, JPLIS_ERRORMESSAGE_CANNOTSTART ", processJavaStart failed" ); | 
| 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 | } | 
| 946 |  |