1 /* 2 * Copyright (c) 2003, 2008, 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 convertCapabilityAtrributes(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 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 /* 194 * Add to the jarfile 195 */ 196 appendClassPath(agent, jarfile); 197 198 /* 199 * The value of the Premain-Class attribute becomes the agent 200 * class name. The manifest is in UTF8 so need to convert to 201 * modified UTF8 (see JNI spec). 202 */ 203 oldLen = (int)strlen(premainClass); 204 newLen = modifiedUtf8LengthOfUtf8(premainClass, oldLen); 205 if (newLen == oldLen) { 206 premainClass = strdup(premainClass); 207 } else { 208 char* str = (char*)malloc( newLen+1 ); 209 if (str != NULL) { 210 convertUtf8ToModifiedUtf8(premainClass, oldLen, str, newLen); 211 } 212 premainClass = str; 213 } 214 if (premainClass == NULL) { 215 fprintf(stderr, "-javaagent: memory allocation failed\n"); 216 free(jarfile); 217 if (options != NULL) free(options); 218 freeAttributes(attributes); 219 return JNI_ERR; 220 } 221 222 /* 223 * If the Boot-Class-Path attribute is specified then we process 224 * each relative URL and add it to the bootclasspath. 225 */ 226 bootClassPath = getAttribute(attributes, "Boot-Class-Path"); 227 if (bootClassPath != NULL) { 228 appendBootClassPath(agent, jarfile, bootClassPath); 229 } 230 231 /* 232 * Convert JAR attributes into agent capabilities 233 */ 234 convertCapabilityAtrributes(attributes, agent); 235 236 /* 237 * Track (record) the agent class name and options data 238 */ 239 initerror = recordCommandLineData(agent, premainClass, options); 240 241 /* 242 * Clean-up 243 */ 244 free(jarfile); 245 if (options != NULL) free(options); 246 freeAttributes(attributes); 247 free(premainClass); 248 } 249 250 switch (initerror) { 251 case JPLIS_INIT_ERROR_NONE: 252 result = JNI_OK; 253 break; 254 case JPLIS_INIT_ERROR_CANNOT_CREATE_NATIVE_AGENT: 255 result = JNI_ERR; 256 fprintf(stderr, "java.lang.instrument/-javaagent: cannot create native agent.\n"); 257 break; 258 case JPLIS_INIT_ERROR_FAILURE: 259 result = JNI_ERR; 260 fprintf(stderr, "java.lang.instrument/-javaagent: initialization of native agent failed.\n"); 261 break; 262 case JPLIS_INIT_ERROR_ALLOCATION_FAILURE: 263 result = JNI_ERR; 264 fprintf(stderr, "java.lang.instrument/-javaagent: allocation failure.\n"); 265 break; 266 case JPLIS_INIT_ERROR_AGENT_CLASS_NOT_SPECIFIED: 267 result = JNI_ERR; 268 fprintf(stderr, "-javaagent: agent class not specified.\n"); 269 break; 270 default: 271 result = JNI_ERR; 272 fprintf(stderr, "java.lang.instrument/-javaagent: unknown error\n"); 273 break; 274 } 275 return result; 276 } 277 278 /* 279 * Agent_OnAttach returns a jint. 0/JNI_OK indicates success and non-0 280 * indicates an error. To allow the attach mechanism throw an 281 * AgentInitializationException with a reasonable exception message we define 282 * a few specific errors here. 283 */ 284 #define AGENT_ERROR_BADJAR ((jint)100) /* Agent JAR not found or no Agent-Class attribute */ 285 #define AGENT_ERROR_NOTONCP ((jint)101) /* Unable to add JAR file to system class path */ 286 #define AGENT_ERROR_STARTFAIL ((jint)102) /* No agentmain method or agentmain failed */ 287 288 /* 289 * This will be called once each time a tool attaches to the VM and loads 290 * the JPLIS library. 291 */ 292 JNIEXPORT jint JNICALL 293 Agent_OnAttach(JavaVM* vm, char *args, void * reserved) { 294 JPLISInitializationError initerror = JPLIS_INIT_ERROR_NONE; 295 jint result = JNI_OK; 296 JPLISAgent * agent = NULL; 297 JNIEnv * jni_env = NULL; 298 299 /* 300 * Need JNIEnv - guaranteed to be called from thread that is already 301 * attached to VM 302 */ 303 result = (*vm)->GetEnv(vm, (void**)&jni_env, JNI_VERSION_1_2); 304 jplis_assert(result==JNI_OK); 305 306 initerror = createNewJPLISAgent(vm, &agent); 307 if ( initerror == JPLIS_INIT_ERROR_NONE ) { 308 int oldLen, newLen; 309 char * jarfile; 310 char * options; 311 jarAttribute* attributes; 312 char * agentClass; 313 char * bootClassPath; 314 jboolean success; 315 316 /* 317 * Parse <jarfile>[=options] into jarfile and options 318 */ 319 if (parseArgumentTail(args, &jarfile, &options) != 0) { 320 return JNI_ENOMEM; 321 } 322 323 /* 324 * Open the JAR file and parse the manifest 325 */ 326 attributes = readAttributes( jarfile ); 327 if (attributes == NULL) { 328 fprintf(stderr, "Error opening zip file or JAR manifest missing: %s\n", jarfile); 329 free(jarfile); 330 if (options != NULL) free(options); 331 return AGENT_ERROR_BADJAR; 332 } 333 334 agentClass = getAttribute(attributes, "Agent-Class"); 335 if (agentClass == NULL) { 336 fprintf(stderr, "Failed to find Agent-Class manifest attribute from %s\n", 337 jarfile); 338 free(jarfile); 339 if (options != NULL) free(options); 340 freeAttributes(attributes); 341 return AGENT_ERROR_BADJAR; 342 } 343 344 /* 345 * Add the jarfile to the system class path 346 */ 347 if (appendClassPath(agent, jarfile)) { 348 fprintf(stderr, "Unable to add %s to system class path " 349 "- not supported by system class loader or configuration error!\n", 350 jarfile); 351 free(jarfile); 352 if (options != NULL) free(options); 353 freeAttributes(attributes); 354 return AGENT_ERROR_NOTONCP; 355 } 356 357 /* 358 * The value of the Agent-Class attribute becomes the agent 359 * class name. The manifest is in UTF8 so need to convert to 360 * modified UTF8 (see JNI spec). 361 */ 362 oldLen = strlen(agentClass); 363 newLen = modifiedUtf8LengthOfUtf8(agentClass, oldLen); 364 if (newLen == oldLen) { 365 agentClass = strdup(agentClass); 366 } else { 367 char* str = (char*)malloc( newLen+1 ); 368 if (str != NULL) { 369 convertUtf8ToModifiedUtf8(agentClass, oldLen, str, newLen); 370 } 371 agentClass = str; 372 } 373 if (agentClass == NULL) { 374 free(jarfile); 375 if (options != NULL) free(options); 376 freeAttributes(attributes); 377 return JNI_ENOMEM; 378 } 379 380 /* 381 * If the Boot-Class-Path attribute is specified then we process 382 * each URL - in the live phase only JAR files will be added. 383 */ 384 bootClassPath = getAttribute(attributes, "Boot-Class-Path"); 385 if (bootClassPath != NULL) { 386 appendBootClassPath(agent, jarfile, bootClassPath); 387 } 388 389 /* 390 * Convert JAR attributes into agent capabilities 391 */ 392 convertCapabilityAtrributes(attributes, agent); 393 394 /* 395 * Create the java.lang.instrument.Instrumentation instance 396 */ 397 success = createInstrumentationImpl(jni_env, agent); 398 jplis_assert(success); 399 400 /* 401 * Turn on the ClassFileLoadHook. 402 */ 403 if (success) { 404 success = setLivePhaseEventHandlers(agent); 405 jplis_assert(success); 406 } 407 408 /* 409 * Start the agent 410 */ 411 if (success) { 412 success = startJavaAgent(agent, 413 jni_env, 414 agentClass, 415 options, 416 agent->mAgentmainCaller); 417 } 418 419 if (!success) { 420 fprintf(stderr, "Agent failed to start!\n"); 421 result = AGENT_ERROR_STARTFAIL; 422 } 423 424 /* 425 * Clean-up 426 */ 427 free(jarfile); 428 if (options != NULL) free(options); 429 free(agentClass); 430 freeAttributes(attributes); 431 } 432 433 return result; 434 } 435 436 437 JNIEXPORT void JNICALL 438 Agent_OnUnload(JavaVM *vm) { 439 } 440 441 442 /* 443 * JVMTI callback support 444 * 445 * We have two "stages" of callback support. 446 * At OnLoad time, we install a VMInit handler. 447 * When the VMInit handler runs, we remove the VMInit handler and install a 448 * ClassFileLoadHook handler. 449 */ 450 451 void JNICALL 452 eventHandlerVMInit( jvmtiEnv * jvmtienv, 453 JNIEnv * jnienv, 454 jthread thread) { 455 JPLISEnvironment * environment = NULL; 456 jboolean success = JNI_FALSE; 457 458 environment = getJPLISEnvironment(jvmtienv); 459 460 /* process the premain calls on the all the JPL agents */ 461 if ( environment != NULL ) { 462 jthrowable outstandingException = preserveThrowable(jnienv); 463 success = processJavaStart( environment->mAgent, 464 jnienv); 465 restoreThrowable(jnienv, outstandingException); 466 } 467 468 /* if we fail to start cleanly, bring down the JVM */ 469 if ( !success ) { 470 abortJVM(jnienv, JPLIS_ERRORMESSAGE_CANNOTSTART); 471 } 472 } 473 474 void JNICALL 475 eventHandlerClassFileLoadHook( jvmtiEnv * jvmtienv, 476 JNIEnv * jnienv, 477 jclass class_being_redefined, 478 jobject loader, 479 const char* name, 480 jobject protectionDomain, 481 jint class_data_len, 482 const unsigned char* class_data, 483 jint* new_class_data_len, 484 unsigned char** new_class_data) { 485 JPLISEnvironment * environment = NULL; 486 487 environment = getJPLISEnvironment(jvmtienv); 488 489 /* if something is internally inconsistent (no agent), just silently return without touching the buffer */ 490 if ( environment != NULL ) { 491 jthrowable outstandingException = preserveThrowable(jnienv); 492 transformClassFile( environment->mAgent, 493 jnienv, 494 loader, 495 name, 496 class_being_redefined, 497 protectionDomain, 498 class_data_len, 499 class_data, 500 new_class_data_len, 501 new_class_data, 502 environment->mIsRetransformer); 503 restoreThrowable(jnienv, outstandingException); 504 } 505 } 506 507 508 509 510 /* 511 * URLs in Boot-Class-Path attributes are separated by one or more spaces. 512 * This function splits the attribute value into a list of path segments. 513 * The attribute value is in UTF8 but cannot contain NUL. Also non US-ASCII 514 * characters must be escaped (URI syntax) so safe to iterate through the 515 * value as a C string. 516 */ 517 static void 518 splitPathList(const char* str, int* pathCount, char*** paths) { 519 int count = 0; 520 char** segments = NULL; 521 char* c = (char*) str; 522 while (*c != '\0') { 523 while (*c == ' ') c++; /* skip leading spaces */ 524 if (*c == '\0') { 525 break; 526 } 527 if (segments == NULL) { 528 segments = (char**)malloc( sizeof(char**) ); 529 } else { 530 segments = (char**)realloc( segments, (count+1)*sizeof(char**) ); 531 } 532 jplis_assert(segments != (char**)NULL); 533 segments[count++] = c; 534 c = strchr(c, ' '); 535 if (c == NULL) { 536 break; 537 } 538 *c = '\0'; 539 c++; 540 } 541 *pathCount = count; 542 *paths = segments; 543 } 544 545 546 /* URI path decoding - ported from src/share/classes/java/net/URI.java */ 547 548 static int 549 decodeNibble(char c) { 550 if ((c >= '0') && (c <= '9')) 551 return c - '0'; 552 if ((c >= 'a') && (c <= 'f')) 553 return c - 'a' + 10; 554 if ((c >= 'A') && (c <= 'F')) 555 return c - 'A' + 10; 556 return -1; 557 } 558 559 static int 560 decodeByte(char c1, char c2) { 561 return (((decodeNibble(c1) & 0xf) << 4) | ((decodeNibble(c2) & 0xf) << 0)); 562 } 563 564 /* 565 * Evaluates all escapes in s. Assumes that escapes are well-formed 566 * syntactically, i.e., of the form %XX. 567 * If the path does not require decoding the the original path is 568 * returned. Otherwise the decoded path (heap allocated) is returned, 569 * along with the length of the decoded path. Note that the return 570 * string will not be null terminated after decoding. 571 */ 572 static 573 char *decodePath(const char *s, int* decodedLen) { 574 int n; 575 char *result; 576 char *resultp; 577 int c; 578 int i; 579 580 n = (int)strlen(s); 581 if (n == 0) { 582 *decodedLen = 0; 583 return (char*)s; 584 } 585 if (strchr(s, '%') == NULL) { 586 *decodedLen = n; 587 return (char*)s; /* no escapes, we are done */ 588 } 589 590 resultp = result = calloc(n+1, 1); 591 c = s[0]; 592 for (i = 0; i < n;) { 593 if (c != '%') { 594 *resultp++ = c; 595 if (++i >= n) 596 break; 597 c = s[i]; 598 continue; 599 } 600 for (;;) { 601 char b1 = s[++i]; 602 char b2 = s[++i]; 603 int decoded = decodeByte(b1, b2); 604 *resultp++ = decoded; 605 if (++i >= n) 606 break; 607 c = s[i]; 608 if (c != '%') 609 break; 610 } 611 } 612 *decodedLen = (int)(resultp - result); 613 return result; // not null terminated. 614 } 615 616 /* 617 * Append the given jar file to the system class path. This should succeed in the 618 * onload phase but may fail in the live phase if the system class loader doesn't 619 * support appending to the class path. 620 */ 621 static int 622 appendClassPath( JPLISAgent* agent, 623 const char* jarfile ) { 624 jvmtiEnv* jvmtienv = jvmti(agent); 625 jvmtiError jvmtierr; 626 627 jvmtierr = (*jvmtienv)->AddToSystemClassLoaderSearch(jvmtienv, jarfile); 628 check_phase_ret_1(jvmtierr); 629 630 if (jvmtierr == JVMTI_ERROR_NONE) { 631 return 0; 632 } else { 633 jvmtiPhase phase; 634 jvmtiError err; 635 636 err = (*jvmtienv)->GetPhase(jvmtienv, &phase); 637 /* can be called from any phase */ 638 jplis_assert(err == JVMTI_ERROR_NONE); 639 640 if (phase == JVMTI_PHASE_LIVE) { 641 switch (jvmtierr) { 642 case JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED : 643 fprintf(stderr, "System class loader does not support adding " 644 "JAR file to system class path during the live phase!\n"); 645 break; 646 default: 647 fprintf(stderr, "Unexpected error (%d) returned by " 648 "AddToSystemClassLoaderSearch\n", jvmtierr); 649 break; 650 } 651 return -1; 652 } 653 jplis_assert(0); 654 } 655 return -2; 656 } 657 658 659 /* 660 * res = func, free'ing the previous value of 'res' if function 661 * returns a new result. 662 */ 663 #define TRANSFORM(res,func) { \ 664 char* tmp = func; \ 665 if (tmp != res) { \ 666 free(res); \ 667 res = tmp; \ 668 } \ 669 jplis_assert((void*)res != (void*)NULL); \ 670 } 671 672 /** 673 * Convert a pathname to canonical form. 674 * This method is exported from libjava. 675 */ 676 extern int 677 Canonicalize(JNIEnv *unused, char *orig, char *out, int len); 678 679 680 /* 681 * This function takes the value of the Boot-Class-Path attribute, 682 * splits it into the individual path segments, and then combines it 683 * with the path to the jar file to create the path to be added 684 * to the bootclasspath. 685 * 686 * Each individual path segment starts out as a UTF8 string. Additionally 687 * as the path is specified to use URI path syntax all non US-ASCII 688 * characters are escaped. Once the URI path is decoded we get a UTF8 689 * string which must then be converted to the platform encoding (as it 690 * will be combined with the platform path of the jar file). Once 691 * converted it is then normalized (remove duplicate slashes, etc.). 692 * If the resulting path is an absolute path (starts with a slash for 693 * example) then the path will be added to the bootclasspath. Otherwise 694 * if it's not absolute then we get the canoncial path of the agent jar 695 * file and then resolve the path in the context of the base path of 696 * the agent jar. 697 */ 698 static void 699 appendBootClassPath( JPLISAgent* agent, 700 const char* jarfile, 701 const char* pathList ) { 702 char canonicalPath[MAXPATHLEN]; 703 char *parent = NULL; 704 int haveBasePath = 0; 705 706 int count, i; 707 char **paths; 708 jvmtiEnv* jvmtienv = jvmti(agent); 709 jvmtiError jvmtierr; 710 711 /* 712 * Split the attribute value into the individual path segments 713 * and process each in sequence 714 */ 715 splitPathList(pathList, &count, &paths); 716 717 for (i=0; i<count; i++) { 718 int len; 719 char* path; 720 char* pos; 721 722 /* 723 * The path segment at this point is a pointer into the attribute 724 * value. As it will go through a number of transformation (tossing away 725 * the previous results as we go along) it make it easier if the path 726 * starts out as a heap allocated string. 727 */ 728 path = strdup(paths[i]); 729 jplis_assert(path != (char*)NULL); 730 731 /* 732 * The attribute is specified to be a list of relative URIs so in theory 733 * there could be a query component - if so, get rid of it. 734 */ 735 pos = strchr(path, '?'); 736 if (pos != NULL) { 737 *pos = '\0'; 738 } 739 740 /* 741 * Check for characters that are not allowed in the path component of 742 * a URI. 743 */ 744 if (validatePathChars(path)) { 745 fprintf(stderr, "WARNING: illegal character in Boot-Class-Path value: %s\n", 746 path); 747 free(path); 748 continue; 749 } 750 751 752 /* 753 * Next decode any escaped characters. The result is a UTF8 string. 754 */ 755 TRANSFORM(path, decodePath(path,&len)); 756 757 /* 758 * Convert to the platform encoding 759 */ 760 { 761 char platform[MAXPATHLEN]; 762 int new_len = convertUft8ToPlatformString(path, len, platform, MAXPATHLEN); 763 free(path); 764 if (new_len < 0) { 765 /* bogus value - exceeds maximum path size or unable to convert */ 766 continue; 767 } 768 path = strdup(platform); 769 jplis_assert(path != (char*)NULL); 770 } 771 772 /* 773 * Post-process the URI path - needed on Windows to transform 774 * /c:/foo to c:/foo. 775 */ 776 TRANSFORM(path, fromURIPath(path)); 777 778 /* 779 * Normalize the path - no duplicate slashes (except UNCs on Windows), trailing 780 * slash removed. 781 */ 782 TRANSFORM(path, normalize(path)); 783 784 /* 785 * If the path is an absolute path then add to the bootclassloader 786 * search path. Otherwise we get the canonical path of the agent jar 787 * and then use its base path (directory) to resolve the given path 788 * segment. 789 * 790 * NOTE: JVMTI is specified to use modified UTF8 strings (like JNI). 791 * In 1.5.0 the AddToBootstrapClassLoaderSearch takes a platform string 792 * - see 5049313. 793 */ 794 if (isAbsolute(path)) { 795 jvmtierr = (*jvmtienv)->AddToBootstrapClassLoaderSearch(jvmtienv, path); 796 } else { 797 char* resolved; 798 799 if (!haveBasePath) { 800 /* Use NULL as the JNIEnv since we know that Canonicalize does not use it. */ 801 if (Canonicalize(NULL, (char*)jarfile, canonicalPath, sizeof(canonicalPath)) != 0) { 802 fprintf(stderr, "WARNING: unable to canonicalize %s\n", jarfile); 803 free(path); 804 continue; 805 } 806 parent = basePath(canonicalPath); 807 jplis_assert(parent != (char*)NULL); 808 haveBasePath = 1; 809 } 810 811 resolved = resolve(parent, path); 812 jvmtierr = (*jvmtienv)->AddToBootstrapClassLoaderSearch(jvmtienv, resolved); 813 } 814 815 /* print warning if boot class path not updated */ 816 if (jvmtierr != JVMTI_ERROR_NONE) { 817 check_phase_blob_ret(jvmtierr, free(path)); 818 819 fprintf(stderr, "WARNING: %s not added to bootstrap class loader search: ", path); 820 switch (jvmtierr) { 821 case JVMTI_ERROR_ILLEGAL_ARGUMENT : 822 fprintf(stderr, "Illegal argument or not JAR file\n"); 823 break; 824 default: 825 fprintf(stderr, "Unexpected error: %d\n", jvmtierr); 826 } 827 } 828 829 /* finished with the path */ 830 free(path); 831 } 832 833 834 /* clean-up */ 835 if (haveBasePath && parent != canonicalPath) { 836 free(parent); 837 } 838 }