/* Callback for JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */ static void JNICALL cbClassFileLoadHook(jvmtiEnv *jvmti, JNIEnv* env, jclass class_being_redefined, jobject loader, const char* name, jobject protection_domain, jint class_data_len, const unsigned char* class_data, jint* new_class_data_len, unsigned char** new_class_data) { enter_critical_section(jvmti); { /* It's possible we get here right after VmDeath event, be careful */ if ( !gdata->vm_is_dead ) { *new_class_data_len = 0; *new_class_data = NULL; /* The tracker class itself? */ if ( interested((char*)name, "", gdata->include, gdata->exclude) && strcmp(name, STRING(MTRACE_class)) != 0 ) { jint cnum; int system_class; unsigned char *new_image; long new_length; ClassInfo *cp; /* Get unique number for every class file image loaded */ cnum = gdata->ccount++; /* Save away class information */ if ( gdata->classes == NULL ) { gdata->classes = (ClassInfo*)malloc( gdata->ccount*sizeof(ClassInfo)); } else { gdata->classes = (ClassInfo*) realloc((void*)gdata->classes, gdata->ccount*sizeof(ClassInfo)); } if ( gdata->classes == NULL ) { fatal_error("ERROR: Out of malloc memory\n"); } cp = gdata->classes + cnum; cp->name = (const char *)strdup(name); cp->calls = 0; cp->mcount = 0; cp->methods = NULL; /* Is it a system class? If the class load is before VmStart * then we will consider it a system class that should * be treated carefully. (See java_crw_demo) */ system_class = 0; if ( !gdata->vm_is_started ) { system_class = 1; } new_image = NULL; new_length = 0; /* Call the class file reader/write demo code */ java_crw_demo(cnum, name, class_data, class_data_len, system_class, STRING(MTRACE_class), "L" STRING(MTRACE_class) ";", STRING(MTRACE_entry), "(II)V", STRING(MTRACE_exit), "(II)V", NULL, NULL, NULL, NULL, &new_image, &new_length, NULL, &mnum_callbacks); /* If we got back a new class image, return it back as "the" * new class image. This must be JVMTI Allocate space. */ if ( new_length > 0 ) { unsigned char *jvmti_space; jvmti_space = (unsigned char *)allocate(jvmti, (jint)new_length); (void)memcpy((void*)jvmti_space, (void*)new_image, (int)new_length); *new_class_data_len = (jint)new_length; *new_class_data = jvmti_space; /* VM will deallocate */ } /* Always free up the space we get from java_crw_demo() */ if ( new_image != NULL ) { (void)free((void*)new_image); /* Free malloc() space with free() */ } } } } exit_critical_section(jvmti); }
/* Callback for JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */ static void JNICALL cbClassFileLoadHook(jvmtiEnv *jvmti, JNIEnv* env, jclass class_being_redefined, jobject loader, const char* name, jobject protection_domain, jint class_data_len, const unsigned char* class_data, jint* new_class_data_len, unsigned char** new_class_data) { enterCriticalSection(jvmti); { /* It's possible we get here right after VmDeath event, be careful */ if ( !gdata->vmDead ) { const char * classname; /* Name can be NULL, make sure we avoid SEGV's */ if ( name == NULL ) { classname = java_crw_demo_classname(class_data, class_data_len, NULL); if ( classname == NULL ) { fatal_error("ERROR: No classname in classfile\n"); } } else { classname = strdup(name); if ( classname == NULL ) { fatal_error("ERROR: Ran out of malloc() space\n"); } } *new_class_data_len = 0; *new_class_data = NULL; /* The tracker class itself? */ if ( strcmp(classname, STRING(HEAP_TRACKER_class)) != 0 ) { jint cnum; int systemClass; unsigned char *newImage; long newLength; /* Get number for every class file image loaded */ cnum = gdata->ccount++; /* Is it a system class? If the class load is before VmStart * then we will consider it a system class that should * be treated carefully. (See java_crw_demo) */ systemClass = 0; if ( !gdata->vmStarted ) { systemClass = 1; } newImage = NULL; newLength = 0; /* Call the class file reader/write demo code */ java_crw_demo(cnum, classname, class_data, class_data_len, systemClass, STRING(HEAP_TRACKER_class), "L" STRING(HEAP_TRACKER_class) ";", NULL, NULL, NULL, NULL, STRING(HEAP_TRACKER_newobj), "(Ljava/lang/Object;)V", STRING(HEAP_TRACKER_newarr), "(Ljava/lang/Object;)V", &newImage, &newLength, NULL, NULL); /* If we got back a new class image, return it back as "the" * new class image. This must be JVMTI Allocate space. */ if ( newLength > 0 ) { unsigned char *jvmti_space; jvmti_space = (unsigned char *)allocate(jvmti, (jint)newLength); (void)memcpy((void*)jvmti_space, (void*)newImage, (int)newLength); *new_class_data_len = (jint)newLength; *new_class_data = jvmti_space; /* VM will deallocate */ } /* Always free up the space we get from java_crw_demo() */ if ( newImage != NULL ) { (void)free((void*)newImage); /* Free malloc() space with free() */ } } (void)free((void*)classname); } } exitCriticalSection(jvmti); }
int main(int argc, char **argv) { int i; unsigned class_number = 0x0FEED000; int obj_watch; int call_sites; int ret_sites; obj_watch = 0; call_sites = 0; ret_sites = 0; if ( argc < 3 ) { char buf[256]; (void)snprintf(buf, sizeof(buf), "Usage: %s input_file output_file", argv[0]); ERROR(buf); } for(i=1; i<argc; i++) { FILE *fin; FILE *fout; const unsigned char *file_image; int file_len; unsigned char *new_file_image; long new_file_len; if ( strcmp(argv[i],"-n")==0 ) { obj_watch = 1; continue; } else if ( strcmp(argv[i], "-c")==0 ) { call_sites = 1; continue; } else if ( strcmp(argv[i], "-r")==0 ) { ret_sites = 1; continue; } fin = fopen(argv[i], "r"); if ( fin == NULL ) { file_error(argv[i], "Cannot open file"); } (void)fseek(fin, 0, SEEK_END); file_len = ftell(fin); if ( file_len<=0 ) { file_error(argv[i], "File has 0 size"); } (void)fseek(fin, 0, SEEK_SET); file_image = (const unsigned char *)malloc((size_t)file_len); assert(file_image!=NULL); if ( fread((void*)file_image, 1, (size_t)file_len, fin)!=(size_t)file_len) { file_error(argv[i], "File read failed"); } java_crw_demo(class_number++, NULL, file_image, file_len, 0, "sun/tools/hprof/Tracker", "Lsun/tools/hprof/Tracker;", call_sites?"CallSite":NULL, call_sites?"(II)V":NULL, ret_sites?"ReturnSite":NULL, ret_sites?"(II)V":NULL, obj_watch?"ObjectInit":NULL, obj_watch?"(Ljava/lang/Object;)V":NULL, obj_watch?"NewArray":NULL, obj_watch?"(Ljava/lang/Object;)V":NULL, &new_file_image, &new_file_len, &error, &mnums); fout = fopen(argv[i+1], "w"); if ( fout == NULL ) { file_error(argv[i+1], "Cannot create file"); } if ( new_file_len > 0 ) { if ( fwrite(new_file_image, 1, (size_t)new_file_len, fout)!=(size_t)new_file_len ) { file_error(argv[i+1], "File write failed"); } (void)printf("Processed file %s to %s\n", argv[i], argv[i+1]); } else { if ( fwrite(file_image, 1, file_len, fout)!=(size_t)file_len ) { file_error(argv[i+1], "File write failed"); } (void)printf("Duplicated file %s to %s (no injections)\n", argv[i], argv[i+1]); } (void)fclose(fout); free((void*)file_image); if ( new_file_image != NULL ) { free(new_file_image); } i++; } return error_code; }
/* * Instrument classfile to invoke new object & array creation hooks in <cinit> of java.lang.Object */ void instrument_classfile( jvmtiEnv* jvmti, JNIEnv* jni , jclass class_being_redefined, jobject loader , const char* name, jobject protection_domain , jint class_data_len, const unsigned char* class_data , jint* new_class_data_len, unsigned char** new_class_data ) { static size_t classfile_index; // determine classname const char* classname = get_classname( name, class_data, class_data_len ); std::string internal_classname("L"); internal_classname += classname; internal_classname += ";"; xpr::sregex thread_filter = xpr::sregex::compile( eraser::agent::instance()->thread_filter_regex_ ); bool is_thread = xpr::regex_match( internal_classname, thread_filter ); bool obj_match = strcmp(classname, "java/lang/Object") == 0; if( !( is_thread || obj_match ) ) { free((void*)classname); return; } logger::instance()->level(200) << classname << std::boolalpha << " is_thread= " << is_thread << " obj_match= " << obj_match << std::endl; // prevent self-instrumentation if( strcmp(classname, PROXY_CLASS) == 0 ) { free((void*)classname); return; } unsigned char* new_image = 0; long new_length = 0; *new_class_data_len = 0; *new_class_data = 0; classfile_index++; bool is_system_class = ( agent::instance()->phase() < JVMTI_PHASE_START ); // go instrument if( obj_match ) java_crw_demo( classfile_index, classname , class_data, class_data_len , is_system_class , PROXY_CLASS , "L" PROXY_CLASS ";" , 0, 0 , 0, 0 , NEW_OBJ_METHOD, "(Ljava/lang/Object;)V" , NEW_ARR_METHOD, "(Ljava/lang/Object;)V" , &new_image , &new_length , 0, 0 ); else if( is_thread ) java_crw_demo( classfile_index, classname , class_data, class_data_len , is_system_class , PROXY_CLASS , "L" PROXY_CLASS ";" , MONITOR_ENTER, "(Ljava/lang/Object;)V" , MONITOR_EXIT, "(Ljava/lang/Object;)V" , 0, 0 , 0, 0 , &new_image , &new_length , 0, 0 ); else BOOST_ASSERT( false ); // memcpy transformed classfile to jvmti allocated memory if( new_length > 0 ) { # if defined( ERASER_DEBUG ) std::ofstream class_dump( "dump", std::ofstream::binary ); class_dump.write( (const char*)new_image, (std::streamsize)new_length ); class_dump.close(); # endif unsigned char *jvmtispace = (unsigned char *)allocate( jvmti, (jint)new_length ); memcpy( (void*)jvmtispace, (void*)new_image, new_length ); *new_class_data_len = new_length; *new_class_data = jvmtispace; } if( new_image != 0 ) free(new_image); free((void*)classname); }