ntdll: Implement RtlCaptureContext on ARM64EC.

This commit is contained in:
Alexandre Julliard 2024-02-28 13:40:33 +01:00
parent 9591a36ee8
commit 31e3c4316d
3 changed files with 142 additions and 71 deletions

View file

@ -1559,12 +1559,75 @@ BOOLEAN WINAPI RtlIsEcCode( const void *ptr )
}
/* capture context information; helper for RtlCaptureContext */
static void __attribute__((used)) capture_context( CONTEXT *context, UINT cpsr, UINT fpcr, UINT fpsr )
{
RUNTIME_FUNCTION *func;
void *handler_data;
ULONG_PTR pc, base, frame;
CONTEXT unwind_context;
context->ContextFlags = CONTEXT_AMD64_FULL;
context->EFlags = cpsr_to_eflags( cpsr );
context->MxCsr = fpcsr_to_mxcsr( fpcr, fpsr );
context->FltSave.ControlWord = 0x27f;
context->FltSave.StatusWord = 0;
context->FltSave.MxCsr = context->MxCsr;
/* unwind one level to get register values from caller function */
unwind_context = *context;
pc = context->Rip - 4;
func = RtlLookupFunctionEntry( pc, &base, NULL );
RtlVirtualUnwind( UNW_FLAG_NHANDLER, base, pc, func, &unwind_context, &handler_data, &frame, NULL );
memcpy( &context->Rax, &unwind_context.Rax, offsetof(CONTEXT,FltSave) - offsetof(CONTEXT,Rax) );
}
/***********************************************************************
* RtlCaptureContext (NTDLL.@)
*/
void WINAPI RtlCaptureContext( CONTEXT *context )
void __attribute__((naked)) RtlCaptureContext( CONTEXT *context )
{
FIXME( "not implemented\n" );
asm( ".seh_proc RtlCaptureContext\n\t"
".seh_endprologue\n\t"
"stp x8, x0, [x0, #0x78]\n\t" /* context->Rax,Rcx */
"stp x1, x27, [x0, #0x88]\n\t" /* context->Rdx,Rbx */
"mov x1, sp\n\t"
"stp x1, x29, [x0, #0x98]\n\t" /* context->Rsp,Rbp */
"stp x25, x26, [x0, #0xa8]\n\t" /* context->Rsi,Rdi */
"stp x2, x3, [x0, #0xb8]\n\t" /* context->R8,R9 */
"stp x4, x5, [x0, #0xc8]\n\t" /* context->R10,R11 */
"stp x19, x20, [x0, #0xd8]\n\t" /* context->R12,R13 */
"stp x21, x22, [x0, #0xe8]\n\t" /* context->R14,R15 */
"str x30, [x0, #0xf8]\n\t" /* context->Rip */
"ubfx x1, x16, #0, #16\n\t"
"stp x30, x1, [x0, #0x120]\n\t" /* context->FloatRegisters[0] */
"ubfx x1, x16, #16, #16\n\t"
"stp x6, x1, [x0, #0x130]\n\t" /* context->FloatRegisters[1] */
"ubfx x1, x16, #32, #16\n\t"
"stp x7, x1, [x0, #0x140]\n\t" /* context->FloatRegisters[2] */
"ubfx x1, x16, #48, #16\n\t"
"stp x9, x1, [x0, #0x150]\n\t" /* context->FloatRegisters[3] */
"ubfx x1, x17, #0, #16\n\t"
"stp x10, x1, [x0, #0x160]\n\t" /* context->FloatRegisters[4] */
"ubfx x1, x17, #16, #16\n\t"
"stp x11, x1, [x0, #0x170]\n\t" /* context->FloatRegisters[5] */
"ubfx x1, x17, #32, #16\n\t"
"stp x12, x1, [x0, #0x180]\n\t" /* context->FloatRegisters[6] */
"ubfx x1, x17, #48, #16\n\t"
"stp x15, x1, [x0, #0x190]\n\t" /* context->FloatRegisters[7] */
"stp q0, q1, [x0, #0x1a0]\n\t" /* context->Xmm0,Xmm1 */
"stp q2, q3, [x0, #0x1c0]\n\t" /* context->Xmm2,Xmm3 */
"stp q4, q5, [x0, #0x1e0]\n\t" /* context->Xmm4,Xmm5 */
"stp q6, q7, [x0, #0x200]\n\t" /* context->Xmm6,Xmm7 */
"stp q8, q9, [x0, #0x220]\n\t" /* context->Xmm8,Xmm9 */
"stp q10, q11, [x0, #0x240]\n\t" /* context->Xmm10,Xmm11 */
"stp q12, q13, [x0, #0x260]\n\t" /* context->Xmm12,Xmm13 */
"stp q14, q15, [x0, #0x280]\n\t" /* context->Xmm14,Xmm15 */
"mrs x1, nzcv\n\t"
"mrs x2, fpcr\n\t"
"mrs x3, fpsr\n\t"
"b capture_context\n\t"
".seh_endproc" );
}

View file

@ -20,6 +20,7 @@
#include <stdarg.h>
#include <stdio.h>
#include <setjmp.h>
#include "ntstatus.h"
#define WIN32_NO_STATUS
@ -110,38 +111,6 @@ static void (WINAPI *pRtlGetUnloadEventTraceEx)(ULONG **element_size, ULONG **el
#endif
#if defined(__x86_64__)
typedef struct _SETJMP_FLOAT128
{
unsigned __int64 DECLSPEC_ALIGN(16) Part[2];
} SETJMP_FLOAT128;
typedef struct _JUMP_BUFFER
{
unsigned __int64 Frame;
unsigned __int64 Rbx;
unsigned __int64 Rsp;
unsigned __int64 Rbp;
unsigned __int64 Rsi;
unsigned __int64 Rdi;
unsigned __int64 R12;
unsigned __int64 R13;
unsigned __int64 R14;
unsigned __int64 R15;
unsigned __int64 Rip;
unsigned long MxCsr;
unsigned short FpCsr;
unsigned short Spare;
SETJMP_FLOAT128 Xmm6;
SETJMP_FLOAT128 Xmm7;
SETJMP_FLOAT128 Xmm8;
SETJMP_FLOAT128 Xmm9;
SETJMP_FLOAT128 Xmm10;
SETJMP_FLOAT128 Xmm11;
SETJMP_FLOAT128 Xmm12;
SETJMP_FLOAT128 Xmm13;
SETJMP_FLOAT128 Xmm14;
SETJMP_FLOAT128 Xmm15;
} _JUMP_BUFFER;
typedef union _UNWIND_CODE
{
@ -173,12 +142,14 @@ typedef struct _UNWIND_INFO
*/
} UNWIND_INFO;
static BOOL is_arm64ec;
static EXCEPTION_DISPOSITION (WINAPI *p__C_specific_handler)(EXCEPTION_RECORD*, ULONG64, CONTEXT*, DISPATCHER_CONTEXT*);
static VOID (WINAPI *pRtlCaptureContext)(CONTEXT*);
static VOID (CDECL *pRtlRestoreContext)(CONTEXT*, EXCEPTION_RECORD*);
static NTSTATUS (WINAPI *pRtlWow64GetThreadContext)(HANDLE, WOW64_CONTEXT *);
static NTSTATUS (WINAPI *pRtlWow64SetThreadContext)(HANDLE, const WOW64_CONTEXT *);
static NTSTATUS (WINAPI *pRtlWow64GetCpuAreaInfo)(WOW64_CPURESERVED*,ULONG,WOW64_CPU_AREA_INFO*);
static NTSTATUS (WINAPI *pRtlGetNativeSystemInformation)(SYSTEM_INFORMATION_CLASS,void*,ULONG,ULONG*);
static int (CDECL *p_setjmp)(_JUMP_BUFFER*);
#endif
@ -3745,34 +3716,53 @@ static void test_thread_context(void)
memset( &expect, 0xcc, sizeof(expect) );
func_ptr( &context, 0, &expect, pRtlCaptureContext );
ok( context.ContextFlags == (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT),
"wrong flags %08lx\n", context.ContextFlags );
COMPARE( Rax );
if (is_arm64ec)
{
ok( context.ContextFlags == (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT),
"wrong flags %08lx\n", context.ContextFlags );
ok( (context.EFlags & ~0xc5) == (expect.EFlags & ~0xc5), "wrong EFlags %lx / %I64x\n",
context.EFlags, expect.EFlags );
ok( context.SegCs == 0xcccc, "wrong SegCs %x\n", context.SegCs);
ok( context.SegDs == 0xcccc, "wrong SegDs %x\n", context.SegDs);
ok( context.SegEs == 0xcccc, "wrong SegEs %x\n", context.SegEs);
ok( context.SegFs == 0xcccc, "wrong SegFs %x\n", context.SegFs);
ok( context.SegGs == 0xcccc, "wrong SegGs %x\n", context.SegGs);
ok( context.SegSs == 0xcccc, "wrong SegSs %x\n", context.SegSs);
}
else
{
ok( context.ContextFlags == (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT),
"wrong flags %08lx\n", context.ContextFlags );
COMPARE( Rax );
COMPARE( Rcx );
COMPARE( Rdx );
COMPARE( R8 );
COMPARE( R9 );
COMPARE( R10 );
COMPARE( R11 );
COMPARE( EFlags );
COMPARE( SegCs );
COMPARE( SegDs );
COMPARE( SegEs );
COMPARE( SegFs );
COMPARE( SegGs );
COMPARE( SegSs );
ok( !memcmp( &context.FltSave, &expect.FltSave, offsetof( XMM_SAVE_AREA32, XmmRegisters )),
"wrong FltSave\n" );
}
COMPARE( Rbx );
COMPARE( Rcx );
COMPARE( Rdx );
COMPARE( Rsi );
COMPARE( Rdi );
COMPARE( R8 );
COMPARE( R9 );
COMPARE( R10 );
COMPARE( R11 );
COMPARE( R12 );
COMPARE( R13 );
COMPARE( R14 );
COMPARE( R15 );
COMPARE( Rbp );
COMPARE( Rsp );
COMPARE( EFlags );
COMPARE( MxCsr );
COMPARE( SegCs );
COMPARE( SegDs );
COMPARE( SegEs );
COMPARE( SegFs );
COMPARE( SegGs );
COMPARE( SegSs );
ok( !memcmp( &context.FltSave, &expect.FltSave, offsetof( XMM_SAVE_AREA32, XmmRegisters )),
"wrong FltSave\n" );
COMPARE( FltSave.MxCsr );
COMPARE( FltSave.ControlWord );
COMPARE( FltSave.StatusWord );
for (i = 0; i < 16; i++)
ok( !memcmp( &context.Xmm0 + i, &expect.FltSave.XmmRegisters[i], sizeof(context.Xmm0) ),
"wrong xmm%u\n", i );
@ -3786,15 +3776,32 @@ static void test_thread_context(void)
status = func_ptr( GetCurrentThread(), &context, &expect, pNtGetContextThread );
ok( status == STATUS_SUCCESS, "NtGetContextThread failed %08lx\n", status );
/* other registers are not preserved */
COMPARE( Rbx );
COMPARE( Rsi );
COMPARE( Rdi );
COMPARE( R12 );
COMPARE( R13 );
COMPARE( R14 );
COMPARE( R15 );
COMPARE( Rbp );
if (is_arm64ec)
{
/* Rsp is the stack upon entry to the ARM64 NtGetContextThread syscall */
ok( context.Rsp <= expect.Rsp - sizeof(ARM64_NT_CONTEXT) && context.Rsp >= expect.Rsp - 0x1000,
"wrong Rsp %p/%p\n", (void *)context.Rsp, (void *)expect.Rsp );
}
else
{
/* other registers are not preserved */
COMPARE( Rbx );
COMPARE( Rsi );
COMPARE( Rdi );
COMPARE( R12 );
COMPARE( R13 );
COMPARE( R14 );
COMPARE( R15 );
COMPARE( Rbp );
/* Rsp is the stack upon entry to NtGetContextThread */
ok( context.Rsp == expect.Rsp - 8,
"wrong Rsp %p/%p\n", (void *)context.Rsp, (void *)expect.Rsp );
/* Rip is somewhere close to the NtGetContextThread implementation */
ok( (char *)context.Rip >= (char *)pNtGetContextThread - 0x40000 &&
(char *)context.Rip <= (char *)pNtGetContextThread + 0x40000,
"wrong Rip %p/%p\n", (void *)context.Rip, (void *)pNtGetContextThread );
}
COMPARE( MxCsr );
COMPARE( SegCs );
COMPARE( SegDs );
@ -3812,13 +3819,6 @@ static void test_thread_context(void)
for (i = 6; i < 16; i++)
ok( !memcmp( &context.Xmm0 + i, &expect.FltSave.XmmRegisters[i], sizeof(context.Xmm0) ),
"wrong xmm%u\n", i );
/* Rsp is the stack upon entry to NtGetContextThread */
ok( context.Rsp == expect.Rsp - 8,
"wrong Rsp %p/%p\n", (void *)context.Rsp, (void *)expect.Rsp );
/* Rip is somewhere close to the NtGetContextThread implementation */
ok( (char *)context.Rip >= (char *)pNtGetContextThread - 0x40000 &&
(char *)context.Rip <= (char *)pNtGetContextThread + 0x40000,
"wrong Rip %p/%p\n", (void *)context.Rip, (void *)pNtGetContextThread );
#undef COMPARE
}
@ -10116,14 +10116,22 @@ START_TEST(exception)
#define X(f) p##f = (void*)GetProcAddress(hntdll, #f)
X(__C_specific_handler);
X(RtlCaptureContext);
X(RtlRestoreContext);
X(RtlWow64GetThreadContext);
X(RtlWow64SetThreadContext);
X(RtlWow64GetCpuAreaInfo);
X(RtlGetNativeSystemInformation);
#undef X
p_setjmp = (void *)GetProcAddress( hmsvcrt, "_setjmp" );
if (pRtlGetNativeSystemInformation)
{
SYSTEM_CPU_INFORMATION info;
ULONG len;
if (!pRtlGetNativeSystemInformation( SystemCpuInformation, &info, sizeof(info), &len ))
is_arm64ec = (info.ProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64);
}
test_exceptions();
test_rtlraiseexception();
test_debug_registers();

View file

@ -63,7 +63,7 @@ static inline UINT eflags_to_cpsr( UINT eflags )
static inline UINT cpsr_to_eflags( UINT cpsr )
{
UINT ret = 0;
UINT ret = 0x202;
if (cpsr & 0x10000000) ret |= 0x0800; /* overflow */
if (cpsr & 0x20000000) ret |= 0x0001; /* carry */