blob: fcf0be365f2480dfad9126e658ca42d80fa95dbf [file] [log] [blame]
/*
* Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <stdlib.h>
#include <string.h>
#include "jni_tools.h"
#include "jvmti_tools.h"
#include "Injector.h"
/* ========================================================================== */
/* Opcode Lengths */
static const u1 opcLengths[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 3, /* 0- 19 */
3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20- 39 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, /* 40- 59 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60- 79 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80- 99 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 100-119 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, /* 120-139 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, /* 140-159 */
3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 0, 0, 1, 1, 1, 1, 1, 1, 3, 3, /* 160-179 */
3, 3, 3, 3, 3, 5, 5, 3, 2, 3, 1, 1, 3, 3, 1, 1, 0, 4, 3, 3, /* 180-199 */
5, 5, 1 /* 200- */
};
static const int GROWTH_FACTOR = 2;
static const char* codeAttributeName = "Code";
static const char* lineNumberAttributeName = "LineNumberTable";
static const char* localVarAttributeName = "LocalVariableTable";
static const char* localVarTypeAttributeName = "LocalVariableTypeTable";
static const char* stackMapAttributeName= "StackMapTable";
static u2 codeAttributeIndex;
static u2 lineNumberAttributeIndex;
static u2 localVarAttributeIndex;
static u2 localVarTypeAttributeIndex;
static u2 stackMapAttributeIndex;
static const u1 SAME_BEGIN = 0;
static const u1 SAME_END = 63;
static const u1 SAME_LOCALS_1_STACK_ITEM_BEGIN = 64;
static const u1 SAME_LOCALS_1_STACK_ITEM_END = 127;
//Tags in the range [128-246] are reserved for future use.
static const u1 SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247;
static const u1 CHOP_BEGIN = 248;
static const u1 CHOP_END = 250;
static const u1 SAME_FRAME_EXTENDED = 251;
static const u1 APPEND_BEGIN = 252;
static const u1 APPEND_END = 254;
static const u1 FULL_FRAME = 255;
static const u1 ITEM_Object = 7;
static const u1 ITEM_Uninitialized = 8;
static u2 stackFrameOffset = 0;
static int mode;
static const u1* orig;
static u1* gen;
static u1* inputPos;
static const u1* endPos;
static u1* genPos;
static u1* markPos;
static char** constantPool;
static u2 constantPoolSize;
static u2 constantPoolCount;
static u1 callBytes[] = { opc_invokestatic, 0, 0 };
static u1 allocBytes[] = { opc_invokestatic, 0, 0 };
static u1 zeroBytes[] = { 0, 0, 0 };
static u4 codeLength;
static u4* map;
static jbyte* widening;
/* ========================================================================== */
#ifdef __cplusplus
extern "C" {
#endif
static u1 get_u1() {
return *inputPos++;
}
static u2 get_u2() {
u1* p = inputPos;
inputPos += 2;
return (u2)p[1] | ((u2)p[0]<<8);
}
static u4 get_u4() {
u1* p = inputPos;
inputPos += 4;
return (u4)p[3] | ((u4)p[2]<<8) | ((u4)p[1]<<16) | ((u4)p[0]<<24);
}
static void put_u1(u1 v) {
*genPos++ = v;
}
static void put_u2(u2 v) {
*genPos++ = (u1)(v>>8);
*genPos++ = (u1)(v & 0xFF);
}
static void put_u4(u4 v) {
*genPos++ = (u1)(v>>24);
*genPos++ = (u1)(v>>16);
*genPos++ = (u1)(v>>8);
*genPos++ = (u1)(v & 0xFF);
}
static void set_u4(u1* pos, u4 v) {
*pos++ = (u1)(v>>24);
*pos++ = (u1)(v>>16);
*pos++ = (u1)(v>>8);
*pos++ = (u1)(v & 0xFF);
}
static u1 copy_u1() {
u1 v = get_u1();
put_u1(v);
return v;
}
static u2 copy_u2() {
u2 v = get_u2();
put_u2(v);
return v;
}
static u4 copy_u4() {
u4 v = get_u4();
put_u4(v);
return v;
}
static void copy(int count) {
memcpy(genPos, inputPos, count);
inputPos += count;
genPos += count;
}
static void skip(int count) {
inputPos += count;
}
static void get(u1* bytes, int count) {
memcpy(bytes, inputPos, count);
inputPos += count;
}
static void put(u1* bytes, int count) {
memcpy(genPos, bytes, count);
genPos += count;
}
static void markLocalPositionStart() {
markPos = inputPos;
}
static u4 localPosition() {
return (u4) (inputPos - markPos);
}
void recallPosition() {
inputPos = markPos;
}
static u4 generatedPosition() {
return (u4) (genPos - gen);
}
static void randomAccessWriteU2(int pos, u2 v) {
gen[pos] = (u1)(v>>8);
gen[pos+1] = (u1)(v & 0xFF);
}
static void randomAccessWriteU4(int pos, u4 v) {
gen[pos] = (u1)(v>>24);
gen[pos+1] = (u1)(v>>16);
gen[pos+2] = (u1)(v>>8);
gen[pos+3] = (u1)(v & 0xFF);
}
static int copyConstantPool(u2 constantPoolCount) {
u2 i;
u2 len;
char* utf8;
constantPoolSize = constantPoolCount;
NSK_DISPLAY1("copying ConstantPool: %d\n", constantPoolSize);
constantPool = (char**) malloc(constantPoolSize * sizeof(char*));
if (!NSK_VERIFY(constantPool != NULL)) {
NSK_COMPLAIN0("out of memory\n");
return NSK_FALSE;
}
memset(constantPool, 0, constantPoolSize * sizeof(char*));
codeAttributeIndex = 0;
lineNumberAttributeIndex = 0;
localVarAttributeIndex = 0;
localVarTypeAttributeIndex = 0;
stackMapAttributeIndex = 0;
for (i = 1; i < constantPoolSize; i++) {
u1 tag = copy_u1();
switch (tag) {
case CONSTANT_Class:
case CONSTANT_String:
case CONSTANT_MethodType:
copy(2);
break;
case CONSTANT_MethodHandle:
copy(3);
break;
case CONSTANT_Fieldref:
case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref:
case CONSTANT_Integer:
case CONSTANT_Float:
case CONSTANT_NameAndType:
case CONSTANT_InvokeDynamic:
copy(4);
break;
case CONSTANT_Long:
case CONSTANT_Double:
copy(8);
i++; /* takes two CP entries */
break;
case CONSTANT_Utf8:
len = copy_u2();
utf8 = (char*) malloc(len + 1);
if (!NSK_VERIFY(utf8 != NULL)) {
NSK_COMPLAIN0("out of memory\n");
return NSK_FALSE;
}
get((u1*) utf8, len);
utf8[len] = 0;
constantPool[i] = utf8;
if (strcmp(utf8, codeAttributeName) == 0) {
codeAttributeIndex = i;
} else if (strcmp(utf8, lineNumberAttributeName) == 0) {
lineNumberAttributeIndex = i;
} else if (strcmp(utf8, localVarAttributeName) == 0) {
localVarAttributeIndex = i;
} else if (strcmp(utf8, localVarTypeAttributeName) == 0) {
localVarTypeAttributeIndex = i;
} else if (strcmp(utf8, stackMapAttributeName) == 0) {
stackMapAttributeIndex = i;
}
put((u1*) utf8, len);
break;
default:
NSK_COMPLAIN2("%d unexpected constant pool tag: %d\n", i, tag);
return NSK_FALSE;
}
}
return NSK_TRUE;
}
static void freeConstantPool() {
u2 i;
for (i = 1; i < constantPoolSize; i++) {
if (constantPool[i] != NULL) {
free(constantPool[i]);
}
}
free(constantPool);
}
/* ========================================================================== */
/* Copy the exception table for this method code */
static void copyExceptionTable() {
u2 tableLength;
u2 i;
tableLength = copy_u2();
NSK_DISPLAY1("ExceptionTable length: %d\n", tableLength);
for (i = tableLength; i > 0; i--) {
put_u2((u2) map[get_u2()]); /* start_pc */
put_u2((u2) map[get_u2()]); /* end_pc */
put_u2((u2) map[get_u2()]); /* handler_pc */
copy(2); /* catch_type */
}
}
/* Copy the line number table for this method code */
static void copyLineNumberAttr() {
u2 tableLength;
u2 i;
copy(4); /* attr len */
tableLength = copy_u2();
NSK_DISPLAY1("LineNumberTable length: %d\n", tableLength);
for (i = tableLength; i > 0; i--) {
put_u2((u2) map[get_u2()]); /* start_pc */
copy(2); /* line_number */
}
}
/* Copy the local variable table for this method code */
static void copyLocalVarAttr() {
u2 tableLength;
u2 startPC;
u2 i;
copy(4); /* attr len */
tableLength = copy_u2();
NSK_DISPLAY1("LocalVariableTable length: %d\n", tableLength);
for (i = tableLength; i > 0; i--) {
startPC = get_u2();
put_u2((u2) map[startPC]); /* start_pc */
put_u2((u2) (map[startPC + get_u2()] - map[startPC])); /* length */
copy(6); /* name_index, descriptor_index, index */
}
}
/* Copy the local variable type table for this method code */
static void copyLocalVarTypeAttr() {
u2 tableLength;
u2 startPC;
u2 i;
copy(4); /* attr len */
tableLength = copy_u2();
NSK_DISPLAY1("LocalVariableTypeTable length: %d\n", tableLength);
for (i = tableLength; i > 0; i--) {
startPC = get_u2();
put_u2((u2) map[startPC]); /* start_pc */
put_u2((u2) (map[startPC + get_u2()] - map[startPC])); /* length */
copy(6); /* name_index, signature_index, index */
}
}
static u2 calculateOffsetDelta(u2 frameNumber, u2 frameOffsetDelta) {
u2 oldOffset;
u2 newOffset;
if(frameNumber == 0) {
stackFrameOffset = frameOffsetDelta;
return (u2) map[stackFrameOffset];
} else {
oldOffset = (u2) map[stackFrameOffset];
stackFrameOffset = stackFrameOffset + frameOffsetDelta + 1;
newOffset = (u2) map[stackFrameOffset - 1];
return newOffset - oldOffset;
}
}
static void copyVerificationTypeInfo(u2 count) {
u2 i;
u2 offset;
u1 tag;
for(i=0; i<count; i++) {
tag = get_u1();
put_u1(tag);
if(tag == ITEM_Object) {
copy_u2();
} else if (tag == ITEM_Uninitialized) {
copy_u2();
offset = get_u2();
put_u2((u2)map[offset]);
}
}
}
static void copyStackMapAttr() {
u2 number_of_entries;
u2 i;
u4 len;
unsigned int frame_type;
u2 frameOffsetDelta;
u2 number_of_stack_items;
u2 number_of_locals;
u1* lenPtr = genPos;
len=copy_u4(); /* attr len */
number_of_entries = copy_u2();
for(i=0; i<number_of_entries; i++) {
frame_type = get_u1();
if((frame_type >= SAME_BEGIN) && (frame_type <= SAME_END)) {
// same_frame {
// u1 frame_type = SAME; /* 0-63 */
// }
put_u1(SAME_FRAME_EXTENDED);
put_u2(calculateOffsetDelta(i, (u2) frame_type));
} else if((frame_type >= SAME_LOCALS_1_STACK_ITEM_BEGIN) && (frame_type<=SAME_LOCALS_1_STACK_ITEM_END)) {
// same_locals_1_stack_item_frame {
// u1 frame_type = SAME_LOCALS_1_STACK_ITEM;/* 64-127 */
// verification_type_info stack[1];
// }
put_u1(SAME_LOCALS_1_STACK_ITEM_EXTENDED);
put_u2(calculateOffsetDelta(i, (u2) (frame_type-64)));
copyVerificationTypeInfo(1);
// Tags in the range [128-246] are reserved for future use.
} else if(frame_type == SAME_LOCALS_1_STACK_ITEM_EXTENDED) {
// same_locals_1_stack_item_frame_extended {
// u1 frame_type = SAME_LOCALS_1_STACK_ITEM_EXTENDED; /* 247 */
// u2 offset_delta;
// verification_type_info stack[1];
// }
put_u1(SAME_LOCALS_1_STACK_ITEM_EXTENDED);
frameOffsetDelta = get_u2();
put_u2(calculateOffsetDelta(i, frameOffsetDelta));
copyVerificationTypeInfo(1);
} else if((frame_type >= CHOP_BEGIN) && (frame_type <= CHOP_END)) {
// chop_frame {
// u1 frame_type = CHOP; /* 248-250 */
// u2 offset_delta;
// }
put_u1((u1)frame_type);
frameOffsetDelta = get_u2();
put_u2(calculateOffsetDelta(i, frameOffsetDelta));
} else if(frame_type == SAME_FRAME_EXTENDED) {
// same_frame_extended {
// u1 frame_type = SAME_FRAME_EXTENDED; /* 251 */
// u2 offset_delta;
// }
put_u1(SAME_FRAME_EXTENDED);
frameOffsetDelta = get_u2();
put_u2(calculateOffsetDelta(i, frameOffsetDelta));
} else if((frame_type >= APPEND_BEGIN) && (frame_type <= APPEND_END)) {
// append_frame {
// u1 frame_type = APPEND; /* 252-254 */
// u2 offset_delta;
// verification_type_info locals[frame_type - 251];
// }
put_u1((u1)frame_type);
frameOffsetDelta = get_u2();
put_u2(calculateOffsetDelta(i, frameOffsetDelta));
copyVerificationTypeInfo((u1)(frame_type - 251));
} else if(frame_type == FULL_FRAME) {
// sfull_frame {
// u1 frame_type = FULL_FRAME; /* 255 */
// u2 offset_delta;
// u2 number_of_locals;
// verification_type_info locals[number_of_locals];
// u2 number_of_stack_items;
// verification_type_info stack[number_of_stack_items];
//}
put_u1(FULL_FRAME);
frameOffsetDelta = get_u2();
put_u2(calculateOffsetDelta(i, frameOffsetDelta));
number_of_locals = copy_u2();
copyVerificationTypeInfo(number_of_locals);
number_of_stack_items = copy_u2();
copyVerificationTypeInfo(number_of_stack_items);
}
}
set_u4(lenPtr,(u4)((genPos-lenPtr) - 4));
}
/* ========================================================================== */
static void injectBytes(u4 at, u4 len) {
u4 i;
NSK_DISPLAY2("Injecting %d bytes at %d\n", len, at);
for (i = at; i <= codeLength; i++) {
map[i] += len;
}
}
static void widen(u4 at, jbyte len) {
u4 i;
jbyte delta = len - widening[at];
NSK_DISPLAY2("Widening to %d bytes at %d\n", len, at);
/* mark at beginning of instruction */
widening[at] = len;
/* inject at end of instruction */
for (i = localPosition(); i <= codeLength; i++) {
map[i] += delta;
}
}
/* ========================================================================== */
/**
* Walk one instruction writing the transformed instruction.
*/
static void writeInstruction() {
u4 pos = localPosition();
u4 newPos = map[pos];
u1 opcode = get_u1();
switch (opcode) {
case opc_wide:
put_u1(opcode);
copy(copy_u1() == opc_iinc ? 4 : 2);
break;
case opc_new:
case opc_newarray:
case opc_anewarray:
case opc_multianewarray:
put_u1(opcode);
copy(opcLengths[opcode] - 1);
if (mode == BCI_MODE_ALLOC) {
put(allocBytes, 3);
}
break;
case opc_jsr_w:
case opc_goto_w:
put_u1(opcode);
put_u4(map[pos + get_u4()] - newPos);
break;
case opc_jsr:
case opc_goto:
case opc_ifeq:
case opc_ifge:
case opc_ifgt:
case opc_ifle:
case opc_iflt:
case opc_ifne:
case opc_if_icmpeq:
case opc_if_icmpne:
case opc_if_icmpge:
case opc_if_icmpgt:
case opc_if_icmple:
case opc_if_icmplt:
case opc_if_acmpeq:
case opc_if_acmpne:
case opc_ifnull:
case opc_ifnonnull: {
u1 newOpcode = opcode;
jbyte widened = widening[pos];
if (widened == 0) { /* not widened */
put_u1(opcode);
put_u2((u2) (map[pos + (jshort) get_u2()] - newPos));
} else if (widened == 2) { /* wide form */
if (opcode == opc_jsr) {
newOpcode = opc_jsr_w;
} else if (opcode == opc_jsr) {
newOpcode = opc_goto_w;
} else {
NSK_COMPLAIN1("unexpected opcode: %d\n", opcode);
}
put_u1(newOpcode);
put_u4(map[pos + (jshort) get_u2()] - newPos);
} else if (widened == 5) { /* insert goto_w */
switch (opcode) {
case opc_ifeq:
newOpcode = opc_ifne;
break;
case opc_ifge:
newOpcode = opc_iflt;
break;
case opc_ifgt:
newOpcode = opc_ifle;
break;
case opc_ifle:
newOpcode = opc_ifgt;
break;
case opc_iflt:
newOpcode = opc_ifge;
break;
case opc_ifne:
newOpcode = opc_ifeq;
break;
case opc_if_icmpeq:
newOpcode = opc_if_icmpne;
break;
case opc_if_icmpne:
newOpcode = opc_if_icmpeq;
break;
case opc_if_icmpge:
newOpcode = opc_if_icmplt;
break;
case opc_if_icmpgt:
newOpcode = opc_if_icmple;
break;
case opc_if_icmple:
newOpcode = opc_if_icmpgt;
break;
case opc_if_icmplt:
newOpcode = opc_if_icmpge;
break;
case opc_if_acmpeq:
newOpcode = opc_if_acmpne;
break;
case opc_if_acmpne:
newOpcode = opc_if_acmpeq;
break;
case opc_ifnull:
newOpcode = opc_ifnonnull;
break;
case opc_ifnonnull:
newOpcode = opc_ifnull;
break;
default:
NSK_COMPLAIN1("unexpected opcode: %d\n", opcode);
break;
}
put_u1(newOpcode); /* write inverse branch */
put_u1(3 + 5); /* beyond if and goto_w */
put_u1(opc_goto_w); /* add a goto_w */
put_u4(map[pos + (jshort) get_u2()] - newPos);
} else {
NSK_COMPLAIN2("unexpected widening: %d, pos=0x%x\n",
widened, pos);
}
break;
}
case opc_tableswitch: {
u4 i, low, high;
put_u1(opcode);
/* skip old padding */
skip(((pos+4) & (~3)) - (pos+1));
/* write new padding */
put(zeroBytes, ((newPos+4) & (~3)) - (newPos+1));
put_u4(map[pos + get_u4()] - newPos);
low = copy_u4();
high = copy_u4();
for (i = low; i <= high; i++) {
put_u4(map[pos + get_u4()] - newPos);
}
break;
}
case opc_lookupswitch: {
u4 i, npairs;
put_u1(opcode);
/* skip old padding */
skip(((pos+4) & (~3)) - (pos+1));
/* write new padding */
put(zeroBytes, ((newPos+4) & (~3)) - (newPos+1));
put_u4(map[pos + get_u4()] - newPos);
npairs = copy_u4();
for (i = npairs; i > 0; i--) {
copy_u4();
put_u4(map[pos + get_u4()] - newPos);
}
break;
}
default:
put_u1(opcode);
copy(opcLengths[opcode] - 1);
break;
}
}
/* ========================================================================== */
/**
* Walk one instruction adjusting for insertions
*/
static int adjustInstruction() {
u4 pos = localPosition();
u4 newPos = map[pos];
u1 opcode = get_u1();
switch (opcode) {
case opc_wide:
skip(get_u1() == opc_iinc ? 4 : 2);
break;
case opc_jsr:
case opc_goto:
case opc_ifeq:
case opc_ifge:
case opc_ifgt:
case opc_ifle:
case opc_iflt:
case opc_ifne:
case opc_if_icmpeq:
case opc_if_icmpne:
case opc_if_icmpge:
case opc_if_icmpgt:
case opc_if_icmple:
case opc_if_icmplt:
case opc_if_acmpeq:
case opc_if_acmpne:
case opc_ifnull:
case opc_ifnonnull: {
jbyte widened = widening[pos];
if (widened == 0) { /* not yet widened */
jint delta = (jshort) get_u2();
u4 target = pos + delta;
u4 newTarget = map[target];
jint newDelta = newTarget - newPos;
if ((newDelta < -32768) || (newDelta > 32767)) {
if ((opcode == opc_jsr) || (opcode == opc_goto)) {
widen(pos, 2); /* will convert to wide */
} else {
widen(pos, 5); /* will inject goto_w */
}
return NSK_FALSE; /* cause restart */
}
}
break;
}
case opc_tableswitch: {
jbyte widened = widening[pos];
u4 low;
jbyte deltaPadding;
/* skip old padding and default */
skip(((pos+4) & (~3)) - (pos+1) + 4);
low = get_u4();
skip((get_u4() - low + 1) * 4);
deltaPadding = ((newPos+4) & (~3)) - newPos - ((pos+4) & (~3)) + pos;
if (widened != deltaPadding) {
widen(pos, deltaPadding);
return NSK_FALSE; /* cause restart */
}
break;
}
case opc_lookupswitch: {
jbyte widened = widening[pos];
jbyte deltaPadding;
/* skip old padding and default */
skip(((pos+4) & (~3)) - (pos+1) + 4);
skip(get_u4() * 8);
deltaPadding = ((newPos+4) & (~3)) - newPos - ((pos+4) & (~3)) + pos;
if (widened != deltaPadding) {
widen(pos, deltaPadding);
return NSK_FALSE; /* cause restart */
}
break;
}
default:
skip(opcLengths[opcode] - 1);
break;
}
return NSK_TRUE;
}
/* ========================================================================== */
/**
* Walk one instruction inserting instrumentation at specified instructions
*/
static void insertAtInstruction() {
u4 pos = localPosition();
u1 opcode = get_u1();
switch (opcode) {
case opc_wide:
/* no support for instrumenting wide instructions */
skip(get_u1() == opc_iinc ? 4 : 2);
break;
case opc_new:
case opc_newarray:
case opc_anewarray:
case opc_multianewarray:
skip(opcLengths[opcode] - 1);
injectBytes(localPosition(), 3);
break;
case opc_tableswitch:
/* skip 4-byte boundry padding and default */
skip(((pos+4) & (~3)) - (pos+1) + 4);
{
u4 low = get_u4();
skip((get_u4() - low + 1) * 4);
}
break;
case opc_lookupswitch:
/* skip 4-byte boundry padding and default */
skip(((pos+4) & (~3)) - (pos+1) + 4);
skip(get_u4() * 8);
break;
default:
skip(opcLengths[opcode] - 1);
break;
}
}
static void adjustOffsets() {
recallPosition();
if (mode == BCI_MODE_CALL) {
/* instrument calls - method entry */
injectBytes(0, 3);
}
if (mode == BCI_MODE_ALLOC) {
/* instrument allocations */
while (localPosition() < codeLength) {
insertAtInstruction();
}
recallPosition();
}
NSK_DISPLAY0("Searching for adjustments...\n");
while (localPosition() < codeLength) {
if (!adjustInstruction()) {
recallPosition();
NSK_DISPLAY0("Restarting adjustments after change...\n");
}
}
NSK_DISPLAY0("Writing new code...\n");
recallPosition();
if (mode == BCI_MODE_CALL) {
put(callBytes, 3);
}
while (localPosition() < codeLength) {
writeInstruction();
}
}
/* ========================================================================== */
static void copyAttr() {
u4 len;
copy(2);
len = copy_u4();
NSK_DISPLAY1("attr len: %d\n", len);
copy(len);
}
static void copyAttrs(u2 attrCount) {
u2 i;
for (i = attrCount; i > 0; i--) {
copyAttr();
}
}
static void copyFields() {
u2 count;
u2 attrCount;
u2 i;
count = copy_u2();
NSK_DISPLAY1("fields count: %d\n", count);
for (i = count; i > 0; i--) {
/* access, name, descriptor */
copy(2 + 2 + 2);
attrCount = copy_u2();
NSK_DISPLAY1("field attrCount: %d\n", attrCount);
copyAttrs(attrCount);
}
}
static void copyAttrForCode() {
u2 nameIndex = copy_u2();
/* check for Code attr */
if (nameIndex == lineNumberAttributeIndex) {
copyLineNumberAttr();
} else if (nameIndex == localVarAttributeIndex) {
copyLocalVarAttr();
} else if (nameIndex == localVarTypeAttributeIndex) {
copyLocalVarTypeAttr();
} else if (nameIndex == stackMapAttributeIndex) {
copyStackMapAttr();
} else {
u4 len = copy_u4();
NSK_DISPLAY1("code attr len: %d\n", len);
copy(len);
}
}
static void copyCodeAttr(char* name) {
u4 attrLengthPos;
u4 attrLength;
u4 newAttrLength;
u4 codeLengthPos;
u4 newCodeLength;
u2 attrCount;
u4 i;
attrLengthPos = generatedPosition();
attrLength = copy_u4();
NSK_DISPLAY2("Code attr found: %s, pos=0x%x\n", name,
inputPos - orig - 6);
/* max_stack, max_locals */
copy(2 + 2);
codeLengthPos = generatedPosition();
codeLength = copy_u4();
if (codeLength == 0) {
NSK_COMPLAIN0("code_length must be greater than zero\n");
return;
}
if (mode == BCI_MODE_EMCP) {
/* copy remainder minus already copied */
copy(attrLength - 8);
return;
}
markLocalPositionStart();
map = (u4*) malloc((codeLength + 1) * sizeof(u4));
for (i = 0; i <= codeLength; i++) {
map[i] = i;
}
widening = (jbyte*) malloc(codeLength + 1);
memset(widening, 0, codeLength + 1);
adjustOffsets();
/* fix up code length */
newCodeLength = generatedPosition() - (codeLengthPos + 4);
randomAccessWriteU4(codeLengthPos, newCodeLength);
NSK_DISPLAY2("code length old: %d, new: %d\n",
codeLength, newCodeLength);
copyExceptionTable();
attrCount = copy_u2();
for (i = attrCount; i > 0; i--) {
copyAttrForCode();
}
free(map);
free(widening);
/* fix up attr length */
newAttrLength = generatedPosition() - (attrLengthPos + 4);
randomAccessWriteU4(attrLengthPos, newAttrLength);
NSK_DISPLAY2("attr length old: %d, new: %d\n",
attrLength, newAttrLength);
}
static void copyAttrForMethod(char* name) {
u2 nameIndex;
nameIndex = copy_u2();
if (nameIndex == codeAttributeIndex) {
copyCodeAttr(name);
} else {
u4 len = copy_u4();
NSK_DISPLAY1("method attr len: %d\n", len);
copy(len);
}
}
static void copyMethod() {
u2 accessFlags;
u2 methodNameIdx;
char* name;
u2 attrCount;
u2 i;
accessFlags = copy_u2();
methodNameIdx = copy_u2();
name = constantPool[methodNameIdx];
/* descriptor */
copy(2);
attrCount = copy_u2();
NSK_DISPLAY1("method attrCount: %d\n", attrCount);
for (i = attrCount; i > 0; i--) {
copyAttrForMethod(name);
}
}
static void copyMethods() {
u2 count;
u2 i;
count = copy_u2();
NSK_DISPLAY1("methods count: %d\n", count);
for (i = count; i > 0; i--) {
copyMethod();
}
}
static u2 writeCPEntryUtf8(char* str) {
u2 i;
u2 len = (u2) strlen(str);
put_u1(CONSTANT_Utf8);
put_u2(len);
for (i = 0; i < len; i++) {
put_u1(str[i]);
}
return constantPoolCount++;
}
static u2 writeCPEntryClass(u2 classNameIndex) {
put_u1(CONSTANT_Class);
put_u2(classNameIndex);
return constantPoolCount++;
}
static u2 writeCPEntryNameAndType(u2 nameIndex, u2 descrIndex) {
put_u1(CONSTANT_NameAndType);
put_u2(nameIndex);
put_u2(descrIndex);
return constantPoolCount++;
}
static u2 writeCPEntryMethodRef(u2 classIndex, u2 nameAndTypeIndex) {
put_u1(CONSTANT_Methodref);
put_u2(classIndex);
put_u2(nameAndTypeIndex);
return constantPoolCount++;
}
static u2 writeCPEntryFieldRef(u2 classIndex, u2 nameAndTypeIndex) {
put_u1(CONSTANT_Fieldref);
put_u2(classIndex);
put_u2(nameAndTypeIndex);
return constantPoolCount++;
}
static u2 addFieldToConstantPool(u2 classIndex, char* fieldName, char* descr) {
u2 fieldNameIndex = writeCPEntryUtf8(fieldName);
u2 descrIndex = writeCPEntryUtf8(descr);
u2 nameAndTypeIndex = writeCPEntryNameAndType(fieldNameIndex, descrIndex);
u2 fieldIndex = writeCPEntryFieldRef(classIndex, nameAndTypeIndex);
return fieldIndex;
}
static u2 addMethodToConstantPool(u2 classIndex, char* methodName, char* descr) {
u2 methodNameIndex = writeCPEntryUtf8(methodName);
u2 descrIndex = writeCPEntryUtf8(descr);
u2 nameAndTypeIndex = writeCPEntryNameAndType(methodNameIndex, descrIndex);
u2 methodIndex = writeCPEntryMethodRef(classIndex, nameAndTypeIndex);
return methodIndex;
}
static u2 addClassToConstantPool(char* className) {
u2 classNameIndex = writeCPEntryUtf8(className);
u2 classIndex = writeCPEntryClass(classNameIndex);
return classIndex;
}
/* ========================================================================== */
int Inject(const u1* old_bytes, const jint old_length,
u1** new_bytes, jint* new_length, int bci_mode) {
u4 constantPoolCountPos;
u2 profiler;
u2 interfaceCount;
u2 attrCount;
//printf("inject\n");
NSK_DISPLAY3("Injecting bytecodes: mode=%d, bytes=0x%p, len=%d\n",
bci_mode, old_bytes, old_length);
mode = bci_mode;
orig = old_bytes;
inputPos = (u1*) orig;
endPos = orig + old_length;
gen = (u1*) malloc(old_length * GROWTH_FACTOR);
if (!NSK_VERIFY(gen != NULL)) {
NSK_COMPLAIN0("out of memory\n");
return NSK_FALSE;
}
genPos = gen;
/* magic + minor/major version */
copy(4 + 2 + 2);
constantPoolCountPos = generatedPosition();
constantPoolCount = copy_u2();
/* copy old constant pool */
if (!copyConstantPool(constantPoolCount)) {
return NSK_FALSE;
}
NSK_DISPLAY1("ConstantPool expanded from: %d\n", constantPoolCount);
profiler = addClassToConstantPool("nsk/share/jvmti/ProfileCollector");
if (mode == BCI_MODE_ALLOC) {
u2 allocTracker =
addMethodToConstantPool(profiler, "allocTracker", "()V");
allocBytes[1] = (u1) (allocTracker >> 8);
allocBytes[2] = (u1) (allocTracker & 0xFF);
}
if (mode == BCI_MODE_CALL) {
u2 callTracker =
addMethodToConstantPool(profiler, "callTracker", "()V");
callBytes[1] = (u1) (callTracker >> 8);
callBytes[2] = (u1) (callTracker & 0xFF);
}
/* access, this, super */
copy(2 + 2 + 2);
interfaceCount = copy_u2();
NSK_DISPLAY1("interfaceCount: %d\n", interfaceCount);
copy(interfaceCount * 2);
copyFields();
copyMethods();
attrCount = copy_u2();
NSK_DISPLAY1("class attrCount: %d\n", attrCount);
copyAttrs(attrCount);
randomAccessWriteU2(constantPoolCountPos, constantPoolCount);
NSK_DISPLAY1("New constant pool size: %d\n", constantPoolCount);
*new_length = (jint) (genPos - gen);
*new_bytes = realloc(gen, *new_length);
freeConstantPool();
return NSK_TRUE;
}
/* ========================================================================== */
#ifdef __cplusplus
}
#endif