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

Jinoh Kang wine at gitlab.winehq.org
Sun Jul 3 11:19:26 CDT 2022


From: Jinoh Kang <jinoh.kang.kr at gmail.com>

Signed-off-by: Jinoh Kang <jinoh.kang.kr at gmail.com>
---
 dlls/icmp/Makefile.in             |   1 +
 dlls/kernel32/tests/Makefile.in   |  14 ++-
 dlls/kernel32/tests/forward1.c    |  19 ++++
 dlls/kernel32/tests/forward1.spec |   2 +
 dlls/kernel32/tests/forward2.c    |   9 ++
 dlls/kernel32/tests/forward2.spec |   2 +
 dlls/kernel32/tests/forward3.c    |   9 ++
 dlls/kernel32/tests/forward3.spec |   2 +
 dlls/kernel32/tests/loader.c      | 177 ++++++++++++++++++++++++++++++
 dlls/kernel32/tests/sforward.c    |  18 +++
 dlls/kernel32/tests/sforward.spec |   1 +
 11 files changed, 252 insertions(+), 2 deletions(-)
 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/sforward.c
 create mode 100644 dlls/kernel32/tests/sforward.spec

diff --git a/dlls/icmp/Makefile.in b/dlls/icmp/Makefile.in
index 93213ab130b..c21eac39cf9 100644
--- a/dlls/icmp/Makefile.in
+++ b/dlls/icmp/Makefile.in
@@ -1,3 +1,4 @@
 MODULE    = icmp.dll
+IMPORTLIB = icmp
 
 EXTRADLLFLAGS = -Wb,--data-only
diff --git a/dlls/kernel32/tests/Makefile.in b/dlls/kernel32/tests/Makefile.in
index e9516603ce9..67775e78a83 100644
--- a/dlls/kernel32/tests/Makefile.in
+++ b/dlls/kernel32/tests/Makefile.in
@@ -1,5 +1,7 @@
 TESTDLL   = kernel32.dll
-IMPORTS   = user32 advapi32
+
+# icmp is for testing export forwarding (to iphlpapi)
+IMPORTS   = user32 advapi32 icmp
 
 SOURCES = \
 	actctx.c \
@@ -37,4 +39,12 @@ SOURCES = \
 	toolhelp.c \
 	version.c \
 	virtual.c \
-	volume.c
+	volume.c \
+	forward1.c \
+	forward1.spec \
+	forward2.c \
+	forward2.spec \
+	forward3.c \
+	forward3.spec \
+	sforward.c \
+	sforward.spec
diff --git a/dlls/kernel32/tests/forward1.c b/dlls/kernel32/tests/forward1.c
new file mode 100644
index 00000000000..6419d95eaea
--- /dev/null
+++ b/dlls/kernel32/tests/forward1.c
@@ -0,0 +1,19 @@
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+BOOL WINAPI DllMain(HINSTANCE instance_new, DWORD reason, LPVOID reserved)
+{
+    if (reason == DLL_PROCESS_ATTACH)
+        DisableThreadLibraryCalls( instance_new );
+    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..d1e77f45f3c
--- /dev/null
+++ b/dlls/kernel32/tests/forward2.c
@@ -0,0 +1,9 @@
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+BOOL WINAPI DllMain(HINSTANCE instance_new, DWORD reason, LPVOID reserved)
+{
+    if (reason == DLL_PROCESS_ATTACH)
+        DisableThreadLibraryCalls( instance_new );
+    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..d1e77f45f3c
--- /dev/null
+++ b/dlls/kernel32/tests/forward3.c
@@ -0,0 +1,9 @@
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+BOOL WINAPI DllMain(HINSTANCE instance_new, DWORD reason, LPVOID reserved)
+{
+    if (reason == DLL_PROCESS_ATTACH)
+        DisableThreadLibraryCalls( instance_new );
+    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/loader.c b/dlls/kernel32/tests/loader.c
index 365f4465fc7..fe5e6815271 100644
--- a/dlls/kernel32/tests/loader.c
+++ b/dlls/kernel32/tests/loader.c
@@ -1642,6 +1642,180 @@ 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], sforward_path[MAX_PATH];
+    HMODULE iphlpapi, icmp, sforward;
+    FARPROC test_func_stub;
+
+    ok( !GetModuleHandleA( "iphlpapi.dll" ), "iphlpapi.dll shall not have already been loaded\n" );
+    ok( !GetModuleHandleA( "icmp.dll" ), "icmp.dll shall not have already been loaded\n" );
+
+    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( sforward_path, MAX_PATH, "%s\\sforward.dll", dir_path );
+    extract_resource( "sforward.dll", "TESTDLL", sforward_path );
+
+    iphlpapi = LoadLibraryA( "iphlpapi.dll" );
+    ok( !!iphlpapi, "couldn't find iphlpapi.dll: %u\n", GetLastError() );
+    icmp = LoadLibraryA( "icmp.dll" );
+    ok( !!icmp, "couldn't find icmp.dll: %u\n", GetLastError() );
+    sforward = LoadLibraryA( sforward_path );
+    ok( !!sforward, "couldn't find %s: %u\n", sforward_path, GetLastError() );
+
+    test_func_stub = GetProcAddress( sforward, "test_func_stub" );
+    ok( !!test_func_stub, "sforward!test_func_stub not found\n" );
+
+    /* When a DLL imports a forwarded export, the loader introduces a module
+     * dependency from the importer DLL to the target DLL of the forwarded
+     * export.  This keeps iphlpapi and icmp from being unloaded.
+     */
+    FreeLibrary( iphlpapi );
+    FreeLibrary( icmp );
+    todo_wine
+    ok( !!GetModuleHandleA( "iphlpapi.dll" ), "iphlpapi.dll unexpectedly unloaded\n" );
+    ok( !!GetModuleHandleA( "icmp.dll" ), "icmp.dll unexpectedly unloaded\n" );
+
+    FreeLibrary( sforward );
+    ok( !GetModuleHandleA( "iphlpapi.dll" ), "iphlpapi.dll unexpectedly kept open\n" );
+    ok( !GetModuleHandleA( "icmp.dll" ), "icmp.dll unexpectedly kept open\n" );
+    ok( !GetModuleHandleA( "sforward.dll" ), "sforward.dll unexpectedly kept open\n" );
+
+    DeleteFileA( sforward_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, oproc1, oproc2, oproc3;
+
+    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");
+
+    oproc1 = GetProcAddress(forward1, (LPSTR)2);
+    ok( !!oproc1, "cannot resolve forward1!#2 (forward_test_func2)\n");
+    oproc2 = GetProcAddress(forward2, (LPSTR)2);
+    ok( !!oproc2, "cannot resolve forward2!#2 (forward_test_func2)\n");
+    oproc3 = GetProcAddress(forward3, (LPSTR)2);
+    ok( !!oproc3, "cannot resolve forward3!#2 (forward_test_func2)\n");
+    ok( oproc1 == oproc3, "forward1!forward_test_func2 is not equal to forward3!forward_test_func2\n");
+    ok( oproc2 == oproc3, "forward2!forward_test_func2 is not equal to forward3!forward_test_func2\n");
+
+    /* GetProcAddress, when called on a forwarded export, has a side effect of
+     * introducing a module dependency from the source forwarder DLL to the
+     * target DLL.  This keeps forward1 and forward2 from being unloaded.
+     */
+    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() );
+
+    /* The mere existence of a forwarded export shall not count as a reference by itself. */
+    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;
@@ -4119,9 +4293,12 @@ START_TEST(loader)
         return;
     }
 
+    test_static_forwarded_import_refs();  /* Must be first; other tests may load iphlpapi.dll */
     test_filenames();
     test_ResolveDelayLoadedAPI();
     test_ImportDescriptors();
+    test_dynamic_forwarded_import_refs();
+    test_dynamic_forward_export_norefs();
     test_section_access();
     test_import_resolution();
     test_ExitProcess();
diff --git a/dlls/kernel32/tests/sforward.c b/dlls/kernel32/tests/sforward.c
new file mode 100644
index 00000000000..e632f69efb1
--- /dev/null
+++ b/dlls/kernel32/tests/sforward.c
@@ -0,0 +1,18 @@
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <ws2tcpip.h>
+#include <iphlpapi.h>
+#include <icmpapi.h>
+
+void test_func_stub(void)
+{
+    HANDLE file = IcmpCreateFile();
+    if (file != INVALID_HANDLE_VALUE) IcmpCloseHandle( file );
+}
+
+BOOL WINAPI DllMain(HINSTANCE instance_new, DWORD reason, LPVOID reserved)
+{
+    if (reason == DLL_PROCESS_ATTACH)
+        DisableThreadLibraryCalls( instance_new );
+    return TRUE;
+}
diff --git a/dlls/kernel32/tests/sforward.spec b/dlls/kernel32/tests/sforward.spec
new file mode 100644
index 00000000000..cb6d4add796
--- /dev/null
+++ b/dlls/kernel32/tests/sforward.spec
@@ -0,0 +1 @@
+@ cdecl test_func_stub()
-- 
GitLab

https://gitlab.winehq.org/wine/wine/-/merge_requests/364



More information about the wine-devel mailing list