[PATCH v7 resend 1/2] kernel32/tests: Test module refcounting with forwarded exports.

Jinoh Kang jinoh.kang.kr at gmail.com
Fri Jan 7 13:10:21 CST 2022


Signed-off-by: Jinoh Kang <jinoh.kang.kr at gmail.com>
---
 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 <windows.h>
+
+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 <windows.h>
+
+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 <windows.h>
+
+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 <windows.h>
+
+#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 f990d632f73..97957604aa0 100644
--- a/dlls/kernel32/tests/loader.c
+++ b/dlls/kernel32/tests/loader.c
@@ -1642,6 +1642,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;
@@ -4122,6 +4318,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




More information about the wine-devel mailing list