/* helper for find_vm_areas_via_probe() and get_memory_info_from_os() * returns the passed-in pc if the probe was successful; else, returns * where the next probe should be (to skip DR memory). * if the probe was successful, returns in prot the results. */ static app_pc probe_address(dcontext_t *dcontext, app_pc pc_in, byte *our_heap_start, byte *our_heap_end, OUT uint *prot) { app_pc base; size_t size; app_pc pc = (app_pc)ALIGN_BACKWARD(pc_in, PAGE_SIZE); ASSERT(ALIGNED(pc, PAGE_SIZE)); ASSERT(prot != NULL); *prot = MEMPROT_NONE; /* skip our own vmheap */ if (pc >= our_heap_start && pc < our_heap_end) return our_heap_end; /* if no vmheap and we probe our own stack, the SIGSEGV handler will * report stack overflow as it checks that prior to handling TRY */ if (is_stack_overflow(dcontext, pc)) return pc + PAGE_SIZE; #ifdef VMX86_SERVER /* Workaround for PR 380621 */ if (is_vmkernel_addr_in_user_space(pc, &base)) { LOG(GLOBAL, LOG_VMAREAS, 4, "%s: skipping vmkernel region " PFX "-" PFX "\n", __func__, pc, base); return base; } #endif /* Only for find_vm_areas_via_probe(), skip modules added by * dl_iterate_get_areas_cb. Subsequent probes are about gettting * info from OS, so do the actual probe. See PR 410907. */ if (!dynamo_initialized && get_memory_info(pc, &base, &size, prot)) return base + size; TRY_EXCEPT(dcontext, /* try */ { PROBE_READ_PC(pc); *prot |= MEMPROT_READ; }, /* except */ { /* nothing: just continue */ });
/** * @param classRecord Record for method class. * @param methodRecord Calle's method record. * @param retAddr What the PC should be upon return. * @return true iff the stack frame was pushed. */ boolean dispatch_special (MethodRecord *methodRecord, byte *retAddr) { #if DEBUG_METHODS int debug_ctr; #endif StackFrame *stackFrame; byte newStackFrameIndex; #if DEBUG_BYTECODE printf ("\n------ dispatch special - %d ------------------\n\n", methodRecord->signatureId); #endif #if DEBUG_METHODS printf ("dispatch_special: %d, %d\n", (int) methodRecord, (int) retAddr); printf ("-- signature id = %d\n", methodRecord->signatureId); printf ("-- code offset = %d\n", methodRecord->codeOffset); printf ("-- flags = %d\n", methodRecord->mflags); printf ("-- num params = %d\n", methodRecord->numParameters); printf ("-- stack ptr = %d\n", (int) get_stack_ptr()); printf ("-- max stack ptr= %d\n", (int) (currentThread->stackArray + (get_array_size(currentThread->stackArray))*2)); #endif pop_words (methodRecord->numParameters); pc = retAddr; if (is_native (methodRecord)) { #if DEBUG_METHODS printf ("-- native\n"); #endif dispatch_native (methodRecord->signatureId, get_stack_ptr() + 1); // Stack frame not pushed return false; } newStackFrameIndex = currentThread->stackFrameArraySize; if (newStackFrameIndex >= get_array_length((Object *) word2ptr (currentThread->stackFrameArray))) { #if !FIXED_STACK_SIZE // int len = get_array_length((Object *) word2ptr (currentThread->stackFrameArray)); int newlen = get_array_length((Object *) word2ptr (currentThread->stackFrameArray)) * 3 / 2; JINT newStackFrameArray = JNULL; // Stack frames are indexed by a byte value so limit the size. if (newlen <= 255) { // increase the stack frame size newStackFrameArray = ptr2word(reallocate_array(word2ptr(currentThread->stackFrameArray), newlen)); } // If can't allocate new stack, give in! if (newStackFrameArray == JNULL) { #endif throw_exception (stackOverflowError); return false; #if !FIXED_STACK_SIZE } // Assign new array currentThread->stackFrameArray = newStackFrameArray; #endif } if (newStackFrameIndex == 0) { // Assign NEW stack frame stackFrame = stackframe_array(); } else { #if DEBUG_METHODS for (debug_ctr = 0; debug_ctr < methodRecord->numParameters; debug_ctr++) printf ("-- param[%d] = %ld\n", debug_ctr, (long) get_stack_ptr()[debug_ctr+1]); #endif // Save OLD stackFrame state stackFrame = stackframe_array() + (newStackFrameIndex - 1); update_stack_frame (stackFrame); // Push NEW stack frame stackFrame++; } // Increment size of stack frame array currentThread->stackFrameArraySize++; // Initialize rest of new stack frame stackFrame->methodRecord = methodRecord; stackFrame->monitor = null; stackFrame->localsBase = get_stack_ptr() + 1; // Initialize auxiliary global variables (registers) pc = get_code_ptr(methodRecord); #if DEBUG_METHODS printf ("pc set to 0x%X\n", (int) pc); #endif init_sp (stackFrame, methodRecord); update_constant_registers (stackFrame); //printf ("m %d stack = %d\n", (int) methodRecord->signatureId, (int) (localsBase - stack_array())); // Check for stack overflow // (stackTop + methodRecord->maxOperands) >= (stack_array() + STACK_SIZE); if (is_stack_overflow (methodRecord)) { #if !FIXED_STACK_SIZE StackFrame *stackBase; int i; // Need at least this many bytes // int len = (int)(stackTop + methodRecord->maxOperands) - (int)(stack_array()) - HEADER_SIZE; // Need to compute new array size (as distinct from number of bytes in array). int newlen = (((int)(stackTop + methodRecord->maxOperands) - (int)(stack_array()) - HEADER_SIZE + 1) / 4) * 3 / 2; JINT newStackArray = ptr2word(reallocate_array(word2ptr(currentThread->stackArray), newlen)); // If can't allocate new stack, give in! if (newStackArray == JNULL) { #endif throw_exception (stackOverflowError); return false; #if !FIXED_STACK_SIZE } // Adjust pointers. newlen = newStackArray - currentThread->stackArray; stackBase = stackframe_array(); stackTop = word2ptr(ptr2word(stackTop) + newlen); localsBase = word2ptr(ptr2word(localsBase) + newlen); #if DEBUG_MEMORY printf("thread=%d, stackTop(%d), localsBase(%d)=%d\n", currentThread->threadId, (int)stackTop, (int)localsBase, (int)(*localsBase)); #endif for (i=currentThread->stackFrameArraySize-1; i >= 0; i--) { stackBase[i].localsBase = word2ptr(ptr2word(stackBase[i].localsBase) + newlen); stackBase[i].stackTop = word2ptr(ptr2word(stackBase[i].stackTop) + newlen); #if DEBUG_MEMORY printf("stackBase[%d].localsBase(%d) = %d\n", i, (int)stackBase[i].localsBase, (int)(*stackBase[i].localsBase)); #endif } // Assign new array currentThread->stackArray = newStackArray; #endif } return true; }
/** * @param classRecord Record for method class. * @param methodRecord Calle's method record. * @param retAddr What the PC should be upon return. * @return true iff the stack frame was pushed. */ boolean dispatch_special (MethodRecord *methodRecord, byte *retAddr) { /** * Note: This code is a little tricky, particularly when used with * a garbage collector. It manipulates the stack frame and in some cases * may need to perform memory allocation. In all cases we must take care * to ensure that if an allocation can be made then any live objects * on the stack must be below the current stack pointer. * In addition to the above we take great care so that this function can * be restarted (to allow us to wait for available memory). To enable this * we avoid making any commitments to changes to global state until both * stacks have been commited. */ #if DEBUG_METHODS int debug_ctr; #endif Object *stackFrameArray; StackFrame *stackFrame; StackFrame *stackBase; int newStackFrameIndex; STACKWORD *newStackTop; #if DEBUG_BYTECODE printf("call method %d ret %x\n", methodRecord - get_method_table(get_class_record(0)), retAddr); printf ("\n------ dispatch special - %d ------------------\n\n", methodRecord->signatureId); #endif #if DEBUG_METHODS printf ("dispatch_special: %d, %d\n", (int) methodRecord, (int) retAddr); printf ("-- signature id = %d\n", methodRecord->signatureId); printf ("-- code offset = %d\n", methodRecord->codeOffset); printf ("-- flags = %d\n", methodRecord->mflags); printf ("-- num params = %d\n", methodRecord->numParameters); //printf ("-- stack ptr = %d\n", (int) get_stack_ptr()); //printf ("-- max stack ptr= %d\n", (int) (currentThread->stackArray + (get_array_size(currentThread->stackArray))*2)); #endif // First deal with the easy case of a native call... if (is_native (methodRecord)) { #if DEBUG_METHODS printf ("-- native\n"); #endif // WARNING: Once the instruction below has been executed we may have // references on the stack that are above the stack pointer. If a GC // gets run when in this state the reference may get collected as // grabage. This means that any native functions that take a reference // parameter and that may end up allocating memory *MUST* protect that // reference before calling the allocator... pop_words_cur (methodRecord->numParameters); switch(dispatch_native (methodRecord->signatureId, get_stack_ptr_cur() + 1)) { case EXEC_RETRY: // Need to re-start the instruction, so reset the state of the stack curStackTop += methodRecord->numParameters; break; case EXEC_CONTINUE: // Normal completion return to the requested point. curPc = retAddr; break; case EXEC_RUN: // We are running new code, curPc will be set. Nothing to do. break; case EXEC_EXCEPTION: // An exception has been thrown. The PC will be set correctly and // the stack may have been adjusted... break; } // Stack frame not pushed return false; } // Now start to build the new stack frames. We start by placing the // the new stack pointer below any params. The params will become locals // in the new frame. newStackTop = get_stack_ptr_cur() - methodRecord->numParameters; newStackFrameIndex = (int)(byte)currentThread->stackFrameIndex; if (newStackFrameIndex >= 255) { throw_new_exception (JAVA_LANG_STACKOVERFLOWERROR); return false; } #if DEBUG_METHODS //for (debug_ctr = 0; debug_ctr < methodRecord->numParameters; debug_ctr++) // printf ("-- param[%d] = %ld\n", debug_ctr, (long) get_stack_ptr()[debug_ctr+1]); #endif stackFrameArray = ref2obj(currentThread->stackFrameArray); stackBase = (StackFrame *)array_start(stackFrameArray); // Setup OLD stackframe ready for return stackFrame = stackBase + (newStackFrameIndex); stackFrame->stackTop = newStackTop; stackFrame->pc = retAddr; // Push NEW stack frame // Increment size of stack frame array but do not commit to it until we have // completely built both new stacks. newStackFrameIndex++; stackFrame++; if (((byte *)stackFrame - (byte *)stackBase) >= get_array_length(stackFrameArray)) { #if FIXED_STACK_SIZE throw_new_exception (JAVA_LANG_STACKOVERFLOWERROR); return false; #else if (expand_call_stack(currentThread) < 0) return false; stackFrame = (StackFrame *)array_start(currentThread->stackFrameArray) + newStackFrameIndex; #endif } // Initialize rest of new stack frame stackFrame->methodRecord = methodRecord; stackFrame->monitor = null; stackFrame->localsBase = newStackTop + 1; // Allocate space for locals etc. newStackTop = init_sp(stackFrame, methodRecord); stackFrame->stackTop = newStackTop; currentThread->stackFrameIndex = newStackFrameIndex; // Check for stack overflow if (is_stack_overflow (newStackTop, methodRecord)) { #if FIXED_STACK_SIZE throw_new_exception (JAVA_LANG_STACKOVERFLOWERROR); return false; #else if (expand_value_stack(currentThread, methodRecord->maxOperands+methodRecord->numLocals) < 0) { currentThread->stackFrameIndex--; return false; } // NOTE at this point newStackTop is no longer valid! newStackTop = stackFrame->stackTop; #endif } // All set. So now we can finally commit to the new stack frames update_constant_registers (stackFrame); curStackTop = newStackTop; // and jump to the start of the new code curPc = get_code_ptr(methodRecord); return true; }
static void general_signal_handler(int signum, siginfo_t* info, void* context) { Registers regs; if (!context) return; // Convert OS context to Registers port_thread_context_to_regs(®s, (ucontext_t*)context); void* fault_addr = info ? info->si_addr : NULL; // Check if SIGSEGV is produced by port_read/write_memory port_tls_data_t* tlsdata = get_private_tls_data(); if (tlsdata && tlsdata->violation_flag) { tlsdata->violation_flag = 0; regs.set_ip(tlsdata->restart_address); return; } if (!tlsdata) // Tread is not attached - attach thread temporarily { int res; tlsdata = (port_tls_data_t*)STD_MALLOC(sizeof(port_tls_data_t)); if (tlsdata) // Try to attach the thread res = port_thread_attach_local(tlsdata, TRUE, TRUE, 0); if (!tlsdata || res != 0) { // Can't process correctly; perform default actions if (FLAG_DBG) { bool result = gdb_crash_handler(®s); _exit(-1); // Exit process if not sucessful... } if (FLAG_CORE && signum != SIGABRT) // SIGABRT can't be rethrown { signal(signum, SIG_DFL); // setup default handler return; } _exit(-1); } // SIGSEGV can represent SO which can't be processed out of signal handler if (signum == SIGSEGV && // This can occur only when a user set an alternative stack is_stack_overflow(tlsdata, fault_addr)) { int result = port_process_signal(PORT_SIGNAL_STACK_OVERFLOW, ®s, fault_addr, FALSE); if (result == 0) { if (port_thread_detach_temporary() == 0) STD_FREE(tlsdata); return; } if (result > 0) tlsdata->debugger = TRUE; else { if (FLAG_CORE) { // Rethrow crash to generate core signal(signum, SIG_DFL); // setup default handler return; } _exit(-1); } } } if (tlsdata->debugger) { bool result = gdb_crash_handler(®s); _exit(-1); // Exit process if not sucessful... } if (signum == SIGABRT && // SIGABRT can't be trown again from c_handler FLAG_DBG) { // So attaching GDB right here bool result = gdb_crash_handler(®s); _exit(-1); // Exit process if not sucessful... } if (signum == SIGSEGV && is_stack_overflow(tlsdata, fault_addr)) { // Second SO while previous SO is not processed yet - is GPF if (tlsdata->restore_guard_page) tlsdata->restore_guard_page = FALSE; else { // To process signal on protected stack area port_thread_clear_guard_page(); // Note: the call above does not disable alternative stack // It can't be made while we are on alternative stack // Alt stack will be disabled explicitly in c_handler() tlsdata->restore_guard_page = TRUE; } } // Prepare registers for transfering control out of signal handler void* callback = (void*)&c_handler; port_set_longjump_regs(callback, ®s, 3, ®s, (void*)(size_t)signum, fault_addr); // Convert prepared Registers back to OS context port_thread_regs_to_context((ucontext_t*)context, ®s); // Return from signal handler to go to C handler }