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 50static int 51appendClassPath(JPLISAgent* agent, 52 const char* jarfile); 53 54static void 55appendBootClassPath(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 */ 65static int 66parseArgumentTail(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 */ 98jboolean 99getBooleanAttribute(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 */ 108void 109convertCapabilityAttributes(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 */ 143JNIEXPORT jint JNICALL 144DEF_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 */ 289JNIEXPORT jint JNICALL 290DEF_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 434JNIEXPORT void JNICALL 435DEF_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 */ 446jint 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 551void JNICALL 552eventHandlerVMInit( 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 590void JNICALL 591eventHandlerClassFileLoadHook( 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 */ 633static void 634splitPathList(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 668static int 669decodeNibble(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 679static int 680decodeByte(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 */ 692static 693char *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 */ 741static int 742appendClassPath( 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 */ 783extern int 784Canonicalize(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 */ 805static void 806appendBootClassPath( 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