Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/instrument/InvocationAdapter.c
38767 views
/*1* Copyright (c) 2003, 2008, 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 "Utilities.h"35#include "JPLISAssert.h"36#include "JPLISAgent.h"37#include "JavaExceptions.h"3839#include "EncodingSupport.h"40#include "FileSystemSupport.h"41#include "JarFacade.h"42#include "PathCharsValidator.h"4344/**45* This module contains the direct interface points with the JVMTI.46* The OnLoad handler is here, along with the various event handlers.47*/4849static int50appendClassPath(JPLISAgent* agent,51const char* jarfile);5253static void54appendBootClassPath(JPLISAgent* agent,55const char* jarfile,56const char* pathList);575859/*60* Parse -javaagent tail, of the form name[=options], into name61* and options. Returned values are heap allocated and options maybe62* NULL. Returns 0 if parse succeeds, -1 if allocation fails.63*/64static int65parseArgumentTail(char* tail, char** name, char** options) {66int len;67char* pos;6869pos = strchr(tail, '=');70len = (pos == NULL) ? (int)strlen(tail) : (int)(pos - tail);7172*name = (char*)malloc(len+1);73if (*name == NULL) {74return -1;75}76memcpy(*name, tail, len);77(*name)[len] = '\0';7879if (pos == NULL) {80*options = NULL;81} else {82char * str = (char*)malloc( (int)strlen(pos + 1) + 1 );83if (str == NULL) {84free(*name);85return -1;86}87strcpy(str, pos +1);88*options = str;89}90return 0;91}9293/*94* Get the value of an attribute in an attribute list. Returns NULL95* if attribute not found.96*/97jboolean98getBooleanAttribute(const jarAttribute* attributes, const char* name) {99char* attributeValue = getAttribute(attributes, name);100return attributeValue != NULL && strcasecmp(attributeValue, "true") == 0;101}102103/*104* Parse any capability settings in the JAR manifest and105* convert them to JVM TI capabilities.106*/107void108convertCapabilityAtrributes(const jarAttribute* attributes, JPLISAgent* agent) {109/* set redefineClasses capability */110if (getBooleanAttribute(attributes, "Can-Redefine-Classes")) {111addRedefineClassesCapability(agent);112}113114/* create an environment which has the retransformClasses capability */115if (getBooleanAttribute(attributes, "Can-Retransform-Classes")) {116retransformableEnvironment(agent);117}118119/* set setNativeMethodPrefix capability */120if (getBooleanAttribute(attributes, "Can-Set-Native-Method-Prefix")) {121addNativeMethodPrefixCapability(agent);122}123124/* for retransformClasses testing, set capability to use original method order */125if (getBooleanAttribute(attributes, "Can-Maintain-Original-Method-Order")) {126addOriginalMethodOrderCapability(agent);127}128}129130/*131* This will be called once for every -javaagent on the command line.132* Each call to Agent_OnLoad will create its own agent and agent data.133*134* The argument tail string provided to Agent_OnLoad will be of form135* <jarfile>[=<options>]. The tail string is split into the jarfile and136* options components. The jarfile manifest is parsed and the value of the137* Premain-Class attribute will become the agent's premain class. The jar138* file is then added to the system class path, and if the Boot-Class-Path139* attribute is present then all relative URLs in the value are processed140* to create boot class path segments to append to the boot class path.141*/142JNIEXPORT jint JNICALL143Agent_OnLoad(JavaVM *vm, char *tail, void * reserved) {144JPLISInitializationError initerror = JPLIS_INIT_ERROR_NONE;145jint result = JNI_OK;146JPLISAgent * agent = NULL;147148initerror = createNewJPLISAgent(vm, &agent);149if ( initerror == JPLIS_INIT_ERROR_NONE ) {150int oldLen, newLen;151char * jarfile;152char * options;153jarAttribute* attributes;154char * premainClass;155char * agentClass;156char * bootClassPath;157158/*159* Parse <jarfile>[=options] into jarfile and options160*/161if (parseArgumentTail(tail, &jarfile, &options) != 0) {162fprintf(stderr, "-javaagent: memory allocation failure.\n");163return JNI_ERR;164}165166/*167* Agent_OnLoad is specified to provide the agent options168* argument tail in modified UTF8. However for 1.5.0 this is169* actually in the platform encoding - see 5049313.170*171* Open zip/jar file and parse archive. If can't be opened or172* not a zip file return error. Also if Premain-Class attribute173* isn't present we return an error.174*/175attributes = readAttributes(jarfile);176if (attributes == NULL) {177fprintf(stderr, "Error opening zip file or JAR manifest missing : %s\n", jarfile);178free(jarfile);179if (options != NULL) free(options);180return JNI_ERR;181}182183premainClass = getAttribute(attributes, "Premain-Class");184if (premainClass == NULL) {185fprintf(stderr, "Failed to find Premain-Class manifest attribute in %s\n",186jarfile);187free(jarfile);188if (options != NULL) free(options);189freeAttributes(attributes);190return JNI_ERR;191}192193/*194* Add to the jarfile195*/196appendClassPath(agent, jarfile);197198/*199* The value of the Premain-Class attribute becomes the agent200* class name. The manifest is in UTF8 so need to convert to201* modified UTF8 (see JNI spec).202*/203oldLen = (int)strlen(premainClass);204newLen = modifiedUtf8LengthOfUtf8(premainClass, oldLen);205/*206* According to JVMS class name is represented as CONSTANT_Utf8_info,207* so its length is u2 (i.e. must be <= 0xFFFF).208* Negative oldLen or newLen means we got signed integer overflow209* (modifiedUtf8LengthOfUtf8 returns negative value if oldLen is negative).210*/211if (oldLen < 0 || newLen < 0 || newLen > 0xFFFF) {212fprintf(stderr, "-javaagent: Premain-Class value is too big\n");213free(jarfile);214if (options != NULL) free(options);215freeAttributes(attributes);216return JNI_ERR;217}218if (newLen == oldLen) {219premainClass = strdup(premainClass);220} else {221char* str = (char*)malloc( newLen+1 );222if (str != NULL) {223convertUtf8ToModifiedUtf8(premainClass, oldLen, str, newLen);224}225premainClass = str;226}227if (premainClass == NULL) {228fprintf(stderr, "-javaagent: memory allocation failed\n");229free(jarfile);230if (options != NULL) free(options);231freeAttributes(attributes);232return JNI_ERR;233}234235/*236* If the Boot-Class-Path attribute is specified then we process237* each relative URL and add it to the bootclasspath.238*/239bootClassPath = getAttribute(attributes, "Boot-Class-Path");240if (bootClassPath != NULL) {241appendBootClassPath(agent, jarfile, bootClassPath);242}243244/*245* Convert JAR attributes into agent capabilities246*/247convertCapabilityAtrributes(attributes, agent);248249/*250* Track (record) the agent class name and options data251*/252initerror = recordCommandLineData(agent, premainClass, options);253254/*255* Clean-up256*/257free(jarfile);258if (options != NULL) free(options);259freeAttributes(attributes);260free(premainClass);261}262263switch (initerror) {264case JPLIS_INIT_ERROR_NONE:265result = JNI_OK;266break;267case JPLIS_INIT_ERROR_CANNOT_CREATE_NATIVE_AGENT:268result = JNI_ERR;269fprintf(stderr, "java.lang.instrument/-javaagent: cannot create native agent.\n");270break;271case JPLIS_INIT_ERROR_FAILURE:272result = JNI_ERR;273fprintf(stderr, "java.lang.instrument/-javaagent: initialization of native agent failed.\n");274break;275case JPLIS_INIT_ERROR_ALLOCATION_FAILURE:276result = JNI_ERR;277fprintf(stderr, "java.lang.instrument/-javaagent: allocation failure.\n");278break;279case JPLIS_INIT_ERROR_AGENT_CLASS_NOT_SPECIFIED:280result = JNI_ERR;281fprintf(stderr, "-javaagent: agent class not specified.\n");282break;283default:284result = JNI_ERR;285fprintf(stderr, "java.lang.instrument/-javaagent: unknown error\n");286break;287}288return result;289}290291/*292* Agent_OnAttach returns a jint. 0/JNI_OK indicates success and non-0293* indicates an error. To allow the attach mechanism throw an294* AgentInitializationException with a reasonable exception message we define295* a few specific errors here.296*/297#define AGENT_ERROR_BADJAR ((jint)100) /* Agent JAR not found or no Agent-Class attribute */298#define AGENT_ERROR_NOTONCP ((jint)101) /* Unable to add JAR file to system class path */299#define AGENT_ERROR_STARTFAIL ((jint)102) /* No agentmain method or agentmain failed */300301/*302* This will be called once each time a tool attaches to the VM and loads303* the JPLIS library.304*/305JNIEXPORT jint JNICALL306Agent_OnAttach(JavaVM* vm, char *args, void * reserved) {307JPLISInitializationError initerror = JPLIS_INIT_ERROR_NONE;308jint result = JNI_OK;309JPLISAgent * agent = NULL;310JNIEnv * jni_env = NULL;311312/*313* Need JNIEnv - guaranteed to be called from thread that is already314* attached to VM315*/316result = (*vm)->GetEnv(vm, (void**)&jni_env, JNI_VERSION_1_2);317jplis_assert(result==JNI_OK);318319initerror = createNewJPLISAgent(vm, &agent);320if ( initerror == JPLIS_INIT_ERROR_NONE ) {321int oldLen, newLen;322char * jarfile;323char * options;324jarAttribute* attributes;325char * agentClass;326char * bootClassPath;327jboolean success;328329/*330* Parse <jarfile>[=options] into jarfile and options331*/332if (parseArgumentTail(args, &jarfile, &options) != 0) {333return JNI_ENOMEM;334}335336/*337* Open the JAR file and parse the manifest338*/339attributes = readAttributes( jarfile );340if (attributes == NULL) {341fprintf(stderr, "Error opening zip file or JAR manifest missing: %s\n", jarfile);342free(jarfile);343if (options != NULL) free(options);344return AGENT_ERROR_BADJAR;345}346347agentClass = getAttribute(attributes, "Agent-Class");348if (agentClass == NULL) {349fprintf(stderr, "Failed to find Agent-Class manifest attribute from %s\n",350jarfile);351free(jarfile);352if (options != NULL) free(options);353freeAttributes(attributes);354return AGENT_ERROR_BADJAR;355}356357/*358* Add the jarfile to the system class path359*/360if (appendClassPath(agent, jarfile)) {361fprintf(stderr, "Unable to add %s to system class path "362"- not supported by system class loader or configuration error!\n",363jarfile);364free(jarfile);365if (options != NULL) free(options);366freeAttributes(attributes);367return AGENT_ERROR_NOTONCP;368}369370/*371* The value of the Agent-Class attribute becomes the agent372* class name. The manifest is in UTF8 so need to convert to373* modified UTF8 (see JNI spec).374*/375oldLen = strlen(agentClass);376newLen = modifiedUtf8LengthOfUtf8(agentClass, oldLen);377/*378* According to JVMS class name is represented as CONSTANT_Utf8_info,379* so its length is u2 (i.e. must be <= 0xFFFF).380* Negative oldLen or newLen means we got signed integer overflow381* (modifiedUtf8LengthOfUtf8 returns negative value if oldLen is negative).382*/383if (oldLen < 0 || newLen < 0 || newLen > 0xFFFF) {384fprintf(stderr, "Agent-Class value is too big\n");385free(jarfile);386if (options != NULL) free(options);387freeAttributes(attributes);388return AGENT_ERROR_BADJAR;389}390if (newLen == oldLen) {391agentClass = strdup(agentClass);392} else {393char* str = (char*)malloc( newLen+1 );394if (str != NULL) {395convertUtf8ToModifiedUtf8(agentClass, oldLen, str, newLen);396}397agentClass = str;398}399if (agentClass == NULL) {400free(jarfile);401if (options != NULL) free(options);402freeAttributes(attributes);403return JNI_ENOMEM;404}405406/*407* If the Boot-Class-Path attribute is specified then we process408* each URL - in the live phase only JAR files will be added.409*/410bootClassPath = getAttribute(attributes, "Boot-Class-Path");411if (bootClassPath != NULL) {412appendBootClassPath(agent, jarfile, bootClassPath);413}414415/*416* Convert JAR attributes into agent capabilities417*/418convertCapabilityAtrributes(attributes, agent);419420/*421* Create the java.lang.instrument.Instrumentation instance422*/423success = createInstrumentationImpl(jni_env, agent);424jplis_assert(success);425426/*427* Turn on the ClassFileLoadHook.428*/429if (success) {430success = setLivePhaseEventHandlers(agent);431jplis_assert(success);432}433434/*435* Start the agent436*/437if (success) {438success = startJavaAgent(agent,439jni_env,440agentClass,441options,442agent->mAgentmainCaller);443}444445if (!success) {446fprintf(stderr, "Agent failed to start!\n");447result = AGENT_ERROR_STARTFAIL;448}449450/*451* Clean-up452*/453free(jarfile);454if (options != NULL) free(options);455free(agentClass);456freeAttributes(attributes);457}458459return result;460}461462463JNIEXPORT void JNICALL464Agent_OnUnload(JavaVM *vm) {465}466467468/*469* JVMTI callback support470*471* We have two "stages" of callback support.472* At OnLoad time, we install a VMInit handler.473* When the VMInit handler runs, we remove the VMInit handler and install a474* ClassFileLoadHook handler.475*/476477void JNICALL478eventHandlerVMInit( jvmtiEnv * jvmtienv,479JNIEnv * jnienv,480jthread thread) {481JPLISEnvironment * environment = NULL;482jboolean success = JNI_FALSE;483484environment = getJPLISEnvironment(jvmtienv);485486/* process the premain calls on the all the JPL agents */487if ( environment != NULL ) {488jthrowable outstandingException = preserveThrowable(jnienv);489success = processJavaStart( environment->mAgent,490jnienv);491restoreThrowable(jnienv, outstandingException);492}493494/* if we fail to start cleanly, bring down the JVM */495if ( !success ) {496abortJVM(jnienv, JPLIS_ERRORMESSAGE_CANNOTSTART);497}498}499500void JNICALL501eventHandlerClassFileLoadHook( jvmtiEnv * jvmtienv,502JNIEnv * jnienv,503jclass class_being_redefined,504jobject loader,505const char* name,506jobject protectionDomain,507jint class_data_len,508const unsigned char* class_data,509jint* new_class_data_len,510unsigned char** new_class_data) {511JPLISEnvironment * environment = NULL;512513environment = getJPLISEnvironment(jvmtienv);514515/* if something is internally inconsistent (no agent), just silently return without touching the buffer */516if ( environment != NULL ) {517jthrowable outstandingException = preserveThrowable(jnienv);518transformClassFile( environment->mAgent,519jnienv,520loader,521name,522class_being_redefined,523protectionDomain,524class_data_len,525class_data,526new_class_data_len,527new_class_data,528environment->mIsRetransformer);529restoreThrowable(jnienv, outstandingException);530}531}532533534535536/*537* URLs in Boot-Class-Path attributes are separated by one or more spaces.538* This function splits the attribute value into a list of path segments.539* The attribute value is in UTF8 but cannot contain NUL. Also non US-ASCII540* characters must be escaped (URI syntax) so safe to iterate through the541* value as a C string.542*/543static void544splitPathList(const char* str, int* pathCount, char*** paths) {545int count = 0;546char** segments = NULL;547char* c = (char*) str;548while (*c != '\0') {549while (*c == ' ') c++; /* skip leading spaces */550if (*c == '\0') {551break;552}553if (segments == NULL) {554segments = (char**)malloc( sizeof(char**) );555} else {556segments = (char**)realloc( segments, (count+1)*sizeof(char**) );557}558jplis_assert(segments != (char**)NULL);559segments[count++] = c;560c = strchr(c, ' ');561if (c == NULL) {562break;563}564*c = '\0';565c++;566}567*pathCount = count;568*paths = segments;569}570571572/* URI path decoding - ported from src/share/classes/java/net/URI.java */573574static int575decodeNibble(char c) {576if ((c >= '0') && (c <= '9'))577return c - '0';578if ((c >= 'a') && (c <= 'f'))579return c - 'a' + 10;580if ((c >= 'A') && (c <= 'F'))581return c - 'A' + 10;582return -1;583}584585static int586decodeByte(char c1, char c2) {587return (((decodeNibble(c1) & 0xf) << 4) | ((decodeNibble(c2) & 0xf) << 0));588}589590/*591* Evaluates all escapes in s. Assumes that escapes are well-formed592* syntactically, i.e., of the form %XX.593* If the path does not require decoding the the original path is594* returned. Otherwise the decoded path (heap allocated) is returned,595* along with the length of the decoded path. Note that the return596* string will not be null terminated after decoding.597*/598static599char *decodePath(const char *s, int* decodedLen) {600int n;601char *result;602char *resultp;603int c;604int i;605606n = (int)strlen(s);607if (n == 0) {608*decodedLen = 0;609return (char*)s;610}611if (strchr(s, '%') == NULL) {612*decodedLen = n;613return (char*)s; /* no escapes, we are done */614}615616resultp = result = calloc(n+1, 1);617c = s[0];618for (i = 0; i < n;) {619if (c != '%') {620*resultp++ = c;621if (++i >= n)622break;623c = s[i];624continue;625}626for (;;) {627char b1 = s[++i];628char b2 = s[++i];629int decoded = decodeByte(b1, b2);630*resultp++ = decoded;631if (++i >= n)632break;633c = s[i];634if (c != '%')635break;636}637}638*decodedLen = (int)(resultp - result);639return result; // not null terminated.640}641642/*643* Append the given jar file to the system class path. This should succeed in the644* onload phase but may fail in the live phase if the system class loader doesn't645* support appending to the class path.646*/647static int648appendClassPath( JPLISAgent* agent,649const char* jarfile ) {650jvmtiEnv* jvmtienv = jvmti(agent);651jvmtiError jvmtierr;652653jvmtierr = (*jvmtienv)->AddToSystemClassLoaderSearch(jvmtienv, jarfile);654check_phase_ret_1(jvmtierr);655656if (jvmtierr == JVMTI_ERROR_NONE) {657return 0;658} else {659jvmtiPhase phase;660jvmtiError err;661662err = (*jvmtienv)->GetPhase(jvmtienv, &phase);663/* can be called from any phase */664jplis_assert(err == JVMTI_ERROR_NONE);665666if (phase == JVMTI_PHASE_LIVE) {667switch (jvmtierr) {668case JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED :669fprintf(stderr, "System class loader does not support adding "670"JAR file to system class path during the live phase!\n");671break;672default:673fprintf(stderr, "Unexpected error (%d) returned by "674"AddToSystemClassLoaderSearch\n", jvmtierr);675break;676}677return -1;678}679jplis_assert(0);680}681return -2;682}683684685/*686* res = func, free'ing the previous value of 'res' if function687* returns a new result.688*/689#define TRANSFORM(res,func) { \690char* tmp = func; \691if (tmp != res) { \692free(res); \693res = tmp; \694} \695jplis_assert((void*)res != (void*)NULL); \696}697698699/*700* This function takes the value of the Boot-Class-Path attribute,701* splits it into the individual path segments, and then combines it702* with the path to the jar file to create the path to be added703* to the bootclasspath.704*705* Each individual path segment starts out as a UTF8 string. Additionally706* as the path is specified to use URI path syntax all non US-ASCII707* characters are escaped. Once the URI path is decoded we get a UTF8708* string which must then be converted to the platform encoding (as it709* will be combined with the platform path of the jar file). Once710* converted it is then normalized (remove duplicate slashes, etc.).711* If the resulting path is an absolute path (starts with a slash for712* example) then the path will be added to the bootclasspath. Otherwise713* if it's not absolute then we get the canoncial path of the agent jar714* file and then resolve the path in the context of the base path of715* the agent jar.716*/717static void718appendBootClassPath( JPLISAgent* agent,719const char* jarfile,720const char* pathList ) {721char canonicalPath[MAXPATHLEN];722char *parent = NULL;723int haveBasePath = 0;724725int count, i;726char **paths;727jvmtiEnv* jvmtienv = jvmti(agent);728jvmtiError jvmtierr;729730/*731* Split the attribute value into the individual path segments732* and process each in sequence733*/734splitPathList(pathList, &count, &paths);735736for (i=0; i<count; i++) {737int len;738char* path;739char* pos;740741/*742* The path segment at this point is a pointer into the attribute743* value. As it will go through a number of transformation (tossing away744* the previous results as we go along) it make it easier if the path745* starts out as a heap allocated string.746*/747path = strdup(paths[i]);748jplis_assert(path != (char*)NULL);749750/*751* The attribute is specified to be a list of relative URIs so in theory752* there could be a query component - if so, get rid of it.753*/754pos = strchr(path, '?');755if (pos != NULL) {756*pos = '\0';757}758759/*760* Check for characters that are not allowed in the path component of761* a URI.762*/763if (validatePathChars(path)) {764fprintf(stderr, "WARNING: illegal character in Boot-Class-Path value: %s\n",765path);766free(path);767continue;768}769770771/*772* Next decode any escaped characters. The result is a UTF8 string.773*/774TRANSFORM(path, decodePath(path,&len));775776/*777* Convert to the platform encoding778*/779{780char platform[MAXPATHLEN];781int new_len = convertUft8ToPlatformString(path, len, platform, MAXPATHLEN);782free(path);783if (new_len < 0) {784/* bogus value - exceeds maximum path size or unable to convert */785continue;786}787path = strdup(platform);788jplis_assert(path != (char*)NULL);789}790791/*792* Post-process the URI path - needed on Windows to transform793* /c:/foo to c:/foo.794*/795TRANSFORM(path, fromURIPath(path));796797/*798* Normalize the path - no duplicate slashes (except UNCs on Windows), trailing799* slash removed.800*/801TRANSFORM(path, normalize(path));802803/*804* If the path is an absolute path then add to the bootclassloader805* search path. Otherwise we get the canonical path of the agent jar806* and then use its base path (directory) to resolve the given path807* segment.808*809* NOTE: JVMTI is specified to use modified UTF8 strings (like JNI).810* In 1.5.0 the AddToBootstrapClassLoaderSearch takes a platform string811* - see 5049313.812*/813if (isAbsolute(path)) {814jvmtierr = (*jvmtienv)->AddToBootstrapClassLoaderSearch(jvmtienv, path);815} else {816char* resolved;817818if (!haveBasePath) {819if (canonicalize((char*)jarfile, canonicalPath, sizeof(canonicalPath)) != 0) {820fprintf(stderr, "WARNING: unable to canonicalize %s\n", jarfile);821free(path);822continue;823}824parent = basePath(canonicalPath);825jplis_assert(parent != (char*)NULL);826haveBasePath = 1;827}828829resolved = resolve(parent, path);830jvmtierr = (*jvmtienv)->AddToBootstrapClassLoaderSearch(jvmtienv, resolved);831}832833/* print warning if boot class path not updated */834if (jvmtierr != JVMTI_ERROR_NONE) {835check_phase_blob_ret(jvmtierr, free(path));836837fprintf(stderr, "WARNING: %s not added to bootstrap class loader search: ", path);838switch (jvmtierr) {839case JVMTI_ERROR_ILLEGAL_ARGUMENT :840fprintf(stderr, "Illegal argument or not JAR file\n");841break;842default:843fprintf(stderr, "Unexpected error: %d\n", jvmtierr);844}845}846847/* finished with the path */848free(path);849}850851852/* clean-up */853if (haveBasePath && parent != canonicalPath) {854free(parent);855}856}857858859