1 /* 2 * Copyright (c) 2016, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 #include <stdio.h> 25 #include <string.h> 26 #include "jvmti.h" 27 28 #ifdef __cplusplus 29 extern "C" { 30 #endif 31 32 #ifndef JNI_ENV_ARG 33 34 #ifdef __cplusplus 35 #define JNI_ENV_ARG(x, y) y 36 #define JNI_ENV_PTR(x) x 37 #else 38 #define JNI_ENV_ARG(x,y) x, y 39 #define JNI_ENV_PTR(x) (*x) 40 #endif 41 42 #endif 43 44 #define TranslateError(err) "JVMTI error" 45 46 #define PASSED 0 47 #define FAILED 2 48 49 static const char *EXPECTED_NAME = "java/util/Collections"; 50 static const char *EXC_CNAME = "java/lang/Exception"; 51 52 static jvmtiEnv *jvmti = NULL; 53 static jint result = PASSED; 54 static jboolean printdump = JNI_FALSE; 55 56 static jboolean with_early_vm_start_capability = JNI_FALSE; 57 static jboolean with_early_class_hook_capability = JNI_FALSE; 58 59 static jboolean found_class_in_vm_start = JNI_FALSE; 60 static jboolean found_class_in_primordial = JNI_FALSE; 61 static jboolean found_class_in_cflh_events = JNI_FALSE; 62 63 static int cflh_events_primordial_count = 0; 64 static int cflh_events_vm_start_count = 0; 65 66 static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved); 67 68 JNIEXPORT 69 jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { 70 return Agent_Initialize(jvm, options, reserved); 71 } 72 73 JNIEXPORT 74 jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) { 75 return Agent_Initialize(jvm, options, reserved); 76 } 77 78 JNIEXPORT 79 jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { 80 return JNI_VERSION_9; 81 } 82 83 static 84 jint throw_exc(JNIEnv *env, char *msg) { 85 jclass exc_class = JNI_ENV_PTR(env)->FindClass(JNI_ENV_ARG(env, EXC_CNAME)); 86 87 if (exc_class == NULL) { 88 printf("throw_exc: Error in FindClass(env, %s)\n", EXC_CNAME); 89 return -1; 90 } 91 return JNI_ENV_PTR(env)->ThrowNew(JNI_ENV_ARG(env, exc_class), msg); 92 } 93 94 static void JNICALL 95 Callback_ClassFileLoadHook(jvmtiEnv *jvmti_env, JNIEnv *env, 96 jclass class_being_redefined, 97 jobject loader, const char* name, jobject protection_domain, 98 jint class_data_len, const unsigned char* class_data, 99 jint *new_class_data_len, unsigned char** new_class_data) { 100 jvmtiPhase phase; 101 jvmtiError err; 102 103 err = (*jvmti)->GetPhase(jvmti_env,&phase); 104 if (err != JVMTI_ERROR_NONE) { 105 printf("ClassFileLoadHook event: GetPhase error: %s (%d)\n", TranslateError(err), err); 106 result = FAILED; 107 return; 108 } 109 110 if (phase == JVMTI_PHASE_PRIMORDIAL || phase == JVMTI_PHASE_START) { 111 if (phase == JVMTI_PHASE_START) { 112 cflh_events_vm_start_count++; 113 if(!strcmp(name, EXPECTED_NAME)) { 114 found_class_in_vm_start = JNI_TRUE; 115 } 116 } else { 117 cflh_events_primordial_count++; 118 if(!strcmp(name, EXPECTED_NAME)) { 119 found_class_in_primordial = JNI_TRUE; 120 } 121 } 122 } 123 124 if(!strcmp(name, EXPECTED_NAME)) { 125 found_class_in_cflh_events = JNI_TRUE; 126 } 127 128 if (printdump == JNI_TRUE) { 129 printf(">>> ClassFileLoadHook event: phase(%d), class name %s\n", phase, name); 130 } 131 } 132 133 static 134 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { 135 jint res, size; 136 jvmtiCapabilities caps; 137 jvmtiEventCallbacks callbacks; 138 jvmtiError err; 139 140 if (options != NULL) { 141 if (strstr(options, "with_early_vmstart") != NULL) { 142 with_early_vm_start_capability = JNI_TRUE; 143 } 144 if (strstr(options, "with_early_class_hook") != NULL) { 145 with_early_class_hook_capability = JNI_TRUE; 146 } 147 if (strstr(options, "printdump") != NULL) { 148 printdump = JNI_TRUE; 149 } 150 } 151 152 res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti), 153 JVMTI_VERSION_9); 154 if (res != JNI_OK || jvmti == NULL) { 155 printf(" Error: wrong result of a valid call to GetEnv!\n"); 156 return JNI_ERR; 157 } 158 159 printf("Enabling following capabilities: can_generate_all_class_hook_events"); 160 memset(&caps, 0, sizeof(caps)); 161 caps.can_generate_all_class_hook_events = 1; 162 if (with_early_vm_start_capability == JNI_TRUE) { 163 printf(", can_generate_early_vmstart"); 164 caps.can_generate_early_vmstart = 1; 165 } 166 if (with_early_class_hook_capability == JNI_TRUE) { 167 printf(", can_generate_early_class_hook_events"); 168 caps.can_generate_early_class_hook_events = 1; 169 } 170 printf("\n"); 171 172 err = (*jvmti)->AddCapabilities(jvmti, &caps); 173 if (err != JVMTI_ERROR_NONE) { 174 printf(" Error in AddCapabilites: %s (%d)\n", TranslateError(err), err); 175 return JNI_ERR; 176 } 177 178 size = (jint)sizeof(callbacks); 179 180 memset(&callbacks, 0, sizeof(callbacks)); 181 callbacks.ClassFileLoadHook = Callback_ClassFileLoadHook; 182 183 err = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, size); 184 if (err != JVMTI_ERROR_NONE) { 185 printf(" Error in SetEventCallbacks: %s (%d)\n", TranslateError(err), err); 186 return JNI_ERR; 187 } 188 189 err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL); 190 if (err != JVMTI_ERROR_NONE) { 191 printf(" Error in SetEventNotificationMode: %s (%d)\n", TranslateError(err), err); 192 return JNI_ERR; 193 } 194 195 return JNI_OK; 196 } 197 198 JNIEXPORT jint JNICALL 199 Java_MAAClassFileLoadHook_check(JNIEnv *env, jclass cls) { 200 jobject loader = NULL; 201 202 if (jvmti == NULL) { 203 throw_exc(env, "JVMTI client was not properly loaded!\n"); 204 return FAILED; 205 } 206 207 /* 208 * Expecting that we always get ClassFileLoadHook events in the VM Start phase 209 */ 210 if (cflh_events_vm_start_count == 0) { 211 throw_exc(env, "Didn't get ClassFileLoadHook events in start phase!\n"); 212 return FAILED; 213 } 214 215 if (with_early_class_hook_capability == JNI_TRUE) { 216 /* 217 * Expecting that we get ClassFileLoadHook events in the Primordial phase 218 * when can_generate_all_class_hook_events and can_generate_early_class_hook_events 219 * capabilities are enabled 220 */ 221 if (cflh_events_primordial_count == 0) { 222 throw_exc(env, "Didn't get ClassFileLoadHook events in primordial phase!\n"); 223 return FAILED; 224 } 225 } else { 226 /* 227 * Expecting that we don't get ClassFileLoadHook events in the Primordial phase 228 * when can_generate_early_class_hook_events capability is disabled 229 */ 230 if (cflh_events_primordial_count != 0) { 231 throw_exc(env, "Get ClassFileLoadHook events in primordial phase!\n"); 232 return FAILED; 233 } 234 } 235 236 237 if (with_early_vm_start_capability == JNI_TRUE) { 238 /* 239 * Expecting that "java/util/Collections" class from java.base module is present in the 240 * ClassFileLoadHook events during VM Start phase when can_generate_early_vmstart 241 * capability is enabled 242 */ 243 printf("Expecting to find '%s' class in ClassFileLoadHook events during VM start phase.\n", EXPECTED_NAME); 244 if (found_class_in_vm_start == JNI_FALSE) { 245 throw_exc(env, "Unable to find expected class in ClassLoad events during start phase!\n"); 246 return FAILED; 247 } 248 } else if (with_early_class_hook_capability == JNI_TRUE) { 249 /* 250 * Expecting that "java/util/Collections" class from java.base module is present in the 251 * ClassFileLoadHook events during Primordial phase when can_generate_all_class_hook_events 252 * and can_generate_early_class_hook_events capabilities are enabled and can_generate_early_vmstart 253 * capability is disabled 254 */ 255 printf("Expecting to find '%s' class in ClassFileLoadHook events during VM primordial phase.\n", EXPECTED_NAME); 256 if (found_class_in_primordial == JNI_FALSE) { 257 throw_exc(env, "Unable to find expected class in ClassFileLoadHook events during primordial phase!\n"); 258 return FAILED; 259 } 260 } else { 261 /* 262 * Expecting that "java/util/Collections" class from java.base module is not present in the 263 * ClassFileLoadHook events when can_generate_all_class_hook_events, can_generate_early_class_hook_events 264 * and can_generate_early_vmstart capabilities are disabled 265 */ 266 printf("Expecting that '%s' class is absent in ClassLoadHook events.\n", EXPECTED_NAME);; 267 if (found_class_in_cflh_events == JNI_TRUE) { 268 throw_exc(env, "Class is found in ClassFileLoadHook events!\n"); 269 return FAILED; 270 } 271 } 272 273 return result; 274 } 275 276 #ifdef __cplusplus 277 } 278 #endif