[RFC PATCH] loader: allow overriding pthread_create correctly on linux
Maarten Lankhorst
m.b.lankhorst at gmail.com
Fri Jun 28 05:53:29 CDT 2013
This should be enough to fix bug #30557 in the most thorough possible way.
Only tested on ubuntu precise 32-bits atm, but I believe it should work on 64-bits too.
TODO: pthread_create_2_0 is not handled correctly right now, so it will fail on ancient glibc's < 2.2.
But because NPTL requires a slightly newer glibc than that I do not believe it will be a real issue.
Comments welcome :-)
---
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c
index 01a8026..e9775c3 100644
--- a/dlls/ntdll/thread.c
+++ b/dlls/ntdll/thread.c
@@ -33,6 +33,7 @@
#ifdef HAVE_SYS_SYSCALL_H
#include <sys/syscall.h>
#endif
+#include <errno.h>
#define NONAMELESSUNION
#include "ntstatus.h"
@@ -58,6 +59,7 @@ struct startup_info
TEB *teb;
PRTL_THREAD_START_ROUTINE entry_point;
void *entry_arg;
+ BOOL native_thread;
};
static PEB *peb;
@@ -186,6 +188,20 @@ done:
return status;
}
+#ifdef __linux__
+extern typeof(pthread_create) *__glob_pthread_create, *call_pthread_create;
+static typeof(pthread_create) __hook_pthread_create;
+
+static void thread_wrap_init(void)
+{
+ call_pthread_create = __hook_pthread_create;
+}
+
+#else
+#define __glob_pthread_create pthread_create
+#define thread_wrap_init()
+#endif
+
/***********************************************************************
* thread_init
*
@@ -204,6 +220,7 @@ HANDLE thread_init(void)
struct ntdll_thread_data *thread_data;
static struct debug_info debug_info; /* debug info for initial thread */
+ thread_wrap_init();
virtual_init();
/* reserve space for shared user data */
@@ -327,11 +344,7 @@ void terminate_thread( int status )
pthread_exit( UIntToPtr(status) );
}
-
-/***********************************************************************
- * exit_thread
- */
-void exit_thread( int status )
+static void exit_thread_common( int status )
{
static void *prev_teb;
TEB *teb;
@@ -377,9 +390,60 @@ void exit_thread( int status )
close( ntdll_get_thread_data()->wait_fd[1] );
close( ntdll_get_thread_data()->reply_fd );
close( ntdll_get_thread_data()->request_fd );
+}
+
+void exit_thread( int status )
+{
+ exit_thread_common(status);
pthread_exit( UIntToPtr(status) );
}
+#ifdef __linux__
+
+struct unix_arg {
+ void *(*start)(void *);
+ void *arg;
+};
+
+/* dummy used for comparison */
+static DWORD native_unix_start;
+
+static void call_native_cleanup(void *arg)
+{
+ RtlFreeThreadActivationContextStack();
+ exit_thread_common(0);
+}
+
+static int
+__hook_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
+ void *(*start_routine) (void *), void *parm)
+{
+ NTSTATUS ret;
+ struct unix_arg arg;
+ arg.start = start_routine;
+ arg.arg = parm;
+
+ TRACE("Overriding thread creation!\n");
+ if (attr)
+ FIXME("thread attributes ignored!\n");
+
+ ret = RtlCreateUserThread( NtCurrentProcess(), NULL, FALSE, NULL, 0, 0, (void*)&native_unix_start, &arg, NULL, (void*)thread );
+ if (ret != STATUS_SUCCESS)
+ FIXME("ret: %08x\n", ret);
+ switch (ret) {
+ case STATUS_SUCCESS:
+ return 0;
+ case STATUS_NO_MEMORY:
+ return ENOMEM;
+ case STATUS_TOO_MANY_OPENED_FILES:
+ return EMFILE;
+ default:
+ ERR("Unhandled ntstatus %08x\n", ret);
+ return ENOMEM;
+ }
+}
+
+#endif
/***********************************************************************
* start_thread
@@ -412,9 +476,18 @@ static void start_thread( struct startup_info *info )
if (TRACE_ON(relay))
DPRINTF( "%04x:Starting thread proc %p (arg=%p)\n", GetCurrentThreadId(), func, arg );
- call_thread_entry_point( (LPTHREAD_START_ROUTINE)func, arg );
-}
+#ifdef __linux__
+ if (info->native_thread) {
+ void *(*start)(void*) = (void*)func;
+ FIXME("Started native thread %08x\n", GetCurrentThreadId());
+ pthread_cleanup_push(call_native_cleanup, NULL);
+ pthread_exit(start(arg));
+ pthread_cleanup_pop(1);
+ } else
+#endif
+ call_thread_entry_point( (LPTHREAD_START_ROUTINE)func, arg );
+}
/***********************************************************************
* RtlCreateUserThread (NTDLL.@)
@@ -497,8 +570,18 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR *
info = (struct startup_info *)(teb + 1);
info->teb = teb;
- info->entry_point = start;
- info->entry_arg = param;
+#ifdef __linux__
+ info->native_thread = (void*)start == (void*)&native_unix_start;
+ if (info->native_thread) {
+ struct unix_arg *arg = param;
+ info->entry_point = (void*)arg->start;
+ info->entry_arg = arg->arg;
+ } else
+#endif
+ {
+ info->entry_point = start;
+ info->entry_arg = param;
+ }
thread_data = (struct ntdll_thread_data *)teb->SpareBytes1;
thread_data->request_fd = request_pipe[1];
@@ -513,7 +596,7 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR *
(char *)teb->Tib.StackBase - (char *)teb->DeallocationStack );
pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ); /* force creating a kernel thread */
interlocked_xchg_add( &nb_threads, 1 );
- if (pthread_create( &pthread_id, &attr, (void * (*)(void *))start_thread, info ))
+ if (__glob_pthread_create( &pthread_id, &attr, (void * (*)(void *))start_thread, info ))
{
interlocked_xchg_add( &nb_threads, -1 );
pthread_attr_destroy( &attr );
@@ -523,6 +606,11 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR *
pthread_attr_destroy( &attr );
pthread_sigmask( SIG_SETMASK, &sigset, NULL );
+#ifdef __linux__
+ if (native_thread && id)
+ *(pthread_t*)id = pthread_id;
+ else
+#endif
if (id) id->UniqueThread = ULongToHandle(tid);
if (handle_ptr) *handle_ptr = handle;
else NtClose( handle );
diff --git a/dlls/winegstreamer/glibthread.c b/dlls/winegstreamer/glibthread.c
index 25eceb8..7417941 100644
--- a/dlls/winegstreamer/glibthread.c
+++ b/dlls/winegstreamer/glibthread.c
@@ -43,6 +43,7 @@
#include <stdlib.h>
#include <stdio.h>
+#if 0
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
@@ -388,3 +389,15 @@ void g_thread_impl_init (void)
g_thread_self_tls = TlsAlloc ();
g_thread_init(&g_thread_functions_for_glib_use_default);
}
+
+#else
+
+void g_thread_impl_init (void)
+{
+ static gboolean beenhere = FALSE;
+
+ if (!beenhere++)
+ g_thread_init(NULL);
+}
+
+#endif
diff --git a/libs/wine/loader.c b/libs/wine/loader.c
index 094e5e1..69f4a38 100644
--- a/libs/wine/loader.c
+++ b/libs/wine/loader.c
@@ -69,6 +69,11 @@ char **__wine_main_argv = NULL;
WCHAR **__wine_main_wargv = NULL;
char **__wine_main_environ = NULL;
+#ifdef __linux__
+#include <pthread.h>
+typeof(pthread_create) *call_pthread_create, *__glob_pthread_create;
+#endif
+
struct dll_path_context
{
unsigned int index; /* current index in the dll path list */
diff --git a/libs/wine/wine.map b/libs/wine/wine.map
index 2159fac..694869a 100644
--- a/libs/wine/wine.map
+++ b/libs/wine/wine.map
@@ -117,6 +117,8 @@ WINE_1.0
wine_utf8_mbstowcs;
wine_utf8_wcstombs;
wine_wctype_table;
+ __glob_pthread_create;
+ call_pthread_create;
local: *;
};
diff --git a/loader/main.c b/loader/main.c
index ac67290..14b2428 100644
--- a/loader/main.c
+++ b/loader/main.c
@@ -202,6 +202,27 @@ static int pre_exec(void)
#endif
+#ifdef __linux__
+
+extern typeof(pthread_create) *call_pthread_create, *__glob_pthread_create;
+
+int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
+ void *(*start_routine) (void *), void *arg)
+{
+ return call_pthread_create(thread, attr, start_routine, arg);
+}
+
+static void init_thread_hook(void) {
+ call_pthread_create = __glob_pthread_create = dlvsym(RTLD_NEXT, "pthread_create", "GLIBC_2.2");
+ if (!__glob_pthread_create)
+ call_pthread_create = __glob_pthread_create = dlvsym(RTLD_NEXT, "pthread_create", "GLIBC_2.1");
+}
+
+#else
+
+#define init_thread_hook()
+
+#endif
/**********************************************************************
* main
@@ -211,6 +232,8 @@ int main( int argc, char *argv[] )
char error[1024];
int i;
+ init_thread_hook();
+
if (!getenv( "WINELOADERNOEXEC" )) /* first time around */
{
static char noexec[] = "WINELOADERNOEXEC=1";
More information about the wine-patches
mailing list