/* * Print a verification error. */ static void printError(ILImage *image, ILMethod *method, const char *msg) { #ifndef IL_WITHOUT_TOOLS ILDumpMethodType(stdout, image, ILMethod_Signature(method), 0, ILMethod_Owner(method), ILMethod_Name(method), method); #else fputs(ILClass_Name(ILMethod_Owner(method)), stdout); fputs(".", stdout); fputs(ILMethod_Name(method), stdout); #endif fputs(" - ", stdout); fputs(msg, stdout); putc('\n', stdout); }
int _ILVerify(ILCoder *coder, unsigned char **start, ILMethod *method, ILMethodCode *code, int unsafeAllowed, ILExecThread *thread) { TempAllocator allocator; ILCoderExceptions coderExceptions; ILCoderExceptionBlock *coderException; ILCoderExceptionBlock *currentCoderException; int numHandlers; int extraCodeLen; unsigned long *jumpMask; unsigned char *pc; ILUInt32 len; int result; unsigned opcode; ILUInt32 insnSize; int isStatic, isSynchronized; int insnType; ILUInt32 offset = 0; ILEngineStackItem *stack; ILUInt32 stackSize; #ifdef IL_VERIFY_DEBUG const ILOpcodeInfo *insn = 0; #define MAIN_OPCODE_TABLE ILMainOpcodeTable #define PREFIX_OPCODE_TABLE ILPrefixOpcodeTable #else const ILOpcodeSmallInfo *insn = 0; #define MAIN_OPCODE_TABLE ILMainOpcodeSmallTable #define PREFIX_OPCODE_TABLE ILPrefixOpcodeSmallTable #endif ILType *signature; ILType *type; ILUInt32 numArgs; ILUInt32 numLocals; ILType *localVars; int lastWasJump; ILException *exceptions; ILException *exception; int hasRethrow; int tryInlineType; int coderFlags; unsigned int tryInlineOpcode; unsigned char *tryInlinePc; ILUInt32 optimizationLevel; ILBool lastInsnWasPrefix; ILCoderPrefixInfo prefixInfo; #ifdef IL_CONFIG_DEBUG_LINES int haveDebug = ILDebugPresent(ILProgramItem_Image(method)); #else int haveDebug = 0; #endif /* Include local variables that are required by the include files */ #define IL_VERIFY_LOCALS #include "verify_var.c" #include "verify_const.c" #include "verify_arith.c" #include "verify_conv.c" #include "verify_stack.c" #include "verify_ptr.c" #include "verify_obj.c" #include "verify_call.c" #include "verify_branch.c" #include "verify_except.c" #include "verify_ann.c" #undef IL_VERIFY_LOCALS /* Get the exception list */ if(!ILMethodGetExceptions(method, code, &exceptions)) { return 0; } /* Clear the exception management structure */ ILMemZero(&coderExceptions, sizeof(ILCoderExceptions)); /* * Initialize the size of the additional code generated for * synchronization. */ extraCodeLen = 0; /* And set the last label to the code length */ coderExceptions.lastLabel = code->codeLen; /* Initialize the memory allocator that is used for temporary allocation during bytecode verification */ ILMemZero(allocator.buffer, sizeof(allocator.buffer)); allocator.posn = 0; allocator.overflow = 0; coderFlags = ILCoderGetFlags(coder); optimizationLevel = ILCoderGetOptimizationLevel(coder); isStatic = ILMethod_IsStatic(method); isSynchronized = ILMethod_IsSynchronized(method); result = 0; if(exceptions || isSynchronized) { numHandlers = 0; exception = exceptions; while(exception) { ++numHandlers; exception = exception->next; } if(isSynchronized) { /* We'll need an extra try and fault block for synchronization */ ++numHandlers; } /* * Allocate memory for the exception infos. * There might be create 3 coder exception blocks for one * IL exception. * So we allocate memory for the worst case here. */ coderExceptions.blocks = ILCalloc(sizeof(ILCoderExceptionBlock), numHandlers * 3); if(!coderExceptions.blocks) { return 0; } /* Now setup the exception structure */ exception = exceptions; while(exception) { switch(_ILCoderAddExceptionBlock(&coderExceptions, method, exception)) { case IL_CODER_BRANCH_ERR: { VERIFY_BRANCH_ERROR(); } break; case IL_CODER_TYPE_ERR: { VERIFY_TYPE_ERROR(); } break; } exception = exception->next; } /* * Now check if all exception block limits are in the code. */ len = code->codeLen; coderException = coderExceptions.firstBlock; if(coderException) { /* * Check the start offset of the first exception block in the * lowest list. */ if(coderException->startOffset > len) { VERIFY_BRANCH_ERROR(); } /* * Look for the last exception block in the lowest list. */ while(coderException->nextNested) { coderException = coderException->nextNested; } /* * Check the end offset of the last exception block in the * lowest list. * All other exceprion blocks end at or before this offset. */ if(coderException->endOffset > len) { VERIFY_BRANCH_ERROR(); } } if(isSynchronized) { /* * Wrap the whole function in a try block with a fault handler. */ ILException tempException; tempException.flags = IL_META_EXCEPTION_FAULT; tempException.tryOffset = 0; tempException.tryLength = len; tempException.handlerOffset = len; tempException.handlerLength = 1; tempException.extraArg = 0; tempException.userData = 0; tempException.ptrUserData = 0; tempException.next = 0; switch(_ILCoderAddExceptionBlock(&coderExceptions, method, &tempException)) { case IL_CODER_BRANCH_ERR: { VERIFY_BRANCH_ERROR(); } break; case IL_CODER_TYPE_ERR: { VERIFY_TYPE_ERROR(); } break; } extraCodeLen = 2; } } restart: result = 0; labelList = 0; hasRethrow = 0; /* Reset the prefix information */ ILMemZero(&prefixInfo, sizeof(ILCoderPrefixInfo)); /* Allocate the jump target mask */ jumpMask = (unsigned long *)TempAllocate (&allocator, BYTES_FOR_MASK(code->codeLen + extraCodeLen)); if(!jumpMask) { VERIFY_MEMORY_ERROR(); } /* Scan the code looking for all jump targets, and validating that all instructions are more or less valid */ pc = code->code; len = code->codeLen; while(len > 0) { /* Mark this position in the jump mask as an instruction start */ MarkInsnStart(jumpMask, (ILUInt32)(pc - (unsigned char *)(code->code))); /* Fetch the instruction size and type */ opcode = (unsigned)(pc[0]); if(opcode != IL_OP_PREFIX) { /* Regular opcode */ insnSize = (ILUInt32)(MAIN_OPCODE_TABLE[opcode].size); if(len < insnSize) { VERIFY_TRUNCATED(); } insnType = MAIN_OPCODE_TABLE[opcode].args; } else { /* Prefixed opcode */ if(len < 2) { VERIFY_TRUNCATED(); } opcode = (unsigned)(pc[1]); insnSize = (ILUInt32)(PREFIX_OPCODE_TABLE[opcode].size); if(len < insnSize) { VERIFY_TRUNCATED(); } insnType = PREFIX_OPCODE_TABLE[opcode].args; if(opcode == IL_PREFIX_OP_RETHROW) { hasRethrow = 1; } opcode += IL_OP_PREFIX; } /* Determine how to handle this type of instruction */ switch(insnType) { case IL_OPCODE_ARGS_SHORT_JUMP: { /* 8-bit jump offset */ offset = (ILUInt32)((pc + insnSize) - (unsigned char *)(code->code)) + (ILUInt32)(ILInt32)(ILInt8)(pc[1]); if(offset >= code->codeLen) { VERIFY_BRANCH_ERROR(); } MarkJumpTarget(jumpMask, offset); } break; case IL_OPCODE_ARGS_LONG_JUMP: { /* 32-bit jump offset */ offset = (ILUInt32)((pc + insnSize) - (unsigned char *)(code->code)) + (ILUInt32)(IL_READ_INT32(pc + 1)); if(offset >= code->codeLen) { VERIFY_BRANCH_ERROR(); } MarkJumpTarget(jumpMask, offset); } break; case IL_OPCODE_ARGS_SWITCH: { /* Switch statement */ if(len < 5) { VERIFY_TRUNCATED(); } numArgs = IL_READ_UINT32(pc + 1); insnSize = 5 + numArgs * 4; if(numArgs >= 0x20000000 || len < insnSize) { VERIFY_TRUNCATED(); } while(numArgs > 0) { --numArgs; offset = (ILUInt32)((pc + insnSize) - (unsigned char *)(code->code)) + (ILUInt32)(IL_READ_INT32(pc + 5 + numArgs * 4)); if(offset >= code->codeLen) { VERIFY_BRANCH_ERROR(); } MarkJumpTarget(jumpMask, offset); } } break; case IL_OPCODE_ARGS_ANN_DATA: { /* Variable-length annotation data */ if(opcode == IL_OP_ANN_DATA_S) { if(len < 2) { VERIFY_TRUNCATED(); } insnSize = (((ILUInt32)(pc[1])) & 0xFF) + 2; if(len < insnSize) { VERIFY_TRUNCATED(); } } else { if(len < 6) { VERIFY_TRUNCATED(); } insnSize = (IL_READ_UINT32(pc + 2) + 6); if(len < insnSize) { VERIFY_TRUNCATED(); } } } break; case IL_OPCODE_ARGS_ANN_PHI: { /* Variable-length annotation data */ if(len < 3) { VERIFY_TRUNCATED(); } insnSize = ((ILUInt32)IL_READ_UINT16(pc + 1)) * 2 + 3; if(len < insnSize) { VERIFY_TRUNCATED(); } } break; case IL_OPCODE_ARGS_INVALID: { VERIFY_INSN_ERROR(); } break; default: break; } /* Advance to the next instruction */ pc += insnSize; len -= insnSize; } /* Mark the start and end of exception blocks as special jump targets */ numHandlers = 0; while(numHandlers < coderExceptions.numBlocks) { coderException = &(coderExceptions.blocks[numHandlers]); MarkJumpTarget(jumpMask, coderException->startOffset); MarkSpecialJumpTarget(jumpMask, coderException->startOffset); MarkJumpTarget(jumpMask, coderException->endOffset); MarkSpecialJumpTarget(jumpMask, coderException->endOffset); switch(coderException->flags) { case IL_CODER_HANDLER_TYPE_TRY: { /* Nothing to do here */ } break; case IL_CODER_HANDLER_TYPE_CATCH: { /* This is a typed catch block */ classInfo = coderException->un.handlerBlock.exceptionClass; /* * This block will be called with an object of the given * type on the stack. */ SET_TARGET_STACK(coderException->startOffset, classInfo); } break; case IL_CODER_HANDLER_TYPE_FINALLY: case IL_CODER_HANDLER_TYPE_FAULT: { /* This is a finally or fault clause */ /* The clause will be called with nothing on the stack */ SET_TARGET_STACK_EMPTY(coderException->startOffset); } break; case IL_CODER_HANDLER_TYPE_FILTER: case IL_CODER_HANDLER_TYPE_FILTEREDCATCH: { /* This is an exception filter or the corresponding catch block */ /* * The block will be called with an object on the stack, * so record that in the label list for later */ classInfo = ILClassResolveSystem(ILProgramItem_Image(method), 0, "Object", "System"); if(!classInfo) { /* Ran out of memory trying to create "System.Object" */ VERIFY_MEMORY_ERROR(); } SET_TARGET_STACK(coderException->startOffset, classInfo); } break; } ++numHandlers; } /* Make sure that all jump targets are instruction starts */ len = code->codeLen; while(len > 0) { --len; if(IsJumpTarget(jumpMask, len) && !IsInsnStart(jumpMask, len)) { VERIFY_BRANCH_ERROR(); } } /* Create the stack. We need two extra "slop" items to allow for stack expansion during object construction. See "verify_call.c" for further details */ stack = (ILEngineStackItem *)TempAllocate (&allocator, sizeof(ILEngineStackItem) * (code->maxStack + 2)); if(!stack) { VERIFY_MEMORY_ERROR(); } stackSize = 0; /* Get the method signature, plus the number of arguments and locals */ signature = ILMethod_Signature(method); numArgs = ILTypeNumParams(signature); if(ILType_HasThis(signature)) { /* Account for the "this" argument */ ++numArgs; } if(code->localVarSig) { localVars = ILStandAloneSigGetType(code->localVarSig); numLocals = ILTypeNumLocals(localVars); if(ILTypeNeedsInstantiation(localVars) && ILMember_IsGenericInstance(method)) { ILType *classTypeArgs = ILMethodGetClassTypeArguments(method); ILType *methodTypeArgs = ILMethodGetMethodTypeArguments(method); localVars = ILTypeInstantiate(ILImageToContext(ILProgramItem_Image(method)), localVars, classTypeArgs, methodTypeArgs); } } else { localVars = 0; numLocals = 0; } /* Set up the coder to process the method */ if(!ILCoderSetup(coder, start, method, code, &coderExceptions, hasRethrow)) { VERIFY_MEMORY_ERROR(); } if((coderFlags & IL_CODER_FLAG_METHOD_PROFILE) != 0) { ILCoderProfileStart(coder); } /* Verify the code */ pc = code->code; len = code->codeLen; lastWasJump = 0; /* If the method is synchronized then generate the Monitor.Enter call */ if (isSynchronized) { PUSH_SYNC_OBJECT(); ILCoderCallInlineable(coder, IL_INLINEMETHOD_MONITOR_ENTER, 0, 0); } lastInsnWasPrefix = 0; while(len > 0) { /* Fetch the instruction information block */ offset = (ILUInt32)(pc - (unsigned char *)(code->code)); opcode = pc[0]; if(opcode != IL_OP_PREFIX) { insn = &(MAIN_OPCODE_TABLE[opcode]); } else { opcode = pc[1]; insn = &(PREFIX_OPCODE_TABLE[opcode]); opcode += IL_OP_PREFIX; } insnSize = (ILUInt32)(insn->size); /* Is this a jump target? */ if(IsJumpTarget(jumpMask, offset)) { /* Validate the stack information */ VALIDATE_TARGET_STACK(offset); /* Notify the coder of a label at this position */ #ifdef IL_USE_JIT ILCoderStackRefresh(coder, stack, stackSize); ILCoderLabel(coder, offset); #else ILCoderLabel(coder, offset); ILCoderStackRefresh(coder, stack, stackSize); #endif } else if(lastWasJump) { /* An instruction just after an opcode that jumps to somewhere else in the flow of control. As this isn't a jump target, we assume that the stack must be empty at this point. The validate code will ensure that this is checked */ VALIDATE_TARGET_STACK(offset); /* Reset the coder's notion of the stack to empty */ ILCoderStackRefresh(coder, stack, stackSize); } /* Mark this offset if the method has debug information */ if(haveDebug) { ILCoderMarkBytecode(coder, offset); } /* Validate the stack height changes */ if(stackSize < ((ILUInt32)(insn->popped)) || (stackSize - ((ILUInt32)(insn->popped)) + ((ILUInt32)(insn->pushed))) > code->maxStack) { VERIFY_STACK_ERROR(); } /* * Check if all prefix flags are zero, otherwise an invalid prefix * was used for the last instruction. */ if(!lastInsnWasPrefix) { if((prefixInfo.prefixFlags != 0) || (prefixInfo.noFlags != 0)) { VERIFY_PREFIX_ERROR(); } } /* Verify the instruction */ lastWasJump = 0; lastInsnWasPrefix = 0; switch(opcode) { case IL_OP_NOP: break; /* IL breakpoints are ignored - the coder inserts its own breakpoint handlers where required */ case IL_OP_BREAK: break; #define IL_VERIFY_CODE #include "verify_var.c" #include "verify_const.c" #include "verify_arith.c" #include "verify_conv.c" #include "verify_stack.c" #include "verify_ptr.c" #include "verify_obj.c" #include "verify_branch.c" #include "verify_call.c" #include "verify_except.c" #include "verify_ann.c" #undef IL_VERIFY_CODE } /* Advance to the next instruction */ pc += insnSize; len -= insnSize; } /* If the last instruction was not a jump, then the code may fall off the end of the method */ if(!lastWasJump) { VERIFY_INSN_ERROR(); } /* * Generate the code for the fault block for synchronization. */ if(isSynchronized) { coderException = _ILCoderFindExceptionBlock(&coderExceptions, code->codeLen); /* * This check is for catching bugs. */ if(!coderException || ((coderException->flags & IL_CODER_HANDLER_TYPE_FINALLY) == 0)) { VERIFY_BRANCH_ERROR(); } /* * Insert the start label for the fault handler. */ ILCoderLabel(coder, code->codeLen); /* * Call the Monitor.Exit method. */ PUSH_SYNC_OBJECT(); ILCoderCallInlineable(coder, IL_INLINEMETHOD_MONITOR_EXIT, 0, 0); /* * Leave the fault block. */ ILCoderRetFromFinally(coder); /* * Insert the end label for the fault handler. */ ILCoderLabel(coder, code->codeLen + 1); } /* Mark the end of the method */ ILCoderMarkEnd(coder); /* Output the exception handler table, if necessary */ if(coderExceptions.numBlocks > 0) { ILCoderOutputExceptionTable(coder, &coderExceptions); } /* Finish processing using the coder */ result = ILCoderFinish(coder); /* Do we need to restart due to cache exhaustion in the coder? */ if(result == IL_CODER_END_RESTART) { TempAllocatorDestroy(&allocator); /* Reinitialize the memory allocator that is used for temporary allocation during bytecode verification */ ILMemZero(allocator.buffer, sizeof(allocator.buffer)); allocator.posn = 0; allocator.overflow = 0; /* * Reset the userdata in the exception blocks. */ numHandlers = 0; while(numHandlers < coderExceptions.numBlocks) { coderException = &(coderExceptions.blocks[numHandlers]); coderException->userData = 0; coderException->ptrUserData = 0; ++numHandlers; } goto restart; } #ifdef IL_VERIFY_DEBUG else if(result == IL_CODER_END_TOO_BIG) { ILMethodCode code; ILMethodGetCode(method,&code); fprintf(stderr, "%s::%s - method is too big to be converted (%d%s bytes)\n", ILClass_Name(ILMethod_Owner(method)), ILMethod_Name(method), code.codeLen, (code.moreSections != 0) ? "+" : ""); } #endif result = (result == IL_CODER_END_OK); /* Clean up and exit */ cleanup: TempAllocatorDestroy(&allocator); if(exceptions) { ILMethodFreeExceptions(exceptions); } if(coderExceptions.blocks) { ILFree(coderExceptions.blocks); } return result; }
/* * Inner version of "_ILConvertMethod", which detects the type of * exception to throw, but does not throw it. */ static unsigned char *ConvertMethod(ILExecThread *thread, ILMethod *method, int *errorCode, const char **errorInfo) { ILMethodCode code; ILPInvoke *pinv; ILCoder *coder = thread->process->coder; unsigned char *start; void *cif; void *ctorcif; int isConstructor; #ifdef IL_CONFIG_PINVOKE ILModule *module; const char *name; void *moduleHandle; #endif int result; ILInternalInfo fnInfo; ILInternalInfo ctorfnInfo; /* We need the metadata write lock */ METADATA_WRLOCK(thread); /* Handle locked methods while cctors are executed. */ if((start = (unsigned char *)ILCoderHandleLockedMethod(coder, method))) { METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_OK; return start; } /* Is the method already converted? */ if((start = (unsigned char *)(method->userData)) != 0) { METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_OK; return start; } #ifndef IL_CONFIG_VARARGS /* Vararg methods are not supported */ if((ILMethod_CallConv(method) & IL_META_CALLCONV_MASK) == IL_META_CALLCONV_VARARG) { METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_NOT_IMPLEMENTED; return 0; } #endif /* Make sure that we can lay out the method's class */ if(!_ILLayoutClass(_ILExecThreadProcess(thread), ILMethod_Owner(method))) { METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_TYPE_INIT; return 0; } /* Get the method code */ if(!ILMethodGetCode(method, &code)) { code.code = 0; } /* The conversion is different depending upon whether the method is written in IL or not */ if(code.code) { /* Use the bytecode verifier and coder to convert the method */ if(!_ILVerify(coder, &start, method, &code, ILImageIsSecure(ILProgramItem_Image(method)), thread)) { METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_VERIFY_FAILED; return 0; } } else { /* This is a "PInvoke", "internalcall", or "runtime" method */ pinv = ILPInvokeFind(method); fnInfo.func = 0; fnInfo.marshal = 0; ctorfnInfo.func = 0; ctorfnInfo.marshal = 0; isConstructor = ILMethod_IsConstructor(method); switch(method->implementAttrs & (IL_META_METHODIMPL_CODE_TYPE_MASK | IL_META_METHODIMPL_INTERNAL_CALL | IL_META_METHODIMPL_JAVA)) { case IL_META_METHODIMPL_IL: case IL_META_METHODIMPL_OPTIL: { /* If we don't have a PInvoke record, then we don't know what to map this method call to */ if(!pinv) { METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_ENTRY_POINT; return 0; } #ifdef IL_CONFIG_PINVOKE /* Find the module for the PInvoke record */ module = ILPInvoke_Module(pinv); if(!module) { METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_ENTRY_POINT; return 0; } name = ILModule_Name(module); if(!name || *name == '\0') { METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_ENTRY_POINT; return 0; } moduleHandle = LocateExternalModule (thread->process, name, pinv); if(!moduleHandle) { METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_DLL_NOT_FOUND; *errorInfo = name; return 0; } /* Get the name of the function within the module */ name = ILPInvoke_Alias(pinv); if(!name || *name == '\0') { name = ILMethod_Name(method); } #ifdef IL_WIN32_PLATFORM if(!(pinv->member.attributes & IL_META_PINVOKE_NO_MANGLE)) { /* We have to append an A or W to the function */ /* name depending on the characterset used. */ /* On Windows we have only either Ansi or Utf16 */ int nameLength = strlen(name); ILUInt32 charSetUsed = ILPInvokeGetCharSet(pinv, method); char newName[nameLength + 2]; strcpy(newName, name); if(charSetUsed == IL_META_MARSHAL_UTF16_STRING) { newName[nameLength] = 'W'; } else { newName[nameLength] = 'A'; } newName[nameLength + 1] = '\0'; /* Look up the method within the module */ fnInfo.func = ILDynLibraryGetSymbol(moduleHandle, newName); } if(!fnInfo.func) { /* Look up the method within the module */ fnInfo.func = ILDynLibraryGetSymbol(moduleHandle, name); } #else /* !IL_WIN32_PLATFORM */ /* Look up the method within the module */ fnInfo.func = ILDynLibraryGetSymbol(moduleHandle, name); #endif /* !IL_WIN32_PLATFORM */ #else /* !IL_CONFIG_PINVOKE */ METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_NOT_IMPLEMENTED; return 0; #endif /* IL_CONFIG_PINVOKE */ } break; case IL_META_METHODIMPL_RUNTIME: case IL_META_METHODIMPL_IL | IL_META_METHODIMPL_INTERNAL_CALL: { /* "internalcall" and "runtime" methods must not have PInvoke records associated with them */ if(pinv) { METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_VERIFY_FAILED; return 0; } /* Look up the internalcall function details */ if(!_ILFindInternalCall(ILExecThreadGetProcess(thread), method, 0, &fnInfo)) { if(isConstructor) { if(!_ILFindInternalCall(ILExecThreadGetProcess(thread), method, 1, &ctorfnInfo)) { METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_NOT_IMPLEMENTED; return 0; } } else { METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_NOT_IMPLEMENTED; return 0; } } else if(isConstructor) { _ILFindInternalCall(ILExecThreadGetProcess(thread), method, 1, &ctorfnInfo); } } break; default: { /* No idea how to invoke this method */ METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_VERIFY_FAILED; return 0; } /* Not reached */ } /* Bail out if we did not find the underlying native method */ if(!(fnInfo.func) && !(ctorfnInfo.func)) { METADATA_UNLOCK(thread); if(pinv) *errorCode = IL_CONVERT_ENTRY_POINT; else *errorCode = IL_CONVERT_NOT_IMPLEMENTED; return 0; } #if defined(HAVE_LIBFFI) /* Generate a "cif" structure to handle the native call details */ if(fnInfo.func) { /* Make the "cif" structure for the normal method entry */ cif = _ILMakeCifForMethod(_ILExecThreadProcess(thread), method, (pinv == 0)); if(!cif) { METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_OUT_OF_MEMORY; return 0; } } else { cif = 0; } if(ctorfnInfo.func) { /* Make the "cif" structure for the allocating constructor */ ctorcif = _ILMakeCifForConstructor(_ILExecThreadProcess(thread), method, (pinv == 0)); if(!ctorcif) { METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_OUT_OF_MEMORY; return 0; } } else { ctorcif = 0; } #else /* Use the marshalling function pointer as the cif if no libffi */ cif = fnInfo.marshal; ctorcif = ctorfnInfo.marshal; #endif /* Generate the coder stub for marshalling the call */ if(!isConstructor) { /* We only need the method entry point */ if(!ILCoderSetupExtern(coder, &start, method, fnInfo.func, cif, (pinv == 0))) { METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_OUT_OF_MEMORY; return 0; } while((result = ILCoderFinish(coder)) != IL_CODER_END_OK) { /* Do we need a coder restart due to cache overflow? */ if(result != IL_CODER_END_RESTART) { METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_OUT_OF_MEMORY; return 0; } if(!ILCoderSetupExtern(coder, &start, method, fnInfo.func, cif, (pinv == 0))) { METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_OUT_OF_MEMORY; return 0; } } } else { /* We need both the method and constructor entry points */ if(!ILCoderSetupExternCtor(coder, &start, method, fnInfo.func, cif, ctorfnInfo.func, ctorcif, (pinv == 0))) { METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_OUT_OF_MEMORY; return 0; } while((result = ILCoderFinish(coder)) != IL_CODER_END_OK) { /* Do we need a coder restart due to cache overflow? */ if(result != IL_CODER_END_RESTART) { METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_OUT_OF_MEMORY; return 0; } if(!ILCoderSetupExternCtor(coder, &start, method, fnInfo.func, cif, ctorfnInfo.func, ctorcif, (pinv == 0))) { METADATA_UNLOCK(thread); *errorCode = IL_CONVERT_OUT_OF_MEMORY; return 0; } } } } /* The method is converted now */ /* Run the needed cctors and unlock the metadata too */ ILCoderRunCCtors(coder, start); *errorCode = IL_CONVERT_OK; return start; }
/* * Dump all Java instructions in a given buffer. Returns zero * if there is something wrong with the buffer's format. */ static int DumpJavaInstructions(ILImage *image, ILClass *classInfo, FILE *outstream, unsigned char *buf, unsigned long size, unsigned long addr, ILException *clauses, int flags) { ILUInt32 *jumpPoints; int result = 0; unsigned char *temp; unsigned char *temp2; unsigned long tsize; unsigned long offset; unsigned long dest; unsigned long isize; unsigned long args; const ILOpcodeInfo *info; int argType; unsigned long numItems; unsigned long item; int isWide; /* Allocate a helper array to mark jump points within the code */ jumpPoints = (ILUInt32 *)ILCalloc(((size + 3) & ~3), 1); if(!jumpPoints) { fprintf(stderr, "out of memory\n"); exit(1); } /* Mark the entry point to the method so we get a label for it */ jumpPoints[0] |= (ILUInt32)1; /* Mark the position of exception clauses */ while(clauses != 0) { MarkDest(clauses->tryOffset); MarkDest(clauses->tryOffset + clauses->tryLength); MarkDest(clauses->handlerOffset); clauses = clauses->next; } /* Scan the instruction list to locate jump points */ temp = buf; tsize = size; offset = 0; while(tsize > 0) { isize = JavaInsnSize(temp, tsize, offset); if(!isize) { fprintf(outstream, "\t\t// unknown instruction 0x%02X\n", ((int)(temp[0])) & 0xFF); goto cleanup; } info = &(ILJavaOpcodeTable[((int)(temp[0])) & 0xFF]); if(info->args == IL_OPCODE_ARGS_SHORT_JUMP) { dest = (unsigned long)(((long)offset) + (long)(IL_BREAD_INT16(temp + 1))); MarkDest(dest); } else if(info->args == IL_OPCODE_ARGS_LONG_JUMP) { dest = (unsigned long)(((long)offset) + (long)(IL_BREAD_INT32(temp + 1))); MarkDest(dest); } else if(info->args == IL_OPCODE_ARGS_SWITCH) { /* Align the switch instruction's arguments */ args = 1; while(((offset + args) & 3) != 0) { ++args; } /* Mark the default label */ dest = (unsigned long)(((long)offset) + (long)(IL_BREAD_INT32(temp + args))); MarkDest(dest); /* Process the bulk of the switch */ if(temp[0] == JAVA_OP_TABLESWITCH) { /* Mark all of the labels in a table-based switch */ item = (unsigned long)(IL_BREAD_INT32(temp + args + 8) - IL_BREAD_INT32(temp + args + 4) + 1); temp2 = temp + args + 12; while(item > 0) { dest = (unsigned long)(((long)offset) + (long)(IL_BREAD_INT32(temp2))); MarkDest(dest); --item; temp2 += 4; } } else { /* Mark all of the labels in a lookup-based switch */ item = (unsigned long)(IL_BREAD_UINT32(temp + args + 4)); temp2 = temp + args + 8 + 4; while(item > 0) { dest = (unsigned long)(((long)offset) + (long)(IL_BREAD_INT32(temp2))); MarkDest(dest); --item; temp2 += 8; } } } offset += isize; temp += isize; tsize -= isize; } /* Dump the instructions */ temp = buf; tsize = size; offset = 0; while(tsize > 0) { /* If this is a jump point, then print a label for it */ if((jumpPoints[offset / 32] & (ILUInt32)(1L << (offset % 32))) != 0) { fprintf(outstream, "\t?L%lx:\n", offset + addr); } /* Extract the instruction from the method input stream */ isize = JavaInsnSize(temp, tsize, offset); info = &(ILJavaOpcodeTable[((int)(temp[0])) & 0xFF]); if(*temp == JAVA_OP_WIDE) { /* Process a wide instruction */ info = &(ILJavaOpcodeTable[((int)(temp[1])) & 0xFF]); isWide = 1; args = 2; } else { /* Process an ordinary instruction */ isWide = 0; args = 1; } /* Dump the instruction based on its argument type */ argType = info->args; putc('\t', outstream); putc('\t', outstream); fputs(info->name, outstream); if(argType != IL_OPCODE_ARGS_INVALID && argType != IL_OPCODE_ARGS_NONE) { numItems = (unsigned long)(strlen(info->name)); while(numItems < 10) { putc(' ', outstream); ++numItems; } putc(' ', outstream); } switch(argType) { case IL_OPCODE_ARGS_INVALID: break; case IL_OPCODE_ARGS_NONE: break; case IL_OPCODE_ARGS_INT8: { fprintf(outstream, "%d", (int)(ILInt8)(temp[args])); } break; case IL_OPCODE_ARGS_INT16: { fprintf(outstream, "%d", (int)(ILInt16)(IL_BREAD_UINT16(temp + args))); } break; case IL_OPCODE_ARGS_TOKEN: case IL_OPCODE_ARGS_CALL: { /* An instruction that takes a constant pool entry argument */ if(temp[0] == JAVA_OP_LDC) { item = (unsigned long)(temp[args]); } else { item = (unsigned long)(IL_BREAD_UINT16(temp + args)); } switch(ILJavaGetConstType(classInfo, (ILUInt32)item)) { case JAVA_CONST_UTF8: { const char *str; ILUInt32 len; str = ILJavaGetUTF8String (classInfo, (ILUInt32)item, &len); if(str && len) { ILDumpStringLen(outstream, str, (int)len); } else { fputs("\"\"", outstream); } } break; case JAVA_CONST_INTEGER: { ILInt32 value; if(ILJavaGetInteger(classInfo, (ILUInt32)item, &value)) { fprintf(outstream, "%ld", (long)value); } else { fputs("??", outstream); } } break; case JAVA_CONST_FLOAT: { ILFloat value; unsigned char buf[4]; if(ILJavaGetFloat(classInfo, (ILUInt32)item, &value)) { IL_WRITE_FLOAT(buf, value); fprintf(outstream, "float32(0x%02X%02X%02X%02X)", (((int)(buf[3])) & 0xFF), (((int)(buf[2])) & 0xFF), (((int)(buf[1])) & 0xFF), (((int)(buf[0])) & 0xFF)); } else { fputs("??", outstream); } } break; case JAVA_CONST_LONG: { ILInt64 value; if(ILJavaGetLong(classInfo, (ILUInt32)item, &value)) { fprintf(outstream, "0x%08lX%08lX", (long)((value >> 32) & (long)0xFFFFFFFF), (long)(value & (long)0xFFFFFFFF)); } else { fputs("??", outstream); } } break; case JAVA_CONST_DOUBLE: { ILDouble value; unsigned char buf[8]; if(ILJavaGetDouble(classInfo, (ILUInt32)item, &value)) { IL_WRITE_DOUBLE(buf, value); fprintf(outstream, "float64(0x%02X%02X%02X%02X%02X%02X%02X%02X)", (((int)(buf[7])) & 0xFF), (((int)(buf[6])) & 0xFF), (((int)(buf[5])) & 0xFF), (((int)(buf[4])) & 0xFF), (((int)(buf[3])) & 0xFF), (((int)(buf[2])) & 0xFF), (((int)(buf[1])) & 0xFF), (((int)(buf[0])) & 0xFF)); } else { fputs("??", outstream); } } break; case JAVA_CONST_CLASS: { ILClass *ref; ref = ILJavaGetClass(classInfo, (ILUInt32)item, 1); if(ref) { ILDumpClassName(outstream, image, ref, flags); } else { fputs("??", outstream); } } break; case JAVA_CONST_STRING: { const char *str; ILUInt32 len; str = ILJavaGetString(classInfo, (ILUInt32)item, &len); if(str && len) { ILDumpStringLen(outstream, str, (int)len); } else { fputs("\"\"", outstream); } } break; case JAVA_CONST_FIELDREF: { ILField *field; ILClass *info; field = ILJavaGetField(classInfo, (ILUInt32)item, 1, (temp[0] == JAVA_OP_GETSTATIC || temp[0] == JAVA_OP_PUTSTATIC)); if(field) { ILDumpType(outstream, image, ILField_Type(field), flags); putc(' ', outstream); info = ILField_Owner(field); if(ILClassIsValueType(info)) { fputs("valuetype ", outstream); } else { fputs("class ", outstream); } ILDumpClassName(outstream, image, info, flags); fputs("::", outstream); ILDumpIdentifier(outstream, ILField_Name(field), 0, flags); } else { fputs("??", outstream); } } break; case JAVA_CONST_METHODREF: { ILMethod *method; method = ILJavaGetMethod(classInfo, (ILUInt32)item, 1, (temp[0] == JAVA_OP_INVOKESTATIC)); if(method) { ILDumpMethodType(outstream, image, ILMethod_Signature(method), flags, ILMethod_Owner(method), ILMethod_Name(method), method); } else { fputs("??", outstream); } } break; default: { fputs("??", outstream); } break; } } break; case IL_OPCODE_ARGS_NEW: { /* "newarray" instruction */ switch(temp[args]) { case JAVA_ARRAY_OF_BOOL: { fputs("bool", outstream); } break; case JAVA_ARRAY_OF_CHAR: { fputs("char", outstream); } break; case JAVA_ARRAY_OF_FLOAT: { fputs("float32", outstream); } break; case JAVA_ARRAY_OF_DOUBLE: { fputs("float64", outstream); } break; case JAVA_ARRAY_OF_BYTE: { fputs("int8", outstream); } break; case JAVA_ARRAY_OF_SHORT: { fputs("int16", outstream); } break; case JAVA_ARRAY_OF_INT: { fputs("int32", outstream); } break; case JAVA_ARRAY_OF_LONG: { fputs("int64", outstream); } break; default: { fprintf(outstream, "%d", ((int)(temp[args])) & 0xFF); } break; } } break; case IL_OPCODE_ARGS_SHORT_VAR: { if(!isWide) { fprintf(outstream, "%d", ((int)(temp[args])) & 0xFF); } else { fprintf(outstream, "%d", ((int)(IL_BREAD_UINT16(temp + args)))); } } break; case IL_OPCODE_ARGS_SHORT_JUMP: { dest = (unsigned long)(((long)offset) + (long)(IL_BREAD_INT16(temp + 1))); fprintf(outstream, "?L%lx", dest + addr); } break; case IL_OPCODE_ARGS_LONG_JUMP: { dest = (unsigned long)(((long)offset) + (long)(IL_BREAD_UINT32(temp + 1))); fprintf(outstream, "?L%lx", dest + addr); } break; case IL_OPCODE_ARGS_CALLI: { /* Dump a call to an interface method */ ILMethod *method; item = (unsigned long)(IL_BREAD_UINT16(temp + args)); method = ILJavaGetMethod(classInfo, (ILUInt32)item, 1, 0); if(method) { ILDumpMethodType(outstream, image, ILMethod_Signature(method), flags, ILMethod_Owner(method), ILMethod_Name(method), method); } else { fputs("??", outstream); } fprintf(outstream, " %d", (int)(ILUInt8)(temp[args + 2])); } break; case IL_OPCODE_ARGS_SWITCH: { /* Align the switch instruction's arguments */ while(((offset + args) & 3) != 0) { ++args; } /* Output the default label */ dest = (unsigned long)(((long)offset) + (long)(IL_BREAD_INT32(temp + args))); fprintf(outstream, "?L%lx (", dest + addr); /* Dump the bulk of the switch instruction */ if(temp[0] == JAVA_OP_TABLESWITCH) { /* Dump the base value for the table-based switch */ fprintf(outstream, "%ld : ", (long)(IL_BREAD_INT32(temp + args + 4))); /* Determine the number of items */ numItems = (unsigned long) (IL_BREAD_INT32(temp + args + 8) - IL_BREAD_INT32(temp + args + 4) + 1); /* Dump the labels for the items */ temp2 = temp + args + 12; for(item = 0; item < numItems; ++item) { if(item != 0) { putc(',', outstream); putc(' ', outstream); } dest = (unsigned long)(((long)offset) + (long)(IL_BREAD_INT32(temp2))); fprintf(outstream, "?L%lx", dest + addr); temp2 += 4; } } else { /* Lookup-based switch instruction */ numItems = (unsigned long) (IL_BREAD_UINT32(temp + args + 4)); temp2 = temp + args + 8; for(item = 0; item < numItems; ++item) { if(item != 0) { putc(',', outstream); putc(' ', outstream); } fprintf(outstream, "%ld : ", (long)(IL_BREAD_INT32(temp2))); dest = (unsigned long)(((long)offset) + (long)(IL_BREAD_INT32(temp2 + 4))); fprintf(outstream, "?L%lx", dest + addr); temp2 += 8; } } /* Terminate the switch instruction */ putc(')', outstream); } break; case IL_OPCODE_ARGS_ANN_ARG: { /* Used to indicate the "iinc" instruction */ if(!isWide) { fprintf(outstream, "%d, %d", ((int)(temp[args])) & 0xFF, ((int)((ILInt8)(temp[args + 1])))); } else { fprintf(outstream, "%d, %d", ((int)(IL_BREAD_UINT16(temp + args))), ((int)(IL_BREAD_INT16(temp + args + 2)))); } } break; default: break; }
void *_ILMakeCifForConstructor(ILExecProcess *process, ILMethod *method, int isInternal) { ILType *signature = ILMethod_Signature(method); ILUInt32 numArgs; ILUInt32 numParams; ffi_cif *cif; ffi_type **args; ffi_type *rtype; ILUInt32 arg; ILUInt32 param; /* Determine the number of argument blocks that we need */ numArgs = numParams = ILTypeNumParams(signature); if(isInternal) { /* This is an "internalcall" or "runtime" method which needs an extra argument for the thread */ ++numArgs; } /* Allocate space for the cif */ cif = (ffi_cif *)ILMalloc(sizeof(ffi_cif) + sizeof(ffi_type *) * numArgs); if(!cif) { return 0; } args = ((ffi_type **)(cif + 1)); /* The return value is always a pointer, indicating the object that was just allocated by the constructor */ rtype = &ffi_type_pointer; /* Convert the argument types */ arg = 0; if(isInternal) { /* Pointer argument for the thread */ args[arg++] = &ffi_type_pointer; } for(param = 1; param <= numParams; ++param) { args[arg++] = TypeToFFI(process, ILTypeGetEnumType (ILTypeGetParam(signature, param)), isInternal); } /* Limit the number of arguments if we cannot use raw mode */ if(!_ILCVMCanUseRawCalls(method, isInternal) && numArgs > (CVM_MAX_NATIVE_ARGS + 1)) { numArgs = CVM_MAX_NATIVE_ARGS + 1; } /* Prepare the "ffi_cif" structure for the call */ if(ffi_prep_cif(cif, FFI_DEFAULT_ABI, numArgs, rtype, args) != FFI_OK) { fprintf(stderr, "Cannot marshal a type in the definition of %s::%s\n", ILClass_Name(ILMethod_Owner(method)), ILMethod_Name(method)); return 0; } /* Ready to go */ return (void *)cif; }