diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 6175bb9b230..b94ad9c4d91 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -88,6 +88,30 @@ WINE_DEFAULT_DEBUG_CHANNEL(seh); #include static inline int arch_prctl( int func, void *ptr ) { return syscall( __NR_arch_prctl, func, ptr ); } +extern int CDECL alloc_fs_sel( int sel, void *base ); +__ASM_GLOBAL_FUNC( alloc_fs_sel, + /* switch to 32-bit stack */ + "pushq %rbx\n\t" + "pushq %rdi\n\t" + "movq %rsp,%rdi\n\t" + "movq %gs:0x8,%rsp\n\t" /* NtCurrentTeb()->Tib.StackBase */ + "subl $0x10,%esp\n\t" + /* setup modify_ldt struct on 32-bit stack */ + "movl %ecx,(%rsp)\n\t" /* entry_number */ + "movl %edx,4(%rsp)\n\t" /* base */ + "movl $~0,8(%rsp)\n\t" /* limit */ + "movl $0x41,12(%rsp)\n\t" /* seg_32bit | usable */ + /* invoke 32-bit syscall */ + "movl %esp,%ebx\n\t" + "movl $0xf3,%eax\n\t" /* SYS_set_thread_area */ + "int $0x80\n\t" + /* restore stack */ + "movl (%rsp),%eax\n\t" /* entry_number */ + "movq %rdi,%rsp\n\t" + "popq %rdi\n\t" + "popq %rbx\n\t" + "ret" ); + #ifndef FP_XSTATE_MAGIC1 #define FP_XSTATE_MAGIC1 0x46505853 #endif @@ -367,6 +391,7 @@ static inline void context_init_xstate( CONTEXT *context, void *xstate_buffer ) static USHORT cs32_sel; /* selector for %cs in 32-bit mode */ static USHORT cs64_sel; /* selector for %cs in 64-bit mode */ static USHORT ds64_sel; /* selector for %ds/%es/%ss in 64-bit mode */ +static USHORT fs32_sel; /* selector for %fs in 32-bit mode */ /*********************************************************************** * Definitions for Dwarf unwind tables @@ -1911,7 +1936,7 @@ NTSTATUS set_thread_wow64_context( HANDLE handle, const void *ctx, ULONG size ) { wow_frame->SegDs = ds64_sel; wow_frame->SegEs = ds64_sel; - wow_frame->SegFs = 0; /* FIXME */ + wow_frame->SegFs = fs32_sel; wow_frame->SegGs = ds64_sel; } if (flags & CONTEXT_I386_DEBUG_REGISTERS) @@ -2743,6 +2768,7 @@ void signal_init_thread( TEB *teb ) #if defined __linux__ arch_prctl( ARCH_SET_GS, teb ); arch_prctl( ARCH_GET_FS, &amd64_thread_data()->pthread_teb ); + if (fs32_sel) alloc_fs_sel( fs32_sel >> 3, (char *)teb + teb->WowTebOffset ); #elif defined (__FreeBSD__) || defined (__FreeBSD_kernel__) amd64_set_gsbase( teb ); #elif defined(__NetBSD__) @@ -2793,7 +2819,15 @@ void signal_init_process(void) #ifdef __linux__ if (NtCurrentTeb()->WowTebOffset) { + void *teb32 = (char *)NtCurrentTeb() + NtCurrentTeb()->WowTebOffset; + int sel; + cs32_sel = 0x23; + if ((sel = alloc_fs_sel( -1, teb32 )) != -1) + { + fs32_sel = (sel << 3) | 3; + } + else ERR( "failed to allocate %%fs selector\n" ); } #endif @@ -2842,6 +2876,7 @@ void DECLSPEC_HIDDEN call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, B context.SegCs = cs64_sel; context.SegDs = ds64_sel; context.SegEs = ds64_sel; + context.SegFs = fs32_sel; context.SegGs = ds64_sel; context.SegSs = ds64_sel; context.EFlags = 0x200;