Alexandre Julliard : ntdll: Allow ntdll.so to be loaded before wine_init() has run.
Alexandre Julliard
julliard at winehq.org
Thu May 14 16:17:45 CDT 2020
Module: wine
Branch: master
Commit: b7150570d374bf52bf30355e763ae8a70e14a5c3
URL: https://source.winehq.org/git/wine.git/?a=commit;h=b7150570d374bf52bf30355e763ae8a70e14a5c3
Author: Alexandre Julliard <julliard at winehq.org>
Date: Thu May 14 15:26:32 2020 +0200
ntdll: Allow ntdll.so to be loaded before wine_init() has run.
Signed-off-by: Alexandre Julliard <julliard at winehq.org>
---
dlls/ntdll/Makefile.in | 2 +-
dlls/ntdll/loader.c | 10 +++
dlls/ntdll/ntdll.spec | 1 +
dlls/ntdll/unix/loader.c | 193 ++++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 203 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in
index 3673f4a342..813d9a43e2 100644
--- a/dlls/ntdll/Makefile.in
+++ b/dlls/ntdll/Makefile.in
@@ -3,7 +3,7 @@ MODULE = ntdll.dll
IMPORTLIB = ntdll
IMPORTS = winecrt0
EXTRAINCL = $(UNWIND_CFLAGS)
-EXTRALIBS = -lwine $(IOKIT_LIBS) $(RT_LIBS) $(PTHREAD_LIBS) $(UNWIND_LIBS)
+EXTRALIBS = -lwine $(IOKIT_LIBS) $(COREFOUNDATION_LIBS) $(CORESERVICES_LIBS) $(RT_LIBS) $(PTHREAD_LIBS) $(UNWIND_LIBS) $(I386_LIBS)
EXTRADLLFLAGS = -nodefaultlibs -Wl,--image-base,0x7bc00000
C_SRCS = \
diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c
index bd13d17b2e..35dc7e1eaa 100644
--- a/dlls/ntdll/loader.c
+++ b/dlls/ntdll/loader.c
@@ -4437,3 +4437,13 @@ void __wine_process_init(void)
server_init_process_done();
}
+
+/***********************************************************************
+ * __wine_set_unix_funcs
+ */
+void CDECL __wine_set_unix_funcs( int version, const struct unix_funcs *funcs )
+{
+ assert( version == NTDLL_UNIXLIB_VERSION );
+ unix_funcs = funcs;
+ __wine_process_init();
+}
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index 136d98f6d8..1326c62fcf 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -1573,6 +1573,7 @@
@ cdecl wine_server_release_fd(long long)
@ cdecl wine_server_send_fd(long)
@ cdecl __wine_make_process_system()
+@ cdecl __wine_set_unix_funcs(long ptr)
@ extern -arch=i386 __wine_ldt_copy
# Debugging
diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c
index 68f3723e96..4008c0a55d 100644
--- a/dlls/ntdll/unix/loader.c
+++ b/dlls/ntdll/unix/loader.c
@@ -27,15 +27,21 @@
#include <assert.h>
#include <stdarg.h>
+#include <stdio.h>
#ifdef HAVE_SYS_MMAN_H
# include <sys/mman.h>
#endif
#ifdef __APPLE__
+# include <CoreFoundation/CoreFoundation.h>
+# define LoadResource MacLoadResource
+# define GetCurrentThread MacGetCurrentThread
+# include <CoreServices/CoreServices.h>
+# undef LoadResource
+# undef GetCurrentThread
+# include <pthread.h>
# include <mach-o/getsect.h>
#endif
-#include <stdio.h>
-
#include "ntstatus.h"
#define WIN32_NO_STATUS
#define NONAMELESSUNION
@@ -47,6 +53,7 @@
#include "wine/library.h"
extern IMAGE_NT_HEADERS __wine_spec_nt_header;
+extern void CDECL __wine_set_unix_funcs( int version, const struct unix_funcs *funcs );
static inline void *get_rva( const IMAGE_NT_HEADERS *nt, ULONG_PTR addr )
{
@@ -328,6 +335,42 @@ static void fixup_ntdll_imports( const IMAGE_NT_HEADERS *nt, HMODULE ntdll_modul
}
}
+/***********************************************************************
+ * load_ntdll
+ */
+static HMODULE load_ntdll(void)
+{
+ const IMAGE_NT_HEADERS *nt;
+ HMODULE module;
+ Dl_info info;
+ char *name;
+ void *handle;
+
+ if (!dladdr( load_ntdll, &info ))
+ {
+ fprintf( stderr, "cannot get path to ntdll.so\n" );
+ exit(1);
+ }
+ name = malloc( strlen(info.dli_fname) + 5 );
+ strcpy( name, info.dli_fname );
+ strcpy( name + strlen(info.dli_fname) - 3, ".dll.so" );
+ if (!(handle = dlopen( name, RTLD_NOW )))
+ {
+ fprintf( stderr, "failed to load %s: %s\n", name, dlerror() );
+ exit(1);
+ }
+ if (!(nt = dlsym( handle, "__wine_spec_nt_header" )))
+ {
+ fprintf( stderr, "NT header not found in %s (too old?)\n", name );
+ exit(1);
+ }
+ free( name );
+ module = (HMODULE)((nt->OptionalHeader.ImageBase + 0xffff) & ~0xffff);
+ map_so_dll( nt, module );
+ return module;
+}
+
+
/***********************************************************************
* unix_funcs
*/
@@ -336,8 +379,154 @@ static struct unix_funcs unix_funcs =
map_so_dll,
};
+
+#ifdef __APPLE__
+struct apple_stack_info
+{
+ void *stack;
+ size_t desired_size;
+};
+
+static void *apple_wine_thread( void *arg )
+{
+ __wine_set_unix_funcs( NTDLL_UNIXLIB_VERSION, &unix_funcs );
+ return NULL;
+}
+
+/***********************************************************************
+ * apple_alloc_thread_stack
+ *
+ * Callback for wine_mmap_enum_reserved_areas to allocate space for
+ * the secondary thread's stack.
+ */
+#ifndef _WIN64
+static int apple_alloc_thread_stack( void *base, size_t size, void *arg )
+{
+ struct apple_stack_info *info = arg;
+
+ /* For mysterious reasons, putting the thread stack at the very top
+ * of the address space causes subsequent execs to fail, even on the
+ * child side of a fork. Avoid the top 16MB. */
+ char * const limit = (char*)0xff000000;
+ if ((char *)base >= limit) return 0;
+ if (size > limit - (char*)base)
+ size = limit - (char*)base;
+ if (size < info->desired_size) return 0;
+ info->stack = wine_anon_mmap( (char *)base + size - info->desired_size,
+ info->desired_size, PROT_READ|PROT_WRITE, MAP_FIXED );
+ return (info->stack != (void *)-1);
+}
+#endif
+
+/***********************************************************************
+ * apple_create_wine_thread
+ *
+ * Spin off a secondary thread to complete Wine initialization, leaving
+ * the original thread for the Mac frameworks.
+ *
+ * Invoked as a CFRunLoopSource perform callback.
+ */
+static void apple_create_wine_thread( void *arg )
+{
+ int success = 0;
+ pthread_t thread;
+ pthread_attr_t attr;
+
+ if (!pthread_attr_init( &attr ))
+ {
+#ifndef _WIN64
+ struct apple_stack_info info;
+
+ /* Try to put the new thread's stack in the reserved area. If this
+ * fails, just let it go wherever. It'll be a waste of space, but we
+ * can go on. */
+ if (!pthread_attr_getstacksize( &attr, &info.desired_size ) &&
+ wine_mmap_enum_reserved_areas( apple_alloc_thread_stack, &info, 1 ))
+ {
+ wine_mmap_remove_reserved_area( info.stack, info.desired_size, 0 );
+ pthread_attr_setstackaddr( &attr, (char*)info.stack + info.desired_size );
+ }
+#endif
+
+ if (!pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ) &&
+ !pthread_create( &thread, &attr, apple_wine_thread, NULL ))
+ success = 1;
+
+ pthread_attr_destroy( &attr );
+ }
+ if (!success) exit(1);
+}
+
+
+/***********************************************************************
+ * apple_main_thread
+ *
+ * Park the process's original thread in a Core Foundation run loop for
+ * use by the Mac frameworks, especially receiving and handling
+ * distributed notifications. Spin off a new thread for the rest of the
+ * Wine initialization.
+ */
+static void apple_main_thread(void)
+{
+ CFRunLoopSourceContext source_context = { 0 };
+ CFRunLoopSourceRef source;
+
+ if (!pthread_main_np()) return;
+
+ /* Multi-processing Services can get confused about the main thread if the
+ * first time it's used is on a secondary thread. Use it here to make sure
+ * that doesn't happen. */
+ MPTaskIsPreemptive(MPCurrentTaskID());
+
+ /* Give ourselves the best chance of having the distributed notification
+ * center scheduled on this thread's run loop. In theory, it's scheduled
+ * in the first thread to ask for it. */
+ CFNotificationCenterGetDistributedCenter();
+
+ /* We use this run loop source for two purposes. First, a run loop exits
+ * if it has no more sources scheduled. So, we need at least one source
+ * to keep the run loop running. Second, although it's not critical, it's
+ * preferable for the Wine initialization to not proceed until we know
+ * the run loop is running. So, we signal our source immediately after
+ * adding it and have its callback spin off the Wine thread. */
+ source_context.perform = apple_create_wine_thread;
+ source = CFRunLoopSourceCreate( NULL, 0, &source_context );
+ CFRunLoopAddSource( CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes );
+ CFRunLoopSourceSignal( source );
+ CFRelease( source );
+ CFRunLoopRun(); /* Should never return, except on error. */
+}
+#endif /* __APPLE__ */
+
+/***********************************************************************
+ * __wine_main
+ *
+ * Main entry point called by the wine loader.
+ */
+void __wine_main( int argc, char *argv[], char *envp[] )
+{
+ extern int __wine_main_argc;
+ extern char **__wine_main_argv;
+ extern char **__wine_main_environ;
+ HMODULE module;
+
+ wine_init_argv0_path( argv[0] );
+ __wine_main_argc = argc;
+ __wine_main_argv = argv;
+ __wine_main_environ = envp;
+
+ module = load_ntdll();
+ fixup_ntdll_imports( &__wine_spec_nt_header, module );
+#ifdef __APPLE__
+ apple_main_thread();
+#endif
+ __wine_set_unix_funcs( NTDLL_UNIXLIB_VERSION, &unix_funcs );
+}
+
/***********************************************************************
* __wine_init_unix_lib
+ *
+ * Lib entry point called by ntdll.dll.so if not yet initialized.
*/
NTSTATUS __cdecl __wine_init_unix_lib( HMODULE module, const void *ptr_in, void *ptr_out )
{
More information about the wine-cvs
mailing list