Alexandre Julliard : ntdll: Reimplement LDT support for the TEB selector.
Alexandre Julliard
julliard at winehq.org
Wed Apr 1 15:50:59 CDT 2020
Module: wine
Branch: master
Commit: b644034a52360abba3726a28729eac0ff009fdd4
URL: https://source.winehq.org/git/wine.git/?a=commit;h=b644034a52360abba3726a28729eac0ff009fdd4
Author: Alexandre Julliard <julliard at winehq.org>
Date: Wed Apr 1 12:35:11 2020 +0200
ntdll: Reimplement LDT support for the TEB selector.
Signed-off-by: Alexandre Julliard <julliard at winehq.org>
---
dlls/ntdll/signal_i386.c | 113 +++++++++++++++++++++++++++++++++++++++++------
1 file changed, 99 insertions(+), 14 deletions(-)
diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c
index 60f4637e2d..b4e2d15e98 100644
--- a/dlls/ntdll/signal_i386.c
+++ b/dlls/ntdll/signal_i386.c
@@ -569,6 +569,14 @@ static inline int is_gdt_sel( WORD sel )
return !(sel & 4);
}
+/***********************************************************************
+ * ldt_is_system
+ */
+static inline int ldt_is_system( WORD sel )
+{
+ return is_gdt_sel( sel ) || ((sel >> 3) < first_ldt_entry);
+}
+
/***********************************************************************
* dispatch_signal
*/
@@ -893,8 +901,7 @@ static inline void *init_handler( const ucontext_t *sigcontext, WORD *fs, WORD *
}
#endif
- if (!wine_ldt_is_system(CS_sig(sigcontext)) ||
- !wine_ldt_is_system(SS_sig(sigcontext))) /* 16-bit mode */
+ if (!ldt_is_system(CS_sig(sigcontext)) || !ldt_is_system(SS_sig(sigcontext))) /* 16-bit mode */
{
/*
* Win16 or DOS protected mode. Note that during switch
@@ -1580,7 +1587,7 @@ static inline DWORD is_privileged_instr( CONTEXT *context )
BYTE instr[16];
unsigned int i, len, prefix_count = 0;
- if (!wine_ldt_is_system( context->SegCs )) return 0;
+ if (!ldt_is_system( context->SegCs )) return 0;
len = virtual_uninterrupted_read_memory( (BYTE *)context->Eip, instr, sizeof(instr) );
for (i = 0; i < len; i++) switch (instr[i])
@@ -1647,7 +1654,7 @@ static inline BOOL check_invalid_gs( ucontext_t *sigcontext, CONTEXT *context )
WORD system_gs = x86_thread_data()->gs;
if (context->SegGs == system_gs) return FALSE;
- if (!wine_ldt_is_system( context->SegCs )) return FALSE;
+ if (!ldt_is_system( context->SegCs )) return FALSE;
/* only handle faults in system libraries */
if (virtual_is_valid_code_address( instr, 1 )) return FALSE;
@@ -2069,7 +2076,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
stack->rec.NumberParameters = 2;
stack->rec.ExceptionInformation[0] = 0;
/* if error contains a LDT selector, use that as fault address */
- if ((err & 7) == 4 && !wine_ldt_is_system( err | 7 ))
+ if ((err & 7) == 4 && !ldt_is_system( err | 7 ))
stack->rec.ExceptionInformation[1] = err & ~7;
else
{
@@ -2291,6 +2298,11 @@ int CDECL __wine_set_signal_handler(unsigned int sig, wine_signal_handler wsh)
/***********************************************************************
* LDT support
*/
+
+#define LDT_SIZE 8192
+
+static WORD gdt_fs_sel;
+
static RTL_CRITICAL_SECTION ldt_section;
static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
{
@@ -2384,6 +2396,83 @@ static void ldt_set_entry( WORD sel, LDT_ENTRY entry )
WINE_LDT_FLAGS_ALLOCATED);
}
+static void ldt_init(void)
+{
+#ifdef __linux__
+ /* the preloader may have allocated it already */
+ gdt_fs_sel = wine_get_fs();
+ if (!gdt_fs_sel || !is_gdt_sel( gdt_fs_sel ))
+ {
+ struct modify_ldt_s ldt_info = { -1 };
+
+ ldt_info.seg_32bit = 1;
+ ldt_info.usable = 1;
+ if (set_thread_area( &ldt_info ) >= 0) gdt_fs_sel = (ldt_info.entry_number << 3) | 3;
+ else gdt_fs_sel = 0;
+ }
+#elif defined(__FreeBSD__) || defined (__FreeBSD_kernel__)
+ gdt_fs_sel = GSEL( GUFS_SEL, SEL_UPL );
+#endif
+}
+
+WORD ldt_alloc_fs( TEB *teb, int first_thread )
+{
+ LDT_ENTRY entry;
+ int idx;
+
+ if (gdt_fs_sel) return gdt_fs_sel;
+
+ entry = ldt_make_entry( teb, teb_size - 1, WINE_LDT_FLAGS_DATA | WINE_LDT_FLAGS_32BIT );
+
+ if (first_thread) /* no locking for first thread */
+ {
+ /* leave some space if libc is using the LDT for %gs */
+ if (!is_gdt_sel( wine_get_gs() )) first_ldt_entry = 512;
+ idx = first_ldt_entry;
+ ldt_set_entry( (idx << 3) | 7, entry );
+ }
+ else
+ {
+ ldt_lock();
+ for (idx = first_ldt_entry; idx < LDT_SIZE; idx++)
+ {
+ if (wine_ldt_copy.flags[idx]) continue;
+ ldt_set_entry( (idx << 3) | 7, entry );
+ break;
+ }
+ ldt_unlock();
+ if (idx == LDT_SIZE) return 0;
+ }
+ return (idx << 3) | 7;
+}
+
+static void ldt_free_fs( WORD sel )
+{
+ if (sel == gdt_fs_sel) return;
+
+ ldt_lock();
+ wine_ldt_copy.flags[sel >> 3] = 0;
+ ldt_unlock();
+}
+
+static void ldt_set_fs( WORD sel, TEB *teb )
+{
+ if (sel == gdt_fs_sel)
+ {
+#ifdef __linux__
+ struct modify_ldt_s ldt_info = { sel >> 3 };
+
+ ldt_info.base_addr = teb;
+ ldt_info.limit = teb_size - 1;
+ ldt_info.seg_32bit = 1;
+ if (set_thread_area( &ldt_info ) < 0) perror( "set_thread_area" );
+#elif defined(__FreeBSD__) || defined (__FreeBSD_kernel__) || defined(__DragonFly__)
+ i386_set_fsbase( teb );
+#endif
+ }
+ wine_set_fs( sel );
+}
+
/**********************************************************************
* get_thread_ldt_entry
@@ -2462,6 +2551,7 @@ NTSTATUS signal_alloc_thread( TEB **teb )
SIZE_T size;
void *addr = NULL;
NTSTATUS status;
+ int first_thread = !sigstack_alignment;
if (!sigstack_alignment)
{
@@ -2471,6 +2561,7 @@ NTSTATUS signal_alloc_thread( TEB **teb )
while ((1u << sigstack_alignment) < min_size) sigstack_alignment++;
signal_stack_mask = (1 << sigstack_alignment) - 1;
signal_stack_size = (1 << sigstack_alignment) - teb_size;
+ ldt_init();
}
size = signal_stack_mask + 1;
@@ -2481,7 +2572,7 @@ NTSTATUS signal_alloc_thread( TEB **teb )
(*teb)->Tib.Self = &(*teb)->Tib;
(*teb)->Tib.ExceptionList = (void *)~0UL;
thread_data = (struct x86_thread_data *)(*teb)->SystemReserved2;
- if (!(thread_data->fs = wine_ldt_alloc_fs()))
+ if (!(thread_data->fs = ldt_alloc_fs( *teb, first_thread )))
{
size = 0;
NtFreeVirtualMemory( NtCurrentProcess(), &addr, &size, MEM_RELEASE );
@@ -2500,7 +2591,7 @@ void signal_free_thread( TEB *teb )
SIZE_T size = 0;
struct x86_thread_data *thread_data = (struct x86_thread_data *)teb->SystemReserved2;
- wine_ldt_free_fs( thread_data->fs );
+ ldt_free_fs( thread_data->fs );
NtFreeVirtualMemory( NtCurrentProcess(), (void **)&teb, &size, MEM_RELEASE );
}
@@ -2512,7 +2603,6 @@ void signal_init_thread( TEB *teb )
{
const WORD fpu_cw = 0x27f;
struct x86_thread_data *thread_data = (struct x86_thread_data *)teb->SystemReserved2;
- LDT_ENTRY fs_entry;
stack_t ss;
#ifdef __APPLE__
@@ -2528,10 +2618,7 @@ void signal_init_thread( TEB *teb )
ss.ss_flags = 0;
if (sigaltstack(&ss, NULL) == -1) perror( "sigaltstack" );
- wine_ldt_set_base( &fs_entry, teb );
- wine_ldt_set_limit( &fs_entry, teb_size - 1 );
- wine_ldt_set_flags( &fs_entry, WINE_LDT_FLAGS_DATA|WINE_LDT_FLAGS_32BIT );
- wine_ldt_init_fs( thread_data->fs, &fs_entry );
+ ldt_set_fs( thread_data->fs, teb );
thread_data->gs = wine_get_gs();
#ifdef __GNUC__
@@ -2579,8 +2666,6 @@ void signal_init_process(void)
sig_act.sa_sigaction = trap_handler;
if (sigaction( SIGTRAP, &sig_act, NULL ) == -1) goto error;
#endif
-
- wine_ldt_init_locking( ldt_lock, ldt_unlock );
return;
error:
More information about the wine-cvs
mailing list