ntdll: Switch to the kernel stack for syscalls on ARM.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2021-06-11 11:24:28 +02:00
parent 08c4419a49
commit 7954b86f6b
2 changed files with 200 additions and 123 deletions

View File

@ -172,20 +172,31 @@ enum arm_trap_code
struct syscall_frame
{
DWORD pad;
DWORD cpsr;
DWORD r4;
DWORD r5;
DWORD r6;
DWORD r7;
DWORD r8;
DWORD r9;
DWORD r10;
DWORD r11;
DWORD ret_addr;
DWORD thunk_addr;
DWORD r0; /* 000 */
DWORD r1; /* 004 */
DWORD r2; /* 008 */
DWORD r3; /* 00c */
DWORD r4; /* 010 */
DWORD r5; /* 014 */
DWORD r6; /* 018 */
DWORD r7; /* 01c */
DWORD r8; /* 020 */
DWORD r9; /* 024 */
DWORD r10; /* 028 */
DWORD r11; /* 02c */
DWORD r12; /* 030 */
DWORD pc; /* 034 */
DWORD sp; /* 038 */
DWORD lr; /* 03c */
DWORD cpsr; /* 040 */
DWORD restore_flags; /* 044 */
DWORD fpscr; /* 048 */
DWORD align; /* 04c */
ULONGLONG d[32]; /* 050 */
};
C_ASSERT( sizeof( struct syscall_frame ) == 0x150);
struct arm_thread_data
{
void *exit_frame; /* 1d4 exit frame pointer */
@ -201,6 +212,12 @@ static inline struct arm_thread_data *arm_thread_data(void)
return (struct arm_thread_data *)ntdll_get_thread_data()->cpu_data;
}
static BOOL is_inside_syscall( ucontext_t *sigcontext )
{
return ((char *)SP_sig(sigcontext) >= (char *)ntdll_get_thread_data()->kernel_stack &&
(char *)SP_sig(sigcontext) <= (char *)arm_thread_data()->syscall_frame);
}
/***********************************************************************
* unwind_builtin_dll
@ -301,28 +318,6 @@ static void restore_context( const CONTEXT *context, ucontext_t *sigcontext )
}
/***********************************************************************
* set_cpu_context
*
* Set the new CPU context.
*/
void DECLSPEC_HIDDEN set_cpu_context( const CONTEXT *context );
__ASM_GLOBAL_FUNC( set_cpu_context,
"ldr r2, [r0, #0x44]\n\t" /* context->Cpsr */
"tst r2, #0x20\n\t" /* thumb? */
"ldr r1, [r0, #0x40]\n\t" /* context->Pc */
"ite ne\n\t"
"orrne r1, r1, #1\n\t" /* Adjust PC according to thumb */
"biceq r1, r1, #1\n\t" /* Adjust PC according to arm */
"msr CPSR_f, r2\n\t"
"ldr lr, [r0, #0x3c]\n\t" /* context->Lr */
"ldr sp, [r0, #0x38]\n\t" /* context->Sp */
"push {r1}\n\t"
"add r0, #0x4\n\t"
"ldm r0, {r0-r12}\n\t" /* context->R0..R12 */
"pop {pc}" )
/***********************************************************************
* signal_restore_full_cpu_context
*
@ -330,6 +325,7 @@ __ASM_GLOBAL_FUNC( set_cpu_context,
*/
void signal_restore_full_cpu_context(void)
{
arm_thread_data()->syscall_frame->restore_flags |= CONTEXT_INTEGER;
}
@ -340,15 +336,46 @@ void signal_restore_full_cpu_context(void)
NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context )
{
NTSTATUS ret;
BOOL self;
struct syscall_frame *frame = arm_thread_data()->syscall_frame;
DWORD flags = context->ContextFlags & ~CONTEXT_ARM;
BOOL self = (handle == GetCurrentThread());
ret = set_thread_context( handle, context, &self, IMAGE_FILE_MACHINE_ARMNT );
if (self && ret == STATUS_SUCCESS)
if (!self)
{
arm_thread_data()->syscall_frame = NULL;
set_cpu_context( context );
ret = set_thread_context( handle, context, &self, IMAGE_FILE_MACHINE_ARMNT );
if (ret || !self) return ret;
}
return ret;
if (flags & CONTEXT_INTEGER)
{
frame->r0 = context->R0;
frame->r1 = context->R1;
frame->r2 = context->R2;
frame->r3 = context->R3;
frame->r4 = context->R4;
frame->r5 = context->R5;
frame->r6 = context->R6;
frame->r7 = context->R7;
frame->r8 = context->R8;
frame->r9 = context->R9;
frame->r10 = context->R10;
frame->r11 = context->R11;
frame->r12 = context->R12;
}
if (flags & CONTEXT_CONTROL)
{
frame->sp = context->Sp;
frame->lr = context->Lr;
frame->pc = context->Pc & ~1;
frame->cpsr = context->Cpsr;
if (context->Cpsr & 0x20) frame->pc |= 1; /* thumb */
}
if (flags & CONTEXT_FLOATING_POINT)
{
frame->fpscr = context->Fpscr;
memcpy( frame->d, context->u.D, sizeof(context->u.D) );
}
frame->restore_flags |= flags & ~CONTEXT_INTEGER;
return STATUS_SUCCESS;
}
@ -373,10 +400,10 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context )
{
if (needed_flags & CONTEXT_INTEGER)
{
context->R0 = 0;
context->R1 = 0;
context->R2 = 0;
context->R3 = 0;
context->R0 = frame->r0;
context->R1 = frame->r1;
context->R2 = frame->r2;
context->R3 = frame->r3;
context->R4 = frame->r4;
context->R5 = frame->r5;
context->R6 = frame->r6;
@ -385,18 +412,23 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context )
context->R9 = frame->r9;
context->R10 = frame->r10;
context->R11 = frame->r11;
context->R12 = 0;
context->R12 = frame->r12;
context->ContextFlags |= CONTEXT_INTEGER;
}
if (needed_flags & CONTEXT_CONTROL)
{
context->Sp = (DWORD)(frame + 1);
context->Lr = frame->thunk_addr;
context->Pc = frame->thunk_addr;
context->Sp = frame->sp;
context->Lr = frame->lr;
context->Pc = frame->pc;
context->Cpsr = frame->cpsr;
context->ContextFlags |= CONTEXT_CONTROL;
}
if (needed_flags & CONTEXT_FLOATING_POINT) FIXME( "floating point not implemented\n" );
if (needed_flags & CONTEXT_FLOATING_POINT)
{
context->Fpscr = frame->fpscr;
memcpy( context->u.D, frame->d, sizeof(frame->d) );
context->ContextFlags |= CONTEXT_FLOATING_POINT;
}
}
return STATUS_SUCCESS;
}
@ -464,66 +496,70 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
/***********************************************************************
* call_user_apc_dispatcher
*/
__ASM_GLOBAL_FUNC( call_user_apc_dispatcher,
"mov r5, r1\n\t" /* arg1 */
"mov r6, r2\n\t" /* arg2 */
"mov r7, r3\n\t" /* arg3 */
"ldr r8, [sp]\n\t" /* func */
"ldr r9, [sp, #4]\n\t" /* dispatcher */
"ldr r10, [sp, #8]\n\t" /* status */
"mrc p15, 0, r11, c13, c0, 2\n\t" /* NtCurrentTeb() */
"cmp r0, #0\n\t" /* context_ptr */
"beq 1f\n\t"
"ldr r0, [r0, #0x38]\n\t" /* context_ptr->Sp */
"sub r0, r0, #0x1c8\n\t" /* sizeof(CONTEXT) + offsetof(frame,r4) */
"mov ip, #0\n\t"
"str ip, [r11, #0x1d8]\n\t" /* arm_thread_data()->syscall_frame */
"mov sp, r0\n\t"
"b 2f\n"
"1:\tldr r0, [r11, #0x1d8]\n\t"
"sub r0, #0x1a0\n\t"
"mov sp, r0\n\t"
"mov r0, #3\n\t"
"movt r0, #32\n\t"
"str r0, [sp]\n\t" /* context.ContextFlags = CONTEXT_FULL */
"mov r1, sp\n\t"
"mov r0, #~1\n\t"
"bl " __ASM_NAME("NtGetContextThread") "\n\t"
"str r10, [sp, #4]\n\t" /* context.R0 = status */
"mov r0, sp\n\t"
"mov ip, #0\n\t"
"str ip, [r11, #0x1d8]\n\t"
"2:\tmov r1, r5\n\t" /* arg1 */
"mov r2, r6\n\t" /* arg2 */
"mov r3, r7\n\t" /* arg3 */
"push {r8, r9}\n\t" /* func */
"ldr lr, [r0, #0x3c]\n\t" /* context.Lr */
"bx r9" )
NTSTATUS WINAPI call_user_apc_dispatcher( CONTEXT *context, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3,
PNTAPCFUNC func,
void (WINAPI *dispatcher)(CONTEXT*,ULONG_PTR,ULONG_PTR,ULONG_PTR,PNTAPCFUNC),
NTSTATUS status )
{
struct syscall_frame *frame = arm_thread_data()->syscall_frame;
ULONG sp = context ? context->Sp : frame->sp;
struct apc_stack_layout
{
void *func;
void *align;
CONTEXT context;
} *stack;
sp &= ~15;
stack = (struct apc_stack_layout *)sp - 1;
if (context)
{
memmove( &stack->context, context, sizeof(stack->context) );
NtSetContextThread( GetCurrentThread(), &stack->context );
}
else
{
stack->context.ContextFlags = CONTEXT_FULL;
NtGetContextThread( GetCurrentThread(), &stack->context );
stack->context.R0 = status;
}
frame->sp = (DWORD)stack;
frame->pc = (DWORD)dispatcher;
frame->r0 = (DWORD)&stack->context;
frame->r1 = arg1;
frame->r2 = arg2;
frame->r3 = arg3;
stack->func = func;
frame->restore_flags |= CONTEXT_CONTROL | CONTEXT_INTEGER;
return status;
}
/***********************************************************************
* call_raise_user_exception_dispatcher
*/
__ASM_GLOBAL_FUNC( call_raise_user_exception_dispatcher,
"mov r2, r0\n\t" /* dispatcher */
"b " __ASM_NAME("call_user_exception_dispatcher") )
void WINAPI call_raise_user_exception_dispatcher( NTSTATUS (WINAPI *dispatcher)(void) )
{
arm_thread_data()->syscall_frame->pc = (DWORD)dispatcher;
}
/***********************************************************************
* call_user_exception_dispatcher
*/
__ASM_GLOBAL_FUNC( call_user_exception_dispatcher,
"mrc p15, 0, r7, c13, c0, 2\n\t" /* NtCurrentTeb() */
"ldr r3, [r7, #0x1d8]\n\t" /* arm_thread_data()->syscall_frame */
"mov ip, #0\n\t"
"str ip, [r7, #0x1d8]\n\t"
"add r3, r3, #8\n\t"
"ldm r3, {r5-r11}\n\t"
"ldr r4, [r3, #32]\n\t"
"ldr lr, [r3, #36]\n\t"
"add r3, #40\n\t"
"mov sp, r3\n\t"
"bx r2" )
NTSTATUS WINAPI call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context,
NTSTATUS (WINAPI *dispatcher)(EXCEPTION_RECORD*,CONTEXT*) )
{
struct syscall_frame *frame = arm_thread_data()->syscall_frame;
NTSTATUS status = NtSetContextThread( GetCurrentThread(), context );
if (status) return status;
frame->r0 = (DWORD)rec;
frame->r1 = (DWORD)context;
frame->pc = (DWORD)dispatcher;
frame->restore_flags |= CONTEXT_INTEGER | CONTEXT_CONTROL;
return status;
}
/***********************************************************************
@ -536,7 +572,7 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec )
struct syscall_frame *frame = arm_thread_data()->syscall_frame;
DWORD i;
if (!frame) return FALSE;
if (!is_inside_syscall( context )) return FALSE;
TRACE( "code=%x flags=%x addr=%p pc=%08x tid=%04x\n",
rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress,
@ -564,7 +600,7 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec )
}
else
{
TRACE( "returning to user mode ip=%08x ret=%08x\n", frame->ret_addr, rec->ExceptionCode );
TRACE( "returning to user mode ip=%08x ret=%08x\n", frame->pc, rec->ExceptionCode );
REGn_sig(0, context) = rec->ExceptionCode;
REGn_sig(4, context) = frame->r4;
REGn_sig(5, context) = frame->r5;
@ -574,11 +610,10 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec )
REGn_sig(9, context) = frame->r9;
REGn_sig(10, context) = frame->r10;
FP_sig(context) = frame->r11;
LR_sig(context) = frame->thunk_addr;
SP_sig(context) = (DWORD)&frame->r4;
PC_sig(context) = frame->thunk_addr;
LR_sig(context) = frame->lr;
SP_sig(context) = frame->sp;
PC_sig(context) = frame->pc;
CPSR_sig(context) = frame->cpsr;
arm_thread_data()->syscall_frame = NULL;
}
return TRUE;
}
@ -762,9 +797,19 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext )
{
CONTEXT context;
save_context( &context, sigcontext );
wait_suspend( &context );
restore_context( &context, sigcontext );
if (is_inside_syscall( sigcontext ))
{
context.ContextFlags = CONTEXT_FULL;
NtGetContextThread( GetCurrentThread(), &context );
wait_suspend( &context );
NtSetContextThread( GetCurrentThread(), &context );
}
else
{
save_context( &context, sigcontext );
wait_suspend( &context );
restore_context( &context, sigcontext );
}
}
@ -828,6 +873,9 @@ void signal_init_thread( TEB *teb )
void signal_init_process(void)
{
struct sigaction sig_act;
void *kernel_stack = (char *)ntdll_get_thread_data()->kernel_stack + kernel_stack_size;
arm_thread_data()->syscall_frame = (struct syscall_frame *)kernel_stack - 1;
sig_act.sa_mask = server_block_set;
sig_act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
@ -911,8 +959,13 @@ __ASM_GLOBAL_FUNC( signal_start_thread,
/* store exit frame */
"ldr r3, [sp, #40]\n\t" /* teb */
"str sp, [r3, #0x1d4]\n\t" /* arm_thread_data()->exit_frame */
/* set syscall frame */
"ldr r6, [r3, #0x1d8]\n\t" /* arm_thread_data()->syscall_frame */
"cbnz r6, 1f\n\t"
"sub r6, sp, #0x150\n\t" /* sizeof(struct syscall_frame) */
"str r6, [r3, #0x1d8]\n\t" /* arm_thread_data()->syscall_frame */
/* switch to thread stack */
"ldr r4, [r3, #4]\n\t" /* teb->Tib.StackBase */
"1:\tldr r4, [r3, #4]\n\t" /* teb->Tib.StackBase */
"sub r4, #0x1000\n\t"
"mov sp, r4\n\t"
/* attach dlls */

View File

@ -1665,15 +1665,27 @@ static void output_syscall_dispatcher(void)
output( "\tjmp 2b\n" );
break;
case CPU_ARM:
output( "\tpush {r4-r11}\n" );
output( "\tadd r6, sp, #40\n" ); /* stack parameters */
output( "\tsub sp, sp, #8\n" );
output( "\tmrc p15, 0, r7, c13, c0, 2\n" ); /* NtCurrentTeb() */
output( "\tadd r7, #0x1d8\n" ); /* arm_thread_data()->syscall_frame */
output( "\tmrc p15, 0, r1, c13, c0, 2\n" ); /* NtCurrentTeb() */
output( "\tldr r1, [r1, #0x1d8]\n" ); /* arm_thread_data()->syscall_frame */
output( "\tadd r0, r1, #0x10\n" );
output( "\tstm r0, {r4-r12,lr}\n" );
output( "\tstr sp, [r1, #0x38]\n" );
output( "\tstr r3, [r1, #0x3c]\n" );
output( "\tmrs r0, CPSR\n" );
output( "\tbfi r0, lr, #5, #1\n" ); /* set thumb bit */
output( "\tstr r0, [sp, #4]\n" );
output( "\tstr sp, [r7]\n" ); /* syscall frame */
output( "\tstr r0, [r1, #0x40]\n" );
output( "\tmov r0, #0\n" );
output( "\tstr r0, [r1, #0x44]\n" ); /* frame->restore_flags */
if (strcmp( float_abi_option, "soft" ))
{
output( "\tvmrs r0, fpscr\n" );
output( "\tstr r0, [r1, #0x48]\n" );
output( "\tadd r0, r1, #0x50\n" );
output( "\tvstm r0, {d0-d15}\n" );
}
output( "\tmov r6, sp\n" );
output( "\tmov sp, r1\n" );
output( "\tmov r8, r1\n" );
output( "\tldr r5, 6f\n");
if (UsePIC) output( "1:\tadd r5, pc\n");
output( "\tubfx r4, ip, #12, #2\n" ); /* syscall table number */
@ -1698,11 +1710,24 @@ static void output_syscall_dispatcher(void)
output( "\tldr r5, [r4]\n"); /* table->ServiceTable */
output( "\tldr ip, [r5, ip, lsl #2]\n");
output( "\tblx ip\n");
output( "4:\tmov ip, #0\n" );
output( "\tstr ip, [r7]\n" );
output( "\tsub ip, r6, #40\n" );
output( "\tmov sp, ip\n" );
output( "\tpop {r4-r12,pc}\n" );
output( "\tldr ip, [r8, #0x44]\n" ); /* frame->restore_flags */
if (strcmp( float_abi_option, "soft" ))
{
output( "\ttst ip, #4\n" ); /* CONTEXT_FLOATING_POINT */
output( "\tbeq 3f\n" );
output( "\tldr r4, [r8, #0x48]\n" );
output( "\tvmsr fpscr, r4\n" );
output( "\tadd r4, r8, #0x50\n" );
output( "\tvldm r4, {d0-d15}\n" );
output( "3:\n" );
}
output( "\ttst ip, #2\n" ); /* CONTEXT_INTEGER */
output( "\tit ne\n" );
output( "\tldmne r8, {r0-r3}\n" );
output( "4:\tldr lr, [r8, #0x3c]\n" );
output( "\tldr sp, [r8, #0x38]\n" );
output( "\tadd r8, r8, #0x10\n" );
output( "\tldm r8, {r4-r12,pc}\n" );
output( "5:\tmovw r0, #0x%x\n", invalid_param & 0xffff );
output( "\tmovt r0, #0x%x\n", invalid_param >> 16 );
output( "\tb 4b\n" );
@ -1940,7 +1965,7 @@ void output_syscalls( DLLSPEC *spec )
output( "\tmov r3, lr\n" );
output( "\tbl %s\n", asm_name("__wine_syscall") );
output( "\tadd sp, #16\n" );
output( "\tbx ip\n" );
output( "\tbx lr\n" );
break;
case CPU_ARM64:
output( "\tmov x8, #%u\n", i );
@ -1969,7 +1994,6 @@ void output_syscalls( DLLSPEC *spec )
output( "\t.align %d\n", get_alignment(16) );
output( "\t%s\n", func_declaration("__wine_syscall") );
output( "%s:\n", asm_name("__wine_syscall") );
output( "\tpush {r3,lr}\n" );
if (UsePIC)
{
output( "\tldr r0, 2f\n");