blob: cbd53646f9ec9dfffaba82f9fb0c5a8d937202de [file]
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#include "pal/dbgmsg.h"
SET_DEFAULT_DEBUG_CHANNEL(THREAD); // some headers have code with asserts, so do this first
#include "pal/palinternal.h"
#include "pal/context.h"
#include "pal/debug.h"
#include "pal/thread.hpp"
#include <sys/ptrace.h>
#include <errno.h>
#include <unistd.h>
// in context2.S
extern void CONTEXT_CaptureContext(LPCONTEXT lpContext);
#ifdef _X86_
#define CONTEXT_ALL_FLOATING (CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS)
#elif defined(_AMD64_)
#define CONTEXT_ALL_FLOATING CONTEXT_FLOATING_POINT
#elif defined(_ARM_)
#define CONTEXT_ALL_FLOATING CONTEXT_FLOATING_POINT
#elif defined(_ARM64_)
#define CONTEXT_ALL_FLOATING CONTEXT_FLOATING_POINT
#else
#error Unexpected architecture.
#endif
#if !HAVE_MACH_EXCEPTIONS
#ifndef __GLIBC__
typedef int __ptrace_request;
#endif
#if HAVE_MACHINE_REG_H
#include <machine/reg.h>
#endif // HAVE_MACHINE_REG_H
#if HAVE_MACHINE_NPX_H
#include <machine/npx.h>
#endif // HAVE_MACHINE_NPX_H
#if HAVE_PT_REGS
#include <asm/ptrace.h>
#endif // HAVE_PT_REGS
#ifdef _AMD64_
#define ASSIGN_CONTROL_REGS \
ASSIGN_REG(Rbp) \
ASSIGN_REG(Rip) \
ASSIGN_REG(SegCs) \
ASSIGN_REG(EFlags) \
ASSIGN_REG(Rsp) \
#define ASSIGN_INTEGER_REGS \
ASSIGN_REG(Rdi) \
ASSIGN_REG(Rsi) \
ASSIGN_REG(Rbx) \
ASSIGN_REG(Rdx) \
ASSIGN_REG(Rcx) \
ASSIGN_REG(Rax) \
ASSIGN_REG(R8) \
ASSIGN_REG(R9) \
ASSIGN_REG(R10) \
ASSIGN_REG(R11) \
ASSIGN_REG(R12) \
ASSIGN_REG(R13) \
ASSIGN_REG(R14) \
ASSIGN_REG(R15) \
#elif defined(_X86_)
#define ASSIGN_CONTROL_REGS \
ASSIGN_REG(Ebp) \
ASSIGN_REG(Eip) \
ASSIGN_REG(SegCs) \
ASSIGN_REG(EFlags) \
ASSIGN_REG(Esp) \
ASSIGN_REG(SegSs) \
#define ASSIGN_INTEGER_REGS \
ASSIGN_REG(Edi) \
ASSIGN_REG(Esi) \
ASSIGN_REG(Ebx) \
ASSIGN_REG(Edx) \
ASSIGN_REG(Ecx) \
ASSIGN_REG(Eax) \
#elif defined(_ARM_)
#define ASSIGN_CONTROL_REGS \
ASSIGN_REG(Sp) \
ASSIGN_REG(Lr) \
ASSIGN_REG(Pc) \
ASSIGN_REG(Cpsr) \
#define ASSIGN_INTEGER_REGS \
ASSIGN_REG(R0) \
ASSIGN_REG(R1) \
ASSIGN_REG(R2) \
ASSIGN_REG(R3) \
ASSIGN_REG(R4) \
ASSIGN_REG(R5) \
ASSIGN_REG(R6) \
ASSIGN_REG(R7) \
ASSIGN_REG(R8) \
ASSIGN_REG(R9) \
ASSIGN_REG(R10) \
ASSIGN_REG(R11) \
ASSIGN_REG(R12)
#elif defined(_ARM64_)
#define ASSIGN_CONTROL_REGS \
ASSIGN_REG(Sp) \
ASSIGN_REG(Lr) \
ASSIGN_REG(Pc)
#define ASSIGN_INTEGER_REGS \
ASSIGN_REG(X0) \
ASSIGN_REG(X1) \
ASSIGN_REG(X2) \
ASSIGN_REG(X3) \
ASSIGN_REG(X4) \
ASSIGN_REG(X5) \
ASSIGN_REG(X6) \
ASSIGN_REG(X7) \
ASSIGN_REG(X8) \
ASSIGN_REG(X9) \
ASSIGN_REG(X10) \
ASSIGN_REG(X11) \
ASSIGN_REG(X12) \
ASSIGN_REG(X13) \
ASSIGN_REG(X14) \
ASSIGN_REG(X15) \
ASSIGN_REG(X16) \
ASSIGN_REG(X17) \
ASSIGN_REG(X18) \
ASSIGN_REG(X19) \
ASSIGN_REG(X20) \
ASSIGN_REG(X21) \
ASSIGN_REG(X22) \
ASSIGN_REG(X23) \
ASSIGN_REG(X24) \
ASSIGN_REG(X25) \
ASSIGN_REG(X26) \
ASSIGN_REG(X27) \
ASSIGN_REG(X28)
#else
#error Don't know how to assign registers on this architecture
#endif
#define ASSIGN_ALL_REGS \
ASSIGN_CONTROL_REGS \
ASSIGN_INTEGER_REGS \
/*++
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(&registers, 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;
}
/* How to consider the case when self is different from the current
thread of its owner process. Machine registers values could be retreived
by a ptrace(pid, ...) call or from the "/proc/%pid/reg" file content.
Unfortunately, these two methods only depend on process ID, not on
thread ID. */
if (dwProcessId == GetCurrentProcessId())
{
if (self != pthread_self())
{
DWORD flags;
// There aren't any APIs for this. We can potentially get the
// context of another thread by using per-thread signals, but
// on FreeBSD signal handlers that are called as a result
// of signals raised via pthread_kill don't get a valid
// sigcontext or ucontext_t. But we need this to return TRUE
// to avoid an assertion in the CLR in code that manages to
// cope reasonably well without a valid thread context.
// Given that, we'll zero out our structure and return TRUE.
ERROR("GetThreadContext on a thread other than the current "
"thread is returning TRUE\n");
flags = lpContext->ContextFlags;
memset(lpContext, 0, sizeof(*lpContext));
lpContext->ContextFlags = flags;
ret = TRUE;
goto EXIT;
}
}
if (lpContext->ContextFlags &
(CONTEXT_CONTROL | CONTEXT_INTEGER))
{
if (CONTEXT_GetRegisters(dwProcessId, lpContext) == FALSE)
{
SetLastError(ERROR_INTERNAL_ERROR);
goto EXIT;
}
}
ret = TRUE;
EXIT:
return ret;
}
/*++
Function:
SetThreadContext
See MSDN doc.
--*/
BOOL
CONTEXT_SetThreadContext(
DWORD dwProcessId,
pthread_t self,
CONST CONTEXT *lpContext)
{
BOOL ret = FALSE;
#if HAVE_PT_REGS
struct pt_regs ptrace_registers;
#elif HAVE_BSD_REGS_T
struct reg ptrace_registers;
#endif
if (lpContext == NULL)
{
ERROR("Invalid lpContext parameter value\n");
SetLastError(ERROR_NOACCESS);
goto EXIT;
}
/* How to consider the case when self is different from the current
thread of its owner process. Machine registers values could be retreived
by a ptrace(pid, ...) call or from the "/proc/%pid/reg" file content.
Unfortunately, these two methods only depend on process ID, not on
thread ID. */
if (dwProcessId == GetCurrentProcessId())
{
#ifdef FEATURE_PAL_SXS
// Need to implement SetThreadContext(current thread) for the IX architecture; look at common_signal_handler.
_ASSERT(FALSE);
#endif // FEATURE_PAL_SXS
ASSERT("SetThreadContext should be called for cross-process only.\n");
SetLastError(ERROR_INVALID_PARAMETER);
goto EXIT;
}
if (lpContext->ContextFlags &
(CONTEXT_CONTROL | CONTEXT_INTEGER))
{
#if HAVE_PT_REGS
if (ptrace((__ptrace_request)PT_GETREGS, dwProcessId, (caddr_t)&ptrace_registers, 0) == -1)
#elif HAVE_BSD_REGS_T
if (PAL_PTRACE(PT_GETREGS, dwProcessId, &ptrace_registers, 0) == -1)
#endif
{
ASSERT("Failed ptrace(PT_GETREGS, processId:%d) errno:%d (%s)\n",
dwProcessId, errno, strerror(errno));
SetLastError(ERROR_INTERNAL_ERROR);
goto EXIT;
}
#if HAVE_PT_REGS
#define ASSIGN_REG(reg) PTREG_##reg(ptrace_registers) = lpContext->reg;
#elif HAVE_BSD_REGS_T
#define ASSIGN_REG(reg) BSDREG_##reg(ptrace_registers) = lpContext->reg;
#else
#define ASSIGN_REG(reg)
ASSERT("Don't know how to set the context of another process on this platform!");
return FALSE;
#endif
if (lpContext->ContextFlags & CONTEXT_CONTROL)
{
ASSIGN_CONTROL_REGS
}
if (lpContext->ContextFlags & CONTEXT_INTEGER)
{
ASSIGN_INTEGER_REGS
}
#undef ASSIGN_REG
#if HAVE_PT_REGS
if (ptrace((__ptrace_request)PT_SETREGS, dwProcessId, (caddr_t)&ptrace_registers, 0) == -1)
#elif HAVE_BSD_REGS_T
if (PAL_PTRACE(PT_SETREGS, dwProcessId, &ptrace_registers, 0) == -1)
#endif
{
ASSERT("Failed ptrace(PT_SETREGS, processId:%d) errno:%d (%s)\n",
dwProcessId, errno, strerror(errno));
SetLastError(ERROR_INTERNAL_ERROR);
goto EXIT;
}
}
ret = TRUE;
EXIT:
return ret;
}
/*++
Function :
CONTEXTToNativeContext
Converts a CONTEXT record to a native context.
Parameters :
CONST CONTEXT *lpContext : CONTEXT to convert
native_context_t *native : native context to fill in
Return value :
None
--*/
void CONTEXTToNativeContext(CONST CONTEXT *lpContext, native_context_t *native)
{
#define ASSIGN_REG(reg) MCREG_##reg(native->uc_mcontext) = lpContext->reg;
if ((lpContext->ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL)
{
ASSIGN_CONTROL_REGS
}
if ((lpContext->ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER)
{
ASSIGN_INTEGER_REGS
}
#undef ASSIGN_REG
#if HAVE_GREGSET_T || HAVE_GREGSET_T
#if HAVE_GREGSET_T
if (native->uc_mcontext.fpregs == nullptr)
#elif HAVE___GREGSET_T
if (native->uc_mcontext.__fpregs == nullptr)
#endif
{
// If the pointer to the floating point state in the native context
// is not valid, we can't copy floating point registers regardless of
// whether CONTEXT_FLOATING_POINT is set in the CONTEXT's flags.
return;
}
#endif
if ((lpContext->ContextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT)
{
#ifdef _AMD64_
FPREG_ControlWord(native) = lpContext->FltSave.ControlWord;
FPREG_StatusWord(native) = lpContext->FltSave.StatusWord;
FPREG_TagWord(native) = lpContext->FltSave.TagWord;
FPREG_ErrorOffset(native) = lpContext->FltSave.ErrorOffset;
FPREG_ErrorSelector(native) = lpContext->FltSave.ErrorSelector;
FPREG_DataOffset(native) = lpContext->FltSave.DataOffset;
FPREG_DataSelector(native) = lpContext->FltSave.DataSelector;
FPREG_MxCsr(native) = lpContext->FltSave.MxCsr;
FPREG_MxCsr_Mask(native) = lpContext->FltSave.MxCsr_Mask;
for (int i = 0; i < 8; i++)
{
FPREG_St(native, i) = lpContext->FltSave.FloatRegisters[i];
}
for (int i = 0; i < 16; i++)
{
FPREG_Xmm(native, i) = lpContext->FltSave.XmmRegisters[i];
}
#endif
}
}
/*++
Function :
CONTEXTFromNativeContext
Converts a native context to a CONTEXT record.
Parameters :
const native_context_t *native : native context to convert
LPCONTEXT lpContext : CONTEXT to fill in
ULONG contextFlags : flags that determine which registers are valid in
native and which ones to set in lpContext
Return value :
None
--*/
void CONTEXTFromNativeContext(const native_context_t *native, LPCONTEXT lpContext,
ULONG contextFlags)
{
lpContext->ContextFlags = contextFlags;
#define ASSIGN_REG(reg) lpContext->reg = MCREG_##reg(native->uc_mcontext);
if ((contextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL)
{
ASSIGN_CONTROL_REGS
#ifdef _ARM_
// WinContext assumes that the least bit of Pc is always 1 (denoting thumb)
// although the pc value retrived from native context might not have set the least bit.
// This becomes especially problematic if the context is on the JIT_WRITEBARRIER.
lpContext->Pc |= 0x1;
#endif
}
if ((contextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER)
{
ASSIGN_INTEGER_REGS
}
#undef ASSIGN_REG
if ((contextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT)
{
#ifdef _AMD64_
lpContext->FltSave.ControlWord = FPREG_ControlWord(native);
lpContext->FltSave.StatusWord = FPREG_StatusWord(native);
lpContext->FltSave.TagWord = FPREG_TagWord(native);
lpContext->FltSave.ErrorOffset = FPREG_ErrorOffset(native);
lpContext->FltSave.ErrorSelector = FPREG_ErrorSelector(native);
lpContext->FltSave.DataOffset = FPREG_DataOffset(native);
lpContext->FltSave.DataSelector = FPREG_DataSelector(native);
lpContext->FltSave.MxCsr = FPREG_MxCsr(native);
lpContext->FltSave.MxCsr_Mask = FPREG_MxCsr_Mask(native);
for (int i = 0; i < 8; i++)
{
lpContext->FltSave.FloatRegisters[i] = FPREG_St(native, i);
}
for (int i = 0; i < 16; i++)
{
lpContext->FltSave.XmmRegisters[i] = FPREG_Xmm(native, i);
}
#endif
}
}
/*++
Function :
GetNativeContextPC
Returns the program counter from the native context.
Parameters :
const native_context_t *native : native context
Return value :
The program counter from the native context.
--*/
LPVOID GetNativeContextPC(const native_context_t *context)
{
#ifdef _AMD64_
return (LPVOID)MCREG_Rip(context->uc_mcontext);
#elif defined(_X86_)
return (LPVOID) MCREG_Eip(context->uc_mcontext);
#elif defined(_ARM_)
return (LPVOID) MCREG_Pc(context->uc_mcontext);
#elif defined(_ARM64_)
return (LPVOID) MCREG_Pc(context->uc_mcontext);
#else
# error implement me for this architecture
#endif
}
/*++
Function :
CONTEXTGetExceptionCodeForSignal
Translates signal and context information to a Win32 exception code.
Parameters :
const siginfo_t *siginfo : signal information from a signal handler
const native_context_t *context : context information
Return value :
The Win32 exception code that corresponds to the signal and context
information.
--*/
#ifdef ILL_ILLOPC
// If si_code values are available for all signals, use those.
DWORD CONTEXTGetExceptionCodeForSignal(const siginfo_t *siginfo,
const native_context_t *context)
{
// IMPORTANT NOTE: This function must not call any signal unsafe functions
// since it is called from signal handlers.
// That includes ASSERT and TRACE macros.
switch (siginfo->si_signo)
{
case SIGILL:
switch (siginfo->si_code)
{
case ILL_ILLOPC: // Illegal opcode
case ILL_ILLOPN: // Illegal operand
case ILL_ILLADR: // Illegal addressing mode
case ILL_ILLTRP: // Illegal trap
case ILL_COPROC: // Co-processor error
return EXCEPTION_ILLEGAL_INSTRUCTION;
case ILL_PRVOPC: // Privileged opcode
case ILL_PRVREG: // Privileged register
return EXCEPTION_PRIV_INSTRUCTION;
case ILL_BADSTK: // Internal stack error
return EXCEPTION_STACK_OVERFLOW;
default:
break;
}
break;
case SIGFPE:
switch (siginfo->si_code)
{
case FPE_INTDIV:
return EXCEPTION_INT_DIVIDE_BY_ZERO;
case FPE_INTOVF:
return EXCEPTION_INT_OVERFLOW;
case FPE_FLTDIV:
return EXCEPTION_FLT_DIVIDE_BY_ZERO;
case FPE_FLTOVF:
return EXCEPTION_FLT_OVERFLOW;
case FPE_FLTUND:
return EXCEPTION_FLT_UNDERFLOW;
case FPE_FLTRES:
return EXCEPTION_FLT_INEXACT_RESULT;
case FPE_FLTINV:
return EXCEPTION_FLT_INVALID_OPERATION;
case FPE_FLTSUB:
return EXCEPTION_FLT_INVALID_OPERATION;
default:
break;
}
break;
case SIGSEGV:
switch (siginfo->si_code)
{
case SI_USER: // User-generated signal, sometimes sent
// for SIGSEGV under normal circumstances
case SEGV_MAPERR: // Address not mapped to object
case SEGV_ACCERR: // Invalid permissions for mapped object
return EXCEPTION_ACCESS_VIOLATION;
#ifdef SI_KERNEL
case SI_KERNEL:
{
return EXCEPTION_ACCESS_VIOLATION;
}
#endif
default:
break;
}
break;
case SIGBUS:
switch (siginfo->si_code)
{
case BUS_ADRALN: // Invalid address alignment
return EXCEPTION_DATATYPE_MISALIGNMENT;
case BUS_ADRERR: // Non-existent physical address
return EXCEPTION_ACCESS_VIOLATION;
case BUS_OBJERR: // Object-specific hardware error
default:
break;
}
case SIGTRAP:
switch (siginfo->si_code)
{
#ifdef SI_KERNEL
case SI_KERNEL:
#endif
case SI_USER:
case TRAP_BRKPT: // Process breakpoint
return EXCEPTION_BREAKPOINT;
case TRAP_TRACE: // Process trace trap
return EXCEPTION_SINGLE_STEP;
default:
// Got unknown SIGTRAP signal with code siginfo->si_code;
return EXCEPTION_ILLEGAL_INSTRUCTION;
}
default:
break;
}
// Got unknown signal number siginfo->si_signo with code siginfo->si_code;
return EXCEPTION_ILLEGAL_INSTRUCTION;
}
#else // ILL_ILLOPC
DWORD CONTEXTGetExceptionCodeForSignal(const siginfo_t *siginfo,
const native_context_t *context)
{
// IMPORTANT NOTE: This function must not call any signal unsafe functions
// since it is called from signal handlers.
// That includes ASSERT and TRACE macros.
int trap;
if (siginfo->si_signo == SIGFPE)
{
// Floating point exceptions are mapped by their si_code.
switch (siginfo->si_code)
{
case FPE_INTDIV :
return EXCEPTION_INT_DIVIDE_BY_ZERO;
case FPE_INTOVF :
return EXCEPTION_INT_OVERFLOW;
case FPE_FLTDIV :
return EXCEPTION_FLT_DIVIDE_BY_ZERO;
case FPE_FLTOVF :
return EXCEPTION_FLT_OVERFLOW;
case FPE_FLTUND :
return EXCEPTION_FLT_UNDERFLOW;
case FPE_FLTRES :
return EXCEPTION_FLT_INEXACT_RESULT;
case FPE_FLTINV :
return EXCEPTION_FLT_INVALID_OPERATION;
case FPE_FLTSUB :/* subscript out of range */
return EXCEPTION_FLT_INVALID_OPERATION;
default:
// Got unknown signal code siginfo->si_code;
return 0;
}
}
trap = context->uc_mcontext.mc_trapno;
switch (trap)
{
case T_PRIVINFLT : /* privileged instruction */
return EXCEPTION_PRIV_INSTRUCTION;
case T_BPTFLT : /* breakpoint instruction */
return EXCEPTION_BREAKPOINT;
case T_ARITHTRAP : /* arithmetic trap */
return 0; /* let the caller pick an exception code */
#ifdef T_ASTFLT
case T_ASTFLT : /* system forced exception : ^C, ^\. SIGINT signal
handler shouldn't be calling this function, since
it doesn't need an exception code */
// Trap code T_ASTFLT received, shouldn't get here;
return 0;
#endif // T_ASTFLT
case T_PROTFLT : /* protection fault */
return EXCEPTION_ACCESS_VIOLATION;
case T_TRCTRAP : /* debug exception (sic) */
return EXCEPTION_SINGLE_STEP;
case T_PAGEFLT : /* page fault */
return EXCEPTION_ACCESS_VIOLATION;
case T_ALIGNFLT : /* alignment fault */
return EXCEPTION_DATATYPE_MISALIGNMENT;
case T_DIVIDE :
return EXCEPTION_INT_DIVIDE_BY_ZERO;
case T_NMI : /* non-maskable trap */
return EXCEPTION_ILLEGAL_INSTRUCTION;
case T_OFLOW :
return EXCEPTION_INT_OVERFLOW;
case T_BOUND : /* bound instruction fault */
return EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
case T_DNA : /* device not available fault */
return EXCEPTION_ILLEGAL_INSTRUCTION;
case T_DOUBLEFLT : /* double fault */
return EXCEPTION_ILLEGAL_INSTRUCTION;
case T_FPOPFLT : /* fp coprocessor operand fetch fault */
return EXCEPTION_FLT_INVALID_OPERATION;
case T_TSSFLT : /* invalid tss fault */
return EXCEPTION_ILLEGAL_INSTRUCTION;
case T_SEGNPFLT : /* segment not present fault */
return EXCEPTION_ACCESS_VIOLATION;
case T_STKFLT : /* stack fault */
return EXCEPTION_STACK_OVERFLOW;
case T_MCHK : /* machine check trap */
return EXCEPTION_ILLEGAL_INSTRUCTION;
case T_RESERVED : /* reserved (unknown) */
return EXCEPTION_ILLEGAL_INSTRUCTION;
default:
// Got unknown trap code trap;
break;
}
return EXCEPTION_ILLEGAL_INSTRUCTION;
}
#endif // ILL_ILLOPC
#else // !HAVE_MACH_EXCEPTIONS
#include <mach/message.h>
#include <mach/thread_act.h>
#include "../exception/machexception.h"
/*++
Function:
CONTEXT_GetThreadContextFromPort
Helper for GetThreadContext that uses a mach_port
--*/
kern_return_t
CONTEXT_GetThreadContextFromPort(
mach_port_t Port,
LPCONTEXT lpContext)
{
// Extract the CONTEXT from the Mach thread.
kern_return_t MachRet = KERN_SUCCESS;
mach_msg_type_number_t StateCount;
thread_state_flavor_t StateFlavor;
if (lpContext->ContextFlags & (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS))
{
#ifdef _X86_
x86_thread_state32_t State;
StateFlavor = x86_THREAD_STATE32;
#elif defined(_AMD64_)
x86_thread_state64_t State;
StateFlavor = x86_THREAD_STATE64;
#else
#error Unexpected architecture.
#endif
StateCount = sizeof(State) / sizeof(natural_t);
MachRet = thread_get_state(Port, StateFlavor, (thread_state_t)&State, &StateCount);
if (MachRet != KERN_SUCCESS)
{
ASSERT("thread_get_state(THREAD_STATE) failed: %d\n", MachRet);
goto exit;
}
CONTEXT_GetThreadContextFromThreadState(StateFlavor, (thread_state_t)&State, lpContext);
}
if (lpContext->ContextFlags & CONTEXT_ALL_FLOATING) {
#ifdef _X86_
x86_float_state32_t State;
StateFlavor = x86_FLOAT_STATE32;
#elif defined(_AMD64_)
x86_float_state64_t State;
StateFlavor = x86_FLOAT_STATE64;
#else
#error Unexpected architecture.
#endif
StateCount = sizeof(State) / sizeof(natural_t);
MachRet = thread_get_state(Port, StateFlavor, (thread_state_t)&State, &StateCount);
if (MachRet != KERN_SUCCESS)
{
ASSERT("thread_get_state(FLOAT_STATE) failed: %d\n", MachRet);
goto exit;
}
CONTEXT_GetThreadContextFromThreadState(StateFlavor, (thread_state_t)&State, lpContext);
}
exit:
return MachRet;
}
#ifdef __DARWIN_UNIX03
#define PSTATE_WRAP(a,b) a->__##b
#else
#define PSTATE_WRAP(a,b) a->b
#endif
/*++
Function:
CONTEXT_GetThreadContextFromThreadState
--*/
void
CONTEXT_GetThreadContextFromThreadState(
thread_state_flavor_t threadStateFlavor,
thread_state_t threadState,
LPCONTEXT lpContext)
{
switch (threadStateFlavor)
{
#ifdef _X86_
case x86_THREAD_STATE32:
if (lpContext->ContextFlags & (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS))
{
x86_thread_state32_t *pState = (x86_thread_state32_t *)threadState;
lpContext->Eax = PSTATE_WRAP(pState, eax);
lpContext->Ebx = PSTATE_WRAP(pState, ebx);
lpContext->Ecx = PSTATE_WRAP(pState, ecx);
lpContext->Edx = PSTATE_WRAP(pState, edx);
lpContext->Edi = PSTATE_WRAP(pState, edi);
lpContext->Esi = PSTATE_WRAP(pState, esi);
lpContext->Ebp = PSTATE_WRAP(pState, ebp);
lpContext->Esp = PSTATE_WRAP(pState, esp);
lpContext->SegSs = PSTATE_WRAP(pState, ss);
lpContext->EFlags = PSTATE_WRAP(pState, eflags);
lpContext->Eip = PSTATE_WRAP(pState, eip);
lpContext->SegCs = PSTATE_WRAP(pState, cs);
lpContext->SegDs_PAL_Undefined = PSTATE_WRAP(pState, ds);
lpContext->SegEs_PAL_Undefined = PSTATE_WRAP(pState, es);
lpContext->SegFs_PAL_Undefined = PSTATE_WRAP(pState, fs);
lpContext->SegGs_PAL_Undefined = PSTATE_WRAP(pState, gs);
}
break;
case x86_FLOAT_STATE32:
{
x86_float_state32_t *pState = (x86_float_state32_t *)threadState;
if (lpContext->ContextFlags & CONTEXT_FLOATING_POINT)
{
lpContext->FloatSave.ControlWord = *(DWORD*)&PSTATE_WRAP(pState, fpu_fcw);
lpContext->FloatSave.StatusWord = *(DWORD*)&PSTATE_WRAP(pState, fpu_fsw);
lpContext->FloatSave.TagWord = PSTATE_WRAP(pState, fpu_ftw);
lpContext->FloatSave.ErrorOffset = PSTATE_WRAP(pState, fpu_ip);
lpContext->FloatSave.ErrorSelector = PSTATE_WRAP(pState, fpu_cs);
lpContext->FloatSave.DataOffset = PSTATE_WRAP(pState, fpu_dp);
lpContext->FloatSave.DataSelector = PSTATE_WRAP(pState, fpu_ds);
lpContext->FloatSave.Cr0NpxState = PSTATE_WRAP(pState, fpu_mxcsr);
// Windows stores the floating point registers in a packed layout (each 10-byte register end to end
// for a total of 80 bytes). But Mach returns each register in an 16-bit structure (presumably for
// alignment purposes). So we can't just memcpy the registers over in a single block, we need to copy
// them individually.
for (int i = 0; i < 8; i++)
memcpy(&lpContext->FloatSave.RegisterArea[i * 10], (&PSTATE_WRAP(pState, fpu_stmm0))[i].__mmst_reg, 10);
}
if (lpContext->ContextFlags & CONTEXT_EXTENDED_REGISTERS)
{
// The only extended register information that Mach will tell us about are the xmm register values.
// Both Windows and Mach store the registers in a packed layout (each of the 8 registers is 16 bytes)
// so we can simply memcpy them across.
memcpy(lpContext->ExtendedRegisters + CONTEXT_EXREG_XMM_OFFSET, &PSTATE_WRAP(pState, fpu_xmm0), 8 * 16);
}
}
break;
#elif defined(_AMD64_)
case x86_THREAD_STATE64:
if (lpContext->ContextFlags & (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS))
{
x86_thread_state64_t *pState = (x86_thread_state64_t *)threadState;
lpContext->Rax = pState->__rax;
lpContext->Rbx = pState->__rbx;
lpContext->Rcx = pState->__rcx;
lpContext->Rdx = pState->__rdx;
lpContext->Rdi = pState->__rdi;
lpContext->Rsi = pState->__rsi;
lpContext->Rbp = pState->__rbp;
lpContext->Rsp = pState->__rsp;
lpContext->R8 = pState->__r8;
lpContext->R9 = pState->__r9;
lpContext->R10 = pState->__r10;
lpContext->R11 = pState->__r11;
lpContext->R12 = pState->__r12;
lpContext->R13 = pState->__r13;
lpContext->R14 = pState->__r14;
lpContext->R15 = pState->__r15;
lpContext->EFlags = pState->__rflags;
lpContext->Rip = pState->__rip;
lpContext->SegCs = pState->__cs;
// RtlRestoreContext uses the actual ss instead of this one
// to build the iret frame so just set it zero.
lpContext->SegSs = 0;
lpContext->SegDs = 0;
lpContext->SegEs = 0;
lpContext->SegFs = pState->__fs;
lpContext->SegGs = pState->__gs;
}
break;
case x86_FLOAT_STATE64:
if (lpContext->ContextFlags & CONTEXT_FLOATING_POINT)
{
x86_float_state64_t *pState = (x86_float_state64_t *)threadState;
lpContext->FltSave.ControlWord = *(DWORD*)&pState->__fpu_fcw;
lpContext->FltSave.StatusWord = *(DWORD*)&pState->__fpu_fsw;
lpContext->FltSave.TagWord = pState->__fpu_ftw;
lpContext->FltSave.ErrorOffset = pState->__fpu_ip;
lpContext->FltSave.ErrorSelector = pState->__fpu_cs;
lpContext->FltSave.DataOffset = pState->__fpu_dp;
lpContext->FltSave.DataSelector = pState->__fpu_ds;
lpContext->FltSave.MxCsr = pState->__fpu_mxcsr;
lpContext->FltSave.MxCsr_Mask = pState->__fpu_mxcsrmask; // note: we don't save the mask for x86
// Windows stores the floating point registers in a packed layout (each 10-byte register end to end
// for a total of 80 bytes). But Mach returns each register in an 16-bit structure (presumably for
// alignment purposes). So we can't just memcpy the registers over in a single block, we need to copy
// them individually.
for (int i = 0; i < 8; i++)
memcpy(&lpContext->FltSave.FloatRegisters[i], (&pState->__fpu_stmm0)[i].__mmst_reg, 10);
// AMD64's FLOATING_POINT includes the xmm registers.
memcpy(&lpContext->Xmm0, &pState->__fpu_xmm0, 8 * 16);
}
break;
#else
#error Unexpected architecture.
#endif
case x86_THREAD_STATE:
{
x86_thread_state_t *pState = (x86_thread_state_t *)threadState;
CONTEXT_GetThreadContextFromThreadState((thread_state_flavor_t)pState->tsh.flavor, (thread_state_t)&pState->uts, lpContext);
}
break;
case x86_FLOAT_STATE:
{
x86_float_state_t *pState = (x86_float_state_t *)threadState;
CONTEXT_GetThreadContextFromThreadState((thread_state_flavor_t)pState->fsh.flavor, (thread_state_t)&pState->ufs, lpContext);
}
break;
default:
ASSERT("Invalid thread state flavor %d\n", threadStateFlavor);
break;
}
}
/*++
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;
}
/*++
Function:
SetThreadContextOnPort
Helper for CONTEXT_SetThreadContext
--*/
kern_return_t
CONTEXT_SetThreadContextOnPort(
mach_port_t Port,
IN CONST CONTEXT *lpContext)
{
kern_return_t MachRet = KERN_SUCCESS;
mach_msg_type_number_t StateCount;
thread_state_flavor_t StateFlavor;
if (lpContext->ContextFlags & (CONTEXT_CONTROL|CONTEXT_INTEGER))
{
#ifdef _X86_
x86_thread_state32_t State;
StateFlavor = x86_THREAD_STATE32;
PSTATE_WRAP((&State), eax) = lpContext->Eax;
PSTATE_WRAP((&State), ebx) = lpContext->Ebx;
PSTATE_WRAP((&State), ecx) = lpContext->Ecx;
PSTATE_WRAP((&State), edx) = lpContext->Edx;
PSTATE_WRAP((&State), edi) = lpContext->Edi;
PSTATE_WRAP((&State), esi) = lpContext->Esi;
PSTATE_WRAP((&State), ebp) = lpContext->Ebp;
PSTATE_WRAP((&State), esp) = lpContext->Esp;
PSTATE_WRAP((&State), ss) = lpContext->SegSs;
PSTATE_WRAP((&State), eflags) = lpContext->EFlags;
PSTATE_WRAP((&State), eip) = lpContext->Eip;
PSTATE_WRAP((&State), cs) = lpContext->SegCs;
PSTATE_WRAP((&State), ds) = lpContext->SegDs_PAL_Undefined;
PSTATE_WRAP((&State), es) = lpContext->SegEs_PAL_Undefined;
PSTATE_WRAP((&State), fs) = lpContext->SegFs_PAL_Undefined;
PSTATE_WRAP((&State), gs) = lpContext->SegGs_PAL_Undefined;
#elif defined(_AMD64_)
x86_thread_state64_t State;
StateFlavor = x86_THREAD_STATE64;
State.__rax = lpContext->Rax;
State.__rbx = lpContext->Rbx;
State.__rcx = lpContext->Rcx;
State.__rdx = lpContext->Rdx;
State.__rdi = lpContext->Rdi;
State.__rsi = lpContext->Rsi;
State.__rbp = lpContext->Rbp;
State.__rsp = lpContext->Rsp;
State.__r8 = lpContext->R8;
State.__r9 = lpContext->R9;
State.__r10 = lpContext->R10;
State.__r11 = lpContext->R11;
State.__r12 = lpContext->R12;
State.__r13 = lpContext->R13;
State.__r14 = lpContext->R14;
State.__r15 = lpContext->R15;
State.__rflags = lpContext->EFlags;
State.__rip = lpContext->Rip;
State.__cs = lpContext->SegCs;
State.__fs = lpContext->SegFs;
State.__gs = lpContext->SegGs;
#else
#error Unexpected architecture.
#endif
StateCount = sizeof(State) / sizeof(natural_t);
MachRet = thread_set_state(Port,
StateFlavor,
(thread_state_t)&State,
StateCount);
if (MachRet != KERN_SUCCESS)
{
ASSERT("thread_set_state(THREAD_STATE) failed: %d\n", MachRet);
goto EXIT;
}
}
if (lpContext->ContextFlags & CONTEXT_ALL_FLOATING)
{
#ifdef _X86_
x86_float_state32_t State;
StateFlavor = x86_FLOAT_STATE32;
#elif defined(_AMD64_)
x86_float_state64_t State;
StateFlavor = x86_FLOAT_STATE64;
#else
#error Unexpected architecture.
#endif
StateCount = sizeof(State) / sizeof(natural_t);
// If we're setting only one of the floating point or extended registers (of which Mach supports only
// the xmm values) then we don't have values for the other set. This is a problem since Mach only
// supports setting both groups as a single unit. So in this case we'll need to fetch the current
// values first.
if ((lpContext->ContextFlags & CONTEXT_ALL_FLOATING) !=
CONTEXT_ALL_FLOATING)
{
mach_msg_type_number_t StateCountGet = StateCount;
MachRet = thread_get_state(Port,
StateFlavor,
(thread_state_t)&State,
&StateCountGet);
if (MachRet != KERN_SUCCESS)
{
ASSERT("thread_get_state(FLOAT_STATE) failed: %d\n", MachRet);
goto EXIT;
}
_ASSERTE(StateCountGet == StateCount);
}
if (lpContext->ContextFlags & CONTEXT_FLOATING_POINT)
{
#ifdef _X86_
*(DWORD*)&PSTATE_WRAP((&State), fpu_fcw) = lpContext->FloatSave.ControlWord;
*(DWORD*)&PSTATE_WRAP((&State), fpu_fsw) = lpContext->FloatSave.StatusWord;
PSTATE_WRAP((&State), fpu_ftw) = lpContext->FloatSave.TagWord;
PSTATE_WRAP((&State), fpu_ip) = lpContext->FloatSave.ErrorOffset;
PSTATE_WRAP((&State), fpu_cs) = lpContext->FloatSave.ErrorSelector;
PSTATE_WRAP((&State), fpu_dp) = lpContext->FloatSave.DataOffset;
PSTATE_WRAP((&State), fpu_ds) = lpContext->FloatSave.DataSelector;
PSTATE_WRAP((&State), fpu_mxcsr) = lpContext->FloatSave.Cr0NpxState;
// Windows stores the floating point registers in a packed layout (each 10-byte register end to
// end for a total of 80 bytes). But Mach returns each register in an 16-bit structure (presumably
// for alignment purposes). So we can't just memcpy the registers over in a single block, we need
// to copy them individually.
for (int i = 0; i < 8; i++)
memcpy((&PSTATE_WRAP((&State), fpu_stmm0))[i].__mmst_reg, &lpContext->FloatSave.RegisterArea[i * 10], 10);
#elif defined(_AMD64_)
*(DWORD*)&State.__fpu_fcw = lpContext->FltSave.ControlWord;
*(DWORD*)&State.__fpu_fsw = lpContext->FltSave.StatusWord;
State.__fpu_ftw = lpContext->FltSave.TagWord;
State.__fpu_ip = lpContext->FltSave.ErrorOffset;
State.__fpu_cs = lpContext->FltSave.ErrorSelector;
State.__fpu_dp = lpContext->FltSave.DataOffset;
State.__fpu_ds = lpContext->FltSave.DataSelector;
State.__fpu_mxcsr = lpContext->FltSave.MxCsr;
State.__fpu_mxcsrmask = lpContext->FltSave.MxCsr_Mask; // note: we don't save the mask for x86
// Windows stores the floating point registers in a packed layout (each 10-byte register end to
// end for a total of 80 bytes). But Mach returns each register in an 16-bit structure (presumably
// for alignment purposes). So we can't just memcpy the registers over in a single block, we need
// to copy them individually.
for (int i = 0; i < 8; i++)
memcpy((&State.__fpu_stmm0)[i].__mmst_reg, &lpContext->FltSave.FloatRegisters[i], 10);
memcpy(&State.__fpu_xmm0, &lpContext->Xmm0, 8 * 16);
#else
#error Unexpected architecture.
#endif
}
#ifdef _X86_
if (lpContext->ContextFlags & CONTEXT_EXTENDED_REGISTERS)
{
// The only extended register information that Mach will tell us about are the xmm register
// values. Both Windows and Mach store the registers in a packed layout (each of the 8 registers
// is 16 bytes) so we can simply memcpy them across.
memcpy(&PSTATE_WRAP((&State), fpu_xmm0), lpContext->ExtendedRegisters + CONTEXT_EXREG_XMM_OFFSET, 8 * 16);
}
#endif // _X86_
MachRet = thread_set_state(Port,
StateFlavor,
(thread_state_t)&State,
StateCount);
if (MachRet != KERN_SUCCESS)
{
ASSERT("thread_set_state(FLOAT_STATE) failed: %d\n", MachRet);
goto EXIT;
}
}
EXIT:
return MachRet;
}
/*++
Function:
SetThreadContext
See MSDN doc.
--*/
BOOL
CONTEXT_SetThreadContext(
DWORD dwProcessId,
pthread_t self,
CONST CONTEXT *lpContext)
{
BOOL ret = FALSE;
if (lpContext == NULL)
{
ERROR("Invalid lpContext parameter value\n");
SetLastError(ERROR_NOACCESS);
goto EXIT;
}
if (dwProcessId != GetCurrentProcessId())
{
// GetThreadContext() of a thread in another process
ASSERT("Cross-process GetThreadContext() is not supported\n");
SetLastError(ERROR_NOACCESS);
goto EXIT;
}
if (self != pthread_self())
{
// hThread is in the current process, but isn't the current
// thread. Extract the CONTEXT from the Mach thread.
mach_port_t mptPort;
mptPort = pthread_mach_thread_np(self);
ret = (CONTEXT_SetThreadContextOnPort(mptPort, lpContext) == KERN_SUCCESS);
}
else
{
MachSetThreadContext(const_cast<CONTEXT *>(lpContext));
ASSERT("MachSetThreadContext should never return\n");
}
EXIT:
return ret;
}
#endif // !HAVE_MACH_EXCEPTIONS
/*++
Function:
DBG_FlushInstructionCache: processor-specific portion of
FlushInstructionCache
See MSDN doc.
--*/
BOOL
DBG_FlushInstructionCache(
IN LPCVOID lpBaseAddress,
IN SIZE_T dwSize)
{
// Intrinsic should do the right thing across all platforms
__builtin___clear_cache((char *)lpBaseAddress, (char *)((INT_PTR)lpBaseAddress + dwSize));
return TRUE;
}