[PATCH v2 1/1] kernel32/tests: Test module refcounting with forwarded exports.
Jinoh Kang
wine at gitlab.winehq.org
Sun Jul 3 12:03:33 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..5d8e57141e0 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 %lu\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 %lu\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: %lu\n", GetLastError() );
+ icmp = LoadLibraryA( "icmp.dll" );
+ ok( !!icmp, "couldn't find icmp.dll: %lu\n", GetLastError() );
+ sforward = LoadLibraryA( sforward_path );
+ ok( !!sforward, "couldn't find %s: %lu\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 %lu\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: %lu\n", forward1_path, GetLastError() );
+ forward2 = LoadLibraryA( forward2_path );
+ ok( !!forward2, "couldn't find %s: %lu\n", forward2_path, GetLastError() );
+ forward3 = LoadLibraryA( forward3_path );
+ ok( !!forward3, "couldn't find %s: %lu\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 %lu\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: %lu\n", forward1_path, GetLastError() );
+ forward2 = LoadLibraryA( forward2_path );
+ ok( !!forward2, "couldn't find %s: %lu\n", forward2_path, GetLastError() );
+ forward3 = LoadLibraryA( forward3_path );
+ ok( !!forward3, "couldn't find %s: %lu\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