ntdll: Add support for loading a Unix helper library from builtin dlls.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2020-09-21 13:41:28 +02:00
parent 6d7037c9ff
commit a014f413d6
7 changed files with 224 additions and 11 deletions

View file

@ -128,6 +128,7 @@ typedef struct _wine_modref
{ {
LDR_DATA_TABLE_ENTRY ldr; LDR_DATA_TABLE_ENTRY ldr;
struct file_id id; struct file_id id;
void *unix_entry;
int alloc_deps; int alloc_deps;
int nDeps; int nDeps;
struct _wine_modref **deps; 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; const WCHAR *name, *p;
NTSTATUS status; NTSTATUS status;
void *module = NULL; void *module = NULL, *unix_entry = NULL;
SECTION_IMAGE_INFORMATION image_info; SECTION_IMAGE_INFORMATION image_info;
/* Fix the name in case we have a full path and extension */ /* 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; 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 (status) return status;
if ((*pwm = get_modref( *module_ptr ))) /* already loaded */ 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) ); 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 ); 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; return status;
} }
@ -2755,6 +2757,29 @@ done:
return nts; 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.@) * LdrLoadDll (NTDLL.@)
*/ */

View file

@ -1597,7 +1597,10 @@
@ cdecl -syscall wine_server_release_fd(long long) @ cdecl -syscall wine_server_release_fd(long long)
@ cdecl -syscall wine_server_send_fd(long) @ cdecl -syscall wine_server_send_fd(long)
@ cdecl -syscall __wine_make_process_system() @ cdecl -syscall __wine_make_process_system()
# Unix interface
@ cdecl __wine_set_unix_funcs(long ptr) @ cdecl __wine_set_unix_funcs(long ptr)
@ cdecl __wine_init_unix_lib(long long ptr ptr)
@ extern __wine_syscall_dispatcher @ extern __wine_syscall_dispatcher
@ extern -arch=i386 __wine_ldt_copy @ extern -arch=i386 __wine_ldt_copy

View file

@ -123,6 +123,8 @@ const char *build_dir = NULL;
const char *config_dir = NULL; const char *config_dir = NULL;
const char **dll_paths = NULL; const char **dll_paths = NULL;
const char *user_name = NULL; const char *user_name = NULL;
static HMODULE ntdll_module;
static const IMAGE_EXPORT_DIRECTORY *ntdll_exports;
struct file_id struct file_id
{ {
@ -136,6 +138,7 @@ struct builtin_module
struct file_id id; struct file_id id;
void *handle; void *handle;
void *module; void *module;
void *unix_module;
}; };
static struct list builtin_modules = LIST_INIT( builtin_modules ); 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; if (!(builtin = malloc( sizeof(*builtin) ))) return STATUS_NO_MEMORY;
builtin->handle = handle; builtin->handle = handle;
builtin->module = module; builtin->module = module;
builtin->unix_module = NULL;
if (st) if (st)
{ {
builtin->id.dev = st->st_dev; builtin->id.dev = st->st_dev;
@ -782,11 +786,76 @@ static ULONG_PTR find_named_export( HMODULE module, const IMAGE_EXPORT_DIRECTORY
return 0; 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 ) static void load_ntdll_functions( HMODULE module )
{ {
const IMAGE_EXPORT_DIRECTORY *ntdll_exports = get_export_dir( module );
void **ptr; void **ptr;
ntdll_exports = get_export_dir( module );
assert( ntdll_exports ); assert( ntdll_exports );
#define GET_FUNC(name) \ #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 * load_so_dll
*/ */
@ -1136,11 +1254,11 @@ static NTSTATUS open_builtin_file( char *name, void **module, SECTION_IMAGE_INFO
/*********************************************************************** /***********************************************************************
* load_builtin_dll * 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 ) SECTION_IMAGE_INFORMATION *image_info )
{ {
unsigned int i, pos, len, namelen, maxlen = 0; unsigned int i, pos, len, namelen, maxlen = 0;
char *ptr, *file; char *ptr = NULL, *file, *ext = NULL;
NTSTATUS status = STATUS_DLL_NOT_FOUND; NTSTATUS status = STATUS_DLL_NOT_FOUND;
BOOL found_image = FALSE; 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; if (name[i] > 127) goto done;
file[pos + i] = (char)name[i]; file[pos + i] = (char)name[i];
if (file[pos + i] >= 'A' && file[pos + i] <= 'Z') file[pos + i] += 'a' - 'A'; 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] = '/'; file[--pos] = '/';
@ -1166,7 +1285,7 @@ static NTSTATUS CDECL load_builtin_dll( const WCHAR *name, void **module,
ptr = file + pos; ptr = file + pos;
namelen = len + 1; namelen = len + 1;
file[pos + len + 1] = 0; 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, ptr, namelen );
ptr = prepend( ptr, "/dlls", sizeof("/dlls") - 1 ); ptr = prepend( ptr, "/dlls", sizeof("/dlls") - 1 );
ptr = prepend( ptr, build_dir, strlen(build_dir) ); 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; ptr = file + pos;
namelen = len + 1; namelen = len + 1;
file[pos + len + 1] = 0; 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, ptr, namelen );
ptr = prepend( ptr, "/programs", sizeof("/programs") - 1 ); ptr = prepend( ptr, "/programs", sizeof("/programs") - 1 );
ptr = prepend( ptr, build_dir, strlen(build_dir) ); 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) ); WARN( "cannot find builtin library for %s\n", debugstr_w(name) );
done: done:
if (!status && ext)
{
strcpy( ext, ".so" );
dlopen_unix_dll( *module, ptr, unix_entry );
}
free( file ); free( file );
return status; return status;
} }
@ -1215,6 +1339,7 @@ static NTSTATUS CDECL unload_builtin_dll( void *module )
if (builtin->module != module) continue; if (builtin->module != module) continue;
list_remove( &builtin->entry ); list_remove( &builtin->entry );
if (builtin->handle) dlclose( builtin->handle ); if (builtin->handle) dlclose( builtin->handle );
if (builtin->unix_module) dlclose( builtin->unix_module );
free( builtin ); free( builtin );
return STATUS_SUCCESS; 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 ); if (status) fatal_error( "failed to load %s error %x\n", name, status );
free( name ); free( name );
load_ntdll_functions( module ); load_ntdll_functions( module );
ntdll_module = module;
} }

View file

@ -27,7 +27,7 @@
struct _DISPATCHER_CONTEXT; struct _DISPATCHER_CONTEXT;
/* increment this when you change the function table */ /* increment this when you change the function table */
#define NTDLL_UNIXLIB_VERSION 104 #define NTDLL_UNIXLIB_VERSION 105
struct unix_funcs struct unix_funcs
{ {
@ -86,7 +86,7 @@ struct unix_funcs
/* loader functions */ /* loader functions */
NTSTATUS (CDECL *load_so_dll)( UNICODE_STRING *nt_name, void **module ); 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 ); SECTION_IMAGE_INFORMATION *image_info );
NTSTATUS (CDECL *unload_builtin_dll)( void *module ); NTSTATUS (CDECL *unload_builtin_dll)( void *module );
void (CDECL *init_builtin_dll)( void *module ); void (CDECL *init_builtin_dll)( void *module );

View file

@ -14,4 +14,5 @@ C_SRCS = \
exe_wmain.c \ exe_wmain.c \
register.c \ register.c \
setjmp.c \ setjmp.c \
stub.c stub.c \
unix_lib.c

55
dlls/winecrt0/unix_lib.c Normal file
View file

@ -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 <stdarg.h>
#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 */

View file

@ -3756,6 +3756,9 @@ static inline PLIST_ENTRY RemoveTailList(PLIST_ENTRY le)
#ifdef __WINESRC__ #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 */ /* The thread information for 16-bit threads */
/* NtCurrentTeb()->SubSystemTib points to this */ /* NtCurrentTeb()->SubSystemTib points to this */
typedef struct typedef struct