/*++ Function: RtlpRaiseException Parameters: ExceptionRecord - the Windows exception record to throw Note: The name of this function and the name of the ExceptionRecord parameter is used in the sos lldb plugin code to read the exception record. See coreclr\src\ToolBox\SOS\lldbplugin\debugclient.cpp. --*/ PAL_NORETURN static void RtlpRaiseException(EXCEPTION_RECORD *ExceptionRecord) { // Capture the context of RtlpRaiseException. CONTEXT ContextRecord; ZeroMemory(&ContextRecord, sizeof(CONTEXT)); ContextRecord.ContextFlags = CONTEXT_FULL; CONTEXT_CaptureContext(&ContextRecord); // Find the caller of RtlpRaiseException. PAL_VirtualUnwind(&ContextRecord, NULL); // The frame we're looking at now is RaiseException. We have to unwind one // level further to get the actual context user code could be resumed at. PAL_VirtualUnwind(&ContextRecord, NULL); #if defined(_X86_) ExceptionRecord->ExceptionAddress = (void *) ContextRecord.Eip; #elif defined(_AMD64_) ExceptionRecord->ExceptionAddress = (void *) ContextRecord.Rip; #elif defined(_ARM_) || defined(_ARM64_) ExceptionRecord->ExceptionAddress = (void *) ContextRecord.Pc; #else #error unsupported architecture #endif EXCEPTION_POINTERS pointers; pointers.ExceptionRecord = ExceptionRecord; pointers.ContextRecord = &ContextRecord; SEHRaiseException(InternalGetCurrentThread(), &pointers, 0); }
VOID PALAPI RaiseException(IN DWORD dwExceptionCode, IN DWORD dwExceptionFlags, IN DWORD nNumberOfArguments, IN CONST ULONG_PTR *lpArguments) { // PERF_ENTRY_ONLY is used here because RaiseException may or may not // return. We can not get latency data without PERF_EXIT. For this reason, // PERF_ENTRY_ONLY is used to profile frequency only. PERF_ENTRY_ONLY(RaiseException); ENTRY("RaiseException(dwCode=%#x, dwFlags=%#x, nArgs=%u, lpArguments=%p)\n", dwExceptionCode, dwExceptionFlags, nNumberOfArguments, lpArguments); /* Validate parameters */ if (dwExceptionCode & RESERVED_SEH_BIT) { WARN("Exception code %08x has bit 28 set; clearing it.\n", dwExceptionCode); dwExceptionCode ^= RESERVED_SEH_BIT; } if (nNumberOfArguments > EXCEPTION_MAXIMUM_PARAMETERS) { WARN("Number of arguments (%d) exceeds the limit " "EXCEPTION_MAXIMUM_PARAMETERS (%d); ignoring extra parameters.\n", nNumberOfArguments, EXCEPTION_MAXIMUM_PARAMETERS); nNumberOfArguments = EXCEPTION_MAXIMUM_PARAMETERS; } CONTEXT *contextRecord; EXCEPTION_RECORD *exceptionRecord; AllocateExceptionRecords(&exceptionRecord, &contextRecord); ZeroMemory(exceptionRecord, sizeof(EXCEPTION_RECORD)); exceptionRecord->ExceptionCode = dwExceptionCode; exceptionRecord->ExceptionFlags = dwExceptionFlags; exceptionRecord->ExceptionRecord = NULL; exceptionRecord->ExceptionAddress = NULL; // will be set by RtlpRaiseException exceptionRecord->NumberParameters = nNumberOfArguments; if (nNumberOfArguments) { CopyMemory(exceptionRecord->ExceptionInformation, lpArguments, nNumberOfArguments * sizeof(ULONG_PTR)); } // Capture the context of RaiseException. ZeroMemory(contextRecord, sizeof(CONTEXT)); contextRecord->ContextFlags = CONTEXT_FULL; CONTEXT_CaptureContext(contextRecord); // We have to unwind one level to get the actual context user code could be resumed at. PAL_VirtualUnwind(contextRecord, NULL); exceptionRecord->ExceptionAddress = (void *)CONTEXTGetPC(contextRecord); RtlpRaiseException(exceptionRecord, contextRecord); LOGEXIT("RaiseException returns\n"); }
/*++ Function: CONTEXT_GetRegisters Abstract retrieve the machine registers value of the indicated process. Parameter processId: process ID lpContext: context structure in which the machine registers value will be returned. Return returns TRUE if it succeeds, FALSE otherwise --*/ BOOL CONTEXT_GetRegisters(DWORD processId, LPCONTEXT lpContext) { #if HAVE_BSD_REGS_T int regFd = -1; #endif // HAVE_BSD_REGS_T BOOL bRet = FALSE; if (processId == GetCurrentProcessId()) { CONTEXT_CaptureContext(lpContext); } else { ucontext_t registers; #if HAVE_PT_REGS struct pt_regs ptrace_registers; if (ptrace((__ptrace_request)PT_GETREGS, processId, (caddr_t) &ptrace_registers, 0) == -1) #elif HAVE_BSD_REGS_T struct reg ptrace_registers; if (PAL_PTRACE(PT_GETREGS, processId, &ptrace_registers, 0) == -1) #endif { ASSERT("Failed ptrace(PT_GETREGS, processId:%d) errno:%d (%s)\n", processId, errno, strerror(errno)); } #if HAVE_PT_REGS #define ASSIGN_REG(reg) MCREG_##reg(registers.uc_mcontext) = PTREG_##reg(ptrace_registers); #elif HAVE_BSD_REGS_T #define ASSIGN_REG(reg) MCREG_##reg(registers.uc_mcontext) = BSDREG_##reg(ptrace_registers); #else #define ASSIGN_REG(reg) ASSERT("Don't know how to get the context of another process on this platform!"); return bRet; #endif ASSIGN_ALL_REGS #undef ASSIGN_REG CONTEXTFromNativeContext(®isters, lpContext, lpContext->ContextFlags); } bRet = TRUE; #if HAVE_BSD_REGS_T if (regFd != -1) { close(regFd); } #endif // HAVE_BSD_REGS_T return bRet; }
/*++ Function: GetThreadContext See MSDN doc. --*/ BOOL CONTEXT_GetThreadContext( DWORD dwProcessId, pthread_t self, LPCONTEXT lpContext) { BOOL ret = FALSE; if (lpContext == NULL) { ERROR("Invalid lpContext parameter value\n"); SetLastError(ERROR_NOACCESS); goto EXIT; } if (GetCurrentProcessId() == dwProcessId) { if (self != pthread_self()) { // the target thread is in the current process, but isn't // the current one: extract the CONTEXT from the Mach thread. mach_port_t mptPort; mptPort = pthread_mach_thread_np(self); ret = (CONTEXT_GetThreadContextFromPort(mptPort, lpContext) == KERN_SUCCESS); } else { CONTEXT_CaptureContext(lpContext); ret = TRUE; } } else { ASSERT("Cross-process GetThreadContext() is not supported on this platform\n"); SetLastError(ERROR_NOACCESS); } EXIT: return ret; }