| /* |
| * 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 |