Path: blob/master/src/java.instrument/share/native/libinstrument/InvocationAdapter.c
41149 views
/*1* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26* Copyright 2003 Wily Technology, Inc.27*/2829#include <string.h>30#include <stdlib.h>3132#include "jni.h"3334#include "jdk_util.h"3536#include "Utilities.h"37#include "JPLISAssert.h"38#include "JPLISAgent.h"39#include "JavaExceptions.h"4041#include "EncodingSupport.h"42#include "FileSystemSupport.h"43#include "JarFacade.h"44#include "PathCharsValidator.h"4546/**47* This module contains the direct interface points with the JVMTI.48* The OnLoad handler is here, along with the various event handlers.49*/5051static int52appendClassPath(JPLISAgent* agent,53const char* jarfile);5455static void56appendBootClassPath(JPLISAgent* agent,57const char* jarfile,58const char* pathList);596061/*62* Parse -javaagent tail, of the form name[=options], into name63* and options. Returned values are heap allocated and options maybe64* NULL. Returns 0 if parse succeeds, -1 if allocation fails.65*/66static int67parseArgumentTail(char* tail, char** name, char** options) {68int len;69char* pos;7071pos = strchr(tail, '=');72len = (pos == NULL) ? (int)strlen(tail) : (int)(pos - tail);7374*name = (char*)malloc(len+1);75if (*name == NULL) {76return -1;77}78memcpy(*name, tail, len);79(*name)[len] = '\0';8081if (pos == NULL) {82*options = NULL;83} else {84char * str = (char*)malloc( (int)strlen(pos + 1) + 1 );85if (str == NULL) {86free(*name);87return -1;88}89strcpy(str, pos +1);90*options = str;91}92return 0;93}9495/*96* Get the value of an attribute in an attribute list. Returns NULL97* if attribute not found.98*/99jboolean100getBooleanAttribute(const jarAttribute* attributes, const char* name) {101char* attributeValue = getAttribute(attributes, name);102return attributeValue != NULL && strcasecmp(attributeValue, "true") == 0;103}104105/*106* Parse any capability settings in the JAR manifest and107* convert them to JVM TI capabilities.108*/109void110convertCapabilityAttributes(const jarAttribute* attributes, JPLISAgent* agent) {111/* set redefineClasses capability */112if (getBooleanAttribute(attributes, "Can-Redefine-Classes")) {113addRedefineClassesCapability(agent);114}115116/* create an environment which has the retransformClasses capability */117if (getBooleanAttribute(attributes, "Can-Retransform-Classes")) {118retransformableEnvironment(agent);119}120121/* set setNativeMethodPrefix capability */122if (getBooleanAttribute(attributes, "Can-Set-Native-Method-Prefix")) {123addNativeMethodPrefixCapability(agent);124}125126/* for retransformClasses testing, set capability to use original method order */127if (getBooleanAttribute(attributes, "Can-Maintain-Original-Method-Order")) {128addOriginalMethodOrderCapability(agent);129}130}131132/*133* This will be called once for every -javaagent on the command line.134* Each call to Agent_OnLoad will create its own agent and agent data.135*136* The argument tail string provided to Agent_OnLoad will be of form137* <jarfile>[=<options>]. The tail string is split into the jarfile and138* options components. The jarfile manifest is parsed and the value of the139* Premain-Class attribute will become the agent's premain class. The jar140* file is then added to the system class path, and if the Boot-Class-Path141* attribute is present then all relative URLs in the value are processed142* to create boot class path segments to append to the boot class path.143*/144JNIEXPORT jint JNICALL145DEF_Agent_OnLoad(JavaVM *vm, char *tail, void * reserved) {146JPLISInitializationError initerror = JPLIS_INIT_ERROR_NONE;147jint result = JNI_OK;148JPLISAgent * agent = NULL;149150initerror = createNewJPLISAgent(vm, &agent);151if ( initerror == JPLIS_INIT_ERROR_NONE ) {152int oldLen, newLen;153char * jarfile;154char * options;155jarAttribute* attributes;156char * premainClass;157char * bootClassPath;158159/*160* Parse <jarfile>[=options] into jarfile and options161*/162if (parseArgumentTail(tail, &jarfile, &options) != 0) {163fprintf(stderr, "-javaagent: memory allocation failure.\n");164return JNI_ERR;165}166167/*168* Agent_OnLoad is specified to provide the agent options169* argument tail in modified UTF8. However for 1.5.0 this is170* actually in the platform encoding - see 5049313.171*172* Open zip/jar file and parse archive. If can't be opened or173* not a zip file return error. Also if Premain-Class attribute174* isn't present we return an error.175*/176attributes = readAttributes(jarfile);177if (attributes == NULL) {178fprintf(stderr, "Error opening zip file or JAR manifest missing : %s\n", jarfile);179free(jarfile);180if (options != NULL) free(options);181return JNI_ERR;182}183184premainClass = getAttribute(attributes, "Premain-Class");185if (premainClass == NULL) {186fprintf(stderr, "Failed to find Premain-Class manifest attribute in %s\n",187jarfile);188free(jarfile);189if (options != NULL) free(options);190freeAttributes(attributes);191return JNI_ERR;192}193194/* Save the jarfile name */195agent->mJarfile = jarfile;196197/*198* The value of the Premain-Class attribute becomes the agent199* class name. The manifest is in UTF8 so need to convert to200* modified UTF8 (see JNI spec).201*/202oldLen = (int)strlen(premainClass);203newLen = modifiedUtf8LengthOfUtf8(premainClass, oldLen);204/*205* According to JVMS class name is represented as CONSTANT_Utf8_info,206* so its length is u2 (i.e. must be <= 0xFFFF).207* Negative oldLen or newLen means we got signed integer overflow208* (modifiedUtf8LengthOfUtf8 returns negative value if oldLen is negative).209*/210if (oldLen < 0 || newLen < 0 || newLen > 0xFFFF) {211fprintf(stderr, "-javaagent: Premain-Class value is too big\n");212free(jarfile);213if (options != NULL) free(options);214freeAttributes(attributes);215return JNI_ERR;216}217if (newLen == oldLen) {218premainClass = strdup(premainClass);219} else {220char* str = (char*)malloc( newLen+1 );221if (str != NULL) {222convertUtf8ToModifiedUtf8(premainClass, oldLen, str, newLen);223}224premainClass = str;225}226if (premainClass == NULL) {227fprintf(stderr, "-javaagent: memory allocation failed\n");228free(jarfile);229if (options != NULL) free(options);230freeAttributes(attributes);231return JNI_ERR;232}233234/*235* If the Boot-Class-Path attribute is specified then we process236* each relative URL and add it to the bootclasspath.237*/238bootClassPath = getAttribute(attributes, "Boot-Class-Path");239if (bootClassPath != NULL) {240appendBootClassPath(agent, jarfile, bootClassPath);241}242243/*244* Convert JAR attributes into agent capabilities245*/246convertCapabilityAttributes(attributes, agent);247248/*249* Track (record) the agent class name and options data250*/251initerror = recordCommandLineData(agent, premainClass, options);252253/*254* Clean-up255*/256if (options != NULL) free(options);257freeAttributes(attributes);258free(premainClass);259}260261switch (initerror) {262case JPLIS_INIT_ERROR_NONE:263result = JNI_OK;264break;265case JPLIS_INIT_ERROR_CANNOT_CREATE_NATIVE_AGENT:266result = JNI_ERR;267fprintf(stderr, "java.lang.instrument/-javaagent: cannot create native agent.\n");268break;269case JPLIS_INIT_ERROR_FAILURE:270result = JNI_ERR;271fprintf(stderr, "java.lang.instrument/-javaagent: initialization of native agent failed.\n");272break;273case JPLIS_INIT_ERROR_ALLOCATION_FAILURE:274result = JNI_ERR;275fprintf(stderr, "java.lang.instrument/-javaagent: allocation failure.\n");276break;277case JPLIS_INIT_ERROR_AGENT_CLASS_NOT_SPECIFIED:278result = JNI_ERR;279fprintf(stderr, "-javaagent: agent class not specified.\n");280break;281default:282result = JNI_ERR;283fprintf(stderr, "java.lang.instrument/-javaagent: unknown error\n");284break;285}286return result;287}288289/*290* Agent_OnAttach returns a jint. 0/JNI_OK indicates success and non-0291* indicates an error. To allow the attach mechanism throw an292* AgentInitializationException with a reasonable exception message we define293* a few specific errors here.294*/295#define AGENT_ERROR_BADJAR ((jint)100) /* Agent JAR not found or no Agent-Class attribute */296#define AGENT_ERROR_NOTONCP ((jint)101) /* Unable to add JAR file to system class path */297#define AGENT_ERROR_STARTFAIL ((jint)102) /* No agentmain method or agentmain failed */298299/*300* This will be called once each time a tool attaches to the VM and loads301* the JPLIS library.302*/303JNIEXPORT jint JNICALL304DEF_Agent_OnAttach(JavaVM* vm, char *args, void * reserved) {305JPLISInitializationError initerror = JPLIS_INIT_ERROR_NONE;306jint result = JNI_OK;307JPLISAgent * agent = NULL;308JNIEnv * jni_env = NULL;309310/*311* Need JNIEnv - guaranteed to be called from thread that is already312* attached to VM313*/314result = (*vm)->GetEnv(vm, (void**)&jni_env, JNI_VERSION_1_2);315jplis_assert(result==JNI_OK);316317initerror = createNewJPLISAgent(vm, &agent);318if ( initerror == JPLIS_INIT_ERROR_NONE ) {319int oldLen, newLen;320char * jarfile;321char * options;322jarAttribute* attributes;323char * agentClass;324char * bootClassPath;325jboolean success;326327/*328* Parse <jarfile>[=options] into jarfile and options329*/330if (parseArgumentTail(args, &jarfile, &options) != 0) {331return JNI_ENOMEM;332}333334/*335* Open the JAR file and parse the manifest336*/337attributes = readAttributes( jarfile );338if (attributes == NULL) {339fprintf(stderr, "Error opening zip file or JAR manifest missing: %s\n", jarfile);340free(jarfile);341if (options != NULL) free(options);342return AGENT_ERROR_BADJAR;343}344345agentClass = getAttribute(attributes, "Agent-Class");346if (agentClass == NULL) {347fprintf(stderr, "Failed to find Agent-Class manifest attribute from %s\n",348jarfile);349free(jarfile);350if (options != NULL) free(options);351freeAttributes(attributes);352return AGENT_ERROR_BADJAR;353}354355/*356* Add the jarfile to the system class path357*/358if (appendClassPath(agent, jarfile)) {359fprintf(stderr, "Unable to add %s to system class path "360"- not supported by system class loader or configuration error!\n",361jarfile);362free(jarfile);363if (options != NULL) free(options);364freeAttributes(attributes);365return AGENT_ERROR_NOTONCP;366}367368/*369* The value of the Agent-Class attribute becomes the agent370* class name. The manifest is in UTF8 so need to convert to371* modified UTF8 (see JNI spec).372*/373oldLen = (int)strlen(agentClass);374newLen = modifiedUtf8LengthOfUtf8(agentClass, oldLen);375/*376* According to JVMS class name is represented as CONSTANT_Utf8_info,377* so its length is u2 (i.e. must be <= 0xFFFF).378* Negative oldLen or newLen means we got signed integer overflow379* (modifiedUtf8LengthOfUtf8 returns negative value if oldLen is negative).380*/381if (oldLen < 0 || newLen < 0 || newLen > 0xFFFF) {382fprintf(stderr, "Agent-Class value is too big\n");383free(jarfile);384if (options != NULL) free(options);385freeAttributes(attributes);386return AGENT_ERROR_BADJAR;387}388if (newLen == oldLen) {389agentClass = strdup(agentClass);390} else {391char* str = (char*)malloc( newLen+1 );392if (str != NULL) {393convertUtf8ToModifiedUtf8(agentClass, oldLen, str, newLen);394}395agentClass = str;396}397if (agentClass == NULL) {398free(jarfile);399if (options != NULL) free(options);400freeAttributes(attributes);401return JNI_ENOMEM;402}403404/*405* If the Boot-Class-Path attribute is specified then we process406* each URL - in the live phase only JAR files will be added.407*/408bootClassPath = getAttribute(attributes, "Boot-Class-Path");409if (bootClassPath != NULL) {410appendBootClassPath(agent, jarfile, bootClassPath);411}412413/*414* Convert JAR attributes into agent capabilities415*/416convertCapabilityAttributes(attributes, agent);417418/*419* Create the java.lang.instrument.Instrumentation instance420*/421success = createInstrumentationImpl(jni_env, agent);422jplis_assert(success);423424/*425* Setup ClassFileLoadHook handler.426*/427if (success) {428success = setLivePhaseEventHandlers(agent);429jplis_assert(success);430}431432/*433* Start the agent434*/435if (success) {436success = startJavaAgent(agent,437jni_env,438agentClass,439options,440agent->mAgentmainCaller);441}442443if (!success) {444fprintf(stderr, "Agent failed to start!\n");445result = AGENT_ERROR_STARTFAIL;446}447448/*449* Clean-up450*/451free(jarfile);452if (options != NULL) free(options);453free(agentClass);454freeAttributes(attributes);455}456457return result;458}459460461JNIEXPORT void JNICALL462DEF_Agent_OnUnload(JavaVM *vm) {463}464465/**466* Invoked by the java launcher to load an agent in the main executable JAR.467* The Launcher-Agent-Class attribute in the main manifest of the JAR file468* is the agent class.469*470* Returns JNI_OK if the agent is loaded and initialized; JNI_ERR if this471* function fails, possibly with a pending exception.472*/473jint loadAgent(JNIEnv* env, jstring path) {474JavaVM* vm;475JPLISAgent* agent;476const char* jarfile = NULL;477jarAttribute* attributes = NULL;478char* agentClass = NULL;479char* bootClassPath;480int oldLen, newLen;481jint result = JNI_ERR;482483if ((*env)->GetJavaVM(env, &vm) < 0) {484return JNI_ERR;485}486487// create JPLISAgent with JVMTI environment488if (createNewJPLISAgent(vm, &agent) != JPLIS_INIT_ERROR_NONE) {489return JNI_ERR;490}491492// get path to JAR file as UTF-8 string493jarfile = (*env)->GetStringUTFChars(env, path, NULL);494if (jarfile == NULL) {495return JNI_ERR;496}497498// read the attributes in the main section of JAR manifest499attributes = readAttributes(jarfile);500if (attributes == NULL) {501goto releaseAndReturn;502}503504// Launcher-Agent-Class is required505agentClass = getAttribute(attributes, "Launcher-Agent-Class");506if (agentClass == NULL) {507goto releaseAndReturn;508}509510// The value of Launcher-Agent-Class is in UTF-8, convert it to modified UTF-8511oldLen = (int) strlen(agentClass);512newLen = modifiedUtf8LengthOfUtf8(agentClass, oldLen);513/*514* According to JVMS class name is represented as CONSTANT_Utf8_info,515* so its length is u2 (i.e. must be <= 0xFFFF).516* Negative oldLen or newLen means we got signed integer overflow517* (modifiedUtf8LengthOfUtf8 returns negative value if oldLen is negative).518*/519if (oldLen < 0 || newLen < 0 || newLen > 0xFFFF) {520goto releaseAndReturn;521}522if (newLen == oldLen) {523agentClass = strdup(agentClass);524} else {525char* str = (char*) malloc(newLen + 1);526if (str != NULL) {527convertUtf8ToModifiedUtf8(agentClass, oldLen, str, newLen);528}529agentClass = str;530}531if (agentClass == NULL) {532jthrowable oome = createThrowable(env, "java/lang/OutOfMemoryError", NULL);533if (oome != NULL) (*env)->Throw(env, oome);534goto releaseAndReturn;535}536537// Boot-Class-Path538bootClassPath = getAttribute(attributes, "Boot-Class-Path");539if (bootClassPath != NULL) {540appendBootClassPath(agent, jarfile, bootClassPath);541}542543// Can-XXXX capabilities544convertCapabilityAttributes(attributes, agent);545546// Create the java.lang.instrument.Instrumentation object547if (!createInstrumentationImpl(env, agent)) {548goto releaseAndReturn;549}550551// Enable the ClassFileLoadHook552if (!setLivePhaseEventHandlers(agent)) {553goto releaseAndReturn;554}555556// invoke the agentmain method557if (!startJavaAgent(agent, env, agentClass, "", agent->mAgentmainCaller)) {558goto releaseAndReturn;559}560561// initialization complete562result = JNI_OK;563564releaseAndReturn:565if (agentClass != NULL) {566free(agentClass);567}568if (attributes != NULL) {569freeAttributes(attributes);570}571if (jarfile != NULL) {572(*env)->ReleaseStringUTFChars(env, path, jarfile);573}574575return result;576}577578/*579* JVMTI callback support580*581* We have two "stages" of callback support.582* At OnLoad time, we install a VMInit handler.583* When the VMInit handler runs, we remove the VMInit handler and install a584* ClassFileLoadHook handler.585*/586587void JNICALL588eventHandlerVMInit( jvmtiEnv * jvmtienv,589JNIEnv * jnienv,590jthread thread) {591JPLISEnvironment * environment = NULL;592jboolean success = JNI_FALSE;593594environment = getJPLISEnvironment(jvmtienv);595596/* process the premain calls on the all the JPL agents */597if (environment == NULL) {598abortJVM(jnienv, JPLIS_ERRORMESSAGE_CANNOTSTART ", getting JPLIS environment failed");599}600jthrowable outstandingException = NULL;601/*602* Add the jarfile to the system class path603*/604JPLISAgent * agent = environment->mAgent;605if (appendClassPath(agent, agent->mJarfile)) {606fprintf(stderr, "Unable to add %s to system class path - "607"the system class loader does not define the "608"appendToClassPathForInstrumentation method or the method failed\n",609agent->mJarfile);610free((void *)agent->mJarfile);611abortJVM(jnienv, JPLIS_ERRORMESSAGE_CANNOTSTART ", appending to system class path failed");612}613free((void *)agent->mJarfile);614agent->mJarfile = NULL;615616outstandingException = preserveThrowable(jnienv);617success = processJavaStart( environment->mAgent, jnienv);618restoreThrowable(jnienv, outstandingException);619620/* if we fail to start cleanly, bring down the JVM */621if ( !success ) {622abortJVM(jnienv, JPLIS_ERRORMESSAGE_CANNOTSTART ", processJavaStart failed");623}624}625626void JNICALL627eventHandlerClassFileLoadHook( jvmtiEnv * jvmtienv,628JNIEnv * jnienv,629jclass class_being_redefined,630jobject loader,631const char* name,632jobject protectionDomain,633jint class_data_len,634const unsigned char* class_data,635jint* new_class_data_len,636unsigned char** new_class_data) {637JPLISEnvironment * environment = NULL;638639environment = getJPLISEnvironment(jvmtienv);640641/* if something is internally inconsistent (no agent), just silently return without touching the buffer */642if ( environment != NULL ) {643jthrowable outstandingException = preserveThrowable(jnienv);644transformClassFile( environment->mAgent,645jnienv,646loader,647name,648class_being_redefined,649protectionDomain,650class_data_len,651class_data,652new_class_data_len,653new_class_data,654environment->mIsRetransformer);655restoreThrowable(jnienv, outstandingException);656}657}658659660661662/*663* URLs in Boot-Class-Path attributes are separated by one or more spaces.664* This function splits the attribute value into a list of path segments.665* The attribute value is in UTF8 but cannot contain NUL. Also non US-ASCII666* characters must be escaped (URI syntax) so safe to iterate through the667* value as a C string.668*/669static void670splitPathList(const char* str, int* pathCount, char*** paths) {671int count = 0;672char** segments = NULL;673char** new_segments;674char* c = (char*) str;675while (*c != '\0') {676while (*c == ' ') c++; /* skip leading spaces */677if (*c == '\0') {678break;679}680new_segments = (char**)realloc(segments, (count+1)*sizeof(char*));681if (new_segments == NULL) {682jplis_assert(0);683free(segments);684count = 0;685segments = NULL;686break;687}688segments = new_segments;689segments[count++] = c;690c = strchr(c, ' ');691if (c == NULL) {692break;693}694*c = '\0';695c++;696}697*pathCount = count;698*paths = segments;699}700701702/* URI path decoding - ported from src/share/classes/java/net/URI.java */703704static int705decodeNibble(char c) {706if ((c >= '0') && (c <= '9'))707return c - '0';708if ((c >= 'a') && (c <= 'f'))709return c - 'a' + 10;710if ((c >= 'A') && (c <= 'F'))711return c - 'A' + 10;712return -1;713}714715static int716decodeByte(char c1, char c2) {717return (((decodeNibble(c1) & 0xf) << 4) | ((decodeNibble(c2) & 0xf) << 0));718}719720/*721* Evaluates all escapes in s. Assumes that escapes are well-formed722* syntactically, i.e., of the form %XX.723* If the path does not require decoding the original path is724* returned. Otherwise the decoded path (heap allocated) is returned,725* along with the length of the decoded path. Note that the return726* string will not be null terminated after decoding.727*/728static729char *decodePath(const char *s, int* decodedLen) {730int n;731char *result;732char *resultp;733int c;734int i;735736n = (int)strlen(s);737if (n == 0) {738*decodedLen = 0;739return (char*)s;740}741if (strchr(s, '%') == NULL) {742*decodedLen = n;743return (char*)s; /* no escapes, we are done */744}745746resultp = result = calloc(n+1, 1);747if (result == NULL) {748*decodedLen = 0;749return NULL;750}751c = s[0];752for (i = 0; i < n;) {753if (c != '%') {754*resultp++ = c;755if (++i >= n)756break;757c = s[i];758continue;759}760for (;;) {761char b1 = s[++i];762char b2 = s[++i];763int decoded = decodeByte(b1, b2);764*resultp++ = decoded;765if (++i >= n)766break;767c = s[i];768if (c != '%')769break;770}771}772*decodedLen = (int)(resultp - result);773return result; // not null terminated.774}775776/*777* Append the given jar file to the system class path. This should succeed in the778* onload phase but may fail in the live phase if the system class loader doesn't779* support appending to the class path.780*/781static int782appendClassPath( JPLISAgent* agent,783const char* jarfile ) {784jvmtiEnv* jvmtienv = jvmti(agent);785jvmtiError jvmtierr;786787jvmtierr = (*jvmtienv)->AddToSystemClassLoaderSearch(jvmtienv, jarfile);788check_phase_ret_1(jvmtierr);789790switch (jvmtierr) {791case JVMTI_ERROR_NONE :792return 0;793case JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED :794fprintf(stderr, "System class loader does not define "795"the appendToClassPathForInstrumentation method\n");796break;797default:798fprintf(stderr, "Unexpected error (%d) returned by "799"AddToSystemClassLoaderSearch\n", jvmtierr);800break;801}802return -1;803}804805806/*807* res = func, free'ing the previous value of 'res' if function808* returns a new result.809*/810#define TRANSFORM(res,func) { \811char* tmp = func; \812if (tmp != res) { \813free(res); \814res = tmp; \815} \816jplis_assert((void*)res != (void*)NULL); \817}818819/*820* This function takes the value of the Boot-Class-Path attribute,821* splits it into the individual path segments, and then combines it822* with the path to the jar file to create the path to be added823* to the bootclasspath.824*825* Each individual path segment starts out as a UTF8 string. Additionally826* as the path is specified to use URI path syntax all non US-ASCII827* characters are escaped. Once the URI path is decoded we get a UTF8828* string which must then be converted to the platform encoding (as it829* will be combined with the platform path of the jar file). Once830* converted it is then normalized (remove duplicate slashes, etc.).831* If the resulting path is an absolute path (starts with a slash for832* example) then the path will be added to the bootclasspath. Otherwise833* if it's not absolute then we get the canoncial path of the agent jar834* file and then resolve the path in the context of the base path of835* the agent jar.836*/837static void838appendBootClassPath( JPLISAgent* agent,839const char* jarfile,840const char* pathList ) {841char canonicalPath[MAXPATHLEN];842char *parent = NULL;843int haveBasePath = 0;844845int count, i;846char **paths;847jvmtiEnv* jvmtienv = jvmti(agent);848jvmtiError jvmtierr;849850/*851* Split the attribute value into the individual path segments852* and process each in sequence853*/854splitPathList(pathList, &count, &paths);855856for (i=0; i<count; i++) {857int len;858char* path;859char* pos;860861/*862* The path segment at this point is a pointer into the attribute863* value. As it will go through a number of transformation (tossing away864* the previous results as we go along) it make it easier if the path865* starts out as a heap allocated string.866*/867path = strdup(paths[i]);868jplis_assert(path != (char*)NULL);869870/*871* The attribute is specified to be a list of relative URIs so in theory872* there could be a query component - if so, get rid of it.873*/874pos = strchr(path, '?');875if (pos != NULL) {876*pos = '\0';877}878879/*880* Check for characters that are not allowed in the path component of881* a URI.882*/883if (validatePathChars(path)) {884fprintf(stderr, "WARNING: illegal character in Boot-Class-Path value: %s\n",885path);886free(path);887continue;888}889890891/*892* Next decode any escaped characters. The result is a UTF8 string.893*/894TRANSFORM(path, decodePath(path,&len));895896/*897* Convert to the platform encoding898*/899{900char platform[MAXPATHLEN];901int new_len = convertUft8ToPlatformString(path, len, platform, MAXPATHLEN);902free(path);903if (new_len < 0) {904/* bogus value - exceeds maximum path size or unable to convert */905continue;906}907path = strdup(platform);908jplis_assert(path != (char*)NULL);909}910911/*912* Post-process the URI path - needed on Windows to transform913* /c:/foo to c:/foo.914*/915TRANSFORM(path, fromURIPath(path));916917/*918* Normalize the path - no duplicate slashes (except UNCs on Windows), trailing919* slash removed.920*/921TRANSFORM(path, normalize(path));922923/*924* If the path is an absolute path then add to the bootclassloader925* search path. Otherwise we get the canonical path of the agent jar926* and then use its base path (directory) to resolve the given path927* segment.928*929* NOTE: JVMTI is specified to use modified UTF8 strings (like JNI).930* In 1.5.0 the AddToBootstrapClassLoaderSearch takes a platform string931* - see 5049313.932*/933if (isAbsolute(path)) {934jvmtierr = (*jvmtienv)->AddToBootstrapClassLoaderSearch(jvmtienv, path);935} else {936char* resolved;937938if (!haveBasePath) {939if (JDK_Canonicalize((char*)jarfile, canonicalPath, sizeof(canonicalPath)) != 0) {940fprintf(stderr, "WARNING: unable to canonicalize %s\n", jarfile);941free(path);942continue;943}944parent = basePath(canonicalPath);945jplis_assert(parent != (char*)NULL);946haveBasePath = 1;947}948949resolved = resolve(parent, path);950jvmtierr = (*jvmtienv)->AddToBootstrapClassLoaderSearch(jvmtienv, resolved);951free(resolved);952}953954/* print warning if boot class path not updated */955if (jvmtierr != JVMTI_ERROR_NONE) {956check_phase_blob_ret(jvmtierr, free(path));957958fprintf(stderr, "WARNING: %s not added to bootstrap class loader search: ", path);959switch (jvmtierr) {960case JVMTI_ERROR_ILLEGAL_ARGUMENT :961fprintf(stderr, "Illegal argument or not JAR file\n");962break;963default:964fprintf(stderr, "Unexpected error: %d\n", jvmtierr);965}966}967968/* finished with the path */969free(path);970}971972973/* clean-up */974if (haveBasePath && parent != canonicalPath) {975free(parent);976}977}978979980