From 5b8f46cbd6c338fe8fc080e5fea870627f266de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20M=C3=BCller?= Date: Mon, 3 Apr 2017 05:30:27 +0200 Subject: ntdll: Implement HashLinks field in LDR module data. --- dlls/kernel32/tests/loader.c | 75 ++++++++++++++++++++++++++++++++++++++++++++ dlls/ntdll/loader.c | 63 +++++++++++++++++++++++++++++++++++-- include/winternl.h | 6 ++-- 3 files changed, 140 insertions(+), 4 deletions(-) diff --git a/dlls/kernel32/tests/loader.c b/dlls/kernel32/tests/loader.c index 1f6f3176760..f51ac62b976 100644 --- a/dlls/kernel32/tests/loader.c +++ b/dlls/kernel32/tests/loader.c @@ -28,6 +28,7 @@ #include "windef.h" #include "winbase.h" #include "winternl.h" +#include "winuser.h" #include "wine/test.h" #include "delayloadhandler.h" @@ -3036,6 +3037,79 @@ static void test_InMemoryOrderModuleList(void) ok(entry2 == mark2, "expected entry2 == mark2, got %p and %p\n", entry2, mark2); } +static inline WCHAR toupperW(WCHAR c) +{ + WCHAR tmp = c; + CharUpperBuffW(&tmp, 1); + return tmp; +} + +static ULONG hash_basename(const WCHAR *basename) +{ + WORD version = MAKEWORD(NtCurrentTeb()->Peb->OSMinorVersion, + NtCurrentTeb()->Peb->OSMajorVersion); + ULONG hash = 0; + + if (version >= 0x0602) + { + for (; *basename; basename++) + hash = hash * 65599 + toupperW(*basename); + } + else if (version == 0x0601) + { + for (; *basename; basename++) + hash = hash + 65599 * toupperW(*basename); + } + else + hash = toupperW(basename[0]) - 'A'; + + return hash & 31; +} + +static void test_HashLinks(void) +{ + static WCHAR ntdllW[] = {'n','t','d','l','l','.','d','l','l',0}; + static WCHAR kernel32W[] = {'k','e','r','n','e','l','3','2','.','d','l','l',0}; + + LIST_ENTRY *hash_map, *entry, *mark; + LDR_MODULE *module; + BOOL found; + + entry = &NtCurrentTeb()->Peb->LdrData->InLoadOrderModuleList; + entry = entry->Flink; + + module = CONTAINING_RECORD(entry, LDR_MODULE, InLoadOrderModuleList); + entry = module->HashLinks.Blink; + + hash_map = entry - hash_basename(module->BaseDllName.Buffer); + + mark = &hash_map[hash_basename(ntdllW)]; + found = FALSE; + for (entry = mark->Flink; entry != mark; entry = entry->Flink) + { + module = CONTAINING_RECORD(entry, LDR_MODULE, HashLinks); + if (!lstrcmpiW(module->BaseDllName.Buffer, ntdllW)) + { + found = TRUE; + break; + } + } + ok(found, "Could not find ntdll\n"); + + mark = &hash_map[hash_basename(kernel32W)]; + found = FALSE; + for (entry = mark->Flink; entry != mark; entry = entry->Flink) + { + module = CONTAINING_RECORD(entry, LDR_MODULE, HashLinks); + if (!lstrcmpiW(module->BaseDllName.Buffer, kernel32W)) + { + found = TRUE; + break; + } + } + ok(found, "Could not find kernel32\n"); +} + START_TEST(loader) { int argc; @@ -3097,4 +3171,5 @@ START_TEST(loader) test_import_resolution(); test_ExitProcess(); test_InMemoryOrderModuleList(); + test_HashLinks(); } diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index 4ff69b674a3..691eb60e865 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -79,6 +79,9 @@ static const char * const reason_names[] = static const WCHAR dllW[] = {'.','d','l','l',0}; +#define HASH_MAP_SIZE 32 +static LIST_ENTRY hash_table[HASH_MAP_SIZE]; + /* internal representation of 32bit modules. per process. */ typedef struct _wine_modref { @@ -148,7 +151,6 @@ static inline void ascii_to_unicode( WCHAR *dst, const char *src, size_t len ) while (len--) *dst++ = (unsigned char)*src++; } - /************************************************************************* * call_dll_entry_point * @@ -439,6 +441,51 @@ static BOOL load_mscoree( void ) return TRUE; } +/************************************************************************* + * hash_basename + * + * Calculates the bucket index of a dll using the basename. + */ +static ULONG hash_basename(const WCHAR *basename) +{ + WORD version = MAKEWORD(NtCurrentTeb()->Peb->OSMinorVersion, + NtCurrentTeb()->Peb->OSMajorVersion); + ULONG hash = 0; + + if (version >= 0x0602) + { + for (; *basename; basename++) + hash = hash * 65599 + toupperW(*basename); + } + else if (version == 0x0601) + { + for (; *basename; basename++) + hash = hash + 65599 * toupperW(*basename); + } + else + hash = toupperW(basename[0]) - 'A'; + + return hash & (HASH_MAP_SIZE-1); +} + +/************************************************************************* + * recompute_hash_maps + * + * Recomputes the LDR hash map (necessary when windows version changes). + */ +static void recompute_hash_map(void) +{ + PLIST_ENTRY mark, entry; + PLDR_MODULE mod; + + mark = &NtCurrentTeb()->Peb->LdrData->InLoadOrderModuleList; + for (entry = mark->Flink; entry != mark; entry = entry->Flink) + { + mod = CONTAINING_RECORD(entry, LDR_MODULE, InLoadOrderModuleList); + RemoveEntryList( &mod->HashLinks ); + InsertTailList( &hash_table[hash_basename(mod->BaseDllName.Buffer)], &mod->HashLinks ); + } +} /************************************************************************* * get_modref @@ -1056,7 +1103,6 @@ static WINE_MODREF *alloc_module( HMODULE hModule, LPCWSTR filename ) wm->ldr.TlsIndex = -1; wm->ldr.LoadCount = 1; wm->ldr.SectionHandle = NULL; - wm->ldr.CheckSum = 0; wm->ldr.TimeDateStamp = 0; wm->ldr.ActivationContext = 0; @@ -1077,6 +1123,8 @@ static WINE_MODREF *alloc_module( HMODULE hModule, LPCWSTR filename ) &wm->ldr.InLoadOrderModuleList); InsertTailList(&NtCurrentTeb()->Peb->LdrData->InMemoryOrderModuleList, &wm->ldr.InMemoryOrderModuleList); + InsertTailList(&hash_table[hash_basename(wm->ldr.BaseDllName.Buffer)], + &wm->ldr.HashLinks); /* wait until init is called for inserting into this list */ wm->ldr.InInitializationOrderModuleList.Flink = NULL; @@ -1784,6 +1832,7 @@ static void load_builtin_callback( void *module, const char *filename ) /* the module has only be inserted in the load & memory order lists */ RemoveEntryList(&wm->ldr.InLoadOrderModuleList); RemoveEntryList(&wm->ldr.InMemoryOrderModuleList); + RemoveEntryList(&wm->ldr.HashLinks); /* FIXME: free the modref */ builtin_load_info->status = STATUS_DLL_NOT_FOUND; return; @@ -1999,6 +2048,7 @@ static NTSTATUS load_native_dll( LPCWSTR load_path, LPCWSTR name, HANDLE file, /* the module has only be inserted in the load & memory order lists */ RemoveEntryList(&wm->ldr.InLoadOrderModuleList); RemoveEntryList(&wm->ldr.InMemoryOrderModuleList); + RemoveEntryList(&wm->ldr.HashLinks); /* FIXME: there are several more dangling references * left. Including dlls loaded by this dll before the @@ -2977,6 +3027,7 @@ static void free_modref( WINE_MODREF *wm ) { RemoveEntryList(&wm->ldr.InLoadOrderModuleList); RemoveEntryList(&wm->ldr.InMemoryOrderModuleList); + RemoveEntryList(&wm->ldr.HashLinks); if (wm->ldr.InInitializationOrderModuleList.Flink) RemoveEntryList(&wm->ldr.InInitializationOrderModuleList); @@ -3264,6 +3315,9 @@ void WINAPI LdrInitializeThunk( void *kernel_start, ULONG_PTR unknown2, RemoveEntryList( &wm->ldr.InMemoryOrderModuleList ); InsertHeadList( &peb->LdrData->InMemoryOrderModuleList, &wm->ldr.InMemoryOrderModuleList ); + /* the windows version was not set yet when ntdll and kernel32 were loaded */ + recompute_hash_map(); + if ((status = virtual_alloc_thread_stack( NtCurrentTeb(), 0, 0 )) != STATUS_SUCCESS) goto error; if ((status = server_init_process_done( &start_context )) != STATUS_SUCCESS) goto error; @@ -3460,6 +3514,7 @@ void __wine_process_init(void) NTSTATUS status; ANSI_STRING func_name; void (* DECLSPEC_NORETURN CDECL init_func)(void); + DWORD i; main_exe_file = thread_init(); @@ -3469,6 +3524,10 @@ void __wine_process_init(void) load_global_options(); + /* initialize hash table */ + for (i = 0; i < HASH_MAP_SIZE; i++) + InitializeListHead(&hash_table[i]); + /* setup the load callback and create ntdll modref */ wine_dll_set_callback( load_builtin_callback ); diff --git a/include/winternl.h b/include/winternl.h index 4e7d2e966c2..c70d1bd837e 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -2099,8 +2099,7 @@ typedef struct _LDR_MODULE ULONG Flags; SHORT LoadCount; SHORT TlsIndex; - HANDLE SectionHandle; - ULONG CheckSum; + LIST_ENTRY HashLinks; ULONG TimeDateStamp; HANDLE ActivationContext; PVOID PatchInformation; @@ -2110,6 +2109,9 @@ typedef struct _LDR_MODULE PVOID ContextInformation; ULONG_PTR OriginalBase; LARGE_INTEGER LoadTime; + + /* Not part of Win7 but used by Wine */ + HANDLE SectionHandle; } LDR_MODULE, *PLDR_MODULE; /* those defines are (some of the) regular LDR_MODULE.Flags values */ -- 2.14.1