Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.instrument/share/native/libinstrument/JPLISAgent.c
41149 views
1
/*
2
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
/*
27
* Copyright 2003 Wily Technology, Inc.
28
*/
29
30
#include <jni.h>
31
#include <jvm.h>
32
#include <jvmti.h>
33
#include <stdlib.h>
34
#include <string.h>
35
#include "JPLISAgent.h"
36
#include "JPLISAssert.h"
37
#include "Utilities.h"
38
#include "Reentrancy.h"
39
#include "JavaExceptions.h"
40
41
#include "EncodingSupport.h"
42
#include "FileSystemSupport.h" /* For MAXPATHLEN & uintptr_t */
43
44
#include "sun_instrument_InstrumentationImpl.h"
45
46
/*
47
* The JPLISAgent manages the initialization all of the Java programming language Agents.
48
* It also supports the native method bridge between the JPLIS and the JVMTI.
49
* It maintains a single JVMTI Env that all JPL agents share.
50
* It parses command line requests and creates individual Java agents.
51
*/
52
53
54
/*
55
* private prototypes
56
*/
57
58
/* Allocates an unformatted JPLIS agent data structure. Returns NULL if allocation fails. */
59
JPLISAgent *
60
allocateJPLISAgent(jvmtiEnv * jvmtiEnv);
61
62
/* Initializes an already-allocated JPLIS agent data structure. */
63
JPLISInitializationError
64
initializeJPLISAgent( JPLISAgent * agent,
65
JavaVM * vm,
66
jvmtiEnv * jvmtienv);
67
/* De-allocates a JPLIS agent data structure. Only used in partial-failure cases at startup;
68
* in normal usage the JPLIS agent lives forever
69
*/
70
void
71
deallocateJPLISAgent( jvmtiEnv * jvmtienv,
72
JPLISAgent * agent);
73
74
/* Does one-time work to interrogate the JVM about capabilities and cache the answers. */
75
void
76
checkCapabilities(JPLISAgent * agent);
77
78
/* Takes the elements of the command string (agent class name and options string) and
79
* create java strings for them.
80
* Returns true if a classname was found. Makes no promises beyond the textual; says nothing about whether
81
* the class exists or can be loaded.
82
* If return value is true, sets outputClassname to a non-NULL local JNI reference.
83
* If return value is true, sets outputOptionsString either to NULL or to a non-NULL local JNI reference.
84
* If return value is false, neither output parameter is set.
85
*/
86
jboolean
87
commandStringIntoJavaStrings( JNIEnv * jnienv,
88
const char * classname,
89
const char * optionsString,
90
jstring * outputClassname,
91
jstring * outputOptionsString);
92
93
/* Start one Java agent from the supplied parameters.
94
* Most of the logic lives in a helper function that lives over in Java code--
95
* we pass parameters out to Java and use our own Java helper to actually
96
* load the agent and call the premain.
97
* Returns true if the Java agent class is loaded and the premain/agentmain method completes
98
* with no exceptions, false otherwise.
99
*/
100
jboolean
101
invokeJavaAgentMainMethod( JNIEnv * jnienv,
102
jobject instrumentationImpl,
103
jmethodID agentMainMethod,
104
jstring className,
105
jstring optionsString);
106
107
/* Once we have loaded the Java agent and called the premain,
108
* we can release the copies we have been keeping of the command line
109
* data (agent class name and option strings).
110
*/
111
void
112
deallocateCommandLineData(JPLISAgent * agent);
113
114
/*
115
* Common support for various class list fetchers.
116
*/
117
typedef jvmtiError (*ClassListFetcher)
118
( jvmtiEnv * jvmtiEnv,
119
jobject classLoader,
120
jint * classCount,
121
jclass ** classes);
122
123
/* Fetcher that ignores the class loader parameter, and uses the JVMTI to get a list of all classes.
124
* Returns a jvmtiError according to the underlying JVMTI service.
125
*/
126
jvmtiError
127
getAllLoadedClassesClassListFetcher( jvmtiEnv * jvmtiEnv,
128
jobject classLoader,
129
jint * classCount,
130
jclass ** classes);
131
132
/* Fetcher that uses the class loader parameter, and uses the JVMTI to get a list of all classes
133
* for which the supplied loader is the initiating loader.
134
* Returns a jvmtiError according to the underlying JVMTI service.
135
*/
136
jvmtiError
137
getInitiatedClassesClassListFetcher( jvmtiEnv * jvmtiEnv,
138
jobject classLoader,
139
jint * classCount,
140
jclass ** classes);
141
142
/*
143
* Common guts for two native methods, which are the same except for the policy for fetching
144
* the list of classes.
145
* Either returns a local JNI reference to an array of references to java.lang.Class.
146
* Can throw, if it does will alter the JNIEnv with an outstanding exception.
147
*/
148
jobjectArray
149
commonGetClassList( JNIEnv * jnienv,
150
JPLISAgent * agent,
151
jobject classLoader,
152
ClassListFetcher fetcher);
153
154
155
/*
156
* Misc. utilities.
157
*/
158
159
/* Checked exception mapper used by the redefine classes implementation.
160
* Allows ClassNotFoundException or UnmodifiableClassException; maps others
161
* to InternalError. Can return NULL in an error case.
162
*/
163
jthrowable
164
redefineClassMapper( JNIEnv * jnienv,
165
jthrowable throwableToMap);
166
167
/* Turns a buffer of jclass * into a Java array whose elements are java.lang.Class.
168
* Can throw, if it does will alter the JNIEnv with an outstanding exception.
169
*/
170
jobjectArray
171
getObjectArrayFromClasses(JNIEnv* jnienv, jclass* classes, jint classCount);
172
173
174
JPLISEnvironment *
175
getJPLISEnvironment(jvmtiEnv * jvmtienv) {
176
JPLISEnvironment * environment = NULL;
177
jvmtiError jvmtierror = JVMTI_ERROR_NONE;
178
179
jvmtierror = (*jvmtienv)->GetEnvironmentLocalStorage(
180
jvmtienv,
181
(void**)&environment);
182
/* can be called from any phase */
183
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
184
185
if (jvmtierror == JVMTI_ERROR_NONE) {
186
jplis_assert(environment != NULL);
187
jplis_assert(environment->mJVMTIEnv == jvmtienv);
188
} else {
189
environment = NULL;
190
}
191
return environment;
192
}
193
194
/*
195
* OnLoad processing code.
196
*/
197
198
/*
199
* Creates a new JPLISAgent.
200
* Returns error if the agent cannot be created and initialized.
201
* The JPLISAgent* pointed to by agent_ptr is set to the new broker,
202
* or NULL if an error has occurred.
203
*/
204
JPLISInitializationError
205
createNewJPLISAgent(JavaVM * vm, JPLISAgent **agent_ptr) {
206
JPLISInitializationError initerror = JPLIS_INIT_ERROR_NONE;
207
jvmtiEnv * jvmtienv = NULL;
208
jint jnierror = JNI_OK;
209
210
*agent_ptr = NULL;
211
jnierror = (*vm)->GetEnv( vm,
212
(void **) &jvmtienv,
213
JVMTI_VERSION_1_1);
214
if ( jnierror != JNI_OK ) {
215
initerror = JPLIS_INIT_ERROR_CANNOT_CREATE_NATIVE_AGENT;
216
} else {
217
JPLISAgent * agent = allocateJPLISAgent(jvmtienv);
218
if ( agent == NULL ) {
219
initerror = JPLIS_INIT_ERROR_ALLOCATION_FAILURE;
220
} else {
221
initerror = initializeJPLISAgent( agent,
222
vm,
223
jvmtienv);
224
if ( initerror == JPLIS_INIT_ERROR_NONE ) {
225
*agent_ptr = agent;
226
} else {
227
deallocateJPLISAgent(jvmtienv, agent);
228
}
229
}
230
231
/* don't leak envs */
232
if ( initerror != JPLIS_INIT_ERROR_NONE ) {
233
jvmtiError jvmtierror = (*jvmtienv)->DisposeEnvironment(jvmtienv);
234
/* can be called from any phase */
235
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
236
}
237
}
238
239
return initerror;
240
}
241
242
/*
243
* Allocates a JPLISAgent. Returns NULL if it cannot be allocated
244
*/
245
JPLISAgent *
246
allocateJPLISAgent(jvmtiEnv * jvmtienv) {
247
return (JPLISAgent *) allocate( jvmtienv,
248
sizeof(JPLISAgent));
249
}
250
251
JPLISInitializationError
252
initializeJPLISAgent( JPLISAgent * agent,
253
JavaVM * vm,
254
jvmtiEnv * jvmtienv) {
255
jvmtiError jvmtierror = JVMTI_ERROR_NONE;
256
jvmtiPhase phase;
257
258
agent->mJVM = vm;
259
agent->mNormalEnvironment.mJVMTIEnv = jvmtienv;
260
agent->mNormalEnvironment.mAgent = agent;
261
agent->mNormalEnvironment.mIsRetransformer = JNI_FALSE;
262
agent->mRetransformEnvironment.mJVMTIEnv = NULL; /* NULL until needed */
263
agent->mRetransformEnvironment.mAgent = agent;
264
agent->mRetransformEnvironment.mIsRetransformer = JNI_FALSE; /* JNI_FALSE until mJVMTIEnv is set */
265
agent->mAgentmainCaller = NULL;
266
agent->mInstrumentationImpl = NULL;
267
agent->mPremainCaller = NULL;
268
agent->mTransform = NULL;
269
agent->mRedefineAvailable = JNI_FALSE; /* assume no for now */
270
agent->mRedefineAdded = JNI_FALSE;
271
agent->mNativeMethodPrefixAvailable = JNI_FALSE; /* assume no for now */
272
agent->mNativeMethodPrefixAdded = JNI_FALSE;
273
agent->mAgentClassName = NULL;
274
agent->mOptionsString = NULL;
275
agent->mJarfile = NULL;
276
277
/* make sure we can recover either handle in either direction.
278
* the agent has a ref to the jvmti; make it mutual
279
*/
280
jvmtierror = (*jvmtienv)->SetEnvironmentLocalStorage(
281
jvmtienv,
282
&(agent->mNormalEnvironment));
283
/* can be called from any phase */
284
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
285
286
/* check what capabilities are available */
287
checkCapabilities(agent);
288
289
/* check phase - if live phase then we don't need the VMInit event */
290
jvmtierror = (*jvmtienv)->GetPhase(jvmtienv, &phase);
291
/* can be called from any phase */
292
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
293
if (phase == JVMTI_PHASE_LIVE) {
294
return JPLIS_INIT_ERROR_NONE;
295
}
296
297
if (phase != JVMTI_PHASE_ONLOAD) {
298
/* called too early or called too late; either way bail out */
299
return JPLIS_INIT_ERROR_FAILURE;
300
}
301
302
/* now turn on the VMInit event */
303
if ( jvmtierror == JVMTI_ERROR_NONE ) {
304
jvmtiEventCallbacks callbacks;
305
memset(&callbacks, 0, sizeof(callbacks));
306
callbacks.VMInit = &eventHandlerVMInit;
307
308
jvmtierror = (*jvmtienv)->SetEventCallbacks( jvmtienv,
309
&callbacks,
310
sizeof(callbacks));
311
check_phase_ret_blob(jvmtierror, JPLIS_INIT_ERROR_FAILURE);
312
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
313
}
314
315
if ( jvmtierror == JVMTI_ERROR_NONE ) {
316
jvmtierror = (*jvmtienv)->SetEventNotificationMode(
317
jvmtienv,
318
JVMTI_ENABLE,
319
JVMTI_EVENT_VM_INIT,
320
NULL /* all threads */);
321
check_phase_ret_blob(jvmtierror, JPLIS_INIT_ERROR_FAILURE);
322
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
323
}
324
325
return (jvmtierror == JVMTI_ERROR_NONE)? JPLIS_INIT_ERROR_NONE : JPLIS_INIT_ERROR_FAILURE;
326
}
327
328
void
329
deallocateJPLISAgent(jvmtiEnv * jvmtienv, JPLISAgent * agent) {
330
deallocate(jvmtienv, agent);
331
}
332
333
334
JPLISInitializationError
335
recordCommandLineData( JPLISAgent * agent,
336
const char * agentClassName,
337
const char * optionsString ) {
338
JPLISInitializationError initerror = JPLIS_INIT_ERROR_NONE;
339
char * ourCopyOfAgentClassName = NULL;
340
char * ourCopyOfOptionsString = NULL;
341
342
/* if no actual params, bail out now */
343
if ((agentClassName == NULL) || (*agentClassName == 0)) {
344
initerror = JPLIS_INIT_ERROR_AGENT_CLASS_NOT_SPECIFIED;
345
} else {
346
ourCopyOfAgentClassName = allocate(jvmti(agent), strlen(agentClassName)+1);
347
if (ourCopyOfAgentClassName == NULL) {
348
initerror = JPLIS_INIT_ERROR_ALLOCATION_FAILURE;
349
} else {
350
if (optionsString != NULL) {
351
ourCopyOfOptionsString = allocate(jvmti(agent), strlen(optionsString)+1);
352
if (ourCopyOfOptionsString == NULL) {
353
deallocate(jvmti(agent), ourCopyOfAgentClassName);
354
initerror = JPLIS_INIT_ERROR_ALLOCATION_FAILURE;
355
}
356
}
357
}
358
}
359
360
if (initerror == JPLIS_INIT_ERROR_NONE) {
361
strcpy(ourCopyOfAgentClassName, agentClassName);
362
if (optionsString != NULL) {
363
strcpy(ourCopyOfOptionsString, optionsString);
364
}
365
agent->mAgentClassName = ourCopyOfAgentClassName;
366
agent->mOptionsString = ourCopyOfOptionsString;
367
}
368
369
return initerror;
370
}
371
372
/*
373
* VMInit processing code.
374
*/
375
376
377
/*
378
* If this call fails, the JVM launch will ultimately be aborted,
379
* so we don't have to be super-careful to clean up in partial failure
380
* cases.
381
*/
382
jboolean
383
processJavaStart( JPLISAgent * agent,
384
JNIEnv * jnienv) {
385
jboolean result;
386
387
/*
388
* OK, Java is up now. We can start everything that needs Java.
389
*/
390
391
/*
392
* First make our fallback InternalError throwable.
393
*/
394
result = initializeFallbackError(jnienv);
395
jplis_assert_msg(result, "fallback init failed");
396
397
/*
398
* Now make the InstrumentationImpl instance.
399
*/
400
if ( result ) {
401
result = createInstrumentationImpl(jnienv, agent);
402
jplis_assert_msg(result, "instrumentation instance creation failed");
403
}
404
405
406
/*
407
* Register a handler for ClassFileLoadHook (without enabling this event).
408
* Turn off the VMInit handler.
409
*/
410
if ( result ) {
411
result = setLivePhaseEventHandlers(agent);
412
jplis_assert_msg(result, "setting of live phase VM handlers failed");
413
}
414
415
/*
416
* Load the Java agent, and call the premain.
417
*/
418
if ( result ) {
419
result = startJavaAgent(agent, jnienv,
420
agent->mAgentClassName, agent->mOptionsString,
421
agent->mPremainCaller);
422
jplis_assert_msg(result, "agent load/premain call failed");
423
}
424
425
/*
426
* Finally surrender all of the tracking data that we don't need any more.
427
* If something is wrong, skip it, we will be aborting the JVM anyway.
428
*/
429
if ( result ) {
430
deallocateCommandLineData(agent);
431
}
432
433
return result;
434
}
435
436
jboolean
437
startJavaAgent( JPLISAgent * agent,
438
JNIEnv * jnienv,
439
const char * classname,
440
const char * optionsString,
441
jmethodID agentMainMethod) {
442
jboolean success = JNI_FALSE;
443
jstring classNameObject = NULL;
444
jstring optionsStringObject = NULL;
445
446
success = commandStringIntoJavaStrings( jnienv,
447
classname,
448
optionsString,
449
&classNameObject,
450
&optionsStringObject);
451
452
if (success) {
453
success = invokeJavaAgentMainMethod( jnienv,
454
agent->mInstrumentationImpl,
455
agentMainMethod,
456
classNameObject,
457
optionsStringObject);
458
}
459
460
return success;
461
}
462
463
void
464
deallocateCommandLineData( JPLISAgent * agent) {
465
deallocate(jvmti(agent), (void*)agent->mAgentClassName);
466
deallocate(jvmti(agent), (void*)agent->mOptionsString);
467
468
/* zero things out so it is easier to see what is going on */
469
agent->mAgentClassName = NULL;
470
agent->mOptionsString = NULL;
471
}
472
473
/*
474
* Create the java.lang.instrument.Instrumentation instance
475
* and access information for it (method IDs, etc)
476
*/
477
jboolean
478
createInstrumentationImpl( JNIEnv * jnienv,
479
JPLISAgent * agent) {
480
jclass implClass = NULL;
481
jboolean errorOutstanding = JNI_FALSE;
482
jobject resultImpl = NULL;
483
jmethodID premainCallerMethodID = NULL;
484
jmethodID agentmainCallerMethodID = NULL;
485
jmethodID transformMethodID = NULL;
486
jmethodID constructorID = NULL;
487
jobject localReference = NULL;
488
489
/* First find the class of our implementation */
490
implClass = (*jnienv)->FindClass( jnienv,
491
JPLIS_INSTRUMENTIMPL_CLASSNAME);
492
errorOutstanding = checkForAndClearThrowable(jnienv);
493
errorOutstanding = errorOutstanding || (implClass == NULL);
494
jplis_assert_msg(!errorOutstanding, "find class on InstrumentationImpl failed");
495
496
if ( !errorOutstanding ) {
497
constructorID = (*jnienv)->GetMethodID( jnienv,
498
implClass,
499
JPLIS_INSTRUMENTIMPL_CONSTRUCTOR_METHODNAME,
500
JPLIS_INSTRUMENTIMPL_CONSTRUCTOR_METHODSIGNATURE);
501
errorOutstanding = checkForAndClearThrowable(jnienv);
502
errorOutstanding = errorOutstanding || (constructorID == NULL);
503
jplis_assert_msg(!errorOutstanding, "find constructor on InstrumentationImpl failed");
504
}
505
506
if ( !errorOutstanding ) {
507
jlong peerReferenceAsScalar = (jlong)(intptr_t) agent;
508
localReference = (*jnienv)->NewObject( jnienv,
509
implClass,
510
constructorID,
511
peerReferenceAsScalar,
512
agent->mRedefineAdded,
513
agent->mNativeMethodPrefixAdded);
514
errorOutstanding = checkForAndClearThrowable(jnienv);
515
errorOutstanding = errorOutstanding || (localReference == NULL);
516
jplis_assert_msg(!errorOutstanding, "call constructor on InstrumentationImpl failed");
517
}
518
519
if ( !errorOutstanding ) {
520
resultImpl = (*jnienv)->NewGlobalRef(jnienv, localReference);
521
errorOutstanding = checkForAndClearThrowable(jnienv);
522
jplis_assert_msg(!errorOutstanding, "copy local ref to global ref");
523
}
524
525
/* Now look up the method ID for the pre-main caller (we will need this more than once) */
526
if ( !errorOutstanding ) {
527
premainCallerMethodID = (*jnienv)->GetMethodID( jnienv,
528
implClass,
529
JPLIS_INSTRUMENTIMPL_PREMAININVOKER_METHODNAME,
530
JPLIS_INSTRUMENTIMPL_PREMAININVOKER_METHODSIGNATURE);
531
errorOutstanding = checkForAndClearThrowable(jnienv);
532
errorOutstanding = errorOutstanding || (premainCallerMethodID == NULL);
533
jplis_assert_msg(!errorOutstanding, "can't find premain invoker methodID");
534
}
535
536
/* Now look up the method ID for the agent-main caller */
537
if ( !errorOutstanding ) {
538
agentmainCallerMethodID = (*jnienv)->GetMethodID( jnienv,
539
implClass,
540
JPLIS_INSTRUMENTIMPL_AGENTMAININVOKER_METHODNAME,
541
JPLIS_INSTRUMENTIMPL_AGENTMAININVOKER_METHODSIGNATURE);
542
errorOutstanding = checkForAndClearThrowable(jnienv);
543
errorOutstanding = errorOutstanding || (agentmainCallerMethodID == NULL);
544
jplis_assert_msg(!errorOutstanding, "can't find agentmain invoker methodID");
545
}
546
547
/* Now look up the method ID for the transform method (we will need this constantly) */
548
if ( !errorOutstanding ) {
549
transformMethodID = (*jnienv)->GetMethodID( jnienv,
550
implClass,
551
JPLIS_INSTRUMENTIMPL_TRANSFORM_METHODNAME,
552
JPLIS_INSTRUMENTIMPL_TRANSFORM_METHODSIGNATURE);
553
errorOutstanding = checkForAndClearThrowable(jnienv);
554
errorOutstanding = errorOutstanding || (transformMethodID == NULL);
555
jplis_assert_msg(!errorOutstanding, "can't find transform methodID");
556
}
557
558
if ( !errorOutstanding ) {
559
agent->mInstrumentationImpl = resultImpl;
560
agent->mPremainCaller = premainCallerMethodID;
561
agent->mAgentmainCaller = agentmainCallerMethodID;
562
agent->mTransform = transformMethodID;
563
}
564
565
return !errorOutstanding;
566
}
567
568
jboolean
569
commandStringIntoJavaStrings( JNIEnv * jnienv,
570
const char * classname,
571
const char * optionsString,
572
jstring * outputClassname,
573
jstring * outputOptionsString) {
574
jstring classnameJavaString = NULL;
575
jstring optionsJavaString = NULL;
576
jboolean errorOutstanding = JNI_TRUE;
577
578
classnameJavaString = (*jnienv)->NewStringUTF(jnienv, classname);
579
errorOutstanding = checkForAndClearThrowable(jnienv);
580
jplis_assert_msg(!errorOutstanding, "can't create class name java string");
581
582
if ( !errorOutstanding ) {
583
if ( optionsString != NULL) {
584
optionsJavaString = (*jnienv)->NewStringUTF(jnienv, optionsString);
585
errorOutstanding = checkForAndClearThrowable(jnienv);
586
jplis_assert_msg(!errorOutstanding, "can't create options java string");
587
}
588
589
if ( !errorOutstanding ) {
590
*outputClassname = classnameJavaString;
591
*outputOptionsString = optionsJavaString;
592
}
593
}
594
595
return !errorOutstanding;
596
}
597
598
599
jboolean
600
invokeJavaAgentMainMethod( JNIEnv * jnienv,
601
jobject instrumentationImpl,
602
jmethodID mainCallingMethod,
603
jstring className,
604
jstring optionsString) {
605
jboolean errorOutstanding = JNI_FALSE;
606
607
jplis_assert(mainCallingMethod != NULL);
608
if ( mainCallingMethod != NULL ) {
609
(*jnienv)->CallVoidMethod( jnienv,
610
instrumentationImpl,
611
mainCallingMethod,
612
className,
613
optionsString);
614
errorOutstanding = checkForThrowable(jnienv);
615
if ( errorOutstanding ) {
616
logThrowable(jnienv);
617
}
618
checkForAndClearThrowable(jnienv);
619
}
620
return !errorOutstanding;
621
}
622
623
jboolean
624
setLivePhaseEventHandlers( JPLISAgent * agent) {
625
jvmtiEventCallbacks callbacks;
626
jvmtiEnv * jvmtienv = jvmti(agent);
627
jvmtiError jvmtierror;
628
629
/* first swap out the handlers (switch from the VMInit handler, which we do not need,
630
* to the ClassFileLoadHook handler, which is what the agents need from now on)
631
*/
632
memset(&callbacks, 0, sizeof(callbacks));
633
callbacks.ClassFileLoadHook = &eventHandlerClassFileLoadHook;
634
635
jvmtierror = (*jvmtienv)->SetEventCallbacks( jvmtienv,
636
&callbacks,
637
sizeof(callbacks));
638
check_phase_ret_false(jvmtierror);
639
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
640
641
642
if ( jvmtierror == JVMTI_ERROR_NONE ) {
643
/* turn off VMInit */
644
jvmtierror = (*jvmtienv)->SetEventNotificationMode(
645
jvmtienv,
646
JVMTI_DISABLE,
647
JVMTI_EVENT_VM_INIT,
648
NULL /* all threads */);
649
check_phase_ret_false(jvmtierror);
650
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
651
}
652
653
return (jvmtierror == JVMTI_ERROR_NONE);
654
}
655
656
/**
657
* Check if the can_redefine_classes capability is available.
658
*/
659
void
660
checkCapabilities(JPLISAgent * agent) {
661
jvmtiEnv * jvmtienv = jvmti(agent);
662
jvmtiCapabilities potentialCapabilities;
663
jvmtiError jvmtierror;
664
665
memset(&potentialCapabilities, 0, sizeof(potentialCapabilities));
666
667
jvmtierror = (*jvmtienv)->GetPotentialCapabilities(jvmtienv, &potentialCapabilities);
668
check_phase_ret(jvmtierror);
669
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
670
671
if ( jvmtierror == JVMTI_ERROR_NONE ) {
672
if ( potentialCapabilities.can_redefine_classes == 1 ) {
673
agent->mRedefineAvailable = JNI_TRUE;
674
}
675
if ( potentialCapabilities.can_set_native_method_prefix == 1 ) {
676
agent->mNativeMethodPrefixAvailable = JNI_TRUE;
677
}
678
}
679
}
680
681
/**
682
* Enable native method prefix in one JVM TI environment
683
*/
684
void
685
enableNativeMethodPrefixCapability(jvmtiEnv * jvmtienv) {
686
jvmtiCapabilities desiredCapabilities;
687
jvmtiError jvmtierror;
688
689
jvmtierror = (*jvmtienv)->GetCapabilities(jvmtienv, &desiredCapabilities);
690
/* can be called from any phase */
691
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
692
desiredCapabilities.can_set_native_method_prefix = 1;
693
jvmtierror = (*jvmtienv)->AddCapabilities(jvmtienv, &desiredCapabilities);
694
check_phase_ret(jvmtierror);
695
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
696
}
697
698
699
/**
700
* Add the can_set_native_method_prefix capability
701
*/
702
void
703
addNativeMethodPrefixCapability(JPLISAgent * agent) {
704
if (agent->mNativeMethodPrefixAvailable && !agent->mNativeMethodPrefixAdded) {
705
jvmtiEnv * jvmtienv = agent->mNormalEnvironment.mJVMTIEnv;
706
enableNativeMethodPrefixCapability(jvmtienv);
707
708
jvmtienv = agent->mRetransformEnvironment.mJVMTIEnv;
709
if (jvmtienv != NULL) {
710
enableNativeMethodPrefixCapability(jvmtienv);
711
}
712
agent->mNativeMethodPrefixAdded = JNI_TRUE;
713
}
714
}
715
716
/**
717
* Add the can_maintain_original_method_order capability (for testing)
718
*/
719
void
720
addOriginalMethodOrderCapability(JPLISAgent * agent) {
721
jvmtiEnv * jvmtienv = jvmti(agent);
722
jvmtiCapabilities desiredCapabilities;
723
jvmtiError jvmtierror;
724
725
jvmtierror = (*jvmtienv)->GetCapabilities(jvmtienv, &desiredCapabilities);
726
/* can be called from any phase */
727
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
728
desiredCapabilities.can_maintain_original_method_order = 1;
729
jvmtierror = (*jvmtienv)->AddCapabilities(jvmtienv, &desiredCapabilities);
730
check_phase_ret(jvmtierror);
731
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
732
}
733
734
/**
735
* Add the can_redefine_classes capability
736
*/
737
void
738
addRedefineClassesCapability(JPLISAgent * agent) {
739
jvmtiEnv * jvmtienv = jvmti(agent);
740
jvmtiCapabilities desiredCapabilities;
741
jvmtiError jvmtierror;
742
743
if (agent->mRedefineAvailable && !agent->mRedefineAdded) {
744
jvmtierror = (*jvmtienv)->GetCapabilities(jvmtienv, &desiredCapabilities);
745
/* can be called from any phase */
746
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
747
desiredCapabilities.can_redefine_classes = 1;
748
jvmtierror = (*jvmtienv)->AddCapabilities(jvmtienv, &desiredCapabilities);
749
check_phase_ret(jvmtierror);
750
751
/*
752
* With mixed premain/agentmain agents then it's possible that the
753
* capability was potentially available in the onload phase but
754
* subsequently unavailable in the live phase.
755
*/
756
jplis_assert(jvmtierror == JVMTI_ERROR_NONE ||
757
jvmtierror == JVMTI_ERROR_NOT_AVAILABLE);
758
if (jvmtierror == JVMTI_ERROR_NONE) {
759
agent->mRedefineAdded = JNI_TRUE;
760
}
761
}
762
}
763
764
static jobject
765
getModuleObject(jvmtiEnv* jvmti,
766
jobject loaderObject,
767
const char* cname) {
768
jvmtiError err = JVMTI_ERROR_NONE;
769
jobject moduleObject = NULL;
770
771
/* find last slash in the class name */
772
char* last_slash = (cname == NULL) ? NULL : strrchr(cname, '/');
773
int len = (last_slash == NULL) ? 0 : (int)(last_slash - cname);
774
char* pkg_name_buf = (char*)malloc(len + 1);
775
776
if (pkg_name_buf == NULL) {
777
fprintf(stderr, "OOM error in native tmp buffer allocation");
778
return NULL;
779
}
780
if (last_slash != NULL) {
781
strncpy(pkg_name_buf, cname, len);
782
}
783
pkg_name_buf[len] = '\0';
784
785
err = (*jvmti)->GetNamedModule(jvmti, loaderObject, pkg_name_buf, &moduleObject);
786
free((void*)pkg_name_buf);
787
check_phase_ret_blob(err, NULL);
788
jplis_assert_msg(err == JVMTI_ERROR_NONE, "error in the JVMTI GetNamedModule");
789
790
return moduleObject;
791
}
792
793
/*
794
* Support for the JVMTI callbacks
795
*/
796
797
void
798
transformClassFile( JPLISAgent * agent,
799
JNIEnv * jnienv,
800
jobject loaderObject,
801
const char* name,
802
jclass classBeingRedefined,
803
jobject protectionDomain,
804
jint class_data_len,
805
const unsigned char* class_data,
806
jint* new_class_data_len,
807
unsigned char** new_class_data,
808
jboolean is_retransformer) {
809
jboolean errorOutstanding = JNI_FALSE;
810
jstring classNameStringObject = NULL;
811
jarray classFileBufferObject = NULL;
812
jarray transformedBufferObject = NULL;
813
jsize transformedBufferSize = 0;
814
unsigned char * resultBuffer = NULL;
815
jboolean shouldRun = JNI_FALSE;
816
817
/* only do this if we aren't already in the middle of processing a class on this thread */
818
shouldRun = tryToAcquireReentrancyToken(
819
jvmti(agent),
820
NULL); /* this thread */
821
822
if ( shouldRun ) {
823
/* first marshall all the parameters */
824
classNameStringObject = (*jnienv)->NewStringUTF(jnienv,
825
name);
826
errorOutstanding = checkForAndClearThrowable(jnienv);
827
jplis_assert_msg(!errorOutstanding, "can't create name string");
828
829
if ( !errorOutstanding ) {
830
classFileBufferObject = (*jnienv)->NewByteArray(jnienv,
831
class_data_len);
832
errorOutstanding = checkForAndClearThrowable(jnienv);
833
jplis_assert_msg(!errorOutstanding, "can't create byte array");
834
}
835
836
if ( !errorOutstanding ) {
837
jbyte * typedBuffer = (jbyte *) class_data; /* nasty cast, dumb JNI interface, const missing */
838
/* The sign cast is safe. The const cast is dumb. */
839
(*jnienv)->SetByteArrayRegion( jnienv,
840
classFileBufferObject,
841
0,
842
class_data_len,
843
typedBuffer);
844
errorOutstanding = checkForAndClearThrowable(jnienv);
845
jplis_assert_msg(!errorOutstanding, "can't set byte array region");
846
}
847
848
/* now call the JPL agents to do the transforming */
849
/* potential future optimization: may want to skip this if there are none */
850
if ( !errorOutstanding ) {
851
jobject moduleObject = NULL;
852
853
if (classBeingRedefined == NULL) {
854
moduleObject = getModuleObject(jvmti(agent), loaderObject, name);
855
} else {
856
// Redefine or retransform, InstrumentationImpl.transform() will use
857
// classBeingRedefined.getModule() to get the module.
858
}
859
jplis_assert(agent->mInstrumentationImpl != NULL);
860
jplis_assert(agent->mTransform != NULL);
861
transformedBufferObject = (*jnienv)->CallObjectMethod(
862
jnienv,
863
agent->mInstrumentationImpl,
864
agent->mTransform,
865
moduleObject,
866
loaderObject,
867
classNameStringObject,
868
classBeingRedefined,
869
protectionDomain,
870
classFileBufferObject,
871
is_retransformer);
872
errorOutstanding = checkForAndClearThrowable(jnienv);
873
jplis_assert_msg(!errorOutstanding, "transform method call failed");
874
}
875
876
/* Finally, unmarshall the parameters (if someone touched the buffer, tell the JVM) */
877
if ( !errorOutstanding ) {
878
if ( transformedBufferObject != NULL ) {
879
transformedBufferSize = (*jnienv)->GetArrayLength( jnienv,
880
transformedBufferObject);
881
errorOutstanding = checkForAndClearThrowable(jnienv);
882
jplis_assert_msg(!errorOutstanding, "can't get array length");
883
884
if ( !errorOutstanding ) {
885
/* allocate the response buffer with the JVMTI allocate call.
886
* This is what the JVMTI spec says to do for Class File Load hook responses
887
*/
888
jvmtiError allocError = (*(jvmti(agent)))->Allocate(jvmti(agent),
889
transformedBufferSize,
890
&resultBuffer);
891
errorOutstanding = (allocError != JVMTI_ERROR_NONE);
892
jplis_assert_msg(!errorOutstanding, "can't allocate result buffer");
893
}
894
895
if ( !errorOutstanding ) {
896
(*jnienv)->GetByteArrayRegion( jnienv,
897
transformedBufferObject,
898
0,
899
transformedBufferSize,
900
(jbyte *) resultBuffer);
901
errorOutstanding = checkForAndClearThrowable(jnienv);
902
jplis_assert_msg(!errorOutstanding, "can't get byte array region");
903
904
/* in this case, we will not return the buffer to the JVMTI,
905
* so we need to deallocate it ourselves
906
*/
907
if ( errorOutstanding ) {
908
deallocate( jvmti(agent),
909
(void*)resultBuffer);
910
}
911
}
912
913
if ( !errorOutstanding ) {
914
*new_class_data_len = (transformedBufferSize);
915
*new_class_data = resultBuffer;
916
}
917
}
918
}
919
920
/* release the token */
921
releaseReentrancyToken( jvmti(agent),
922
NULL); /* this thread */
923
924
}
925
926
return;
927
}
928
929
/*
930
* Misc. internal utilities.
931
*/
932
933
/*
934
* The only checked exceptions we can throw are ClassNotFoundException and
935
* UnmodifiableClassException. All others map to InternalError.
936
*/
937
jthrowable
938
redefineClassMapper( JNIEnv * jnienv,
939
jthrowable throwableToMap) {
940
jthrowable mappedThrowable = NULL;
941
942
jplis_assert(isSafeForJNICalls(jnienv));
943
jplis_assert(!isUnchecked(jnienv, throwableToMap));
944
945
if ( isInstanceofClassName( jnienv,
946
throwableToMap,
947
"java/lang/ClassNotFoundException") ) {
948
mappedThrowable = throwableToMap;
949
} else {
950
if ( isInstanceofClassName( jnienv,
951
throwableToMap,
952
"java/lang/instrument/UnmodifiableClassException")) {
953
mappedThrowable = throwableToMap;
954
} else {
955
jstring message = NULL;
956
957
message = getMessageFromThrowable(jnienv, throwableToMap);
958
mappedThrowable = createInternalError(jnienv, message);
959
}
960
}
961
962
jplis_assert(isSafeForJNICalls(jnienv));
963
return mappedThrowable;
964
}
965
966
jobjectArray
967
getObjectArrayFromClasses(JNIEnv* jnienv, jclass* classes, jint classCount) {
968
jclass classArrayClass = NULL;
969
jobjectArray localArray = NULL;
970
jint classIndex = 0;
971
jboolean errorOccurred = JNI_FALSE;
972
973
/* get the class array class */
974
classArrayClass = (*jnienv)->FindClass(jnienv, "java/lang/Class");
975
errorOccurred = checkForThrowable(jnienv);
976
977
if (!errorOccurred) {
978
jplis_assert_msg(classArrayClass != NULL, "FindClass returned null class");
979
980
/* create the array for the classes */
981
localArray = (*jnienv)->NewObjectArray(jnienv, classCount, classArrayClass, NULL);
982
errorOccurred = checkForThrowable(jnienv);
983
984
if (!errorOccurred) {
985
jplis_assert_msg(localArray != NULL, "NewObjectArray returned null array");
986
987
/* now copy refs to all the classes and put them into the array */
988
for (classIndex = 0; classIndex < classCount; classIndex++) {
989
/* put class into array */
990
(*jnienv)->SetObjectArrayElement(jnienv, localArray, classIndex, classes[classIndex]);
991
errorOccurred = checkForThrowable(jnienv);
992
993
if (errorOccurred) {
994
localArray = NULL;
995
break;
996
}
997
}
998
}
999
}
1000
1001
return localArray;
1002
}
1003
1004
1005
/* Return the environment with the retransformation capability.
1006
* Create it if it doesn't exist.
1007
* Return NULL if it can't be created.
1008
*/
1009
jvmtiEnv *
1010
retransformableEnvironment(JPLISAgent * agent) {
1011
jvmtiEnv * retransformerEnv = NULL;
1012
jint jnierror = JNI_OK;
1013
jvmtiCapabilities desiredCapabilities;
1014
jvmtiEventCallbacks callbacks;
1015
jvmtiError jvmtierror;
1016
1017
if (agent->mRetransformEnvironment.mJVMTIEnv != NULL) {
1018
return agent->mRetransformEnvironment.mJVMTIEnv;
1019
}
1020
jnierror = (*agent->mJVM)->GetEnv( agent->mJVM,
1021
(void **) &retransformerEnv,
1022
JVMTI_VERSION_1_1);
1023
if ( jnierror != JNI_OK ) {
1024
return NULL;
1025
}
1026
jvmtierror = (*retransformerEnv)->GetCapabilities(retransformerEnv, &desiredCapabilities);
1027
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
1028
desiredCapabilities.can_retransform_classes = 1;
1029
if (agent->mNativeMethodPrefixAdded) {
1030
desiredCapabilities.can_set_native_method_prefix = 1;
1031
}
1032
1033
jvmtierror = (*retransformerEnv)->AddCapabilities(retransformerEnv, &desiredCapabilities);
1034
if (jvmtierror != JVMTI_ERROR_NONE) {
1035
/* cannot get the capability, dispose of the retransforming environment */
1036
jvmtierror = (*retransformerEnv)->DisposeEnvironment(retransformerEnv);
1037
jplis_assert(jvmtierror == JVMTI_ERROR_NOT_AVAILABLE);
1038
return NULL;
1039
}
1040
memset(&callbacks, 0, sizeof(callbacks));
1041
callbacks.ClassFileLoadHook = &eventHandlerClassFileLoadHook;
1042
1043
jvmtierror = (*retransformerEnv)->SetEventCallbacks(retransformerEnv,
1044
&callbacks,
1045
sizeof(callbacks));
1046
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
1047
if (jvmtierror == JVMTI_ERROR_NONE) {
1048
// install the retransforming environment
1049
agent->mRetransformEnvironment.mJVMTIEnv = retransformerEnv;
1050
agent->mRetransformEnvironment.mIsRetransformer = JNI_TRUE;
1051
1052
// Make it for ClassFileLoadHook handling
1053
jvmtierror = (*retransformerEnv)->SetEnvironmentLocalStorage(
1054
retransformerEnv,
1055
&(agent->mRetransformEnvironment));
1056
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
1057
if (jvmtierror == JVMTI_ERROR_NONE) {
1058
return retransformerEnv;
1059
}
1060
}
1061
return NULL;
1062
}
1063
1064
1065
/*
1066
* Underpinnings for native methods
1067
*/
1068
1069
jboolean
1070
isModifiableClass(JNIEnv * jnienv, JPLISAgent * agent, jclass clazz) {
1071
jvmtiEnv * jvmtienv = jvmti(agent);
1072
jvmtiError jvmtierror;
1073
jboolean is_modifiable = JNI_FALSE;
1074
1075
jvmtierror = (*jvmtienv)->IsModifiableClass( jvmtienv,
1076
clazz,
1077
&is_modifiable);
1078
check_phase_ret_false(jvmtierror);
1079
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
1080
1081
return is_modifiable;
1082
}
1083
1084
jboolean
1085
isRetransformClassesSupported(JNIEnv * jnienv, JPLISAgent * agent) {
1086
return agent->mRetransformEnvironment.mIsRetransformer;
1087
}
1088
1089
void
1090
setHasTransformers(JNIEnv * jnienv, JPLISAgent * agent, jboolean has) {
1091
jvmtiEnv * jvmtienv = jvmti(agent);
1092
jvmtiError jvmtierror;
1093
1094
jplis_assert(jvmtienv != NULL);
1095
jvmtierror = (*jvmtienv)->SetEventNotificationMode(
1096
jvmtienv,
1097
has? JVMTI_ENABLE : JVMTI_DISABLE,
1098
JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
1099
NULL /* all threads */);
1100
check_phase_ret(jvmtierror);
1101
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
1102
}
1103
1104
void
1105
setHasRetransformableTransformers(JNIEnv * jnienv, JPLISAgent * agent, jboolean has) {
1106
jvmtiEnv * retransformerEnv = retransformableEnvironment(agent);
1107
jvmtiError jvmtierror;
1108
1109
jplis_assert(retransformerEnv != NULL);
1110
jvmtierror = (*retransformerEnv)->SetEventNotificationMode(
1111
retransformerEnv,
1112
has? JVMTI_ENABLE : JVMTI_DISABLE,
1113
JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
1114
NULL /* all threads */);
1115
check_phase_ret(jvmtierror);
1116
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
1117
}
1118
1119
void
1120
retransformClasses(JNIEnv * jnienv, JPLISAgent * agent, jobjectArray classes) {
1121
jvmtiEnv * retransformerEnv = retransformableEnvironment(agent);
1122
jboolean errorOccurred = JNI_FALSE;
1123
jvmtiError errorCode = JVMTI_ERROR_NONE;
1124
jsize numClasses = 0;
1125
jclass * classArray = NULL;
1126
1127
/* This is supposed to be checked by caller, but just to be sure */
1128
if (retransformerEnv == NULL) {
1129
jplis_assert(retransformerEnv != NULL);
1130
errorOccurred = JNI_TRUE;
1131
errorCode = JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
1132
}
1133
1134
/* This was supposed to be checked by caller too */
1135
if (!errorOccurred && classes == NULL) {
1136
jplis_assert(classes != NULL);
1137
errorOccurred = JNI_TRUE;
1138
errorCode = JVMTI_ERROR_NULL_POINTER;
1139
}
1140
1141
if (!errorOccurred) {
1142
numClasses = (*jnienv)->GetArrayLength(jnienv, classes);
1143
errorOccurred = checkForThrowable(jnienv);
1144
jplis_assert(!errorOccurred);
1145
1146
if (!errorOccurred && numClasses == 0) {
1147
jplis_assert(numClasses != 0);
1148
errorOccurred = JNI_TRUE;
1149
errorCode = JVMTI_ERROR_NULL_POINTER;
1150
}
1151
}
1152
1153
if (!errorOccurred) {
1154
classArray = (jclass *) allocate(retransformerEnv,
1155
numClasses * sizeof(jclass));
1156
errorOccurred = (classArray == NULL);
1157
jplis_assert(!errorOccurred);
1158
if (errorOccurred) {
1159
errorCode = JVMTI_ERROR_OUT_OF_MEMORY;
1160
}
1161
}
1162
1163
if (!errorOccurred) {
1164
jint index;
1165
for (index = 0; index < numClasses; index++) {
1166
classArray[index] = (*jnienv)->GetObjectArrayElement(jnienv, classes, index);
1167
errorOccurred = checkForThrowable(jnienv);
1168
jplis_assert(!errorOccurred);
1169
if (errorOccurred) {
1170
break;
1171
}
1172
1173
if (classArray[index] == NULL) {
1174
jplis_assert(classArray[index] != NULL);
1175
errorOccurred = JNI_TRUE;
1176
errorCode = JVMTI_ERROR_NULL_POINTER;
1177
break;
1178
}
1179
}
1180
}
1181
1182
if (!errorOccurred) {
1183
errorCode = (*retransformerEnv)->RetransformClasses(retransformerEnv,
1184
numClasses, classArray);
1185
errorOccurred = (errorCode != JVMTI_ERROR_NONE);
1186
}
1187
1188
/* Give back the buffer if we allocated it. Throw any exceptions after.
1189
*/
1190
if (classArray != NULL) {
1191
deallocate(retransformerEnv, (void*)classArray);
1192
}
1193
1194
/* Return back if we executed the JVMTI API in a wrong phase
1195
*/
1196
check_phase_ret(errorCode);
1197
1198
if (errorCode != JVMTI_ERROR_NONE) {
1199
createAndThrowThrowableFromJVMTIErrorCode(jnienv, errorCode);
1200
}
1201
1202
mapThrownThrowableIfNecessary(jnienv, redefineClassMapper);
1203
}
1204
1205
/*
1206
* Java code must not call this with a null list or a zero-length list.
1207
*/
1208
void
1209
redefineClasses(JNIEnv * jnienv, JPLISAgent * agent, jobjectArray classDefinitions) {
1210
jvmtiEnv* jvmtienv = jvmti(agent);
1211
jboolean errorOccurred = JNI_FALSE;
1212
jclass classDefClass = NULL;
1213
jmethodID getDefinitionClassMethodID = NULL;
1214
jmethodID getDefinitionClassFileMethodID = NULL;
1215
jvmtiClassDefinition* classDefs = NULL;
1216
jbyteArray* targetFiles = NULL;
1217
jsize numDefs = 0;
1218
1219
jplis_assert(classDefinitions != NULL);
1220
1221
numDefs = (*jnienv)->GetArrayLength(jnienv, classDefinitions);
1222
errorOccurred = checkForThrowable(jnienv);
1223
jplis_assert(!errorOccurred);
1224
1225
if (!errorOccurred) {
1226
jplis_assert(numDefs > 0);
1227
/* get method IDs for methods to call on class definitions */
1228
classDefClass = (*jnienv)->FindClass(jnienv, "java/lang/instrument/ClassDefinition");
1229
errorOccurred = checkForThrowable(jnienv);
1230
jplis_assert(!errorOccurred);
1231
}
1232
1233
if (!errorOccurred) {
1234
getDefinitionClassMethodID = (*jnienv)->GetMethodID( jnienv,
1235
classDefClass,
1236
"getDefinitionClass",
1237
"()Ljava/lang/Class;");
1238
errorOccurred = checkForThrowable(jnienv);
1239
jplis_assert(!errorOccurred);
1240
}
1241
1242
if (!errorOccurred) {
1243
getDefinitionClassFileMethodID = (*jnienv)->GetMethodID( jnienv,
1244
classDefClass,
1245
"getDefinitionClassFile",
1246
"()[B");
1247
errorOccurred = checkForThrowable(jnienv);
1248
jplis_assert(!errorOccurred);
1249
}
1250
1251
if (!errorOccurred) {
1252
classDefs = (jvmtiClassDefinition *) allocate(
1253
jvmtienv,
1254
numDefs * sizeof(jvmtiClassDefinition));
1255
errorOccurred = (classDefs == NULL);
1256
jplis_assert(!errorOccurred);
1257
if ( errorOccurred ) {
1258
createAndThrowThrowableFromJVMTIErrorCode(jnienv, JVMTI_ERROR_OUT_OF_MEMORY);
1259
}
1260
1261
else {
1262
/*
1263
* We have to save the targetFile values that we compute so
1264
* that we can release the class_bytes arrays that are
1265
* returned by GetByteArrayElements(). In case of a JNI
1266
* error, we can't (easily) recompute the targetFile values
1267
* and we still want to free any memory we allocated.
1268
*/
1269
targetFiles = (jbyteArray *) allocate(jvmtienv,
1270
numDefs * sizeof(jbyteArray));
1271
errorOccurred = (targetFiles == NULL);
1272
jplis_assert(!errorOccurred);
1273
if ( errorOccurred ) {
1274
deallocate(jvmtienv, (void*)classDefs);
1275
createAndThrowThrowableFromJVMTIErrorCode(jnienv,
1276
JVMTI_ERROR_OUT_OF_MEMORY);
1277
}
1278
else {
1279
jint i, j;
1280
1281
// clear classDefs so we can correctly free memory during errors
1282
memset(classDefs, 0, numDefs * sizeof(jvmtiClassDefinition));
1283
1284
for (i = 0; i < numDefs; i++) {
1285
jclass classDef = NULL;
1286
1287
classDef = (*jnienv)->GetObjectArrayElement(jnienv, classDefinitions, i);
1288
errorOccurred = checkForThrowable(jnienv);
1289
jplis_assert(!errorOccurred);
1290
if (errorOccurred) {
1291
break;
1292
}
1293
1294
classDefs[i].klass = (*jnienv)->CallObjectMethod(jnienv, classDef, getDefinitionClassMethodID);
1295
errorOccurred = checkForThrowable(jnienv);
1296
jplis_assert(!errorOccurred);
1297
if (errorOccurred) {
1298
break;
1299
}
1300
1301
targetFiles[i] = (*jnienv)->CallObjectMethod(jnienv, classDef, getDefinitionClassFileMethodID);
1302
errorOccurred = checkForThrowable(jnienv);
1303
jplis_assert(!errorOccurred);
1304
if (errorOccurred) {
1305
break;
1306
}
1307
1308
classDefs[i].class_byte_count = (*jnienv)->GetArrayLength(jnienv, targetFiles[i]);
1309
errorOccurred = checkForThrowable(jnienv);
1310
jplis_assert(!errorOccurred);
1311
if (errorOccurred) {
1312
break;
1313
}
1314
1315
/*
1316
* Allocate class_bytes last so we don't have to free
1317
* memory on a partial row error.
1318
*/
1319
classDefs[i].class_bytes = (unsigned char*)(*jnienv)->GetByteArrayElements(jnienv, targetFiles[i], NULL);
1320
errorOccurred = checkForThrowable(jnienv);
1321
jplis_assert(!errorOccurred);
1322
if (errorOccurred) {
1323
break;
1324
}
1325
}
1326
1327
if (!errorOccurred) {
1328
jvmtiError errorCode = JVMTI_ERROR_NONE;
1329
errorCode = (*jvmtienv)->RedefineClasses(jvmtienv, numDefs, classDefs);
1330
if (errorCode == JVMTI_ERROR_WRONG_PHASE) {
1331
/* insulate caller from the wrong phase error */
1332
errorCode = JVMTI_ERROR_NONE;
1333
} else {
1334
errorOccurred = (errorCode != JVMTI_ERROR_NONE);
1335
if ( errorOccurred ) {
1336
createAndThrowThrowableFromJVMTIErrorCode(jnienv, errorCode);
1337
}
1338
}
1339
}
1340
1341
/*
1342
* Cleanup memory that we allocated above. If we had a
1343
* JNI error, a JVM/TI error or no errors, index 'i'
1344
* tracks how far we got in processing the classDefs
1345
* array. Note: ReleaseByteArrayElements() is safe to
1346
* call with a JNI exception pending.
1347
*/
1348
for (j = 0; j < i; j++) {
1349
if ((jbyte *)classDefs[j].class_bytes != NULL) {
1350
(*jnienv)->ReleaseByteArrayElements(jnienv,
1351
targetFiles[j], (jbyte *)classDefs[j].class_bytes,
1352
0 /* copy back and free */);
1353
/*
1354
* Only check for error if we didn't already have one
1355
* so we don't overwrite errorOccurred.
1356
*/
1357
if (!errorOccurred) {
1358
errorOccurred = checkForThrowable(jnienv);
1359
jplis_assert(!errorOccurred);
1360
}
1361
}
1362
}
1363
deallocate(jvmtienv, (void*)targetFiles);
1364
deallocate(jvmtienv, (void*)classDefs);
1365
}
1366
}
1367
}
1368
1369
mapThrownThrowableIfNecessary(jnienv, redefineClassMapper);
1370
}
1371
1372
/* Cheesy sharing. ClassLoader may be null. */
1373
jobjectArray
1374
commonGetClassList( JNIEnv * jnienv,
1375
JPLISAgent * agent,
1376
jobject classLoader,
1377
ClassListFetcher fetcher) {
1378
jvmtiEnv * jvmtienv = jvmti(agent);
1379
jboolean errorOccurred = JNI_FALSE;
1380
jvmtiError jvmtierror = JVMTI_ERROR_NONE;
1381
jint classCount = 0;
1382
jclass * classes = NULL;
1383
jobjectArray localArray = NULL;
1384
1385
/* retrieve the classes from the JVMTI agent */
1386
jvmtierror = (*fetcher)( jvmtienv,
1387
classLoader,
1388
&classCount,
1389
&classes);
1390
check_phase_ret_blob(jvmtierror, localArray);
1391
errorOccurred = (jvmtierror != JVMTI_ERROR_NONE);
1392
jplis_assert(!errorOccurred);
1393
1394
if ( errorOccurred ) {
1395
createAndThrowThrowableFromJVMTIErrorCode(jnienv, jvmtierror);
1396
} else {
1397
localArray = getObjectArrayFromClasses( jnienv,
1398
classes,
1399
classCount);
1400
errorOccurred = checkForThrowable(jnienv);
1401
jplis_assert(!errorOccurred);
1402
1403
/* do this whether or not we saw a problem */
1404
deallocate(jvmtienv, (void*)classes);
1405
}
1406
1407
mapThrownThrowableIfNecessary(jnienv, mapAllCheckedToInternalErrorMapper);
1408
return localArray;
1409
1410
}
1411
1412
jvmtiError
1413
getAllLoadedClassesClassListFetcher( jvmtiEnv * jvmtienv,
1414
jobject classLoader,
1415
jint * classCount,
1416
jclass ** classes) {
1417
return (*jvmtienv)->GetLoadedClasses(jvmtienv, classCount, classes);
1418
}
1419
1420
jobjectArray
1421
getAllLoadedClasses(JNIEnv * jnienv, JPLISAgent * agent) {
1422
return commonGetClassList( jnienv,
1423
agent,
1424
NULL,
1425
getAllLoadedClassesClassListFetcher);
1426
}
1427
1428
jvmtiError
1429
getInitiatedClassesClassListFetcher( jvmtiEnv * jvmtienv,
1430
jobject classLoader,
1431
jint * classCount,
1432
jclass ** classes) {
1433
return (*jvmtienv)->GetClassLoaderClasses(jvmtienv, classLoader, classCount, classes);
1434
}
1435
1436
1437
jobjectArray
1438
getInitiatedClasses(JNIEnv * jnienv, JPLISAgent * agent, jobject classLoader) {
1439
return commonGetClassList( jnienv,
1440
agent,
1441
classLoader,
1442
getInitiatedClassesClassListFetcher);
1443
}
1444
1445
jlong
1446
getObjectSize(JNIEnv * jnienv, JPLISAgent * agent, jobject objectToSize) {
1447
jvmtiEnv * jvmtienv = jvmti(agent);
1448
jlong objectSize = -1;
1449
jvmtiError jvmtierror = JVMTI_ERROR_NONE;
1450
1451
jvmtierror = (*jvmtienv)->GetObjectSize(jvmtienv, objectToSize, &objectSize);
1452
check_phase_ret_0(jvmtierror);
1453
jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
1454
if ( jvmtierror != JVMTI_ERROR_NONE ) {
1455
createAndThrowThrowableFromJVMTIErrorCode(jnienv, jvmtierror);
1456
}
1457
1458
mapThrownThrowableIfNecessary(jnienv, mapAllCheckedToInternalErrorMapper);
1459
return objectSize;
1460
}
1461
1462
void
1463
appendToClassLoaderSearch(JNIEnv * jnienv, JPLISAgent * agent, jstring jarFile, jboolean isBootLoader)
1464
{
1465
jvmtiEnv * jvmtienv = jvmti(agent);
1466
jboolean errorOutstanding;
1467
jvmtiError jvmtierror;
1468
const char* utf8Chars;
1469
jsize utf8Len;
1470
jboolean isCopy;
1471
char platformChars[MAXPATHLEN];
1472
int platformLen;
1473
1474
utf8Len = (*jnienv)->GetStringUTFLength(jnienv, jarFile);
1475
errorOutstanding = checkForAndClearThrowable(jnienv);
1476
1477
if (!errorOutstanding) {
1478
utf8Chars = (*jnienv)->GetStringUTFChars(jnienv, jarFile, &isCopy);
1479
errorOutstanding = checkForAndClearThrowable(jnienv);
1480
1481
if (!errorOutstanding && utf8Chars != NULL) {
1482
/*
1483
* JVMTI spec'ed to use modified UTF8. At this time this is not implemented
1484
* the platform encoding is used.
1485
*/
1486
platformLen = convertUft8ToPlatformString((char*)utf8Chars, utf8Len, platformChars, MAXPATHLEN);
1487
if (platformLen < 0) {
1488
createAndThrowInternalError(jnienv);
1489
(*jnienv)->ReleaseStringUTFChars(jnienv, jarFile, utf8Chars);
1490
return;
1491
}
1492
1493
(*jnienv)->ReleaseStringUTFChars(jnienv, jarFile, utf8Chars);
1494
errorOutstanding = checkForAndClearThrowable(jnienv);
1495
1496
if (!errorOutstanding) {
1497
1498
if (isBootLoader) {
1499
jvmtierror = (*jvmtienv)->AddToBootstrapClassLoaderSearch(jvmtienv, platformChars);
1500
} else {
1501
jvmtierror = (*jvmtienv)->AddToSystemClassLoaderSearch(jvmtienv, platformChars);
1502
}
1503
check_phase_ret(jvmtierror);
1504
1505
if ( jvmtierror != JVMTI_ERROR_NONE ) {
1506
createAndThrowThrowableFromJVMTIErrorCode(jnienv, jvmtierror);
1507
}
1508
}
1509
}
1510
}
1511
1512
mapThrownThrowableIfNecessary(jnienv, mapAllCheckedToInternalErrorMapper);
1513
}
1514
1515
/*
1516
* Set the prefixes used to wrap native methods (so they can be instrumented).
1517
* Each transform can set a prefix, any that have been set come in as prefixArray.
1518
* Convert them in native strings in a native array then call JVM TI.
1519
* One a given call, this function handles either the prefixes for retransformable
1520
* transforms or for normal transforms.
1521
*/
1522
void
1523
setNativeMethodPrefixes(JNIEnv * jnienv, JPLISAgent * agent, jobjectArray prefixArray,
1524
jboolean isRetransformable) {
1525
jvmtiEnv* jvmtienv;
1526
jvmtiError err = JVMTI_ERROR_NONE;
1527
jsize arraySize;
1528
jboolean errorOccurred = JNI_FALSE;
1529
1530
jplis_assert(prefixArray != NULL);
1531
1532
if (isRetransformable) {
1533
jvmtienv = agent->mRetransformEnvironment.mJVMTIEnv;
1534
} else {
1535
jvmtienv = agent->mNormalEnvironment.mJVMTIEnv;
1536
}
1537
arraySize = (*jnienv)->GetArrayLength(jnienv, prefixArray);
1538
errorOccurred = checkForThrowable(jnienv);
1539
jplis_assert(!errorOccurred);
1540
1541
if (!errorOccurred) {
1542
/* allocate the native to hold the native prefixes */
1543
const char** prefixes = (const char**) allocate(jvmtienv,
1544
arraySize * sizeof(char*));
1545
/* since JNI ReleaseStringUTFChars needs the jstring from which the native
1546
* string was allocated, we store them in a parallel array */
1547
jstring* originForRelease = (jstring*) allocate(jvmtienv,
1548
arraySize * sizeof(jstring));
1549
errorOccurred = (prefixes == NULL || originForRelease == NULL);
1550
jplis_assert(!errorOccurred);
1551
if ( errorOccurred ) {
1552
createAndThrowThrowableFromJVMTIErrorCode(jnienv, JVMTI_ERROR_OUT_OF_MEMORY);
1553
}
1554
else {
1555
jint inx = 0;
1556
jint i;
1557
for (i = 0; i < arraySize; i++) {
1558
jstring prefixStr = NULL;
1559
const char* prefix;
1560
jsize prefixLen;
1561
jboolean isCopy;
1562
1563
prefixStr = (jstring) ((*jnienv)->GetObjectArrayElement(jnienv,
1564
prefixArray, i));
1565
errorOccurred = checkForThrowable(jnienv);
1566
jplis_assert(!errorOccurred);
1567
if (errorOccurred) {
1568
break;
1569
}
1570
if (prefixStr == NULL) {
1571
continue;
1572
}
1573
1574
prefixLen = (*jnienv)->GetStringUTFLength(jnienv, prefixStr);
1575
errorOccurred = checkForThrowable(jnienv);
1576
jplis_assert(!errorOccurred);
1577
if (errorOccurred) {
1578
break;
1579
}
1580
1581
if (prefixLen > 0) {
1582
prefix = (*jnienv)->GetStringUTFChars(jnienv, prefixStr, &isCopy);
1583
errorOccurred = checkForThrowable(jnienv);
1584
jplis_assert(!errorOccurred);
1585
if (!errorOccurred && prefix != NULL) {
1586
prefixes[inx] = prefix;
1587
originForRelease[inx] = prefixStr;
1588
++inx;
1589
}
1590
}
1591
}
1592
1593
err = (*jvmtienv)->SetNativeMethodPrefixes(jvmtienv, inx, (char**)prefixes);
1594
/* can be called from any phase */
1595
jplis_assert(err == JVMTI_ERROR_NONE);
1596
1597
for (i = 0; i < inx; i++) {
1598
(*jnienv)->ReleaseStringUTFChars(jnienv, originForRelease[i], prefixes[i]);
1599
}
1600
}
1601
deallocate(jvmtienv, (void*)prefixes);
1602
deallocate(jvmtienv, (void*)originForRelease);
1603
}
1604
}
1605
1606