// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #include "createdump.h" #include #if defined(__aarch64__) // See src/pal/src/include/pal/context.h #define MCREG_Fp(mc) ((mc).regs[29]) #define MCREG_Lr(mc) ((mc).regs[30]) #define MCREG_Sp(mc) ((mc).sp) #define MCREG_Pc(mc) ((mc).pc) #define MCREG_Cpsr(mc) ((mc).pstate) #endif #ifndef THUMB_CODE #define THUMB_CODE 1 #endif #ifndef __GLIBC__ typedef int __ptrace_request; #endif #define FPREG_ErrorOffset(fpregs) *(DWORD*)&((fpregs).rip) #define FPREG_ErrorSelector(fpregs) *(((WORD*)&((fpregs).rip)) + 2) #define FPREG_DataOffset(fpregs) *(DWORD*)&((fpregs).rdp) #define FPREG_DataSelector(fpregs) *(((WORD*)&((fpregs).rdp)) + 2) extern CrashInfo* g_crashInfo; ThreadInfo::ThreadInfo(pid_t tid) : m_tid(tid) { } ThreadInfo::~ThreadInfo() { } bool ThreadInfo::Initialize(ICLRDataTarget* pDataTarget) { if (!CrashInfo::GetStatus(m_tid, &m_ppid, &m_tgid, nullptr)) { return false; } if (pDataTarget != nullptr) { if (!GetRegistersWithDataTarget(pDataTarget)) { return false; } } else { if (!GetRegistersWithPTrace()) { return false; } } #if defined(__aarch64__) TRACE("Thread %04x PC %016llx SP %016llx\n", m_tid, (unsigned long long)MCREG_Pc(m_gpRegisters), (unsigned long long)MCREG_Sp(m_gpRegisters)); #elif defined(__arm__) TRACE("Thread %04x PC %08lx SP %08lx\n", m_tid, (unsigned long)m_gpRegisters.ARM_pc, (unsigned long)m_gpRegisters.ARM_sp); #elif defined(__x86_64__) TRACE("Thread %04x RIP %016llx RSP %016llx\n", m_tid, (unsigned long long)m_gpRegisters.rip, (unsigned long long)m_gpRegisters.rsp); #elif defined(__i386__) TRACE("Thread %04x EIP %016lx ESP %016lx\n", m_tid, (unsigned long)m_gpRegisters.eip, (unsigned long)m_gpRegisters.esp); #else #error "Unsupported architecture" #endif return true; } void ThreadInfo::ResumeThread() { if (ptrace(PTRACE_DETACH, m_tid, nullptr, nullptr) != -1) { int waitStatus; waitpid(m_tid, &waitStatus, __WALL); } } // Helper for UnwindNativeFrames static void GetFrameLocation(CONTEXT* pContext, uint64_t* ip, uint64_t* sp) { #if defined(__x86_64__) *ip = pContext->Rip; *sp = pContext->Rsp; #elif defined(__i386__) *ip = pContext->Eip; *sp = pContext->Esp; #elif defined(__aarch64__) *ip = pContext->Pc; *sp = pContext->Sp; #elif defined(__arm__) *ip = pContext->Pc & ~THUMB_CODE; *sp = pContext->Sp; #endif } // Helper for UnwindNativeFrames static BOOL ReadMemoryAdapter(PVOID address, PVOID buffer, SIZE_T size) { return g_crashInfo->ReadMemory(address, buffer, size); } void ThreadInfo::UnwindNativeFrames(CrashInfo& crashInfo, CONTEXT* pContext) { uint64_t previousSp = 0; // For each native frame while (true) { uint64_t ip = 0, sp = 0; GetFrameLocation(pContext, &ip, &sp); TRACE("Unwind: sp %" PRIA PRIx64 " ip %" PRIA PRIx64 "\n", sp, ip); if (ip == 0 || sp <= previousSp) { break; } // Add two pages around the instruction pointer to the core dump crashInfo.InsertMemoryRegion(ip - PAGE_SIZE, PAGE_SIZE * 2); // Look up the ip address to get the module base address uint64_t baseAddress = crashInfo.GetBaseAddress(ip); if (baseAddress == 0) { TRACE("Unwind: module base not found ip %" PRIA PRIx64 "\n", ip); break; } // Unwind the native frame adding all the memory accessed to the // core dump via the read memory adapter. if (!PAL_VirtualUnwindOutOfProc(pContext, nullptr, baseAddress, ReadMemoryAdapter)) { TRACE("Unwind: PAL_VirtualUnwindOutOfProc returned false\n"); break; } previousSp = sp; } } bool ThreadInfo::UnwindThread(CrashInfo& crashInfo, IXCLRDataProcess* pClrDataProcess) { TRACE("Unwind: thread %04x\n", Tid()); // Get starting native context for the thread CONTEXT context; GetThreadContext(CONTEXT_ALL, &context); // Unwind the native frames at the top of the stack UnwindNativeFrames(crashInfo, &context); if (pClrDataProcess != nullptr) { ReleaseHolder pTask; ReleaseHolder pStackwalk; // Get the managed stack walker for this thread if (SUCCEEDED(pClrDataProcess->GetTaskByOSThreadID(Tid(), &pTask))) { pTask->CreateStackWalk( CLRDATA_SIMPFRAME_UNRECOGNIZED | CLRDATA_SIMPFRAME_MANAGED_METHOD | CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE | CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE, &pStackwalk); } // For each managed frame (if any) if (pStackwalk != nullptr) { TRACE("Unwind: managed frames\n"); do { // Get the managed stack frame context if (pStackwalk->GetContext(CONTEXT_ALL, sizeof(context), nullptr, (BYTE *)&context) != S_OK) { TRACE("Unwind: stack walker GetContext FAILED\n"); break; } // Unwind all the native frames after the managed frame UnwindNativeFrames(crashInfo, &context); } while (pStackwalk->Next() == S_OK); } } return true; } bool ThreadInfo::GetRegistersWithPTrace() { struct iovec gpRegsVec = { &m_gpRegisters, sizeof(m_gpRegisters) }; if (ptrace((__ptrace_request)PTRACE_GETREGSET, m_tid, NT_PRSTATUS, &gpRegsVec) == -1) { fprintf(stderr, "ptrace(PTRACE_GETREGSET, %d, NT_PRSTATUS) FAILED %d (%s)\n", m_tid, errno, strerror(errno)); return false; } assert(sizeof(m_gpRegisters) == gpRegsVec.iov_len); struct iovec fpRegsVec = { &m_fpRegisters, sizeof(m_fpRegisters) }; if (ptrace((__ptrace_request)PTRACE_GETREGSET, m_tid, NT_FPREGSET, &fpRegsVec) == -1) { #if defined(__arm__) // Some aarch64 kernels may not support NT_FPREGSET for arm processes. We treat this failure as non-fatal. #else fprintf(stderr, "ptrace(PTRACE_GETREGSET, %d, NT_FPREGSET) FAILED %d (%s)\n", m_tid, errno, strerror(errno)); return false; #endif } assert(sizeof(m_fpRegisters) == fpRegsVec.iov_len); #if defined(__i386__) if (ptrace((__ptrace_request)PTRACE_GETFPXREGS, m_tid, nullptr, &m_fpxRegisters) == -1) { fprintf(stderr, "ptrace(GETFPXREGS, %d) FAILED %d (%s)\n", m_tid, errno, strerror(errno)); return false; } #elif defined(__arm__) && defined(__VFP_FP__) && !defined(__SOFTFP__) #if defined(ARM_VFPREGS_SIZE) assert(sizeof(m_vfpRegisters) == ARM_VFPREGS_SIZE); #endif if (ptrace((__ptrace_request)PTRACE_GETVFPREGS, m_tid, nullptr, &m_vfpRegisters) == -1) { fprintf(stderr, "ptrace(PTRACE_GETVFPREGS, %d) FAILED %d (%s)\n", m_tid, errno, strerror(errno)); return false; } #endif return true; } void ThreadInfo::SetRegisters(elf_prstatus *prstatus) { #if defined(__x86_64__) struct user_regs_struct *u_reg = (struct user_regs_struct *)&prstatus->pr_reg; m_gpRegisters.rip = u_reg->rip; m_gpRegisters.rbp = u_reg->rbp; m_gpRegisters.rsp = u_reg->rsp; m_gpRegisters.rax = u_reg->rax; m_gpRegisters.rbx = u_reg->rbx; m_gpRegisters.rcx = u_reg->rcx; m_gpRegisters.rdx = u_reg->rdx; m_gpRegisters.rsi = u_reg->rsi; m_gpRegisters.rdi = u_reg->rdi; m_gpRegisters.cs = u_reg->cs; m_gpRegisters.gs = u_reg->gs; m_gpRegisters.es = u_reg->es; m_gpRegisters.fs = u_reg->fs; m_gpRegisters.ds = u_reg->ds; m_gpRegisters.ss = u_reg->ss; m_gpRegisters.fs_base = u_reg->fs_base; m_gpRegisters.gs_base = u_reg->gs_base; m_gpRegisters.orig_rax = u_reg->orig_rax; m_gpRegisters.r8 = u_reg->r8; m_gpRegisters.r9 = u_reg->r9; m_gpRegisters.r10 = u_reg->r10; m_gpRegisters.r11 = u_reg->r11; m_gpRegisters.r12 = u_reg->r12; m_gpRegisters.r13 = u_reg->r13; m_gpRegisters.r14 = u_reg->r14; m_gpRegisters.r15 = u_reg->r15; m_gpRegisters.eflags = u_reg->eflags; #elif defined(__i386__) struct user_regs_struct *u_reg = (struct user_regs_struct *)&prstatus->pr_reg; m_gpRegisters.ebx = u_reg->ebx; m_gpRegisters.ecx = u_reg->ecx; m_gpRegisters.edx = u_reg->edx; m_gpRegisters.esi = u_reg->esi; m_gpRegisters.edi = u_reg->edi; m_gpRegisters.ebp = u_reg->ebp; m_gpRegisters.eax = u_reg->eax; m_gpRegisters.xds = u_reg->xds; m_gpRegisters.xes = u_reg->xes; m_gpRegisters.xfs = u_reg->xfs; m_gpRegisters.xgs = u_reg->xgs; m_gpRegisters.orig_eax = u_reg->orig_eax; m_gpRegisters.eip = u_reg->eip; m_gpRegisters.xcs = u_reg->xcs; m_gpRegisters.eflags = u_reg->eflags; m_gpRegisters.esp = u_reg->esp; m_gpRegisters.xss = u_reg->xss; #elif defined(__arm__) #define REGS_REGULAR_NUM 13 #define REG_FP 11 #define REG_IP 12 #define REG_SP 13 #define REG_LR 14 #define REG_PC 15 #define REG_SPSR 16 struct user_regs *u_reg = (struct user_regs *)&prstatus->pr_reg; m_gpRegisters.ARM_sp = u_reg->uregs[REG_SP]; m_gpRegisters.ARM_lr = u_reg->uregs[REG_LR]; m_gpRegisters.ARM_pc = u_reg->uregs[REG_PC]; m_gpRegisters.ARM_cpsr = u_reg->uregs[REG_SPSR]; m_gpRegisters.ARM_r0 = u_reg->uregs[0]; m_gpRegisters.ARM_ORIG_r0 = u_reg->uregs[0]; m_gpRegisters.ARM_r1 = u_reg->uregs[1]; m_gpRegisters.ARM_r2 = u_reg->uregs[2]; m_gpRegisters.ARM_r3 = u_reg->uregs[3]; m_gpRegisters.ARM_r4 = u_reg->uregs[4]; m_gpRegisters.ARM_r5 = u_reg->uregs[5]; m_gpRegisters.ARM_r6 = u_reg->uregs[6]; m_gpRegisters.ARM_r7 = u_reg->uregs[7]; m_gpRegisters.ARM_r8 = u_reg->uregs[8]; m_gpRegisters.ARM_r9 = u_reg->uregs[9]; m_gpRegisters.ARM_r10 = u_reg->uregs[10]; m_gpRegisters.ARM_fp = u_reg->uregs[REG_FP]; m_gpRegisters.ARM_ip = u_reg->uregs[REG_IP]; #endif } bool ThreadInfo::GetRegistersWithDataTarget(ICLRDataTarget* pDataTarget) { CONTEXT context; context.ContextFlags = CONTEXT_ALL; if (pDataTarget->GetThreadContext(m_tid, context.ContextFlags, sizeof(context), reinterpret_cast(&context)) != S_OK) { return false; } #if defined(__x86_64__) m_gpRegisters.rbp = context.Rbp; m_gpRegisters.rip = context.Rip; m_gpRegisters.cs = context.SegCs; m_gpRegisters.eflags = context.EFlags; m_gpRegisters.ss = context.SegSs; m_gpRegisters.rsp = context.Rsp; m_gpRegisters.rdi = context.Rdi; m_gpRegisters.rsi = context.Rsi; m_gpRegisters.rbx = context.Rbx; m_gpRegisters.rdx = context.Rdx; m_gpRegisters.rcx = context.Rcx; m_gpRegisters.rax = context.Rax; m_gpRegisters.orig_rax = context.Rax; m_gpRegisters.r8 = context.R8; m_gpRegisters.r9 = context.R9; m_gpRegisters.r10 = context.R10; m_gpRegisters.r11 = context.R11; m_gpRegisters.r12 = context.R12; m_gpRegisters.r13 = context.R13; m_gpRegisters.r14 = context.R14; m_gpRegisters.r15 = context.R15; m_gpRegisters.ds = context.SegDs; m_gpRegisters.es = context.SegEs; m_gpRegisters.fs = context.SegFs; m_gpRegisters.gs = context.SegGs; m_gpRegisters.fs_base = 0; m_gpRegisters.gs_base = 0; m_fpRegisters.cwd = context.FltSave.ControlWord; m_fpRegisters.swd = context.FltSave.StatusWord; m_fpRegisters.ftw = context.FltSave.TagWord; m_fpRegisters.fop = context.FltSave.ErrorOpcode; FPREG_ErrorOffset(m_fpRegisters) = context.FltSave.ErrorOffset; FPREG_ErrorSelector(m_fpRegisters) = context.FltSave.ErrorSelector; FPREG_DataOffset(m_fpRegisters) = context.FltSave.DataOffset; FPREG_DataSelector(m_fpRegisters) = context.FltSave.DataSelector; m_fpRegisters.mxcsr = context.FltSave.MxCsr; m_fpRegisters.mxcr_mask = context.FltSave.MxCsr_Mask; assert(sizeof(context.FltSave.FloatRegisters) == sizeof(m_fpRegisters.st_space)); memcpy(m_fpRegisters.st_space, context.FltSave.FloatRegisters, sizeof(m_fpRegisters.st_space)); assert(sizeof(context.FltSave.XmmRegisters) == sizeof(m_fpRegisters.xmm_space)); memcpy(m_fpRegisters.xmm_space, context.FltSave.XmmRegisters, sizeof(m_fpRegisters.xmm_space)); #elif defined(__aarch64__) // See MCREG maps in PAL's context.h assert(sizeof(m_gpRegisters.regs) == (sizeof(context.X) + sizeof(context.Fp) + sizeof(context.Lr))); memcpy(m_gpRegisters.regs, context.X, sizeof(context.X)); MCREG_Fp(m_gpRegisters) = context.Fp; MCREG_Lr(m_gpRegisters) = context.Lr; MCREG_Sp(m_gpRegisters) = context.Sp; MCREG_Pc(m_gpRegisters) = context.Pc; MCREG_Cpsr(m_gpRegisters) = context.Cpsr; assert(sizeof(m_fpRegisters.vregs) == sizeof(context.V)); memcpy(m_fpRegisters.vregs, context.V, sizeof(context.V)); m_fpRegisters.fpcr = context.Fpcr; m_fpRegisters.fpsr = context.Fpsr; #elif defined(__arm__) m_gpRegisters.ARM_sp = context.Sp; m_gpRegisters.ARM_lr = context.Lr; m_gpRegisters.ARM_pc = context.Pc; m_gpRegisters.ARM_cpsr = context.Cpsr; m_gpRegisters.ARM_r0 = context.R0; m_gpRegisters.ARM_ORIG_r0 = context.R0; m_gpRegisters.ARM_r1 = context.R1; m_gpRegisters.ARM_r2 = context.R2; m_gpRegisters.ARM_r3 = context.R3; m_gpRegisters.ARM_r4 = context.R4; m_gpRegisters.ARM_r5 = context.R5; m_gpRegisters.ARM_r6 = context.R6; m_gpRegisters.ARM_r7 = context.R7; m_gpRegisters.ARM_r8 = context.R8; m_gpRegisters.ARM_r9 = context.R9; m_gpRegisters.ARM_r10 = context.R10; m_gpRegisters.ARM_fp = context.R11; m_gpRegisters.ARM_ip = context.R12; #if defined(__VFP_FP__) && !defined(__SOFTFP__) m_vfpRegisters.fpscr = context.Fpscr; assert(sizeof(context.D) == sizeof(m_vfpRegisters.fpregs)); memcpy(m_vfpRegisters.fpregs, context.D, sizeof(context.D)); #endif #elif defined(__i386__) m_gpRegisters.ebp = context.Ebp; m_gpRegisters.eip = context.Eip; m_gpRegisters.eflags = context.EFlags; m_gpRegisters.esp = context.Esp; m_gpRegisters.edi = context.Edi; m_gpRegisters.esi = context.Esi; m_gpRegisters.ebx = context.Ebx; m_gpRegisters.edx = context.Edx; m_gpRegisters.ecx = context.Ecx; m_gpRegisters.eax = context.Eax; m_gpRegisters.orig_eax = context.Eax; #else #error Platform not supported #endif return true; } void ThreadInfo::GetThreadStack(CrashInfo& crashInfo) { uint64_t startAddress; size_t size; #if defined(__aarch64__) startAddress = MCREG_Sp(m_gpRegisters) & PAGE_MASK; #elif defined(__arm__) startAddress = m_gpRegisters.ARM_sp & PAGE_MASK; #elif defined(__x86_64__) startAddress = m_gpRegisters.rsp & PAGE_MASK; #elif defined(__i386__) startAddress = m_gpRegisters.esp & PAGE_MASK; #endif size = 4 * PAGE_SIZE; MemoryRegion search(0, startAddress, startAddress + PAGE_SIZE); const MemoryRegion* region = CrashInfo::SearchMemoryRegions(crashInfo.OtherMappings(), search); if (region != nullptr) { // Use the mapping found for the size of the thread's stack size = region->EndAddress() - startAddress; if (g_diagnostics) { TRACE("Thread %04x stack found in other mapping (size %08zx): ", m_tid, size); region->Trace(); } } crashInfo.InsertMemoryRegion(startAddress, size); } void ThreadInfo::GetThreadContext(uint32_t flags, CONTEXT* context) const { context->ContextFlags = flags; #if defined(__x86_64__) if ((flags & CONTEXT_CONTROL) == CONTEXT_CONTROL) { context->Rbp = m_gpRegisters.rbp; context->Rip = m_gpRegisters.rip; context->SegCs = m_gpRegisters.cs; context->EFlags = m_gpRegisters.eflags; context->SegSs = m_gpRegisters.ss; context->Rsp = m_gpRegisters.rsp; } if ((flags & CONTEXT_INTEGER) == CONTEXT_INTEGER) { context->Rdi = m_gpRegisters.rdi; context->Rsi = m_gpRegisters.rsi; context->Rbx = m_gpRegisters.rbx; context->Rdx = m_gpRegisters.rdx; context->Rcx = m_gpRegisters.rcx; context->Rax = m_gpRegisters.rax; context->R8 = m_gpRegisters.r8; context->R9 = m_gpRegisters.r9; context->R10 = m_gpRegisters.r10; context->R11 = m_gpRegisters.r11; context->R12 = m_gpRegisters.r12; context->R13 = m_gpRegisters.r13; context->R14 = m_gpRegisters.r14; context->R15 = m_gpRegisters.r15; } if ((flags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS) { context->SegDs = m_gpRegisters.ds; context->SegEs = m_gpRegisters.es; context->SegFs = m_gpRegisters.fs; context->SegGs = m_gpRegisters.gs; } if ((flags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) { context->FltSave.ControlWord = m_fpRegisters.cwd; context->FltSave.StatusWord = m_fpRegisters.swd; context->FltSave.TagWord = m_fpRegisters.ftw; context->FltSave.ErrorOpcode = m_fpRegisters.fop; context->FltSave.ErrorOffset = FPREG_ErrorOffset(m_fpRegisters); context->FltSave.ErrorSelector = FPREG_ErrorSelector(m_fpRegisters); context->FltSave.DataOffset = FPREG_DataOffset(m_fpRegisters); context->FltSave.DataSelector = FPREG_DataSelector(m_fpRegisters); context->FltSave.MxCsr = m_fpRegisters.mxcsr; context->FltSave.MxCsr_Mask = m_fpRegisters.mxcr_mask; assert(sizeof(context->FltSave.FloatRegisters) == sizeof(m_fpRegisters.st_space)); memcpy(context->FltSave.FloatRegisters, m_fpRegisters.st_space, sizeof(context->FltSave.FloatRegisters)); assert(sizeof(context->FltSave.XmmRegisters) == sizeof(m_fpRegisters.xmm_space)); memcpy(context->FltSave.XmmRegisters, m_fpRegisters.xmm_space, sizeof(context->FltSave.XmmRegisters)); } // TODO: debug registers? #elif defined(__aarch64__) if ((flags & CONTEXT_CONTROL) == CONTEXT_CONTROL) { context->Fp = MCREG_Fp(m_gpRegisters); context->Lr = MCREG_Lr(m_gpRegisters); context->Sp = MCREG_Sp(m_gpRegisters); context->Pc = MCREG_Pc(m_gpRegisters); context->Cpsr = MCREG_Cpsr(m_gpRegisters); } if ((flags & CONTEXT_INTEGER) == CONTEXT_INTEGER) { assert(sizeof(m_gpRegisters.regs) == (sizeof(context->X) + sizeof(context->Fp) + sizeof(context->Lr))); memcpy(context->X, m_gpRegisters.regs, sizeof(context->X)); } if ((flags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) { assert(sizeof(m_fpRegisters.vregs) == sizeof(context->V)); memcpy(context->V, m_fpRegisters.vregs, sizeof(context->V)); context->Fpcr = m_fpRegisters.fpcr; context->Fpsr = m_fpRegisters.fpsr; } #elif defined(__arm__) if ((flags & CONTEXT_CONTROL) == CONTEXT_CONTROL) { context->Sp = m_gpRegisters.ARM_sp; context->Lr = m_gpRegisters.ARM_lr; context->Pc = m_gpRegisters.ARM_pc; context->Cpsr = m_gpRegisters.ARM_cpsr; } if ((flags & CONTEXT_INTEGER) == CONTEXT_INTEGER) { context->R0 = m_gpRegisters.ARM_r0; context->R1 = m_gpRegisters.ARM_r1; context->R2 = m_gpRegisters.ARM_r2; context->R3 = m_gpRegisters.ARM_r3; context->R4 = m_gpRegisters.ARM_r4; context->R5 = m_gpRegisters.ARM_r5; context->R6 = m_gpRegisters.ARM_r6; context->R7 = m_gpRegisters.ARM_r7; context->R8 = m_gpRegisters.ARM_r8; context->R9 = m_gpRegisters.ARM_r9; context->R10 = m_gpRegisters.ARM_r10; context->R11 = m_gpRegisters.ARM_fp; context->R12 = m_gpRegisters.ARM_ip; } if ((flags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) { #if defined(__VFP_FP__) && !defined(__SOFTFP__) context->Fpscr = m_vfpRegisters.fpscr; assert(sizeof(context->D) == sizeof(m_vfpRegisters.fpregs)); memcpy(context->D, m_vfpRegisters.fpregs, sizeof(context->D)); #endif } #elif defined(__i386__) if ((flags & CONTEXT_CONTROL) == CONTEXT_CONTROL) { context->Ebp = m_gpRegisters.ebp; context->Eip = m_gpRegisters.eip; context->EFlags = m_gpRegisters.eflags; context->Esp = m_gpRegisters.esp; context->SegCs = m_gpRegisters.xcs; context->SegSs = m_gpRegisters.xss; context->SegGs_PAL_Undefined = m_gpRegisters.xgs; context->SegFs_PAL_Undefined = m_gpRegisters.xfs; context->SegEs_PAL_Undefined = m_gpRegisters.xes; context->SegDs_PAL_Undefined = m_gpRegisters.xds; } if ((flags & CONTEXT_INTEGER) == CONTEXT_INTEGER) { context->Edi = m_gpRegisters.edi; context->Esi = m_gpRegisters.esi; context->Ebx = m_gpRegisters.ebx; context->Edx = m_gpRegisters.edx; context->Ecx = m_gpRegisters.ecx; context->Eax = m_gpRegisters.eax; } #else #error Platform not supported #endif }