使用JVM TI技術插裝位元組碼,實現Java程式碼執行控監Agent
JVM TI技術是JAVA5以後的版本推出的技術,即JVM程式設計介面,該技術廣泛應用於各種開發工具,例如Eclipse等。使用JVM TI可以開發JAVA除錯工具,JAVA程式碼執行監控工具等。同時,瞭解JVM TI技術也有助於JAVA程式設計師深入瞭解JVM的原理,上一篇文章我介紹了採用JVM TI的事件通知技術開發JVM監控工具,但是使用事件通知技術開發應程式效能是非常差勁的,對於原始碼較多的工程,使用事件通知來捕獲JAVA的執行過程,是不可取的,將會對程式的效能造成嚴重的影響。
在JAVA語言中,可以使用AOP技術修改位元組碼,著名的框架有Spring,Guice等框架,可以根據程式設計師的配置對指定的類進行位元組碼插裝,但該技術取決程式設計師的主觀動態,適合在程式的開發過程中使用,如果要實現黑盒的監控過程,是不適用的。例如想知道程式執行過程中呼叫了哪些類,哪些方法,想通過捕獲JAVA呼叫棧瞭解程式的執行效能等等,使用JVM TI技術是適用的,商業工具JTest就是採用JVM TI技術實現的,再如QTP等自動化測試工具對JAVA SE程式的介面識別,也是採用JVM TI技術實現。總而言之,採用JVM TI技術,我們可以實現如下功能:
- 瞭解程式的執行過程
- 監控程式的執行效能
- 除錯你的多執行緒程式
- 開發JAVA的自動化測試工具
- 開發JAVA的單元測試工具
第一步: 本程式我使用VS 2008工具開發和編譯,首先使用VS 2008建立一個Win32本地應用程式,詳細的步驟請大家參考其他專業文章。
第二步:我們需要使用幾個JVM的API類,分別是:agent_util,java_crw_demo兩個API,這兩個api我們可以在JAVA的例子中找到,其中java_crw_demo是用來插裝位元組碼的,agent_util是agent的工具類
1、agent_util.cpp
/* * * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */ // dllmain.cpp : 定義 DLL 應用程式的入口點。 #include "stdafx.h" #include <agent_util.h> /* ------------------------------------------------------------------- */ /* Generic C utility functions */ /* Send message to stdout or whatever the data output location is */ void stdout_message(const char * format, ...) { va_list ap; va_start(ap, format); (void)vfprintf(stdout, format, ap); va_end(ap); } /* Send message to stderr or whatever the error output location is and exit */ void fatal_error(const char * format, ...) { va_list ap; va_start(ap, format); (void)vfprintf(stderr, format, ap); (void)fflush(stderr); va_end(ap); exit(3); } /* Get a token from a string (strtok is not MT-safe) * str String to scan * seps Separation characters * buf Place to put results * max Size of buf * Returns NULL if no token available or can't do the scan. */ char * get_token(char *str, char *seps, char *buf, int max) { int len; buf[0] = 0; if ( str==NULL || str[0]==0 ) { return NULL; } str += strspn(str, seps); if ( str[0]==0 ) { return NULL; } len = (int)strcspn(str, seps); if ( len >= max ) { return NULL; } (void)strncpy(buf, str, len); buf[len] = 0; return str+len; } /* Determines if a class/method is specified by a list item * item String that represents a pattern to match * If it starts with a '*', then any class is allowed * If it ends with a '*', then any method is allowed * cname Class name, e.g. "java.lang.Object" * mname Method name, e.g. "<init>" * Returns 1(true) or 0(false). */ static int covered_by_list_item(char *item, char *cname, char *mname) { int len; len = (int)strlen(item); if ( item[0]=='*' ) { if ( strncmp(mname, item+1, len-1)==0 ) { return 1; } } else if ( item[len-1]=='*' ) { if ( strncmp(cname, item, len-1)==0 ) { return 1; } } else { int cname_len; cname_len = (int)strlen(cname); if ( strncmp(cname, item, (len>cname_len?cname_len:len))==0 ) { if ( cname_len >= len ) { /* No method name supplied in item, we must have matched */ return 1; } else { int mname_len; mname_len = (int)strlen(mname); item += cname_len+1; len -= cname_len+1; if ( strncmp(mname, item, (len>mname_len?mname_len:len))==0 ) { return 1; } } } } return 0; } /* Determines if a class/method is specified by this list * list String of comma separated pattern items * cname Class name, e.g. "java.lang.Object" * mname Method name, e.g. "<init>" * Returns 1(true) or 0(false). */ static int covered_by_list(char *list, char *cname, char *mname) { char token[1024]; char *next; if ( list[0] == 0 ) { return 0; } next = get_token(list, ",", token, sizeof(token)); while ( next != NULL ) { if ( covered_by_list_item(token, cname, mname) ) { return 1; } next = get_token(next, ",", token, sizeof(token)); } return 0; } /* Determines which class and methods we are interested in * cname Class name, e.g. "java.lang.Object" * mname Method name, e.g. "<init>" * include_list Empty or an explicit list for inclusion * exclude_list Empty or an explicit list for exclusion * Returns 1(true) or 0(false). */ int interested(char *cname, char *mname, char *include_list, char *exclude_list) { if ( exclude_list!=NULL && exclude_list[0]!=0 && covered_by_list(exclude_list, cname, mname) ) { return 0; } if ( include_list!=NULL && include_list[0]!=0 && !covered_by_list(include_list, cname, mname) ) { return 0; } return 1; } /* ------------------------------------------------------------------- */ /* Generic JVMTI utility functions */ /* Every JVMTI interface returns an error code, which should be checked * to avoid any cascading errors down the line. * The interface GetErrorName() returns the actual enumeration constant * name, making the error messages much easier to understand. */ void check_jvmti_error(jvmtiEnv *jvmti, jvmtiError errnum, const char *str) { if ( errnum != JVMTI_ERROR_NONE ) { char *errnum_str; errnum_str = NULL; (void)(jvmti)->GetErrorName(errnum, &errnum_str); fatal_error("ERROR: JVMTI: %d(%s): %s\n", errnum, (errnum_str==NULL?"Unknown":errnum_str), (str==NULL?"":str)); } } /* All memory allocated by JVMTI must be freed by the JVMTI Deallocate * interface. */ void deallocate1(jvmtiEnv *jvmti, char *ptr) { jvmtiError error; error = (jvmti)->Deallocate((unsigned char*)ptr); check_jvmti_error(jvmti, error, "Cannot deallocate memory"); } /* Allocation of JVMTI managed memory */ void * allocate(jvmtiEnv *jvmti, jint len) { jvmtiError error; void *ptr; error = (jvmti)->Allocate(len, (unsigned char **)&ptr); check_jvmti_error(jvmti, error, "Cannot allocate memory"); return ptr; } /* Add demo jar file to boot class path (the BCI Tracker class must be * in the boot classpath) * * WARNING: This code assumes that the jar file can be found at one of: * ${JAVA_HOME}/demo/jvmti/${DEMO_NAME}/${DEMO_NAME}.jar * ${JAVA_HOME}/../demo/jvmti/${DEMO_NAME}/${DEMO_NAME}.jar * where JAVA_HOME may refer to the jre directory. * Both these values are added to the boot classpath. * These locations are only true for these demos, installed * in the JDK area. Platform specific code could be used to * find the location of the DLL or .so library, and construct a * path name to the jar file, relative to the library location. */ void add_demo_jar_to_bootclasspath(jvmtiEnv *jvmti, char *demo_name) { jvmtiError error; char *file_sep; int max_len; char *java_home; char jar_path[FILENAME_MAX+1]; java_home = NULL; error = (jvmti)->GetSystemProperty("java.home", &java_home); check_jvmti_error(jvmti, error, "Cannot get java.home property value"); if ( java_home == NULL || java_home[0] == 0 ) { fatal_error("ERROR: Java home not found\n"); } #ifdef WIN32 file_sep = "\\"; #else file_sep = "/"; #endif max_len = (int)(strlen(java_home) + strlen(demo_name)*2 + strlen(file_sep)*5 +16 /* ".." "demo" "jvmti" ".jar" NULL */ ); if ( max_len > (int)sizeof(jar_path) ) { fatal_error("ERROR: Path to jar file too long\n"); } (void)strcpy(jar_path, java_home); (void)strcat(jar_path, file_sep); (void)strcat(jar_path, "demo"); (void)strcat(jar_path, file_sep); (void)strcat(jar_path, "jvmti"); (void)strcat(jar_path, file_sep); (void)strcat(jar_path, demo_name); (void)strcat(jar_path, file_sep); (void)strcat(jar_path, demo_name); (void)strcat(jar_path, ".jar"); error = (jvmti)->AddToBootstrapClassLoaderSearch((const char*)jar_path); check_jvmti_error(jvmti, error, "Cannot add to boot classpath"); (void)strcpy(jar_path, java_home); (void)strcat(jar_path, file_sep); (void)strcat(jar_path, ".."); (void)strcat(jar_path, file_sep); (void)strcat(jar_path, "demo"); (void)strcat(jar_path, file_sep); (void)strcat(jar_path, "jvmti"); (void)strcat(jar_path, file_sep); (void)strcat(jar_path, demo_name); (void)strcat(jar_path, file_sep); (void)strcat(jar_path, demo_name); (void)strcat(jar_path, ".jar"); error = (jvmti)->AddToBootstrapClassLoaderSearch((const char*)jar_path); check_jvmti_error(jvmti, error, "Cannot add to boot classpath"); } /* ------------------------------------------------------------------- */
2、java_crw_demo.cpp
/*
* @(#)java_crw_demo.c 1.39 06/01/27
*
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* -Redistribution of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* -Redistribution in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
* ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
* AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
* REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
* INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
* OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or intended
* for use in the design, construction, operation or maintenance of any
* nuclear facility.
*/
/* Class reader writer (java_crw_demo) for instrumenting bytecodes */
/*
* As long as the callbacks allow for it and the class number is unique,
* this code is completely re-entrant and any number of classfile
* injections can happen at the same time.
*
* The current logic requires a unique number for this class instance
* or (jclass,jobject loader) pair, this is done via the ClassIndex
* in hprof, which is passed in as the 'unsigned cnum' to java_crw_demo().
* It's up to the user of this interface if it wants to use this
* feature.
*
* Example Usage: See file test_crw.c.
*
*/
// dllmain.cpp : 定義 DLL 應用程式的入口點。
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <java_crw_demo.h>
/* Get Java and class file and bytecode information. */
#include <jni.h>
#include "classfile_constants.h"
/* Include our own interface for cross check */
/* Macros over error functions to capture line numbers */
#define CRW_FATAL(ci, message) fatal_error(ci, message, __FILE__, __LINE__)
#if defined(DEBUG) || !defined(NDEBUG)
#define CRW_ASSERT(ci, cond) \
((cond)?(void)0:assert_error(ci, #cond, __FILE__, __LINE__))
#else
#define CRW_ASSERT(ci, cond)
#endif
#define CRW_ASSERT_MI(mi) CRW_ASSERT((mi)?(mi)->ci:NULL,(mi)!=NULL)
#define CRW_ASSERT_CI(ci) CRW_ASSERT(ci, ( (ci) != NULL && \
(ci)->input_position <= (ci)->input_len && \
(ci)->output_position <= (ci)->output_len) )
/* Typedefs for various integral numbers, just for code clarity */
typedef unsigned ClassOpcode; /* One opcode */
typedef unsigned char ByteCode; /* One byte from bytecodes */
typedef int ByteOffset; /* Byte offset */
typedef int ClassConstant; /* Constant pool kind */
typedef long CrwPosition; /* Position in class image */
typedef unsigned short CrwCpoolIndex; /* Index into constant pool */
/* Misc support macros */
/* Given the position of an opcode, find the next 4byte boundary position */
#define NEXT_4BYTE_BOUNDARY(opcode_pos) (((opcode_pos)+4) & (~3))
#define LARGEST_INJECTION (12*3) /* 3 injections at same site */
#define MAXIMUM_NEW_CPOOL_ENTRIES 64 /* don't add more than 32 entries */
/* Constant Pool Entry (internal table that mirrors pool in file image) */
typedef struct {
const char * ptr; /* Pointer to any string */
unsigned short len; /* Length of string */
unsigned int index1; /* 1st 16 bit index or 32bit value. */
unsigned int index2; /* 2nd 16 bit index or 32bit value. */
ClassConstant tag; /* Tag or kind of entry. */
} CrwConstantPoolEntry;
struct MethodImage;
/* Class file image storage structure */
typedef struct CrwClassImage {
/* Unique class number for this class */
unsigned number;
/* Name of class, given or gotten out of class image */
const char * name;
/* Input and Output class images tracking */
const unsigned char * input;
unsigned char * output;
CrwPosition input_len;
CrwPosition output_len;
CrwPosition input_position;
CrwPosition output_position;
/* Mirrored constant pool */
CrwConstantPoolEntry * cpool;
CrwCpoolIndex cpool_max_elements; /* Max count */
CrwCpoolIndex cpool_count_plus_one;
/* Input flags about class (e.g. is it a system class) */
int system_class;
/* Class access flags gotten from file. */
unsigned access_flags;
/* Names of classes and methods. */
char* tclass_name; /* Name of class that has tracker methods. */
char* tclass_sig; /* Signature of class */
char* call_name; /* Method name to call at offset 0 */
char* call_sig; /* Signature of this method */
char* return_name; /* Method name to call before any return */
char* return_sig; /* Signature of this method */
char* obj_init_name; /* Method name to call in Object <init> */
char* obj_init_sig; /* Signature of this method */
char* newarray_name; /* Method name to call after newarray opcodes */
char* newarray_sig; /* Signature of this method */
/* Constant pool index values for new entries */
CrwCpoolIndex tracker_class_index;
CrwCpoolIndex object_init_tracker_index;
CrwCpoolIndex newarray_tracker_index;
CrwCpoolIndex call_tracker_index;
CrwCpoolIndex return_tracker_index;
CrwCpoolIndex class_number_index; /* Class number in pool */
/* Count of injections made into this class */
int injection_count;
/* This class must be the java.lang.Object class */
jboolean is_object_class;
/* This class must be the java.lang.Thread class */
jboolean is_thread_class;
/* Callback functions */
FatalErrorHandler fatal_error_handler;
MethodNumberRegister mnum_callback;
/* Table of method names and descr's */
int method_count;
const char ** method_name;
const char ** method_descr;
struct MethodImage * current_mi;
} CrwClassImage;
/* Injection bytecodes (holds injected bytecodes for each code position) */
typedef struct {
ByteCode * code;
ByteOffset len;
} Injection;
/* Method transformation data (allocated/freed as each method is processed) */
typedef struct MethodImage {
/* Back reference to Class image data. */
CrwClassImage * ci;
/* Unique method number for this class. */
unsigned number;
/* Method name and descr */
const char * name;
const char * descr;
/* Map of input bytecode offsets to output bytecode offsets */
ByteOffset * map;
/* Bytecode injections for each input bytecode offset */
Injection * injections;
/* Widening setting for each input bytecode offset */
signed char * widening;
/* Length of original input bytecodes, and new bytecodes. */
ByteOffset code_len;
ByteOffset new_code_len;
/* Location in input where bytecodes are located. */
CrwPosition start_of_input_bytecodes;
/* Original max_stack and new max stack */
unsigned max_stack;
unsigned new_max_stack;
jboolean object_init_method;
jboolean skip_call_return_sites;
/* Method access flags gotten from file. */
unsigned access_flags;
} MethodImage;
/* ----------------------------------------------------------------- */
/* General support functions (memory and error handling) */
static void
fatal_error(CrwClassImage *ci, const char *message, const char *file, int line)
{
if ( ci != NULL && ci->fatal_error_handler != NULL ) {
(*ci->fatal_error_handler)(message, file, line);
} else {
/* Normal operation should NEVER reach here */
/* NO CRW FATAL ERROR HANDLER! */
(void)fprintf(stderr, "CRW: %s [%s:%d]\n", message, file, line);
abort();
}
}
#if defined(DEBUG) || !defined(NDEBUG)
static void
assert_error(CrwClassImage *ci, const char *condition,
const char *file, int line)
{
char buf[512];
MethodImage *mi;
ByteOffset byte_code_offset;
mi = ci->current_mi;
if ( mi != NULL ) {
byte_code_offset = (ByteOffset)(mi->ci->input_position - mi->start_of_input_bytecodes);
} else {
byte_code_offset=-1;
}
(void)sprintf(buf,
"CRW ASSERTION FAILURE: %s (%s:%s:%d)",
condition,
ci->name==0?"?":ci->name,
mi->name==0?"?":mi->name,
byte_code_offset);
fatal_error(ci, buf, file, line);
}
#endif
static void *
allocate(CrwClassImage *ci, int nbytes)
{
void * ptr;
if ( nbytes <= 0 ) {
CRW_FATAL(ci, "Cannot allocate <= 0 bytes");
}
ptr = malloc(nbytes);
if ( ptr == NULL ) {
CRW_FATAL(ci, "Ran out of malloc memory");
}
return ptr;
}
static void *
reallocate(CrwClassImage *ci, void *optr, int nbytes)
{
void * ptr;
if ( optr == NULL ) {
CRW_FATAL(ci, "Cannot deallocate NULL");
}
if ( nbytes <= 0 ) {
CRW_FATAL(ci, "Cannot reallocate <= 0 bytes");
}
ptr = realloc(optr, nbytes);
if ( ptr == NULL ) {
CRW_FATAL(ci, "Ran out of malloc memory");
}
return ptr;
}
static void *
allocate_clean(CrwClassImage *ci, int nbytes)
{
void * ptr;
if ( nbytes <= 0 ) {
CRW_FATAL(ci, "Cannot allocate <= 0 bytes");
}
ptr = calloc(nbytes, 1);
if ( ptr == NULL ) {
CRW_FATAL(ci, "Ran out of malloc memory");
}
return ptr;
}
static const char *
duplicate(CrwClassImage *ci, const char *str, int len)
{
char *copy;
copy = (char*)allocate(ci, len+1);
(void)memcpy(copy, str, len);
copy[len] = 0;
return (const char *)copy;
}
static void deallocate(CrwClassImage *ci, void *ptr)
{
if ( ptr == NULL ) {
CRW_FATAL(ci, "Cannot deallocate NULL");
}
(void)free(ptr);
}
/* ----------------------------------------------------------------- */
/* Functions for reading/writing bytes to/from the class images */
static unsigned
readU1(CrwClassImage *ci)
{
CRW_ASSERT_CI(ci);
return ((unsigned)(ci->input[ci->input_position++])) & 0xFF;
}
static unsigned
readU2(CrwClassImage *ci)
{
unsigned res;
res = readU1(ci);
return (res << 8) + readU1(ci);
}
static signed short
readS2(CrwClassImage *ci)
{
unsigned res;
res = readU1(ci);
return ((res << 8) + readU1(ci)) & 0xFFFF;
}
static unsigned
readU4(CrwClassImage *ci)
{
unsigned res;
res = readU2(ci);
return (res << 16) + readU2(ci);
}
static void
writeU1(CrwClassImage *ci, unsigned val) /* Only writes out lower 8 bits */
{
CRW_ASSERT_CI(ci);
if ( ci->output != NULL ) {
ci->output[ci->output_position++] = val & 0xFF;
}
}
static void
writeU2(CrwClassImage *ci, unsigned val)
{
writeU1(ci, val >> 8);
writeU1(ci, val);
}
static void
writeU4(CrwClassImage *ci, unsigned val)
{
writeU2(ci, val >> 16);
writeU2(ci, val);
}
static unsigned
copyU1(CrwClassImage *ci)
{
unsigned value;
value = readU1(ci);
writeU1(ci, value);
return value;
}
static unsigned
copyU2(CrwClassImage *ci)
{
unsigned value;
value = readU2(ci);
writeU2(ci, value);
return value;
}
static unsigned
copyU4(CrwClassImage *ci)
{
unsigned value;
value = readU4(ci);
writeU4(ci, value);
return value;
}
static void
copy(CrwClassImage *ci, unsigned count)
{
CRW_ASSERT_CI(ci);
if ( ci->output != NULL ) {
(void)memcpy(ci->output+ci->output_position,
ci->input+ci->input_position, count);
ci->output_position += count;
}
ci->input_position += count;
CRW_ASSERT_CI(ci);
}
static void
skip(CrwClassImage *ci, unsigned count)
{
CRW_ASSERT_CI(ci);
ci->input_position += count;
}
static void
read_bytes(CrwClassImage *ci, void *bytes, unsigned count)
{
CRW_ASSERT_CI(ci);
CRW_ASSERT(ci, bytes!=NULL);
(void)memcpy(bytes, ci->input+ci->input_position, count);
ci->input_position += count;
}
static void
write_bytes(CrwClassImage *ci, void *bytes, unsigned count)
{
CRW_ASSERT_CI(ci);
CRW_ASSERT(ci, bytes!=NULL);
if ( ci->output != NULL ) {
(void)memcpy(ci->output+ci->output_position, bytes, count);
ci->output_position += count;
}
}
static void
random_writeU2(CrwClassImage *ci, CrwPosition pos, unsigned val)
{
CrwPosition save_position;
CRW_ASSERT_CI(ci);
save_position = ci->output_position;
ci->output_position = pos;
writeU2(ci, val);
ci->output_position = save_position;
}
static void
random_writeU4(CrwClassImage *ci, CrwPosition pos, unsigned val)
{
CrwPosition save_position;
CRW_ASSERT_CI(ci);
save_position = ci->output_position;
ci->output_position = pos;
writeU4(ci, val);
ci->output_position = save_position;
}
/* ----------------------------------------------------------------- */
/* Constant Pool handling functions. */
static void
fillin_cpool_entry(CrwClassImage *ci, CrwCpoolIndex i,
ClassConstant tag,
unsigned int index1, unsigned int index2,
const char *ptr, int len)
{
CRW_ASSERT_CI(ci);
CRW_ASSERT(ci, i > 0 && i < ci->cpool_count_plus_one);
ci->cpool[i].tag = tag;
ci->cpool[i].index1 = index1;
ci->cpool[i].index2 = index2;
ci->cpool[i].ptr = ptr;
ci->cpool[i].len = (unsigned short)len;
}
static CrwCpoolIndex
add_new_cpool_entry(CrwClassImage *ci, ClassConstant tag,
unsigned int index1, unsigned int index2,
const char *str, int len)
{
CrwCpoolIndex i;
char *utf8 = NULL;
CRW_ASSERT_CI(ci);
i = ci->cpool_count_plus_one++;
/* NOTE: This implementation does not automatically expand the
* constant pool table beyond the expected number needed
* to handle this particular CrwTrackerInterface injections.
* See MAXIMUM_NEW_CPOOL_ENTRIES
*/
CRW_ASSERT(ci, ci->cpool_count_plus_one < ci->cpool_max_elements );
writeU1(ci, tag);
switch (tag) {
case JVM_CONSTANT_Class:
writeU2(ci, index1);
break;
case JVM_CONSTANT_String:
writeU2(ci, index1);
break;
case JVM_CONSTANT_Fieldref:
case JVM_CONSTANT_Methodref:
case JVM_CONSTANT_InterfaceMethodref:
case JVM_CONSTANT_Integer:
case JVM_CONSTANT_Float:
case JVM_CONSTANT_NameAndType:
writeU2(ci, index1);
writeU2(ci, index2);
break;
case JVM_CONSTANT_Long:
case JVM_CONSTANT_Double:
writeU4(ci, index1);
writeU4(ci, index2);
ci->cpool_count_plus_one++;
CRW_ASSERT(ci, ci->cpool_count_plus_one < ci->cpool_max_elements );
break;
case JVM_CONSTANT_Utf8:
CRW_ASSERT(ci, len==(len & 0xFFFF));
writeU2(ci, len);
write_bytes(ci, (void*)str, len);
utf8 = (char*)duplicate(ci, str, len);
break;
default:
CRW_FATAL(ci, "Unknown constant");
break;
}
fillin_cpool_entry(ci, i, tag, index1, index2, (const char *)utf8, len);
CRW_ASSERT(ci, i > 0 && i < ci->cpool_count_plus_one);
return i;
}
static CrwCpoolIndex
add_new_class_cpool_entry(CrwClassImage *ci, const char *class_name)
{
CrwCpoolIndex name_index;
CrwCpoolIndex class_index;
int len;
CRW_ASSERT_CI(ci);
CRW_ASSERT(ci, class_name!=NULL);
len = (int)strlen(class_name);
name_index = add_new_cpool_entry(ci, JVM_CONSTANT_Utf8, len, 0,
class_name, len);
class_index = add_new_cpool_entry(ci, JVM_CONSTANT_Class, name_index, 0,
NULL, 0);
return class_index;
}
static CrwCpoolIndex
add_new_method_cpool_entry(CrwClassImage *ci, CrwCpoolIndex class_index,
const char *name, const char *descr)
{
CrwCpoolIndex name_index;
CrwCpoolIndex descr_index;
CrwCpoolIndex name_type_index;
int len;
CRW_ASSERT_CI(ci);
CRW_ASSERT(ci, name!=NULL);
CRW_ASSERT(ci, descr!=NULL);
len = (int)strlen(name);
name_index =
add_new_cpool_entry(ci, JVM_CONSTANT_Utf8, len, 0, name, len);
len = (int)strlen(descr);
descr_index =
add_new_cpool_entry(ci, JVM_CONSTANT_Utf8, len, 0, descr, len);
name_type_index =
add_new_cpool_entry(ci, JVM_CONSTANT_NameAndType,
name_index, descr_index, NULL, 0);
return add_new_cpool_entry(ci, JVM_CONSTANT_Methodref,
class_index, name_type_index, NULL, 0);
}
static CrwConstantPoolEntry
cpool_entry(CrwClassImage *ci, CrwCpoolIndex c_index)
{
CRW_ASSERT_CI(ci);
CRW_ASSERT(ci, c_index > 0 && c_index < ci->cpool_count_plus_one);
return ci->cpool[c_index];
}
static void
cpool_setup(CrwClassImage *ci)
{
CrwCpoolIndex i;
CrwPosition cpool_output_position;
int count_plus_one;
CRW_ASSERT_CI(ci);
cpool_output_position = ci->output_position;
count_plus_one = copyU2(ci);
CRW_ASSERT(ci, count_plus_one>1);
ci->cpool_max_elements = count_plus_one+MAXIMUM_NEW_CPOOL_ENTRIES;
ci->cpool = (CrwConstantPoolEntry*)allocate_clean(ci,
(int)((ci->cpool_max_elements)*sizeof(CrwConstantPoolEntry)));
ci->cpool_count_plus_one = (CrwCpoolIndex)count_plus_one;
/* Index zero not in class file */
for (i = 1; i < count_plus_one; ++i) {
CrwCpoolIndex ipos;
ClassConstant tag;
unsigned int index1;
unsigned int index2;
unsigned len;
char * utf8;
ipos = i;
index1 = 0;
index2 = 0;
len = 0;
utf8 = NULL;
tag = copyU1(ci);
switch (tag) {
case JVM_CONSTANT_Class:
index1 = copyU2(ci);
break;
case JVM_CONSTANT_String:
index1 = copyU2(ci);
break;
case JVM_CONSTANT_Fieldref:
case JVM_CONSTANT_Methodref:
case JVM_CONSTANT_InterfaceMethodref:
case JVM_CONSTANT_Integer:
case JVM_CONSTANT_Float:
case JVM_CONSTANT_NameAndType:
index1 = copyU2(ci);
index2 = copyU2(ci);
break;
case JVM_CONSTANT_Long:
case JVM_CONSTANT_Double:
index1 = copyU4(ci);
index2 = copyU4(ci);
++i; /* // these take two CP entries - duh! */
break;
case JVM_CONSTANT_Utf8:
len = copyU2(ci);
index1 = (unsigned short)len;
utf8 = (char*)allocate(ci, len+1);
read_bytes(ci, (void*)utf8, len);
utf8[len] = 0;
write_bytes(ci, (void*)utf8, len);
break;
default:
CRW_FATAL(ci, "Unknown constant");
break;
}
fillin_cpool_entry(ci, ipos, tag, index1, index2, (const char *)utf8, len);
}
if (ci->call_name != NULL || ci->return_name != NULL) {
if ( ci->number != (ci->number & 0x7FFF) ) {
ci->class_number_index =
add_new_cpool_entry(ci, JVM_CONSTANT_Integer,
(ci->number>>16) & 0xFFFF, ci->number & 0xFFFF, NULL, 0);
}
}
if ( ci->tclass_name != NULL ) {
ci->tracker_class_index =
add_new_class_cpool_entry(ci, ci->tclass_name);
}
if (ci->obj_init_name != NULL) {
ci->object_init_tracker_index = add_new_method_cpool_entry(ci,
ci->tracker_class_index,
ci->obj_init_name,
ci->obj_init_sig);
}
if (ci->newarray_name != NULL) {
ci->newarray_tracker_index = add_new_method_cpool_entry(ci,
ci->tracker_class_index,
ci->newarray_name,
ci->newarray_sig);
}
if (ci->call_name != NULL) {
ci->call_tracker_index = add_new_method_cpool_entry(ci,
ci->tracker_class_index,
ci->call_name,
ci->call_sig);
}
if (ci->return_name != NULL) {
ci->return_tracker_index = add_new_method_cpool_entry(ci,
ci->tracker_class_index,
ci->return_name,
ci->return_sig);
}
random_writeU2(ci, cpool_output_position, ci->cpool_count_plus_one);
}
/* ----------------------------------------------------------------- */
/* Functions that create the bytecodes to inject */
static ByteOffset
push_pool_constant_bytecodes(ByteCode *bytecodes, CrwCpoolIndex index)
{
ByteOffset nbytes = 0;
if ( index == (index&0x7F) ) {
bytecodes[nbytes++] = (ByteCode)JVM_OPC_ldc;
} else {
bytecodes[nbytes++] = (ByteCode)JVM_OPC_ldc_w;
bytecodes[nbytes++] = (ByteCode)((index >> 8) & 0xFF);
}
bytecodes[nbytes++] = (ByteCode)(index & 0xFF);
return nbytes;
}
static ByteOffset
push_short_constant_bytecodes(ByteCode *bytecodes, unsigned number)
{
ByteOffset nbytes = 0;
if ( number <= 5 ) {
bytecodes[nbytes++] = (ByteCode)(JVM_OPC_iconst_0+number);
} else if ( number == (number&0x7F) ) {
bytecodes[nbytes++] = (ByteCode)JVM_OPC_bipush;
bytecodes[nbytes++] = (ByteCode)(number & 0xFF);
} else {
bytecodes[nbytes++] = (ByteCode)JVM_OPC_sipush;
bytecodes[nbytes++] = (ByteCode)((number >> 8) & 0xFF);
bytecodes[nbytes++] = (ByteCode)(number & 0xFF);
}
return nbytes;
}
static ByteOffset injection_template(MethodImage *mi, ByteCode *bytecodes, ByteOffset max_nbytes,
CrwCpoolIndex method_index)
{
CrwClassImage * ci;
ByteOffset nbytes = 0;
unsigned max_stack;
int add_dup;
int add_aload;
int push_cnum;
int push_mnum;
ci = mi->ci;
CRW_ASSERT(ci, bytecodes!=NULL);
if ( method_index == 0 ) {
return 0;
}
if ( method_index == ci->newarray_tracker_index) {
max_stack = mi->max_stack + 1;
add_dup = JNI_TRUE;
add_aload = JNI_FALSE;
push_cnum = JNI_FALSE;
push_mnum = JNI_FALSE;
} else if ( method_index == ci->object_init_tracker_index) {
max_stack = mi->max_stack + 1;
add_dup = JNI_FALSE;
add_aload = JNI_TRUE;
push_cnum = JNI_FALSE;
push_mnum = JNI_FALSE;
} else {
max_stack = mi->max_stack + 2;
add_dup = JNI_FALSE;
add_aload = JNI_FALSE;
push_cnum = JNI_TRUE;
push_mnum = JNI_TRUE;
}
if ( add_dup ) {
bytecodes[nbytes++] = (ByteCode)JVM_OPC_dup;
}
if ( add_aload ) {
bytecodes[nbytes++] = (ByteCode)JVM_OPC_aload_0;
}
if ( push_cnum ) {
if ( ci->number == (ci->number & 0x7FFF) ) {
nbytes += push_short_constant_bytecodes(bytecodes+nbytes,
ci->number);
} else {
CRW_ASSERT(ci, ci->class_number_index!=0);
nbytes += push_pool_constant_bytecodes(bytecodes+nbytes,
ci->class_number_index);
}
}
if ( push_mnum ) {
nbytes += push_short_constant_bytecodes(bytecodes+nbytes,
mi->number);
}
bytecodes[nbytes++] = (ByteCode)JVM_OPC_invokestatic;
bytecodes[nbytes++] = (ByteCode)(method_index >> 8);
bytecodes[nbytes++] = (ByteCode)method_index;
bytecodes[nbytes] = 0;
/* bytecodes[nbytes++] = (ByteCode)JVM_OPC_getstatic;
bytecodes[nbytes++] = (ByteCode)(ci->trace_field_index >> 8);
bytecodes[nbytes++] = (ByteCode)ci->trace_field_index;
bytecodes[nbytes++] = (ByteCode)opc_ifne;
bytecodes[nbytes++] = (ByteCode)0;*/
//bytecodes[nbytes++]=(ByteCode)opc_getstatic;
CRW_ASSERT(ci, nbytes<max_nbytes);
/* Make sure the new max_stack is appropriate */
if ( max_stack > mi->new_max_stack ) {
mi->new_max_stack = max_stack;
}
return nbytes;
}
/* Called to create injection code at entry to a method */
static ByteOffset
entry_injection_code(MethodImage *mi, ByteCode *bytecodes, ByteOffset len)
{
CrwClassImage * ci;
ByteOffset nbytes = 0;
CRW_ASSERT_MI(mi);
ci = mi->ci;
if ( mi->object_init_method ) {
nbytes = injection_template(mi,
bytecodes, len, ci->object_init_tracker_index);
}
if ( !mi->skip_call_return_sites ) {
nbytes += injection_template(mi,
bytecodes+nbytes, len-nbytes, ci->call_tracker_index);
}
return nbytes;
}
/* Called to create injection code before an opcode */
static ByteOffset
before_injection_code(MethodImage *mi, ClassOpcode opcode,
ByteCode *bytecodes, ByteOffset len)
{
ByteOffset nbytes = 0;
CRW_ASSERT_MI(mi);
switch ( opcode ) {
case JVM_OPC_return:
case JVM_OPC_ireturn:
case JVM_OPC_lreturn:
case JVM_OPC_freturn:
case JVM_OPC_dreturn:
case JVM_OPC_areturn:
if ( !mi->skip_call_return_sites ) {
nbytes = injection_template(mi,
bytecodes, len, mi->ci->return_tracker_index);
}
break;
default:
break;
}
return nbytes;
}
/* Called to create injection code after an opcode */
static ByteOffset
after_injection_code(MethodImage *mi, ClassOpcode opcode,
ByteCode *bytecodes, ByteOffset len)
{
CrwClassImage* ci;
ByteOffset nbytes;
ci = mi->ci;
nbytes = 0;
CRW_ASSERT_MI(mi);
switch ( opcode ) {
case JVM_OPC_new:
/* Can't inject here cannot pass around uninitialized object */
break;
case JVM_OPC_newarray:
case JVM_OPC_anewarray:
case JVM_OPC_multianewarray:
nbytes = injection_template(mi,
bytecodes, len, ci->newarray_tracker_index);
break;
default:
break;
}
return nbytes;
}
/* Actually inject the bytecodes */
static void
inject_bytecodes(MethodImage *mi, ByteOffset at,
ByteCode *bytecodes, ByteOffset len)
{
Injection injection;
CrwClassImage *ci;
ci = mi->ci;
CRW_ASSERT_MI(mi);
CRW_ASSERT(ci, at <= mi->code_len);
injection = mi->injections[at];
CRW_ASSERT(ci, len <= LARGEST_INJECTION/2);
CRW_ASSERT(ci, injection.len+len <= LARGEST_INJECTION);
/* Either start an injection area or concatenate to what is there */
if ( injection.code == NULL ) {
CRW_ASSERT(ci, injection.len==0);
injection.code = (ByteCode *)allocate_clean(ci, LARGEST_INJECTION+1);
}
(void)memcpy(injection.code+injection.len, bytecodes, len);
injection.len += len;
injection.code[injection.len] = 0;
mi->injections[at] = injection;
ci->injection_count++;
}
/* ----------------------------------------------------------------- */
/* Method handling functions */
static MethodImage *
method_init(CrwClassImage *ci, unsigned mnum, ByteOffset code_len)
{
MethodImage * mi;
ByteOffset i;
mi = (MethodImage*)allocate_clean(ci, (int)sizeof(MethodImage));
mi->ci = ci;
mi->name = ci->method_name[mnum];
mi->descr = ci->method_descr[mnum];
mi->code_len = code_len;
mi->map = (ByteOffset*)allocate_clean(ci,
(int)((code_len+1)*sizeof(ByteOffset)));
for(i=0; i<=code_len; i++) {
mi->map[i] = i;
}
mi->widening = (signed char*)allocate_clean(ci, code_len+1);
mi->injections = (Injection *)allocate_clean(ci,
(int)((code_len+1)*sizeof(Injection)));
mi->number = mnum;
ci->current_mi = mi;
return mi;
}
static void
method_term(MethodImage *mi)
{
CrwClassImage *ci;
ci = mi->ci;
CRW_ASSERT_MI(mi);
if ( mi->map != NULL ) {
deallocate(ci, (void*)mi->map);
mi->map = NULL;
}
if ( mi->widening != NULL ) {
deallocate(ci, (void*)mi->widening);
mi->widening = NULL;
}
if ( mi->injections != NULL ) {
ByteOffset i;
for(i=0; i<= mi->code_len; i++) {
if ( mi->injections[i].code != NULL ) {
deallocate(ci, (void*)mi->injections[i].code);
mi->injections[i].code = NULL;
}
}
deallocate(ci, (void*)mi->injections);
mi->injections = NULL;
}
ci->current_mi = NULL;
deallocate(ci, (void*)mi);
}
static ByteOffset
input_code_offset(MethodImage *mi)
{
CRW_ASSERT_MI(mi);
return (ByteOffset)(mi->ci->input_position - mi->start_of_input_bytecodes);
}
static void
rewind_to_beginning_of_input_bytecodes(MethodImage *mi)
{
CRW_ASSERT_MI(mi);
mi->ci->input_position = mi->start_of_input_bytecodes;
}
/* Starting at original byte position 'at', add 'offset' to it's new
* location. This may be a negative value.
* NOTE: That this map is not the new bytecode location of the opcode
* but the new bytecode location that should be used when
* a goto or jump instruction was targeting the old bytecode
* location.
*/
static void
adjust_map(MethodImage *mi, ByteOffset at, ByteOffset offset)
{
ByteOffset i;
CRW_ASSERT_MI(mi);
for (i = at; i <= mi->code_len; ++i) {
mi->map[i] += offset;
}
}
static void
widen(MethodImage *mi, ByteOffset at, ByteOffset len)
{
int delta;
CRW_ASSERT(mi->ci, at <= mi->code_len);
delta = len - mi->widening[at];
/* Adjust everything from the current input location by delta */
adjust_map(mi, input_code_offset(mi), delta);
/* Mark at beginning of instruction */
mi->widening[at] = (signed char)len;
}
static void
verify_opc_wide(CrwClassImage *ci, ClassOpcode wopcode)
{
switch (wopcode) {
case JVM_OPC_aload: case JVM_OPC_astore:
case JVM_OPC_fload: case JVM_OPC_fstore:
case JVM_OPC_iload: case JVM_OPC_istore:
case JVM_OPC_lload: case JVM_OPC_lstore:
case JVM_OPC_dload: case JVM_OPC_dstore:
case JVM_OPC_ret: case JVM_OPC_iinc:
break;
default:
CRW_FATAL(ci, "Invalid opcode supplied to wide opcode");
break;
}
}
static unsigned
opcode_length(CrwClassImage *ci, ClassOpcode opcode)
{
/* Define array that holds length of an opcode */
static unsigned char _opcode_length[JVM_OPC_MAX+1] =
JVM_OPCODE_LENGTH_INITIALIZER;
if ( opcode > JVM_OPC_MAX ) {
CRW_FATAL(ci, "Invalid opcode supplied to opcode_length()");
}
return _opcode_length[opcode];
}
/* Walk one instruction and inject instrumentation */
static void
inject_for_opcode(MethodImage *mi)
{
CrwClassImage * ci;
ClassOpcode opcode;
int pos;
CRW_ASSERT_MI(mi);
ci = mi->ci;
pos = input_code_offset(mi);
opcode = readU1(ci);
if (opcode == JVM_OPC_wide) {
ClassOpcode wopcode;
wopcode = readU1(ci);
/* lvIndex not used */
(void)readU2(ci);
verify_opc_wide(ci, wopcode);
if ( wopcode==JVM_OPC_iinc ) {
(void)readU1(ci);
(void)readU1(ci);
}
} else {
ByteCode bytecodes[LARGEST_INJECTION+1];
int header;
int instr_len;
int low;
int high;
int npairs;
ByteOffset len;
/* Get bytecodes to inject before this opcode */
len = before_injection_code(mi, opcode, bytecodes, (int)sizeof(bytecodes));
if ( len > 0 ) {
inject_bytecodes(mi, pos, bytecodes, len);
/* Adjust map after processing this opcode */
}
/* Process this opcode */
switch (opcode) {
case JVM_OPC_tableswitch:
header = NEXT_4BYTE_BOUNDARY(pos);
skip(ci, header - (pos+1));
(void)readU4(ci);
low = readU4(ci);
high = readU4(ci);
skip(ci, (high+1-low) * 4);
break;
case JVM_OPC_lookupswitch:
header = NEXT_4BYTE_BOUNDARY(pos);
skip(ci, header - (pos+1));
(void)readU4(ci);
npairs = readU4(ci);
skip(ci, npairs * 8);
break;
default:
instr_len = opcode_length(ci, opcode);
skip(ci, instr_len-1);
break;
}
/* Get position after this opcode is processed */
pos = input_code_offset(mi);
/* Adjust for any before_injection_code() */
if ( len > 0 ) {
/* Adjust everything past this opcode.
* Why past it? Because we want any jumps to this bytecode loc
* to go to the injected code, not where the opcode
* was moved too.
* Consider a 'return' opcode that is jumped too.
* NOTE: This may not be correct in all cases, but will
* when we are only dealing with non-variable opcodes
* like the return opcodes. Be careful if the
* before_injection_code() changes to include other
* opcodes that have variable length.
*/
adjust_map(mi, pos, len);
}
/* Get bytecodes to inject after this opcode */
len = after_injection_code(mi, opcode, bytecodes, (int)sizeof(bytecodes));
if ( len > 0 ) {
inject_bytecodes(mi, pos, bytecodes, len);
/* Adjust for any after_injection_code() */
adjust_map(mi, pos, len);
}
}
}
/* Map original bytecode location to it's new location. (See adjust_map()). */
static ByteOffset
method_code_map(MethodImage *mi, ByteOffset pos)
{
CRW_ASSERT_MI(mi);
CRW_ASSERT(mi->ci, pos <= mi->code_len);
return mi->map[pos];
}
static int
adjust_instruction(MethodImage *mi)
{
CrwClassImage * ci;
ClassOpcode opcode;
int pos;
int new_pos;
CRW_ASSERT_MI(mi);
ci = mi->ci;
pos = input_code_offset(mi);
new_pos = method_code_map(mi,pos);
opcode = readU1(ci);
if (opcode == JVM_OPC_wide) {
ClassOpcode wopcode;
wopcode = readU1(ci);
/* lvIndex not used */
(void)readU2(ci);
verify_opc_wide(ci, wopcode);
if ( wopcode==JVM_OPC_iinc ) {
(void)readU1(ci);
(void)readU1(ci);
}
} else {
int widened;
int header;
int newHeader;
int low;
int high;
int new_pad;
int old_pad;
int delta;
int new_delta;
int delta_pad;
int npairs;
int instr_len;
switch (opcode) {
case JVM_OPC_tableswitch:
widened = mi->widening[pos];
header = NEXT_4BYTE_BOUNDARY(pos);
newHeader = NEXT_4BYTE_BOUNDARY(new_pos);
skip(ci, header - (pos+1));
delta = readU4(ci);
low = readU4(ci);
high = readU4(ci);
skip(ci, (high+1-low) * 4);
new_pad = newHeader - new_pos;
old_pad = header - pos;
delta_pad = new_pad - old_pad;
if (widened != delta_pad) {
widen(mi, pos, delta_pad);
return 0;
}
break;
case JVM_OPC_lookupswitch:
widened = mi->widening[pos];
header = NEXT_4BYTE_BOUNDARY(pos);
newHeader = NEXT_4BYTE_BOUNDARY(new_pos);
skip(ci, header - (pos+1));
delta = readU4(ci);
npairs = readU4(ci);
skip(ci, npairs * 8);
new_pad = newHeader - new_pos;
old_pad = header - pos;
delta_pad = new_pad - old_pad;
if (widened != delta_pad) {
widen(mi, pos, delta_pad);
return 0;
}
break;
case JVM_OPC_jsr: case JVM_OPC_goto:
case JVM_OPC_ifeq: case JVM_OPC_ifge: case JVM_OPC_ifgt:
case JVM_OPC_ifle: case JVM_OPC_iflt: case JVM_OPC_ifne:
case JVM_OPC_if_icmpeq: case JVM_OPC_if_icmpne: case JVM_OPC_if_icmpge:
case JVM_OPC_if_icmpgt: case JVM_OPC_if_icmple: case JVM_OPC_if_icmplt:
case JVM_OPC_if_acmpeq: case JVM_OPC_if_acmpne:
case JVM_OPC_ifnull: case JVM_OPC_ifnonnull:
widened = mi->widening[pos];
delta = readS2(ci);
if (widened == 0) {
new_delta = method_code_map(mi,pos+delta) - new_pos;
if ((new_delta < -32768) || (new_delta > 32767)) {
switch (opcode) {
case JVM_OPC_jsr: case JVM_OPC_goto:
widen(mi, pos, 2);
break;
default:
widen(mi, pos, 5);
break;
}
return 0;
}
}
break;
case JVM_OPC_jsr_w:
case JVM_OPC_goto_w:
(void)readU4(ci);
break;
default:
instr_len = opcode_length(ci, opcode);
skip(ci, instr_len-1);
break;
}
}
return 1;
}
static void
write_instruction(MethodImage *mi)
{
CrwClassImage * ci;
ClassOpcode opcode;
ByteOffset new_code_len;
int pos;
int new_pos;
CRW_ASSERT_MI(mi);
ci = mi->ci;
pos = input_code_offset(mi);
new_pos = method_code_map(mi,pos);
new_code_len = mi->injections[pos].len;
if (new_code_len > 0) {
write_bytes(ci, (void*)mi->injections[pos].code, new_code_len);
}
opcode = readU1(ci);
if (opcode == JVM_OPC_wide) {
ClassOpcode wopcode;
writeU1(ci, opcode);
wopcode = copyU1(ci);
/* lvIndex not used */
(void)copyU2(ci);
verify_opc_wide(ci, wopcode);
if ( wopcode==JVM_OPC_iinc ) {
(void)copyU1(ci);
(void)copyU1(ci);
}
} else {
ClassOpcode new_opcode;
int header;
int newHeader;
int low;
int high;
int i;
int npairs;
int widened;
int instr_len;
int delta;
int new_delta;
switch (opcode) {
case JVM_OPC_tableswitch:
header = NEXT_4BYTE_BOUNDARY(pos);
newHeader = NEXT_4BYTE_BOUNDARY(new_pos);
skip(ci, header - (pos+1));
delta = readU4(ci);
new_delta = method_code_map(mi,pos+delta) - new_pos;
low = readU4(ci);
high = readU4(ci);
writeU1(ci, opcode);
for (i = new_pos+1; i < newHeader; ++i) {
writeU1(ci, 0);
}
writeU4(ci, new_delta);
writeU4(ci, low);
writeU4(ci, high);
for (i = low; i <= high; ++i) {
delta = readU4(ci);
new_delta = method_code_map(mi,pos+delta) - new_pos;
writeU4(ci, new_delta);
}
break;
case JVM_OPC_lookupswitch:
header = NEXT_4BYTE_BOUNDARY(pos);
newHeader = NEXT_4BYTE_BOUNDARY(new_pos);
skip(ci, header - (pos+1));
delta = readU4(ci);
new_delta = method_code_map(mi,pos+delta) - new_pos;
npairs = readU4(ci);
writeU1(ci, opcode);
for (i = new_pos+1; i < newHeader; ++i) {
writeU1(ci, 0);
}
writeU4(ci, new_delta);
writeU4(ci, npairs);
for (i = 0; i< npairs; ++i) {
unsigned match = readU4(ci);
delta = readU4(ci);
new_delta = method_code_map(mi,pos+delta) - new_pos;
writeU4(ci, match);
writeU4(ci, new_delta);
}
break;
case JVM_OPC_jsr: case JVM_OPC_goto:
case JVM_OPC_ifeq: case JVM_OPC_ifge: case JVM_OPC_ifgt:
case JVM_OPC_ifle: case JVM_OPC_iflt: case JVM_OPC_ifne:
case JVM_OPC_if_icmpeq: case JVM_OPC_if_icmpne: case JVM_OPC_if_icmpge:
case JVM_OPC_if_icmpgt: case JVM_OPC_if_icmple: case JVM_OPC_if_icmplt:
case JVM_OPC_if_acmpeq: case JVM_OPC_if_acmpne:
case JVM_OPC_ifnull: case JVM_OPC_ifnonnull:
widened = mi->widening[pos];
delta = readS2(ci);
new_delta = method_code_map(mi,pos+delta) - new_pos;
new_opcode = opcode;
if (widened == 0) {
writeU1(ci, opcode);
writeU2(ci, new_delta);
} else if (widened == 2) {
switch (opcode) {
case JVM_OPC_jsr:
new_opcode = JVM_OPC_jsr_w;
break;
case JVM_OPC_goto:
new_opcode = JVM_OPC_goto_w;
break;
default:
CRW_FATAL(ci, "unexpected opcode");
break;
}
writeU1(ci, new_opcode);
writeU4(ci, new_delta);
} else if (widened == 5) {
switch (opcode) {
case JVM_OPC_ifeq:
new_opcode = JVM_OPC_ifne;
break;
case JVM_OPC_ifge:
new_opcode = JVM_OPC_iflt;
break;
case JVM_OPC_ifgt:
new_opcode = JVM_OPC_ifle;
break;
case JVM_OPC_ifle:
new_opcode = JVM_OPC_ifgt;
break;
case JVM_OPC_iflt:
new_opcode = JVM_OPC_ifge;
break;
case JVM_OPC_ifne:
new_opcode = JVM_OPC_ifeq;
break;
case JVM_OPC_if_icmpeq:
new_opcode = JVM_OPC_if_icmpne;
break;
case JVM_OPC_if_icmpne:
new_opcode = JVM_OPC_if_icmpeq;
break;
case JVM_OPC_if_icmpge:
new_opcode = JVM_OPC_if_icmplt;
break;
case JVM_OPC_if_icmpgt:
new_opcode = JVM_OPC_if_icmple;
break;
case JVM_OPC_if_icmple:
new_opcode = JVM_OPC_if_icmpgt;
break;
case JVM_OPC_if_icmplt:
new_opcode = JVM_OPC_if_icmpge;
break;
case JVM_OPC_if_acmpeq:
new_opcode = JVM_OPC_if_acmpne;
break;
case JVM_OPC_if_acmpne:
new_opcode = JVM_OPC_if_acmpeq;
break;
case JVM_OPC_ifnull:
new_opcode = JVM_OPC_ifnonnull;
break;
case JVM_OPC_ifnonnull:
new_opcode = JVM_OPC_ifnull;
break;
default:
CRW_FATAL(ci, "Unexpected opcode");
break;
}
writeU1(ci, new_opcode); /* write inverse branch */
writeU2(ci, 3 + 5); /* beyond if and goto_w */
writeU1(ci, JVM_OPC_goto_w); /* add a goto_w */
writeU4(ci, new_delta-3); /* write new and wide delta */
} else {
CRW_FATAL(ci, "Unexpected widening");
}
break;
case JVM_OPC_jsr_w:
case JVM_OPC_goto_w:
delta = readU4(ci);
new_delta = method_code_map(mi,pos+delta) - new_pos;
writeU1(ci, opcode);
writeU4(ci, new_delta);
break;
default:
instr_len = opcode_length(ci, opcode);
writeU1(ci, opcode);
copy(ci, instr_len-1);
break;
}
}
}
static void
method_inject_and_write_code(MethodImage *mi)
{
ByteCode bytecodes[LARGEST_INJECTION+1];
ByteOffset len;
CRW_ASSERT_MI(mi);
/* Do injections */
rewind_to_beginning_of_input_bytecodes(mi);
len = entry_injection_code(mi, bytecodes, (int)sizeof(bytecodes));
if ( len > 0 ) {
int pos;
pos = 0;
inject_bytecodes(mi, pos, bytecodes, len);
/* Adjust pos 0 to map to new pos 0, you never want to
* jump into this entry code injection. So the new pos 0
* will be past this entry_injection_code().
*/
adjust_map(mi, pos, len); /* Inject before behavior */
}
while (input_code_offset(mi) < mi->code_len) {
inject_for_opcode(mi);
}
/* Adjust instructions */
rewind_to_beginning_of_input_bytecodes(mi);
while (input_code_offset(mi) < mi->code_len) {
if (!adjust_instruction(mi)) {
rewind_to_beginning_of_input_bytecodes(mi);
}
}
/* Write new instructions */
rewind_to_beginning_of_input_bytecodes(mi);
while (input_code_offset(mi) < mi->code_len) {
write_instruction(mi);
}
}
static void
copy_attribute(CrwClassImage *ci)
{
int len;
(void)copyU2(ci);
len = copyU4(ci);
copy(ci, len);
}
static void
copy_attributes(CrwClassImage *ci)
{
unsigned i;
unsigned count;
count = copyU2(ci);
for (i = 0; i < count; ++i) {
copy_attribute(ci);
}
}
static void
copy_all_fields(CrwClassImage *ci)
{
unsigned i;
unsigned count;
count = copyU2(ci);
for (i = 0; i < count; ++i) {
/* access, name, descriptor */
copy(ci, 6);
copy_attributes(ci);
}
}
static void
write_line_table(MethodImage *mi)
{
unsigned i;
unsigned count;
CrwClassImage * ci;
CRW_ASSERT_MI(mi);
ci = mi->ci;
(void)copyU4(ci);
count = copyU2(ci);
for(i=0; i<count; i++) {
ByteOffset start_pc;
ByteOffset new_start_pc;
start_pc = readU2(ci);
if ( start_pc == 0 ) {
new_start_pc = 0; /* Don't skip entry injection code. */
} else {
new_start_pc = method_code_map(mi, start_pc);
}
writeU2(ci, new_start_pc);
(void)copyU2(ci);
}
}
/* Used for LocalVariableTable and LocalVariableTypeTable attributes */
static void
write_var_table(MethodImage *mi)
{
unsigned i;
unsigned count;
CrwClassImage * ci;
CRW_ASSERT_MI(mi);
ci = mi->ci;
(void)copyU4(ci);
count = copyU2(ci);
for(i=0; i<count; i++) {
ByteOffset start_pc;
ByteOffset new_start_pc;
ByteOffset length;
ByteOffset new_length;
ByteOffset end_pc;
ByteOffset new_end_pc;
start_pc = readU2(ci);
length = readU2(ci);
if ( start_pc == 0 ) {
new_start_pc = 0; /* Don't skip entry injection code. */
} else {
new_start_pc = method_code_map(mi, start_pc);
}
end_pc = start_pc + length;
new_end_pc = method_code_map(mi, end_pc);
new_length = new_end_pc - new_start_pc;
writeU2(ci, new_start_pc);
writeU2(ci, new_length);
(void)copyU2(ci);
(void)copyU2(ci);
(void)copyU2(ci);
}
}
/* The uoffset field is u2 or u4 depending on the code_len.
* Note that the code_len is likely changing, so be careful here.
*/
static unsigned
readUoffset(MethodImage *mi)
{
if ( mi->code_len > 65535 ) {
return readU4(mi->ci);
}
return readU2(mi->ci);
}
static void
writeUoffset(MethodImage *mi, unsigned val)
{
if ( mi->new_code_len > 65535 ) {
writeU4(mi->ci, val);
}
writeU2(mi->ci, val);
}
static unsigned
copyUoffset(MethodImage *mi)
{
unsigned uoffset;
uoffset = readUoffset(mi);
writeUoffset(mi, uoffset);
return uoffset;
}
/* Copy over verification_type_info structure */
static void
copy_verification_types(MethodImage *mi, int ntypes)
{
/* If there were ntypes, we just copy that over, no changes */
if ( ntypes > 0 ) {
int j;
for ( j = 0 ; j < ntypes ; j++ ) {
unsigned tag;
tag = copyU1(mi->ci);
switch ( tag ) {
case JVM_ITEM_Object:
(void)copyU2(mi->ci); /* Constant pool entry */
break;
case JVM_ITEM_Uninitialized:
/* Code offset for 'new' opcode is for this object */
writeUoffset(mi, method_code_map(mi, read