From: Alexander Shaikhulin iwtcex@gmail.com
This mostly involves fs/fsbase micromanagement. --- dlls/ntdll/unix/signal_x86_64.c | 147 ++++++++++++++++++++++++++++++-- 1 file changed, 138 insertions(+), 9 deletions(-)
diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 27b6e1d34e8..ac9408ec912 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -152,6 +152,9 @@ __ASM_GLOBAL_FUNC( alloc_fs_sel,
#elif defined(__FreeBSD__) || defined (__FreeBSD_kernel__)
+#include <machine/cpufunc.h> +#include <machine/segments.h> +#include <machine/specialreg.h> #include <machine/trap.h>
#define RAX_sig(context) ((context)->uc_mcontext.mc_rax) @@ -470,7 +473,7 @@ static inline struct amd64_thread_data *amd64_thread_data(void) return (struct amd64_thread_data *)ntdll_get_thread_data()->cpu_data; }
-#if defined(__linux__) || defined(__APPLE__) +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) static inline TEB *get_current_teb(void) { unsigned long rsp; @@ -1647,7 +1650,7 @@ __ASM_GLOBAL_FUNC( call_user_mode_callback, "movq %rsp,0x328(%r8)\n\t" /* amd64_thread_data()->syscall_frame */ /* switch to user stack */ "movq %rdi,%rsp\n\t" /* user_rsp */ -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) "testl $4,%r14d\n\t" /* SYSCALL_HAVE_PTHREAD_TEB */ "jz 1f\n\t" "movw 0x338(%r8),%fs\n" /* amd64_thread_data()->fs */ @@ -2375,6 +2378,16 @@ static void ldt_set_entry( WORD sel, LDT_ENTRY entry )
#if defined(__APPLE__) if (i386_set_ldt(index, (union ldt_entry *)&entry, 1) < 0) perror("i386_set_ldt"); +#elif defined(__FreeBSD__) + struct i386_ldt_args p; + p.start = index; + p.descs = (struct user_segment_descriptor *)&entry; + p.num = 1; + if (sysarch(I386_SET_LDT, &p) == -1) + { + perror("i386_set_ldt"); + exit(1); + } #else fprintf( stderr, "No LDT support on this platform\n" ); exit(1); @@ -2485,6 +2498,38 @@ static void *mac_thread_gsbase(void) } #endif
+#ifdef __FreeBSD__ +static __siginfohandler_t *libthr_signal_handlers[_SIG_MAXSIG]; + +/* occasionally signals happen right between %fs reset to GUFS32_SEL and fsbase correction, +which results in fsbase being wrong on handler entry; we'll just restore fsbase ourselves */ +static void libthr_sighandler_wrapper(int sig, siginfo_t *info, void *_ucp) { + struct ntdll_thread_data *thread_data = (struct ntdll_thread_data *)&get_current_teb()->GdiTebBatch; + amd64_set_fsbase(((struct amd64_thread_data *)thread_data->cpu_data)->pthread_teb); + libthr_signal_handlers[sig - 1](sig, info, _ucp); +} + +extern int __sys_sigaction(int, const struct sigaction *, struct sigaction *); + +static int wrap_libthr_signal_handlers(void) { + struct sigaction act; + int sig; + + for (sig = 1; sig <= _SIG_MAXSIG; sig++) { + + if (__sys_sigaction(sig, NULL, &act) == -1) return -1; + if (act.sa_sigaction != NULL) { + + libthr_signal_handlers[sig - 1] = act.sa_sigaction; + act.sa_sigaction = libthr_sighandler_wrapper; + + if (__sys_sigaction(sig, &act, NULL) == -1) return -1; + } + } + + return 0; +} +#endif
/********************************************************************** * signal_init_process @@ -2548,6 +2593,37 @@ void signal_init_process(void) break; } } +#elif defined(__FreeBSD__) + if (wow_teb) + { + u_int p[4]; + u_int cpu_stdext_feature; + + LDT_ENTRY fs32_entry = ldt_make_entry(wow_teb, page_size - 1, LDT_FLAGS_DATA | LDT_FLAGS_32BIT); + + cs32_sel = GSEL(GUCODE32_SEL, SEL_UPL); + + amd64_thread_data()->fs = LSEL(first_ldt_entry, SEL_UPL); + ldt_set_entry(amd64_thread_data()->fs, fs32_entry); + + syscall_flags |= SYSCALL_HAVE_PTHREAD_TEB; + + do_cpuid(0, p); + if (p[0] >= 7) + { + cpuid_count(7, 0, p); + cpu_stdext_feature = p[1]; + } + else + { + cpu_stdext_feature = 0; + } + + if (cpu_stdext_feature & CPUID_STDEXT_FSGSBASE) + { + syscall_flags |= SYSCALL_HAVE_WRFSGSBASE; + } + } #endif
sig_act.sa_mask = server_block_set; @@ -2572,6 +2648,9 @@ void signal_init_process(void) #ifdef __APPLE__ sig_act.sa_sigaction = sigsys_handler; if (sigaction( SIGSYS, &sig_act, NULL ) == -1) goto error; +#endif +#ifdef __FreeBSD__ + if (wrap_libthr_signal_handlers() == -1) goto error; #endif return;
@@ -2602,7 +2681,8 @@ void init_syscall_frame( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, arch_prctl( ARCH_GET_FS, &thread_data->pthread_teb ); if (fs32_sel) alloc_fs_sel( fs32_sel >> 3, get_wow_teb( teb )); #elif defined (__FreeBSD__) || defined (__FreeBSD_kernel__) - amd64_set_gsbase( teb ); + amd64_set_gsbase(teb); + amd64_get_fsbase(&thread_data->pthread_teb); #elif defined(__NetBSD__) sysarch( X86_64_SET_GSBASE, &teb ); #elif defined (__APPLE__) @@ -2804,17 +2884,36 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, /* When on the kernel stack, use %r13 instead of %gs to access the TEB. * (on macOS, signal handlers set gsbase to pthread_teb when on the kernel stack). */ -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) "testl $4,%r14d\n\t" /* SYSCALL_HAVE_PTHREAD_TEB */ "jz 2f\n\t" +# ifdef __FreeBSD__ + "movq $0x13,%rsi\n\t" /* GSEL(GUFS32_SEL, SEL_UPL) */ + "movq %rsi,%fs\n\t" +# endif "movq 0x320(%r13),%rsi\n\t" /* amd64_thread_data()->pthread_teb */ "testl $8,%r14d\n\t" /* SYSCALL_HAVE_WRFSGSBASE */ "jz 1f\n\t" "wrfsbase %rsi\n\t" "jmp 2f\n" - "1:\tmov $0x1002,%edi\n\t" /* ARCH_SET_FS */ + "1:\n\t" +# ifdef __linux__ + "mov $0x1002,%edi\n\t" /* ARCH_SET_FS */ "mov $158,%eax\n\t" /* SYS_arch_prctl */ "syscall\n\t" +# elif defined(__FreeBSD__) + "pushq %r8\n\t" + "pushq %r9\n\t" + "pushq %r10\n\t" + "pushq %r11\n\t" + "movq $0xa5,%rax\n\t" /* sysarch */ + "movq $0x81,%rdi\n\t" /* AMD64_SET_FSBASE */ + "syscall\n\t" + "popq %r11\n\t" + "popq %r10\n\t" + "popq %r9\n\t" + "popq %r8\n\t" +# endif "leaq -0x98(%rbp),%rcx\n" "2:\n\t" #elif defined __APPLE__ @@ -2859,10 +2958,15 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, __ASM_CFI(".cfi_remember_state\n\t") __ASM_CFI_CFA_IS_AT2(rcx, 0xa8, 0x01) /* frame->syscall_cfa */ "leaq 0x70(%rcx),%rsp\n\t" /* %rsp > frame means no longer inside syscall */ -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) "testl $4,%r14d\n\t" /* SYSCALL_HAVE_PTHREAD_TEB */ "jz 1f\n\t" "movw 0x338(%r13),%fs\n" /* amd64_thread_data()->fs */ +# ifdef __FreeBSD__ + /* reset %ss (after sysret) for AMD */ + "movw $0x3b,%r14w\n\t" /* GSEL(GUDATA_SEL, SEL_UPL) */ + "movw %r14w,%ss\n\t" +# endif "1:\n\t" #elif defined __APPLE__ "movq %rax,%r8\n\t" @@ -3051,17 +3155,37 @@ __ASM_GLOBAL_FUNC( __wine_unix_call_dispatcher, __ASM_CFI(".cfi_offset %r15,-0x38\n\t") __ASM_CFI(".cfi_undefined %rdi\n\t") __ASM_CFI(".cfi_undefined %rsi\n\t") -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) "testl $4,%r14d\n\t" /* SYSCALL_HAVE_PTHREAD_TEB */ "jz 2f\n\t" +# ifdef __FreeBSD__ + "movq $0x13,%rsi\n\t" /* GSEL(GUFS32_SEL, SEL_UPL) */ + "movq %rsi,%fs\n\t" +# endif + "movq 0x320(%r13),%rsi\n\t" /* amd64_thread_data()->pthread_teb */ "testl $8,%r14d\n\t" /* SYSCALL_HAVE_WRFSGSBASE */ "jz 1f\n\t" "wrfsbase %rsi\n\t" "jmp 2f\n" - "1:\tmov $0x1002,%edi\n\t" /* ARCH_SET_FS */ + "1:\n\t" +# ifdef __linux__ + "mov $0x1002,%edi\n\t" /* ARCH_SET_FS */ "mov $158,%eax\n\t" /* SYS_arch_prctl */ "syscall\n\t" +# elif defined(__FreeBSD__) + "pushq %r8\n\t" + "pushq %r9\n\t" + "pushq %r10\n\t" + "pushq %r11\n\t" + "movq $0xa5,%rax\n\t" /* sysarch */ + "movq $0x81,%rdi\n\t" /* AMD64_SET_FSBASE */ + "syscall\n\t" + "popq %r11\n\t" + "popq %r10\n\t" + "popq %r9\n\t" + "popq %r8\n\t" +#endif "2:\n\t" #elif defined __APPLE__ "movq 0x320(%r13),%rdi\n\t" /* amd64_thread_data()->pthread_teb */ @@ -3087,10 +3211,15 @@ __ASM_GLOBAL_FUNC( __wine_unix_call_dispatcher, /* switch to user stack */ "movq 0x88(%rcx),%rsp\n\t" __ASM_CFI(".cfi_restore_state\n\t") -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) "testl $4,%r14d\n\t" /* SYSCALL_HAVE_PTHREAD_TEB */ "jz 1f\n\t" "movw 0x338(%r13),%fs\n" /* amd64_thread_data()->fs */ +# ifdef __FreeBSD__ + /* reset %ss (after sysret) for AMD */ + "movw $0x3b,%r14w\n\t" /* GSEL(GUDATA_SEL, SEL_UPL) */ + "movw %r14w,%ss\n\t" +# endif "1:\n\t" #elif defined __APPLE__ "movq %rax,%rdx\n\t"