ntdll: Implement LdrRegisterDllNotification and LdrUnregisterDllNotification.

Signed-off-by: Alistair Leslie-Hughes <leslie_alistair@hotmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Michael Müller 2018-06-22 01:54:56 +00:00 committed by Alexandre Julliard
parent 8f4fb12dc5
commit 4c13e1765f
2 changed files with 334 additions and 4 deletions

View File

@ -40,6 +40,7 @@
#include "wine/library.h"
#include "wine/unicode.h"
#include "wine/debug.h"
#include "wine/list.h"
#include "wine/server.h"
#include "ntdll_misc.h"
#include "ddk/wdm.h"
@ -71,6 +72,15 @@ static BOOL imports_fixup_done = FALSE; /* set once the imports have been fixed
static BOOL process_detaching = FALSE; /* set on process detach to avoid deadlocks with thread detach */
static int free_lib_count; /* recursion depth of LdrUnloadDll calls */
struct ldr_notification
{
struct list entry;
PLDR_DLL_NOTIFICATION_FUNCTION callback;
void *context;
};
static struct list ldr_notifications = LIST_INIT( ldr_notifications );
static const char * const reason_names[] =
{
"PROCESS_DETACH",
@ -342,6 +352,29 @@ static ULONG_PTR allocate_stub( const char *dll, const char *name )
static inline ULONG_PTR allocate_stub( const char *dll, const char *name ) { return 0xdeadbeef; }
#endif /* __i386__ */
/* call ldr notifications */
static void call_ldr_notifications( ULONG reason, LDR_MODULE *module )
{
struct ldr_notification *notify, *notify_next;
LDR_DLL_NOTIFICATION_DATA data;
data.Loaded.Flags = 0;
data.Loaded.FullDllName = &module->FullDllName;
data.Loaded.BaseDllName = &module->BaseDllName;
data.Loaded.DllBase = module->BaseAddress;
data.Loaded.SizeOfImage = module->SizeOfImage;
LIST_FOR_EACH_ENTRY_SAFE( notify, notify_next, &ldr_notifications, struct ldr_notification, entry )
{
TRACE_(relay)("\1Call LDR notification callback (proc=%p,reason=%u,data=%p,context=%p)\n",
notify->callback, reason, &data, notify->context );
notify->callback(reason, &data, notify->context);
TRACE_(relay)("\1Ret LDR notification callback (proc=%p,reason=%u,data=%p,context=%p)\n",
notify->callback, reason, &data, notify->context );
}
}
/*************************************************************************
* get_modref
@ -1275,12 +1308,18 @@ static NTSTATUS process_attach( WINE_MODREF *wm, LPVOID lpReserved )
{
WINE_MODREF *prev = current_modref;
current_modref = wm;
call_ldr_notifications( LDR_DLL_NOTIFICATION_REASON_LOADED, &wm->ldr );
status = MODULE_InitDLL( wm, DLL_PROCESS_ATTACH, lpReserved );
if (status == STATUS_SUCCESS)
{
wm->ldr.Flags |= LDR_PROCESS_ATTACHED;
}
else
{
MODULE_InitDLL( wm, DLL_PROCESS_DETACH, lpReserved );
call_ldr_notifications( LDR_DLL_NOTIFICATION_REASON_UNLOADED, &wm->ldr );
/* point to the name so LdrInitializeThunk can print it */
last_failed_modref = wm;
WARN("Initialization of %s failed\n", debugstr_w(wm->ldr.BaseDllName.Buffer));
@ -1353,6 +1392,7 @@ static void process_detach(void)
mod->Flags &= ~LDR_PROCESS_ATTACHED;
MODULE_InitDLL( CONTAINING_RECORD(mod, WINE_MODREF, ldr),
DLL_PROCESS_DETACH, ULongToPtr(process_detaching) );
call_ldr_notifications( LDR_DLL_NOTIFICATION_REASON_UNLOADED, mod );
/* Restart at head of WINE_MODREF list, as entries might have
been added and/or removed while performing the call ... */
@ -1467,8 +1507,27 @@ NTSTATUS WINAPI LdrEnumerateLoadedModules( void *unknown, LDRENUMPROC callback,
NTSTATUS WINAPI LdrRegisterDllNotification(ULONG flags, PLDR_DLL_NOTIFICATION_FUNCTION callback,
void *context, void **cookie)
{
FIXME( "(%04x, %p, %p, %p) stub\n", flags, callback, context, cookie );
return STATUS_NOT_IMPLEMENTED;
struct ldr_notification *notify;
TRACE( "(%x, %p, %p, %p)\n", flags, callback, context, cookie );
if (!callback || !cookie)
return STATUS_INVALID_PARAMETER;
if (flags)
FIXME( "ignoring flags %x\n", flags );
notify = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*notify) );
if (!notify) return STATUS_NO_MEMORY;
notify->callback = callback;
notify->context = context;
RtlEnterCriticalSection( &loader_section );
list_add_tail( &ldr_notifications, &notify->entry );
RtlLeaveCriticalSection( &loader_section );
*cookie = notify;
return STATUS_SUCCESS;
}
/******************************************************************
@ -1476,8 +1535,18 @@ NTSTATUS WINAPI LdrRegisterDllNotification(ULONG flags, PLDR_DLL_NOTIFICATION_FU
*/
NTSTATUS WINAPI LdrUnregisterDllNotification( void *cookie )
{
FIXME( "(%p) stub\n", cookie );
return STATUS_NOT_IMPLEMENTED;
struct ldr_notification *notify = cookie;
TRACE( "(%p)\n", cookie );
if (!notify) return STATUS_INVALID_PARAMETER;
RtlEnterCriticalSection( &loader_section );
list_remove( &notify->entry );
RtlLeaveCriticalSection( &loader_section );
RtlFreeHeap( GetProcessHeap(), 0, notify );
return STATUS_SUCCESS;
}
/******************************************************************

View File

@ -101,6 +101,8 @@ static NTSTATUS (WINAPI *pRtlInitializeCriticalSectionEx)(CRITICAL_SECTION *, U
static NTSTATUS (WINAPI *pLdrEnumerateLoadedModules)(void *, void *, void *);
static NTSTATUS (WINAPI *pRtlMakeSelfRelativeSD)(PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR,LPDWORD);
static NTSTATUS (WINAPI *pRtlAbsoluteToSelfRelativeSD)(PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR,PULONG);
static NTSTATUS (WINAPI *pLdrRegisterDllNotification)(ULONG, PLDR_DLL_NOTIFICATION_FUNCTION, void *, void **);
static NTSTATUS (WINAPI *pLdrUnregisterDllNotification)(void *);
static HMODULE hkernel32 = 0;
static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
@ -108,10 +110,15 @@ static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
#define LEN 16
static const char* src_src = "This is a test!"; /* 16 bytes long, incl NUL */
static WCHAR ws2_32dllW[] = {'w','s','2','_','3','2','.','d','l','l',0};
static WCHAR nsidllW[] = {'n','s','i','.','d','l','l',0};
static WCHAR wintrustdllW[] = {'w','i','n','t','r','u','s','t','.','d','l','l',0};
static WCHAR crypt32dllW[] = {'c','r','y','p','t','3','2','.','d','l','l',0};
static ULONG src_aligned_block[4];
static ULONG dest_aligned_block[32];
static const char *src = (const char*)src_aligned_block;
static char* dest = (char*)dest_aligned_block;
const WCHAR *expected_dll = nsidllW;
static void InitFunctionPtrs(void)
{
@ -157,6 +164,8 @@ static void InitFunctionPtrs(void)
pLdrEnumerateLoadedModules = (void *)GetProcAddress(hntdll, "LdrEnumerateLoadedModules");
pRtlMakeSelfRelativeSD = (void *)GetProcAddress(hntdll, "RtlMakeSelfRelativeSD");
pRtlAbsoluteToSelfRelativeSD = (void *)GetProcAddress(hntdll, "RtlAbsoluteToSelfRelativeSD");
pLdrRegisterDllNotification = (void *)GetProcAddress(hntdll, "LdrRegisterDllNotification");
pLdrUnregisterDllNotification = (void *)GetProcAddress(hntdll, "LdrUnregisterDllNotification");
}
hkernel32 = LoadLibraryA("kernel32.dll");
ok(hkernel32 != 0, "LoadLibrary failed\n");
@ -2257,6 +2266,257 @@ static void test_RtlMakeSelfRelativeSD(void)
ok( status == STATUS_BAD_DESCRIPTOR_FORMAT, "got %08x\n", status );
}
static DWORD (CALLBACK *orig_entry)(HMODULE,DWORD,LPVOID);
static DWORD *dll_main_data;
static inline void *get_rva( HMODULE module, DWORD va )
{
return (void *)((char *)module + va);
}
static void CALLBACK ldr_notify_callback1(ULONG reason, LDR_DLL_NOTIFICATION_DATA *data, void *context)
{
const IMAGE_IMPORT_DESCRIPTOR *imports;
const IMAGE_THUNK_DATA *import_list;
IMAGE_THUNK_DATA *thunk_list;
DWORD *calls = context;
LIST_ENTRY *mark;
LDR_MODULE *mod;
ULONG size;
int i, j;
*calls <<= 4;
*calls |= reason;
if (!lstrcmpiW(data->Loaded.BaseDllName->Buffer, expected_dll))
return;
ok(data->Loaded.Flags == 0, "Expected flags 0, got %x\n", data->Loaded.Flags);
ok(!lstrcmpiW(data->Loaded.BaseDllName->Buffer, expected_dll), "Expected %s, got %s\n",
wine_dbgstr_w(expected_dll), wine_dbgstr_w(data->Loaded.BaseDllName->Buffer));
ok(!!data->Loaded.DllBase, "Expected non zero base address\n");
ok(data->Loaded.SizeOfImage, "Expected non zero image size\n");
/* expect module to be last module listed in LdrData load order list */
mark = &NtCurrentTeb()->Peb->LdrData->InMemoryOrderModuleList;
mod = CONTAINING_RECORD(mark->Blink, LDR_MODULE, InMemoryOrderModuleList);
ok(mod->BaseAddress == data->Loaded.DllBase, "Expected base address %p, got %p\n",
data->Loaded.DllBase, mod->BaseAddress);
ok(!lstrcmpiW(mod->BaseDllName.Buffer, expected_dll), "Expected %s, got %s\n",
wine_dbgstr_w(expected_dll), wine_dbgstr_w(mod->BaseDllName.Buffer));
/* show that imports have already been resolved */
imports = RtlImageDirectoryEntryToData(data->Loaded.DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size);
ok(!!imports, "Expected dll to have imports\n");
for (i = 0; imports[i].Name; i++)
{
thunk_list = get_rva(data->Loaded.DllBase, (DWORD)imports[i].FirstThunk);
if (imports[i].OriginalFirstThunk)
import_list = get_rva(data->Loaded.DllBase, (DWORD)imports[i].OriginalFirstThunk);
else
import_list = thunk_list;
for (j = 0; import_list[j].u1.Ordinal; j++)
{
ok(thunk_list[j].u1.AddressOfData > data->Loaded.SizeOfImage,
"Import has not been resolved: %p\n", (void*)thunk_list[j].u1.Function);
}
}
}
static void CALLBACK ldr_notify_callback2(ULONG reason, LDR_DLL_NOTIFICATION_DATA *data, void *context)
{
DWORD *calls = context;
*calls <<= 4;
*calls |= reason + 2;
}
static BOOL WINAPI fake_dll_main(HINSTANCE instance, DWORD reason, void* reserved)
{
if (reason == DLL_PROCESS_ATTACH)
{
*dll_main_data <<= 4;
*dll_main_data |= 3;
}
else if (reason == DLL_PROCESS_DETACH)
{
*dll_main_data <<= 4;
*dll_main_data |= 4;
}
return orig_entry(instance, reason, reserved);
}
static void CALLBACK ldr_notify_callback_dll_main(ULONG reason, LDR_DLL_NOTIFICATION_DATA *data, void *context)
{
DWORD *calls = context;
LIST_ENTRY *mark;
LDR_MODULE *mod;
*calls <<= 4;
*calls |= reason;
if (reason != LDR_DLL_NOTIFICATION_REASON_LOADED)
return;
mark = &NtCurrentTeb()->Peb->LdrData->InMemoryOrderModuleList;
mod = CONTAINING_RECORD(mark->Blink, LDR_MODULE, InMemoryOrderModuleList);
ok(mod->BaseAddress == data->Loaded.DllBase, "Expected base address %p, got %p\n",
data->Loaded.DllBase, mod->BaseAddress);
if (mod->BaseAddress != data->Loaded.DllBase)
return;
orig_entry = mod->EntryPoint;
mod->EntryPoint = fake_dll_main;
dll_main_data = calls;
}
static BOOL WINAPI fake_dll_main_fail(HINSTANCE instance, DWORD reason, void* reserved)
{
if (reason == DLL_PROCESS_ATTACH)
{
*dll_main_data <<= 4;
*dll_main_data |= 3;
}
else if (reason == DLL_PROCESS_DETACH)
{
*dll_main_data <<= 4;
*dll_main_data |= 4;
}
return FALSE;
}
static void CALLBACK ldr_notify_callback_fail(ULONG reason, LDR_DLL_NOTIFICATION_DATA *data, void *context)
{
DWORD *calls = context;
LIST_ENTRY *mark;
LDR_MODULE *mod;
*calls <<= 4;
*calls |= reason;
if (reason != LDR_DLL_NOTIFICATION_REASON_LOADED)
return;
mark = &NtCurrentTeb()->Peb->LdrData->InMemoryOrderModuleList;
mod = CONTAINING_RECORD(mark->Blink, LDR_MODULE, InMemoryOrderModuleList);
ok(mod->BaseAddress == data->Loaded.DllBase, "Expected base address %p, got %p\n",
data->Loaded.DllBase, mod->BaseAddress);
if (mod->BaseAddress != data->Loaded.DllBase)
return;
orig_entry = mod->EntryPoint;
mod->EntryPoint = fake_dll_main_fail;
dll_main_data = calls;
}
static void CALLBACK ldr_notify_callback_imports(ULONG reason, LDR_DLL_NOTIFICATION_DATA *data, void *context)
{
DWORD *calls = context;
if (reason != LDR_DLL_NOTIFICATION_REASON_LOADED)
return;
if (!lstrcmpiW(data->Loaded.BaseDllName->Buffer, crypt32dllW))
{
*calls <<= 4;
*calls |= 1;
}
if (!lstrcmpiW(data->Loaded.BaseDllName->Buffer, wintrustdllW))
{
*calls <<= 4;
*calls |= 2;
}
}
static void test_LdrRegisterDllNotification(void)
{
void *cookie, *cookie2;
NTSTATUS status;
HMODULE mod;
DWORD calls;
if (!pLdrRegisterDllNotification || !pLdrUnregisterDllNotification)
{
win_skip("Ldr(Un)RegisterDllNotification not available\n");
return;
}
mod = LoadLibraryW(expected_dll);
if(mod)
FreeLibrary(mod);
else
expected_dll = ws2_32dllW; /* XP Default */
/* generic test */
status = pLdrRegisterDllNotification(0, ldr_notify_callback1, &calls, &cookie);
ok(!status, "Expected STATUS_SUCCESS, got %08x\n", status);
calls = 0;
mod = LoadLibraryW(expected_dll);
ok(!!mod, "Failed to load library: %d\n", GetLastError());
ok(calls == LDR_DLL_NOTIFICATION_REASON_LOADED, "Expected LDR_DLL_NOTIFICATION_REASON_LOADED, got %x\n", calls);
calls = 0;
FreeLibrary(mod);
ok(calls == LDR_DLL_NOTIFICATION_REASON_UNLOADED, "Expected LDR_DLL_NOTIFICATION_REASON_UNLOADED, got %x\n", calls);
/* test order of callbacks */
status = pLdrRegisterDllNotification(0, ldr_notify_callback2, &calls, &cookie2);
ok(!status, "Expected STATUS_SUCCESS, got %08x\n", status);
calls = 0;
mod = LoadLibraryW(expected_dll);
ok(!!mod, "Failed to load library: %d\n", GetLastError());
ok(calls == 0x13, "Expected order 0x13, got %x\n", calls);
calls = 0;
FreeLibrary(mod);
ok(calls == 0x24, "Expected order 0x24, got %x\n", calls);
pLdrUnregisterDllNotification(cookie2);
pLdrUnregisterDllNotification(cookie);
/* test dll main order */
status = pLdrRegisterDllNotification(0, ldr_notify_callback_dll_main, &calls, &cookie);
ok(!status, "Expected STATUS_SUCCESS, got %08x\n", status);
calls = 0;
mod = LoadLibraryW(expected_dll);
ok(!!mod, "Failed to load library: %d\n", GetLastError());
ok(calls == 0x13, "Expected order 0x13, got %x\n", calls);
calls = 0;
FreeLibrary(mod);
ok(calls == 0x42, "Expected order 0x42, got %x\n", calls);
pLdrUnregisterDllNotification(cookie);
/* test dll main order */
status = pLdrRegisterDllNotification(0, ldr_notify_callback_fail, &calls, &cookie);
ok(!status, "Expected STATUS_SUCCESS, got %08x\n", status);
calls = 0;
mod = LoadLibraryW(expected_dll);
ok(!mod, "Expected library to fail loading\n");
ok(calls == 0x1342, "Expected order 0x1342, got %x\n", calls);
pLdrUnregisterDllNotification(cookie);
/* test dll with dependencies */
status = pLdrRegisterDllNotification(0, ldr_notify_callback_imports, &calls, &cookie);
ok(!status, "Expected STATUS_SUCCESS, got %08x\n", status);
calls = 0;
mod = LoadLibraryW(wintrustdllW);
ok(!!mod, "Failed to load library: %d\n", GetLastError());
ok(calls == 0x12 || calls == 0x21, "got %x\n", calls);
FreeLibrary(mod);
pLdrUnregisterDllNotification(cookie);
}
START_TEST(rtl)
{
InitFunctionPtrs();
@ -2291,4 +2551,5 @@ START_TEST(rtl)
test_RtlLeaveCriticalSection();
test_LdrEnumerateLoadedModules();
test_RtlMakeSelfRelativeSD();
test_LdrRegisterDllNotification();
}