/* * Create the slots for the declared local variables. * Returns zero if out of memory. */ static int _ILJitLocalSlotsCreateLocals(ILJITCoder *jitCoder, ILJitLocalSlots *localSlots, ILStandAloneSig *localVarSig) { if(localVarSig) { ILType *signature; ILType *type; ILJitType jitType; ILUInt32 num; ILUInt32 current; /* Determine the number of locals to allocate */ if(!(signature = ILStandAloneSigGetType(localVarSig))) { return 0; } num = ILTypeNumLocals(signature); /* Allocate the "jitLocals" array for the variables */ _ILJitLocalsAlloc(*localSlots, num); /* Create the jit values for the local variables */ for(current = 0; current < num; ++current) { ILJitLocalSlot *local = &_ILJitLocalSlotFromSlots(*localSlots, current); if(!(type = ILTypeGetLocal(signature, current))) { return 0; } if(!(jitType = _ILJitGetLocalsType(type, jitCoder->process))) { return 0; } if(!(local->value = jit_value_create(jitCoder->jitFunction, jitType))) { return 0; } local->flags = 0; local->refValue = 0; } /* Record the number of used locals in the local slots. */ localSlots->numSlots = num; } else { /* Set the number of used locals to 0. */ localSlots->numSlots = 0; } return 1; }
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; }