From 8d57b972bba9276ed4ddd4803a92dccc4c80dc8e Mon Sep 17 00:00:00 2001 From: Daniel Lehman Date: Mon, 9 May 2016 11:01:04 -0700 Subject: [PATCH 1/3] kernel32/tests: Add test for library load order creates several temp directories with dynamically created main.dll and dep.dll dependency dlls are loaded and the resulting loaded path and dependency path are checked the search path is altered using several methods to see how behavior changes Signed-off-by: Daniel Lehman --- dlls/kernel32/tests/module.c | 437 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 437 insertions(+) diff --git a/dlls/kernel32/tests/module.c b/dlls/kernel32/tests/module.c index bb22c24..277c276 100644 --- a/dlls/kernel32/tests/module.c +++ b/dlls/kernel32/tests/module.c @@ -18,9 +18,12 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#define NONAMELESSUNION + #include "wine/test.h" #include #include +#include static DWORD (WINAPI *pGetDllDirectoryA)(DWORD,LPSTR); static DWORD (WINAPI *pGetDllDirectoryW)(DWORD,LPWSTR); @@ -739,6 +742,436 @@ static void testK32GetModuleInformation(void) ok(info.EntryPoint != NULL, "Expected nonzero entrypoint\n"); } +#if defined(__x86_64__) || defined(__i386__) + +/* adapted from setupapi/fakedll.c */ +static const char fakedll_signature[] = "Wine Test DLL"; + +static const unsigned int file_alignment = 512; +static const unsigned int section_alignment = 4096; + +struct dll_info +{ + IMAGE_NT_HEADERS *nt; + DWORD file_pos; + DWORD mem_pos; +}; + +#define ALIGN(size,align) (((size) + (align) - 1) & ~((align) - 1)) + +/* add a new section to the dll NT header */ +static void add_section( struct dll_info *info, const char *name, DWORD size, DWORD flags ) +{ + IMAGE_SECTION_HEADER *sec = (IMAGE_SECTION_HEADER *)(info->nt + 1); + + sec += info->nt->FileHeader.NumberOfSections; + memcpy( sec->Name, name, min( strlen(name), sizeof(sec->Name)) ); + sec->Misc.VirtualSize = ALIGN( size, section_alignment ); + sec->VirtualAddress = info->mem_pos; + sec->SizeOfRawData = size; + sec->PointerToRawData = info->file_pos; + sec->Characteristics = flags; + info->file_pos += ALIGN( size, file_alignment ); + info->mem_pos += ALIGN( size, section_alignment ); + info->nt->FileHeader.NumberOfSections++; +} + +/* add a data directory to the dll NT header */ +static inline void add_directory( struct dll_info *info, unsigned int idx, DWORD rva, DWORD size ) +{ + info->nt->OptionalHeader.DataDirectory[idx].VirtualAddress = rva; + info->nt->OptionalHeader.DataDirectory[idx].Size = size; +} + +/* wrapper for WriteFile */ +static inline BOOL xwrite( HANDLE handle, const void *data, DWORD size ) +{ + DWORD res; + + return (WriteFile( handle, data, size, &res, NULL ) && res == size); +} + +/* create the fake dll destination file */ +static HANDLE create_dest_file( const char *name ) +{ + /* first check for an existing file */ + HANDLE h = CreateFileA( name, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL ); + if (h != INVALID_HANDLE_VALUE) + { + /* truncate the file */ + SetFilePointer( h, 0, NULL, FILE_BEGIN ); + SetEndOfFile( h ); + } + else + { + h = CreateFileA( name, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL ); + } + return h; +} + +IMAGE_DOS_HEADER *alloc_header(int nsections, DWORD *ret_size) +{ + IMAGE_DOS_HEADER *dos; + IMAGE_NT_HEADERS *nt; + DWORD header_size; + DWORD lfanew; + BYTE *buffer; + + lfanew = ALIGN(sizeof(*dos) + sizeof(fakedll_signature), 16); + header_size = lfanew + sizeof(*nt) + nsections * sizeof(IMAGE_SECTION_HEADER); + if (!(buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, header_size))) + return NULL; + + dos = (IMAGE_DOS_HEADER *)buffer; + dos->e_magic = IMAGE_DOS_SIGNATURE; + dos->e_cblp = sizeof(*dos); + dos->e_cp = 1; + dos->e_cparhdr = lfanew / 16; + dos->e_minalloc = 0; + dos->e_maxalloc = 0xffff; + dos->e_ss = 0x0000; + dos->e_sp = 0x00b8; + dos->e_lfarlc = lfanew; + dos->e_lfanew = lfanew; + memcpy( dos + 1, fakedll_signature, sizeof(fakedll_signature) ); + + nt = (IMAGE_NT_HEADERS *)(buffer + lfanew); +#if defined __x86_64__ + nt->FileHeader.Machine = IMAGE_FILE_MACHINE_AMD64; +#else + nt->FileHeader.Machine = IMAGE_FILE_MACHINE_I386; +#endif + nt->OptionalHeader.MajorLinkerVersion = 1; + nt->OptionalHeader.MajorOperatingSystemVersion = 1; + nt->OptionalHeader.MajorSubsystemVersion = 1; + nt->OptionalHeader.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI; + + nt->Signature = IMAGE_NT_SIGNATURE; + nt->FileHeader.NumberOfSections = 0; + nt->FileHeader.SizeOfOptionalHeader = IMAGE_SIZEOF_NT_OPTIONAL_HEADER; + nt->OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR_MAGIC; + nt->OptionalHeader.ImageBase = 0x10000000; + nt->OptionalHeader.SectionAlignment = section_alignment; + nt->OptionalHeader.FileAlignment = file_alignment; + nt->OptionalHeader.NumberOfRvaAndSizes = IMAGE_NUMBEROF_DIRECTORY_ENTRIES; + nt->FileHeader.Characteristics = IMAGE_FILE_DLL|IMAGE_FILE_EXECUTABLE_IMAGE|IMAGE_FILE_LARGE_ADDRESS_AWARE; + + *ret_size = header_size; + return dos; +} + +static const char *export_name = "foobar"; +static const BYTE export_func[] = { 0xb8, 0x42, 0x00, 0x00, 0x00, /* mov 0x42, %eax */ + 0xc3 }; /* ret */ + +BOOL create_main_dll(const char *dllname, const char *implib) +{ + BOOL ret; + DWORD len; + HANDLE handle; + DWORD name_len; + DWORD header_size; + IMAGE_DOS_HEADER *dos; + IMAGE_THUNK_DATA thunk[2]; + IMAGE_IMPORT_BY_NAME *name; + IMAGE_IMPORT_DESCRIPTOR desc[2]; + struct dll_info info; + + handle = create_dest_file(dllname); + if (handle == INVALID_HANDLE_VALUE) + return FALSE; + + ret = FALSE; + if (!(dos = alloc_header(1, &header_size))) + goto done; + + /* write imports */ + info.nt = (IMAGE_NT_HEADERS *)((char *)dos + dos->e_lfanew); + info.mem_pos = ALIGN( header_size, section_alignment ); + info.file_pos = ALIGN( header_size, file_alignment ); + if (SetFilePointer(handle, info.file_pos, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) + goto done; + + len = strlen(export_name); + name_len = ALIGN(FIELD_OFFSET(IMAGE_IMPORT_BY_NAME, Name[len+1]), 2); + + /* layout: + IMAGE_THUNK_DATA bound + IMAGE_THUNK_DATA unbound + IMAGE_IMPORT_DESCRIPTOR for import + IMAGE_IMPORT_DESCRIPTOR null terminator + IMAGE_IMPORT_BY_NAME + import name + */ + thunk[0].u1.AddressOfData = info.mem_pos + 2 * sizeof(thunk) + sizeof(desc); + thunk[1].u1.AddressOfData = 0; + if (!xwrite(handle, thunk, sizeof(thunk))) + goto done; + if (!xwrite(handle, thunk, sizeof(thunk))) + goto done; + + desc[0].u.OriginalFirstThunk = info.mem_pos + sizeof(thunk); + desc[0].TimeDateStamp = 0; + desc[0].ForwarderChain = -1; + desc[0].Name = thunk[0].u1.AddressOfData + name_len; + desc[0].FirstThunk = info.mem_pos; + memset(&desc[1], 0, sizeof(desc[1])); + if (!xwrite(handle, &desc, sizeof(desc))) + goto done; + + if (!(name = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, name_len))) + goto done; + name->Hint = 0; + memcpy(name->Name, export_name, len); + ret = xwrite(handle, name, name_len); + HeapFree(GetProcessHeap(), 0, name); + if (!ret) + goto done; + + len = strlen(implib) + 1; + if (!xwrite(handle, implib, len)) + goto done; + + /* add directory and section */ + add_directory(&info, IMAGE_DIRECTORY_ENTRY_IAT, desc[0].FirstThunk, 2 * sizeof(thunk)); + add_directory(&info, IMAGE_DIRECTORY_ENTRY_IMPORT, info.mem_pos + 2 * sizeof(thunk), sizeof(desc)); + + add_section(&info, ".rdata", 2 * sizeof(thunk) + sizeof(desc) + name_len + len, + IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ ); + + /* write header */ + if (SetFilePointer(handle, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) + goto done; + info.nt->OptionalHeader.SizeOfHeaders = ALIGN( header_size, file_alignment ); + info.nt->OptionalHeader.SizeOfImage = ALIGN( info.mem_pos, section_alignment ); + if (!xwrite(handle, dos, header_size)) + goto done; + + ret = TRUE; + +done: + HeapFree(GetProcessHeap(), 0, dos); + CloseHandle(handle); + if (!ret) DeleteFileA(dllname); + return ret; +} + +BOOL create_dep_dll(const char *dllname) +{ + BOOL ret; + WORD ord; + DWORD rva; + DWORD size; + HANDLE handle; + DWORD sym_len; + DWORD name_len; + DWORD header_size; + IMAGE_DOS_HEADER *dos; + IMAGE_EXPORT_DIRECTORY exports; + struct dll_info info; + + handle = create_dest_file(dllname); + if (handle == INVALID_HANDLE_VALUE) + return FALSE; + + ret = FALSE; + if (!(dos = alloc_header(2, &header_size))) + goto done; + + info.nt = (IMAGE_NT_HEADERS *)((char *)dos + dos->e_lfanew); + info.mem_pos = ALIGN( header_size, section_alignment ); + info.file_pos = ALIGN( header_size, file_alignment ); + if (SetFilePointer(handle, info.file_pos, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) + goto done; + + /* write text */ + info.nt->OptionalHeader.BaseOfCode = info.mem_pos; + info.nt->OptionalHeader.SizeOfCode = sizeof(export_func); + if (!xwrite(handle, export_func, sizeof(export_func))) + goto done; + add_section(&info, ".text", sizeof(export_func), + IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ ); + + /* write rdata */ + if (SetFilePointer(handle, info.file_pos, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) + goto done; + + /* layout: + IMAGE_EXPORT_DIRECTORY + DWORD [] functions + DWORD [] names + WORD [] ordinals + library name + export names + */ + exports.Characteristics = 0; + exports.TimeDateStamp = 0; + exports.MajorVersion = 0; + exports.MinorVersion = 0; + exports.Base = 1; + exports.NumberOfFunctions = 1; + exports.NumberOfNames = 1; + exports.AddressOfFunctions = info.mem_pos + sizeof(exports); + exports.AddressOfNames = exports.AddressOfFunctions + sizeof(DWORD); + exports.AddressOfNameOrdinals = exports.AddressOfNames + sizeof(DWORD); + exports.Name = exports.AddressOfNameOrdinals + sizeof(WORD); + if (!xwrite(handle, &exports, sizeof(exports))) + goto done; + rva = info.nt->OptionalHeader.BaseOfCode; + if (!xwrite(handle, &rva, sizeof(rva))) + goto done; + if (strrchr(dllname, '\\')) + dllname = strrchr(dllname, '\\') + 1; + name_len = strlen(dllname) + 1; + rva = exports.Name + name_len; + if (!xwrite(handle, &rva, sizeof(rva))) + goto done; + ord = 0; + if (!xwrite(handle, &ord, sizeof(ord))) + goto done; + + if (!xwrite(handle, dllname, name_len)) + goto done; + + sym_len = strlen(export_name) + 1; + if (!xwrite(handle, export_name, sym_len)) + goto done; + + size = exports.Name + name_len + sym_len - info.mem_pos; + add_directory(&info, IMAGE_FILE_EXPORT_DIRECTORY, info.mem_pos, size); + add_section(&info, ".rdata", size, + IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ ); + + /* write header */ + if (SetFilePointer(handle, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) + goto done; + info.nt->OptionalHeader.SizeOfHeaders = ALIGN( header_size, file_alignment ); + info.nt->OptionalHeader.SizeOfImage = ALIGN( info.mem_pos, section_alignment ); + if (!xwrite(handle, dos, header_size)) + goto done; + + ret = TRUE; + +done: + HeapFree(GetProcessHeap(), 0, dos); + CloseHandle(handle); + if (!ret) DeleteFileA(dllname); + return ret; +} + +static void test_load_expect(const char *dll, DWORD flags, const char *expect_main, const char *expect_dep) +{ + HMODULE hmod = NULL; + HMODULE hdep = NULL; + char path[MAX_PATH]; + + hmod = LoadLibraryExA(dll, NULL, flags); + if (expect_main) + { + ok(!!hmod, "got NULL\n"); + GetModuleFileNameA(hmod, path, MAX_PATH); + ok(!strcmp(expect_main, path), "expected %s, got %s\n", expect_main, path); + } + else + ok(!hmod, "expected NULL, got %p\n", hmod); + + if (expect_dep) + { + hdep = GetModuleHandleA(expect_dep); + ok(!!hdep, "got NULL\n"); + GetModuleFileNameA(hdep, path, MAX_PATH); + ok(!strcmp(expect_dep, path), "expected %s, got %s\n", expect_dep, path); + } + else + ok(!hdep, "expected NULL, got %p\n", hdep); + + FreeLibrary(hmod); +} + +static void testLoadOrder(void) +{ + int i; + DWORD len; + DWORD dirlen; + char *oldenv; + char *newenv; + char path[MAX_PATH]; + char olddir[MAX_PATH]; + char dirs[5][MAX_PATH]; + char deps[5][MAX_PATH]; + char mains[5][MAX_PATH]; + const char *depdll = "dep.dll"; + const char *maindll = "main.dll"; + + /* prevent displaying of the "Unable to load this DLL" message box */ + SetErrorMode(SEM_FAILCRITICALERRORS); + + GetTempPathA(MAX_PATH, path); + for (i = 0; i < sizeof(dirs)/sizeof(dirs[0]); i++) + { + sprintf(dirs[i], "%stest_%c", path, '0' + i); + CreateDirectoryA(dirs[i], NULL); + sprintf(deps[i], "%s\\%s", dirs[i], depdll); + sprintf(mains[i], "%s\\%s", dirs[i], maindll); + create_dep_dll(deps[i]); + create_main_dll(mains[i], depdll); + } + + /* simple load case */ + test_load_expect(deps[0], 0, deps[0], NULL); + + /* simple fail case */ + test_load_expect(mains[0], 0, NULL, NULL); + test_load_expect(mains[0], LOAD_WITH_ALTERED_SEARCH_PATH, mains[0], deps[0]); + + /* simple load case with updated %PATH% */ + dirlen = strlen(dirs[0]); + len = GetEnvironmentVariableA("PATH", NULL, 0); + newenv = HeapAlloc(GetProcessHeap(), 0, len + dirlen + 1); /* + ';' */ + oldenv = newenv + dirlen + 1; + GetEnvironmentVariableA("PATH", oldenv, len); + memcpy(newenv, dirs[0], dirlen); + newenv[dirlen] = ';'; + SetEnvironmentVariableA("PATH", newenv); + + test_load_expect(maindll, 0, mains[0], deps[0]); + test_load_expect(mains[0], 0, mains[0], deps[0]); + + /* SetCurrentDirectory */ + GetCurrentDirectoryA(MAX_PATH, olddir); + ok(SetCurrentDirectoryA(dirs[1]), "failed to SetCurrentDirectory %s\n", dirs[1]); + test_load_expect(maindll, 0, mains[1], deps[1]); + test_load_expect(mains[0], 0, mains[0], deps[1]); + test_load_expect(mains[0], LOAD_WITH_ALTERED_SEARCH_PATH, mains[0], deps[0]); + + if (!pSetDllDirectoryA) + { + win_skip("SetDllDirectoryA not available\n"); + goto cleanup; + } + + /* test SetDllDirectory */ + ok(SetDllDirectoryA(dirs[2]), "failed to SetDllDirectory %s\n", dirs[2]); + test_load_expect(maindll, 0, mains[2], deps[2]); + test_load_expect(mains[0], 0, mains[0], deps[2]); + test_load_expect(mains[0], LOAD_WITH_ALTERED_SEARCH_PATH, mains[0], deps[0]); + +cleanup: + SetCurrentDirectoryA(olddir); + SetDllDirectoryA(NULL); + + for (i = 0; i < sizeof(dirs)/sizeof(dirs[0]); i++) + { + DeleteFileA(mains[i]); + DeleteFileA(deps[i]); + RemoveDirectoryA(dirs[i]); + } + + SetEnvironmentVariableA("PATH", oldenv); + HeapFree(GetProcessHeap(), 0, newenv); +} +#endif + START_TEST(module) { WCHAR filenameW[MAX_PATH]; @@ -768,4 +1201,8 @@ START_TEST(module) testLoadLibraryEx(); testGetModuleHandleEx(); testK32GetModuleInformation(); + +#if defined(__x86_64__) || defined(__i386__) + testLoadOrder(); +#endif } -- 1.9.5