diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index ccaae0a5a25..319cdd8c8a1 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -128,6 +128,7 @@ typedef struct _wine_modref { LDR_DATA_TABLE_ENTRY ldr; struct file_id id; + void *unix_entry; int alloc_deps; int nDeps; struct _wine_modref **deps; @@ -2356,7 +2357,7 @@ static NTSTATUS load_builtin_dll( LPCWSTR load_path, const UNICODE_STRING *nt_na { const WCHAR *name, *p; NTSTATUS status; - void *module = NULL; + void *module = NULL, *unix_entry = NULL; SECTION_IMAGE_INFORMATION image_info; /* Fix the name in case we have a full path and extension */ @@ -2368,7 +2369,7 @@ static NTSTATUS load_builtin_dll( LPCWSTR load_path, const UNICODE_STRING *nt_na if (!module_ptr) module_ptr = &module; - status = unix_funcs->load_builtin_dll( name, module_ptr, &image_info ); + status = unix_funcs->load_builtin_dll( name, module_ptr, &unix_entry, &image_info ); if (status) return status; if ((*pwm = get_modref( *module_ptr ))) /* already loaded */ @@ -2383,7 +2384,8 @@ static NTSTATUS load_builtin_dll( LPCWSTR load_path, const UNICODE_STRING *nt_na TRACE( "loading %s from %s\n", debugstr_w(name), debugstr_us(nt_name) ); status = build_module( load_path, nt_name, module_ptr, &image_info, NULL, flags, pwm ); - if (status && *module_ptr) unix_funcs->unload_builtin_dll( *module_ptr ); + if (!status) (*pwm)->unix_entry = unix_entry; + else if (*module_ptr) unix_funcs->unload_builtin_dll( *module_ptr ); return status; } @@ -2755,6 +2757,29 @@ done: return nts; } + +/*********************************************************************** + * __wine_init_unix_lib + */ +NTSTATUS __cdecl __wine_init_unix_lib( HMODULE module, DWORD reason, const void *ptr_in, void *ptr_out ) +{ + WINE_MODREF *wm; + NTSTATUS ret = STATUS_DLL_NOT_FOUND; + + RtlEnterCriticalSection( &loader_section ); + + if ((wm = get_modref( module ))) + { + NTSTATUS (CDECL *init_func)( HMODULE, DWORD, const void *, void * ) = wm->unix_entry; + if (init_func) ret = init_func( module, reason, ptr_in, ptr_out ); + } + else ret = STATUS_INVALID_HANDLE; + + RtlLeaveCriticalSection( &loader_section ); + return ret; +} + + /****************************************************************** * LdrLoadDll (NTDLL.@) */ diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index af1fac8df8e..55511e23828 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -1597,7 +1597,10 @@ @ cdecl -syscall wine_server_release_fd(long long) @ cdecl -syscall wine_server_send_fd(long) @ cdecl -syscall __wine_make_process_system() + +# Unix interface @ cdecl __wine_set_unix_funcs(long ptr) +@ cdecl __wine_init_unix_lib(long long ptr ptr) @ extern __wine_syscall_dispatcher @ extern -arch=i386 __wine_ldt_copy diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 82fabc5cde8..e7a7c96a974 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -123,6 +123,8 @@ const char *build_dir = NULL; const char *config_dir = NULL; const char **dll_paths = NULL; const char *user_name = NULL; +static HMODULE ntdll_module; +static const IMAGE_EXPORT_DIRECTORY *ntdll_exports; struct file_id { @@ -136,6 +138,7 @@ struct builtin_module struct file_id id; void *handle; void *module; + void *unix_module; }; static struct list builtin_modules = LIST_INIT( builtin_modules ); @@ -146,6 +149,7 @@ static NTSTATUS add_builtin_module( void *module, void *handle, const struct sta if (!(builtin = malloc( sizeof(*builtin) ))) return STATUS_NO_MEMORY; builtin->handle = handle; builtin->module = module; + builtin->unix_module = NULL; if (st) { builtin->id.dev = st->st_dev; @@ -782,11 +786,76 @@ static ULONG_PTR find_named_export( HMODULE module, const IMAGE_EXPORT_DIRECTORY return 0; } +static ULONG_PTR find_pe_export( HMODULE module, const IMAGE_EXPORT_DIRECTORY *exports, + const IMAGE_IMPORT_BY_NAME *name ) +{ + const WORD *ordinals = (const WORD *)((BYTE *)module + exports->AddressOfNameOrdinals); + const DWORD *names = (const DWORD *)((BYTE *)module + exports->AddressOfNames); + + if (name->Hint < exports->NumberOfNames) + { + char *ename = (char *)module + names[name->Hint]; + if (!strcmp( ename, (char *)name->Name )) + return find_ordinal_export( module, exports, ordinals[name->Hint] ); + } + return find_named_export( module, exports, (char *)name->Name ); +} + +static inline void *get_rva( void *module, ULONG_PTR addr ) +{ + return (BYTE *)module + addr; +} + +static NTSTATUS fixup_ntdll_imports( const char *name, HMODULE module ) +{ + const IMAGE_NT_HEADERS *nt; + const IMAGE_IMPORT_DESCRIPTOR *descr; + const IMAGE_THUNK_DATA *import_list; + IMAGE_THUNK_DATA *thunk_list; + + nt = get_rva( module, ((IMAGE_DOS_HEADER *)module)->e_lfanew ); + descr = get_rva( module, nt->OptionalHeader.DataDirectory[IMAGE_FILE_IMPORT_DIRECTORY].VirtualAddress ); + for (; descr->Name && descr->FirstThunk; descr++) + { + thunk_list = get_rva( module, descr->FirstThunk ); + + /* ntdll must be the only import */ + if (strcmp( get_rva( module, descr->Name ), "ntdll.dll" )) + { + ERR( "module %s is importing %s\n", debugstr_a(name), (char *)get_rva( module, descr->Name )); + return STATUS_PROCEDURE_NOT_FOUND; + } + if (descr->u.OriginalFirstThunk) + import_list = get_rva( module, descr->u.OriginalFirstThunk ); + else + import_list = thunk_list; + + while (import_list->u1.Ordinal) + { + if (IMAGE_SNAP_BY_ORDINAL( import_list->u1.Ordinal )) + { + int ordinal = IMAGE_ORDINAL( import_list->u1.Ordinal ) - ntdll_exports->Base; + thunk_list->u1.Function = find_ordinal_export( ntdll_module, ntdll_exports, ordinal ); + if (!thunk_list->u1.Function) ERR( "%s: ntdll.%u not found\n", debugstr_a(name), ordinal ); + } + else /* import by name */ + { + IMAGE_IMPORT_BY_NAME *pe_name = get_rva( module, import_list->u1.AddressOfData ); + thunk_list->u1.Function = find_pe_export( ntdll_module, ntdll_exports, pe_name ); + if (!thunk_list->u1.Function) ERR( "%s: ntdll.%s not found\n", debugstr_a(name), pe_name->Name ); + } + import_list++; + thunk_list++; + } + } + return STATUS_SUCCESS; +} + static void load_ntdll_functions( HMODULE module ) { - const IMAGE_EXPORT_DIRECTORY *ntdll_exports = get_export_dir( module ); void **ptr; + ntdll_exports = get_export_dir( module ); assert( ntdll_exports ); #define GET_FUNC(name) \ @@ -926,6 +995,55 @@ already_loaded: } +/*********************************************************************** + * dlopen_unix_dll + */ +static NTSTATUS dlopen_unix_dll( void *module, const char *name, void **unix_entry ) +{ + struct builtin_module *builtin; + void *unix_module, *handle, *entry; + const IMAGE_NT_HEADERS *nt; + NTSTATUS status = STATUS_INVALID_IMAGE_FORMAT; + + handle = dlopen( name, RTLD_NOW ); + if (!handle) return STATUS_DLL_NOT_FOUND; + if (!(nt = dlsym( handle, "__wine_spec_nt_header" ))) goto done; + if (!(entry = dlsym( handle, "__wine_init_unix_lib" ))) goto done; + + unix_module = (HMODULE)((nt->OptionalHeader.ImageBase + 0xffff) & ~0xffff); + LIST_FOR_EACH_ENTRY( builtin, &builtin_modules, struct builtin_module, entry ) + { + if (builtin->module == module) + { + if (builtin->unix_module == unix_module) /* already loaded */ + { + status = STATUS_SUCCESS; + goto done; + } + if (builtin->unix_module) + { + ERR( "module %p already has a Unix module that's not %s\n", module, debugstr_a(name) ); + goto done; + } + if ((status = map_so_dll( nt, unix_module ))) goto done; + if ((status = fixup_ntdll_imports( name, unix_module ))) goto done; + builtin->unix_module = handle; + *unix_entry = entry; + return STATUS_SUCCESS; + } + else if (builtin->unix_module == unix_module) + { + ERR( "%s already loaded for module %p\n", debugstr_a(name), module ); + goto done; + } + } + ERR( "builtin module not found for %s\n", debugstr_a(name) ); +done: + dlclose( handle ); + return status; +} + + /*********************************************************************** * load_so_dll */ @@ -1136,11 +1254,11 @@ static NTSTATUS open_builtin_file( char *name, void **module, SECTION_IMAGE_INFO /*********************************************************************** * load_builtin_dll */ -static NTSTATUS CDECL load_builtin_dll( const WCHAR *name, void **module, +static NTSTATUS CDECL load_builtin_dll( const WCHAR *name, void **module, void **unix_entry, SECTION_IMAGE_INFORMATION *image_info ) { unsigned int i, pos, len, namelen, maxlen = 0; - char *ptr, *file; + char *ptr = NULL, *file, *ext = NULL; NTSTATUS status = STATUS_DLL_NOT_FOUND; BOOL found_image = FALSE; @@ -1157,6 +1275,7 @@ static NTSTATUS CDECL load_builtin_dll( const WCHAR *name, void **module, if (name[i] > 127) goto done; file[pos + i] = (char)name[i]; if (file[pos + i] >= 'A' && file[pos + i] <= 'Z') file[pos + i] += 'a' - 'A'; + else if (file[pos + i] == '.') ext = file + pos + i; } file[--pos] = '/'; @@ -1166,7 +1285,7 @@ static NTSTATUS CDECL load_builtin_dll( const WCHAR *name, void **module, ptr = file + pos; namelen = len + 1; file[pos + len + 1] = 0; - if (namelen > 4 && !memcmp( ptr + namelen - 4, ".dll", 4 )) namelen -= 4; + if (ext && !strcmp( ext, ".dll" )) namelen -= 4; ptr = prepend( ptr, ptr, namelen ); ptr = prepend( ptr, "/dlls", sizeof("/dlls") - 1 ); ptr = prepend( ptr, build_dir, strlen(build_dir) ); @@ -1177,7 +1296,7 @@ static NTSTATUS CDECL load_builtin_dll( const WCHAR *name, void **module, ptr = file + pos; namelen = len + 1; file[pos + len + 1] = 0; - if (namelen > 4 && !memcmp( ptr + namelen - 4, ".exe", 4 )) namelen -= 4; + if (ext && !strcmp( ext, ".exe" )) namelen -= 4; ptr = prepend( ptr, ptr, namelen ); ptr = prepend( ptr, "/programs", sizeof("/programs") - 1 ); ptr = prepend( ptr, build_dir, strlen(build_dir) ); @@ -1198,6 +1317,11 @@ static NTSTATUS CDECL load_builtin_dll( const WCHAR *name, void **module, WARN( "cannot find builtin library for %s\n", debugstr_w(name) ); done: + if (!status && ext) + { + strcpy( ext, ".so" ); + dlopen_unix_dll( *module, ptr, unix_entry ); + } free( file ); return status; } @@ -1215,6 +1339,7 @@ static NTSTATUS CDECL unload_builtin_dll( void *module ) if (builtin->module != module) continue; list_remove( &builtin->entry ); if (builtin->handle) dlclose( builtin->handle ); + if (builtin->unix_module) dlclose( builtin->unix_module ); free( builtin ); return STATUS_SUCCESS; } @@ -1334,6 +1459,7 @@ static void load_ntdll(void) if (status) fatal_error( "failed to load %s error %x\n", name, status ); free( name ); load_ntdll_functions( module ); + ntdll_module = module; } diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h index d6cd4ed403e..f5c57dde4d3 100644 --- a/dlls/ntdll/unixlib.h +++ b/dlls/ntdll/unixlib.h @@ -27,7 +27,7 @@ struct _DISPATCHER_CONTEXT; /* increment this when you change the function table */ -#define NTDLL_UNIXLIB_VERSION 104 +#define NTDLL_UNIXLIB_VERSION 105 struct unix_funcs { @@ -86,7 +86,7 @@ struct unix_funcs /* loader functions */ NTSTATUS (CDECL *load_so_dll)( UNICODE_STRING *nt_name, void **module ); - NTSTATUS (CDECL *load_builtin_dll)( const WCHAR *name, void **module, + NTSTATUS (CDECL *load_builtin_dll)( const WCHAR *name, void **module, void **unix_entry, SECTION_IMAGE_INFORMATION *image_info ); NTSTATUS (CDECL *unload_builtin_dll)( void *module ); void (CDECL *init_builtin_dll)( void *module ); diff --git a/dlls/winecrt0/Makefile.in b/dlls/winecrt0/Makefile.in index 2f24dd43c79..6e0901db9f2 100644 --- a/dlls/winecrt0/Makefile.in +++ b/dlls/winecrt0/Makefile.in @@ -14,4 +14,5 @@ C_SRCS = \ exe_wmain.c \ register.c \ setjmp.c \ - stub.c + stub.c \ + unix_lib.c diff --git a/dlls/winecrt0/unix_lib.c b/dlls/winecrt0/unix_lib.c new file mode 100644 index 00000000000..e3a1437026a --- /dev/null +++ b/dlls/winecrt0/unix_lib.c @@ -0,0 +1,55 @@ +/* + * Support for the Unix part of builtin dlls + * + * Copyright 2019 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifdef _WIN32 + +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" + +static NTSTATUS (__cdecl *p__wine_init_unix_lib)( HMODULE, DWORD, const void *, void * ); + +static void load_func( void **func, const char *name, void *def ) +{ + if (!*func) + { + HMODULE module = GetModuleHandleA( "ntdll.dll" ); + void *proc = GetProcAddress( module, name ); + InterlockedExchangePointer( func, proc ? proc : def ); + } +} +#define LOAD_FUNC(name) load_func( (void **)&p ## name, #name, fallback ## name ) + +static NTSTATUS __cdecl fallback__wine_init_unix_lib( HMODULE module, DWORD reason, const void *ptr_in, void *ptr_out ) +{ + return STATUS_DLL_NOT_FOUND; +} + +NTSTATUS __cdecl __wine_init_unix_lib( HMODULE module, DWORD reason, const void *ptr_in, void *ptr_out ) +{ + LOAD_FUNC( __wine_init_unix_lib ); + return p__wine_init_unix_lib( module, reason, ptr_in, ptr_out ); +} + +#endif /* _WIN32 */ diff --git a/include/winternl.h b/include/winternl.h index b4621827454..e23a02b9b26 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -3756,6 +3756,9 @@ static inline PLIST_ENTRY RemoveTailList(PLIST_ENTRY le) #ifdef __WINESRC__ +/* Wine internal functions */ +extern NTSTATUS CDECL __wine_init_unix_lib( HMODULE module, DWORD reason, const void *ptr_in, void *ptr_out ); + /* The thread information for 16-bit threads */ /* NtCurrentTeb()->SubSystemTib points to this */ typedef struct