From: Jinoh Kang Subject: [PATCH v7 1/2] kernel32/tests: Test module refcounting with forwarded exports. Message-Id: <8121099e-1cb3-0ed7-c6c4-5a8a2a704751@gmail.com> Date: Thu, 16 Dec 2021 01:40:23 +0900 Signed-off-by: Jinoh Kang --- Notes: v3 -> v4: - iatgas.h - LLVM(Clang), ARM, ARM64 support - Use __ASM_NAME macro - Don't fail test on MSVC - Don't end asm macros with "\n\t" v4 -> v5: - iatgas.h - mark idata sections as RO - loader.c - test for forward export itself v5 -> v6: - loader.c - Fix compilation warning in format string v6 -> v7: - forward4.c - fix building without MinGW dlls/kernel32/tests/Makefile.in | 10 +- dlls/kernel32/tests/forward1.c | 17 +++ dlls/kernel32/tests/forward1.spec | 2 + dlls/kernel32/tests/forward2.c | 7 ++ dlls/kernel32/tests/forward2.spec | 2 + dlls/kernel32/tests/forward3.c | 7 ++ dlls/kernel32/tests/forward3.spec | 2 + dlls/kernel32/tests/forward4.c | 41 ++++++ dlls/kernel32/tests/forward4.spec | 2 + dlls/kernel32/tests/iatgas.h | 85 +++++++++++++ dlls/kernel32/tests/loader.c | 199 ++++++++++++++++++++++++++++++ 11 files changed, 373 insertions(+), 1 deletion(-) create mode 100644 dlls/kernel32/tests/forward1.c create mode 100644 dlls/kernel32/tests/forward1.spec create mode 100644 dlls/kernel32/tests/forward2.c create mode 100644 dlls/kernel32/tests/forward2.spec create mode 100644 dlls/kernel32/tests/forward3.c create mode 100644 dlls/kernel32/tests/forward3.spec create mode 100644 dlls/kernel32/tests/forward4.c create mode 100644 dlls/kernel32/tests/forward4.spec create mode 100644 dlls/kernel32/tests/iatgas.h diff --git a/dlls/kernel32/tests/Makefile.in b/dlls/kernel32/tests/Makefile.in index e9516603ce9..7ef3508bad4 100644 --- a/dlls/kernel32/tests/Makefile.in +++ b/dlls/kernel32/tests/Makefile.in @@ -37,4 +37,12 @@ SOURCES = \ toolhelp.c \ version.c \ virtual.c \ - volume.c + volume.c \ + forward1.c \ + forward1.spec \ + forward2.c \ + forward2.spec \ + forward3.c \ + forward3.spec \ + forward4.c \ + forward4.spec diff --git a/dlls/kernel32/tests/forward1.c b/dlls/kernel32/tests/forward1.c new file mode 100644 index 00000000000..985e193c958 --- /dev/null +++ b/dlls/kernel32/tests/forward1.c @@ -0,0 +1,17 @@ +#define WIN32_LEAN_AND_MEAN +#include + +BOOL WINAPI DllMain(HINSTANCE instance_new, DWORD reason, LPVOID reserved) +{ + return TRUE; +} + +unsigned long forward_test_func(void) +{ + return 0x00005678UL; +} + +unsigned long forward_test_func2(void) +{ + return 0x12340000UL; +} diff --git a/dlls/kernel32/tests/forward1.spec b/dlls/kernel32/tests/forward1.spec new file mode 100644 index 00000000000..bf19fa7e011 --- /dev/null +++ b/dlls/kernel32/tests/forward1.spec @@ -0,0 +1,2 @@ +1 cdecl forward_test_func() +2 cdecl -noname forward_test_func2() diff --git a/dlls/kernel32/tests/forward2.c b/dlls/kernel32/tests/forward2.c new file mode 100644 index 00000000000..0ed3885e0df --- /dev/null +++ b/dlls/kernel32/tests/forward2.c @@ -0,0 +1,7 @@ +#define WIN32_LEAN_AND_MEAN +#include + +BOOL WINAPI DllMain(HINSTANCE instance_new, DWORD reason, LPVOID reserved) +{ + return TRUE; +} diff --git a/dlls/kernel32/tests/forward2.spec b/dlls/kernel32/tests/forward2.spec new file mode 100644 index 00000000000..374156d8d06 --- /dev/null +++ b/dlls/kernel32/tests/forward2.spec @@ -0,0 +1,2 @@ +1 cdecl forward_test_func() forward1.forward_test_func +2 cdecl -noname forward_test_func2() forward1.#2 diff --git a/dlls/kernel32/tests/forward3.c b/dlls/kernel32/tests/forward3.c new file mode 100644 index 00000000000..0ed3885e0df --- /dev/null +++ b/dlls/kernel32/tests/forward3.c @@ -0,0 +1,7 @@ +#define WIN32_LEAN_AND_MEAN +#include + +BOOL WINAPI DllMain(HINSTANCE instance_new, DWORD reason, LPVOID reserved) +{ + return TRUE; +} diff --git a/dlls/kernel32/tests/forward3.spec b/dlls/kernel32/tests/forward3.spec new file mode 100644 index 00000000000..31d019aa071 --- /dev/null +++ b/dlls/kernel32/tests/forward3.spec @@ -0,0 +1,2 @@ +1 cdecl forward_test_func() forward2.forward_test_func +2 cdecl -noname forward_test_func2() forward2.#2 diff --git a/dlls/kernel32/tests/forward4.c b/dlls/kernel32/tests/forward4.c new file mode 100644 index 00000000000..df4cfe41c0c --- /dev/null +++ b/dlls/kernel32/tests/forward4.c @@ -0,0 +1,41 @@ +#define WIN32_LEAN_AND_MEAN +#include + +#if (defined(_MSC_VER) && defined(__clang__)) || defined(__MINGW32__) || defined(__CYGWIN__) +#include "wine/asm.h" +#include "iatgas.h" + +INLINE_IMPORT_BEGIN("forward3.dll") + INLINE_IMPORT_BY_NAME(1, "forward_test_func", __ASM_NAME("forward_test_func")) + INLINE_IMPORT_BY_ORDINAL(2, __ASM_NAME("forward_test_func2")) +INLINE_IMPORT_END() + +DECLSPEC_IMPORT extern unsigned long forward_test_func(void); +DECLSPEC_IMPORT extern unsigned long forward_test_func2(void); + +unsigned long test_func_stub(void) +{ + return forward_test_func() ^ forward_test_func2(); +} + +const char skiptest[] = ""; +#else +unsigned long test_func_stub(void) +{ + /* unimplemented */ + for (;;) DebugBreak(); +} + +#ifdef _MSC_VER +const char skiptest[] = "TODO: support building import libraries for TESTDLLs in MSVC"; +#elif defined(__WINE_PE_BUILD) +const char skiptest[] = "TODO: support building import libraries for TESTDLLs in other compilers"; +#else +const char skiptest[] = "TODO: support building import libraries for TESTDLLs as Unixlib (--without-mingw)"; +#endif +#endif + +BOOL WINAPI DllMain(HINSTANCE instance_new, DWORD reason, LPVOID reserved) +{ + return TRUE; +} diff --git a/dlls/kernel32/tests/forward4.spec b/dlls/kernel32/tests/forward4.spec new file mode 100644 index 00000000000..5ff2f47469c --- /dev/null +++ b/dlls/kernel32/tests/forward4.spec @@ -0,0 +1,2 @@ +@ cdecl test_func_stub() +@ extern skiptest diff --git a/dlls/kernel32/tests/iatgas.h b/dlls/kernel32/tests/iatgas.h new file mode 100644 index 00000000000..8d0184c9ae0 --- /dev/null +++ b/dlls/kernel32/tests/iatgas.h @@ -0,0 +1,85 @@ +/* + * Inline assembly import library generator + * + * Copyright 2021 Jinoh Kang + * + * 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 + */ + +#ifndef __WINE_KERNEL_IATGAS_H +#define __WINE_KERNEL_IATGAS_H + +#if defined(_WIN64) +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define IATGAS_ZERO_EXTEND_32_TO_PTR(line) line "\n\t.skip 4" +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define IATGAS_ZERO_EXTEND_32_TO_PTR(line) ".skip 4\n\t" line +#else +#error unknown byte order +#endif +#define IATGAS_EMIT_PTRALIGN ".p2align 3" +#define IATGAS_EMIT_IMPORT_BY_ORDINAL(ordinal) ".quad (1<<63)|(" #ordinal ")" +#elif defined(_WIN32) +#define IATGAS_ZERO_EXTEND_32_TO_PTR(line) line +#define IATGAS_EMIT_PTRALIGN ".p2align 2" +#define IATGAS_EMIT_IMPORT_BY_ORDINAL(ordinal) ".long (1<<31)|(" #ordinal ")" +#else +#error unknown pointer size +#endif + +#define INLINE_IMPORT_BEGIN_PROLOG(id) INLINE_IMPORT_BEGIN_PROLOG_(id) +#define INLINE_IMPORT_BEGIN_PROLOG_(id) \ + __asm__( \ + ".section \".idata$2\", \"a\"\n\t" /* IMAGE_IMPORT_DESCRIPTOR */ \ + ".rva .Limport_ilt_" #id "\n\t" /* .OriginalFirstThunk */ \ + ".long 0\n\t" /* .TimeDateStamp */ \ + ".long -1\n\t" /* .ForwarderChain */ \ + ".rva .Limport_dllname_" #id "\n\t" /* .Name */ \ + ".rva .Limport_iat_" #id "\n\t" /* .FirstThunk */ \ + ".p2align 2\n\t" /* (section alignment) */ \ + ".section \".idata$4\", \"a\"\n\t" /* IMAGE_THUNK_DATA (ILT) */ \ + IATGAS_EMIT_PTRALIGN "\n\t" \ + ".Limport_ilt_" #id ":\n\t" \ + ".section \".idata$5\", \"a\"\n\t" /* IMAGE_THUNK_DATA (IAT) */ \ + IATGAS_EMIT_PTRALIGN "\n\t" \ + ".Limport_iat_" #id ":\n\t" \ + ".section \".idata$7\", \"a\"\n\t" \ + ".Limport_dllname_" #id ":\n\t" +#define INLINE_IMPORT_BEGIN(dll_name) INLINE_IMPORT_BEGIN_PROLOG(__COUNTER__) \ + ".asciz \"" dll_name "\"\n\t" +#define INLINE_IMPORT_ENTRY(entry, impsym) \ + ".section \".idata$4\", \"a\"\n\t" /* IMAGE_THUNK_DATA (ILT) */ \ + entry "\n\t" /* .AddressOfData | .Ordinal */ \ + ".section \".idata$5\", \"a\"\n\t" \ + "__imp_" impsym ":\n\t" /* IMAGE_THUNK_DATA (IAT) */ \ + entry "\n\t" /* .AddressOfData | .Ordinal */ +#define INLINE_IMPORT_BY_ORDINAL(ordinal, impsym) \ + INLINE_IMPORT_ENTRY(IATGAS_EMIT_IMPORT_BY_ORDINAL(ordinal), impsym) +#define INLINE_IMPORT_BY_NAME(hint, name, impsym) \ + INLINE_IMPORT_ENTRY(IATGAS_ZERO_EXTEND_32_TO_PTR(".rva .Limport_procname_" impsym), impsym) \ + ".section \".idata$6\", \"a\"\n\t" \ + ".Limport_procname_" impsym ":\n\t" /* IMAGE_IMPORT_BY_NAME */ \ + ".short " #hint "\n\t" /* .Hint */ \ + ".asciz \"" name "\"\n\t" /* .Name */ \ + ".p2align 1\n\t" /* (section alignment) */ +#define INLINE_IMPORT_END() \ + ".section \".idata$4\", \"a\"\n\t" /* IMAGE_THUNK_DATA (ILT) */ \ + IATGAS_ZERO_EXTEND_32_TO_PTR(".long 0") "\n\t" \ + ".section \".idata$5\", \"a\"\n\t" /* IMAGE_THUNK_DATA (IAT) */ \ + IATGAS_ZERO_EXTEND_32_TO_PTR(".long 0") "\n\t" \ + ".text" \ + ); + +#endif diff --git a/dlls/kernel32/tests/loader.c b/dlls/kernel32/tests/loader.c index 308cf1a44a0..8c5ab88d9d7 100644 --- a/dlls/kernel32/tests/loader.c +++ b/dlls/kernel32/tests/loader.c @@ -1641,6 +1641,202 @@ static void test_ImportDescriptors(void) } } +static void extract_resource(const char *name, const char *type, const char *path) +{ + DWORD written; + HANDLE file; + HRSRC res; + void *ptr; + + file = CreateFileA(path, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + ok(file != INVALID_HANDLE_VALUE, "file creation failed, at %s, error %d\n", path, GetLastError()); + + res = FindResourceA(NULL, name, type); + ok( res != 0, "couldn't find resource\n" ); + ptr = LockResource( LoadResource( GetModuleHandleA(NULL), res )); + WriteFile( file, ptr, SizeofResource( GetModuleHandleA(NULL), res ), &written, NULL ); + ok( written == SizeofResource( GetModuleHandleA(NULL), res ), "couldn't write resource\n" ); + CloseHandle( file ); +} + +static void test_static_forwarded_import_refs(void) +{ + CHAR temp_path[MAX_PATH], dir_path[MAX_PATH]; + CHAR forward1_path[MAX_PATH]; + CHAR forward2_path[MAX_PATH]; + CHAR forward3_path[MAX_PATH]; + CHAR forward4_path[MAX_PATH]; + HMODULE forward1, forward2, forward3, forward4; + unsigned long result; + unsigned long (*test_func_stub)(void); + const char *skiptest; + BOOL skiptest_flag = FALSE; + + GetTempPathA( ARRAY_SIZE(temp_path), temp_path ); + GetTempFileNameA( temp_path, "ldr", GetTickCount() | 1UL, dir_path ); + ok( CreateDirectoryA( dir_path, NULL ), "failed to create dir %s, error %u\n", + dir_path, GetLastError() ); + + snprintf( forward1_path, MAX_PATH, "%s\\forward1.dll", dir_path ); + snprintf( forward2_path, MAX_PATH, "%s\\forward2.dll", dir_path ); + snprintf( forward3_path, MAX_PATH, "%s\\forward3.dll", dir_path ); + snprintf( forward4_path, MAX_PATH, "%s\\forward4.dll", dir_path ); + extract_resource( "forward1.dll", "TESTDLL", forward1_path ); + extract_resource( "forward2.dll", "TESTDLL", forward2_path ); + extract_resource( "forward3.dll", "TESTDLL", forward3_path ); + extract_resource( "forward4.dll", "TESTDLL", forward4_path ); + + forward1 = LoadLibraryA( forward1_path ); + ok( !!forward1, "couldn't find %s: %u\n", forward1_path, GetLastError() ); + forward2 = LoadLibraryA( forward2_path ); + ok( !!forward2, "couldn't find %s: %u\n", forward2_path, GetLastError() ); + forward3 = LoadLibraryA( forward3_path ); + ok( !!forward3, "couldn't find %s: %u\n", forward3_path, GetLastError() ); + forward4 = LoadLibraryA( forward4_path ); + ok( !!forward4, "couldn't find %s: %u\n", forward4_path, GetLastError() ); + + skiptest = (const char *)GetProcAddress( forward4, "skiptest" ); + if (skiptest && *skiptest) + { + skiptest_flag = TRUE; + skip( "forward4: %s\n", skiptest ); + } + else + { + test_func_stub = (void *)GetProcAddress( forward4, "test_func_stub" ); + ok( !!test_func_stub, "forward4!test_func_stub not found\n" ); + result = test_func_stub ? test_func_stub() : -1UL; + ok( result == 0x12345678UL, "forward4!test_func_stub returned %#lx (expected %#lx)\n", + result, 0x12345678UL ); + } + + FreeLibrary( forward1 ); + FreeLibrary( forward2 ); + FreeLibrary( forward3 ); + + if (!skiptest_flag) + { + todo_wine + ok( !!GetModuleHandleA( "forward1.dll" ), "forward1.dll unexpectedly unloaded\n" ); + todo_wine + ok( !!GetModuleHandleA( "forward2.dll" ), "forward2.dll unexpectedly unloaded\n" ); + ok( !!GetModuleHandleA( "forward3.dll" ), "forward3.dll unexpectedly unloaded\n" ); + } + + FreeLibrary( forward4 ); + + ok( !GetModuleHandleA( "forward1.dll" ), "forward1.dll unexpectedly kept open\n" ); + ok( !GetModuleHandleA( "forward2.dll" ), "forward2.dll unexpectedly kept open\n" ); + ok( !GetModuleHandleA( "forward3.dll" ), "forward3.dll unexpectedly kept open\n" ); + ok( !GetModuleHandleA( "forward4.dll" ), "forward4.dll unexpectedly kept open\n" ); + + DeleteFileA( forward1_path ); + DeleteFileA( forward2_path ); + DeleteFileA( forward3_path ); + DeleteFileA( forward4_path ); + RemoveDirectoryA( dir_path ); +} + +static void test_dynamic_forwarded_import_refs(void) +{ + CHAR temp_path[MAX_PATH], dir_path[MAX_PATH]; + CHAR forward1_path[MAX_PATH]; + CHAR forward2_path[MAX_PATH]; + CHAR forward3_path[MAX_PATH]; + HMODULE forward1, forward2, forward3; + FARPROC proc1, proc2, proc3; + + GetTempPathA( ARRAY_SIZE(temp_path), temp_path ); + GetTempFileNameA( temp_path, "ldr", GetTickCount() | 1UL, dir_path ); + ok( CreateDirectoryA( dir_path, NULL ), "failed to create dir %s, error %u\n", + dir_path, GetLastError() ); + + snprintf( forward1_path, MAX_PATH, "%s\\forward1.dll", dir_path ); + snprintf( forward2_path, MAX_PATH, "%s\\forward2.dll", dir_path ); + snprintf( forward3_path, MAX_PATH, "%s\\forward3.dll", dir_path ); + extract_resource( "forward1.dll", "TESTDLL", forward1_path ); + extract_resource( "forward2.dll", "TESTDLL", forward2_path ); + extract_resource( "forward3.dll", "TESTDLL", forward3_path ); + + forward1 = LoadLibraryA( forward1_path ); + ok( !!forward1, "couldn't find %s: %u\n", forward1_path, GetLastError() ); + forward2 = LoadLibraryA( forward2_path ); + ok( !!forward2, "couldn't find %s: %u\n", forward2_path, GetLastError() ); + forward3 = LoadLibraryA( forward3_path ); + ok( !!forward3, "couldn't find %s: %u\n", forward3_path, GetLastError() ); + + proc1 = GetProcAddress(forward1, "forward_test_func"); + ok( !!proc1, "cannot resolve forward1!forward_test_func\n"); + proc2 = GetProcAddress(forward2, "forward_test_func"); + ok( !!proc2, "cannot resolve forward2!forward_test_func\n"); + proc3 = GetProcAddress(forward3, "forward_test_func"); + ok( !!proc3, "cannot resolve forward3!forward_test_func\n"); + ok( proc1 == proc3, "forward1!forward_test_func is not equal to forward3!forward_test_func\n"); + ok( proc2 == proc3, "forward2!forward_test_func is not equal to forward3!forward_test_func\n"); + + FreeLibrary( forward1 ); + FreeLibrary( forward2 ); + + todo_wine + ok( !!GetModuleHandleA( "forward1.dll" ), "forward1.dll unexpectedly unloaded\n" ); + todo_wine + ok( !!GetModuleHandleA( "forward2.dll" ), "forward2.dll unexpectedly unloaded\n" ); + + FreeLibrary( forward3 ); + + ok( !GetModuleHandleA( "forward1.dll" ), "forward1.dll unexpectedly kept open\n" ); + ok( !GetModuleHandleA( "forward2.dll" ), "forward2.dll unexpectedly kept open\n" ); + ok( !GetModuleHandleA( "forward3.dll" ), "forward3.dll unexpectedly kept open\n" ); + + DeleteFileA( forward1_path ); + DeleteFileA( forward2_path ); + DeleteFileA( forward3_path ); + RemoveDirectoryA( dir_path ); +} + +static void test_dynamic_forward_export_norefs(void) +{ + CHAR temp_path[MAX_PATH], dir_path[MAX_PATH]; + CHAR forward1_path[MAX_PATH]; + CHAR forward2_path[MAX_PATH]; + CHAR forward3_path[MAX_PATH]; + HMODULE forward1, forward2, forward3; + + GetTempPathA( ARRAY_SIZE(temp_path), temp_path ); + GetTempFileNameA( temp_path, "ldr", GetTickCount() | 1UL, dir_path ); + ok( CreateDirectoryA( dir_path, NULL ), "failed to create dir %s, error %u\n", + dir_path, GetLastError() ); + + snprintf( forward1_path, MAX_PATH, "%s\\forward1.dll", dir_path ); + snprintf( forward2_path, MAX_PATH, "%s\\forward2.dll", dir_path ); + snprintf( forward3_path, MAX_PATH, "%s\\forward3.dll", dir_path ); + extract_resource( "forward1.dll", "TESTDLL", forward1_path ); + extract_resource( "forward2.dll", "TESTDLL", forward2_path ); + extract_resource( "forward3.dll", "TESTDLL", forward3_path ); + + forward1 = LoadLibraryA( forward1_path ); + ok( !!forward1, "couldn't find %s: %u\n", forward1_path, GetLastError() ); + forward2 = LoadLibraryA( forward2_path ); + ok( !!forward2, "couldn't find %s: %u\n", forward2_path, GetLastError() ); + forward3 = LoadLibraryA( forward3_path ); + ok( !!forward3, "couldn't find %s: %u\n", forward3_path, GetLastError() ); + + FreeLibrary( forward1 ); + FreeLibrary( forward3 ); + + ok( !GetModuleHandleA( "forward1.dll" ), "forward1.dll unexpectedly kept open\n" ); + ok( !GetModuleHandleA( "forward3.dll" ), "forward3.dll unexpectedly kept open\n" ); + + FreeLibrary( forward2 ); + + ok( !GetModuleHandleA( "forward2.dll" ), "forward2.dll unexpectedly kept open\n" ); + + DeleteFileA( forward1_path ); + DeleteFileA( forward2_path ); + DeleteFileA( forward3_path ); + RemoveDirectoryA( dir_path ); +} + static void test_image_mapping(const char *dll_name, DWORD scn_page_access, BOOL is_dll) { HANDLE hfile, hmap; @@ -4106,6 +4302,9 @@ START_TEST(loader) test_filenames(); test_ResolveDelayLoadedAPI(); test_ImportDescriptors(); + test_static_forwarded_import_refs(); + test_dynamic_forwarded_import_refs(); + test_dynamic_forward_export_norefs(); test_section_access(); test_import_resolution(); test_ExitProcess(); -- 2.31.1