Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
---
dlls/kernel32/tests/Makefile.in | 10 +-
dlls/kernel32/tests/forward1.c | 12 ++
dlls/kernel32/tests/forward1.spec | 1 +
dlls/kernel32/tests/forward2.c | 7 ++
dlls/kernel32/tests/forward2.spec | 1 +
dlls/kernel32/tests/forward3.c | 7 ++
dlls/kernel32/tests/forward3.spec | 1 +
dlls/kernel32/tests/forward4.c | 28 +++++
dlls/kernel32/tests/forward4.spec | 1 +
dlls/kernel32/tests/iatgas.h | 77 +++++++++++++
dlls/kernel32/tests/loader.c | 177 ++++++++++++++++++++++++++++++
11 files changed, 321 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..0a10e18e4ae
--- /dev/null
+++ b/dlls/kernel32/tests/forward1.c
@@ -0,0 +1,12 @@
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+BOOL WINAPI DllMain(HINSTANCE instance_new, DWORD reason, LPVOID reserved)
+{
+ return TRUE;
+}
+
+int forward_test_func(void)
+{
+ return 1;
+}
diff --git a/dlls/kernel32/tests/forward1.spec b/dlls/kernel32/tests/forward1.spec
new file mode 100644
index 00000000000..09911d484a3
--- /dev/null
+++ b/dlls/kernel32/tests/forward1.spec
@@ -0,0 +1 @@
+@ cdecl forward_test_func()
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..9a13a033444
--- /dev/null
+++ b/dlls/kernel32/tests/forward2.spec
@@ -0,0 +1 @@
+@ cdecl forward_test_func() forward1.forward_test_func
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..e2bd24f2a8d
--- /dev/null
+++ b/dlls/kernel32/tests/forward3.spec
@@ -0,0 +1 @@
+@ cdecl forward_test_func() forward2.forward_test_func
diff --git a/dlls/kernel32/tests/forward4.c b/dlls/kernel32/tests/forward4.c
new file mode 100644
index 00000000000..e68a3d374df
--- /dev/null
+++ b/dlls/kernel32/tests/forward4.c
@@ -0,0 +1,28 @@
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#ifdef __GNUC__
+#include "iatgas.h"
+
+INLINE_IMPORT_BEGIN("forward3.dll")
+ IMPORT_ENTRY(0, SYM(forward_test_func), IMPSYM(forward_test_func))
+INLINE_IMPORT_END()
+
+extern int (*IMPSYM(forward_test_func))(void);
+
+int test_func_stub(void)
+{
+ return IMPSYM(forward_test_func)();
+}
+#else
+int test_func_stub(void)
+{
+ /* unimplemented */
+ for (;;);
+}
+#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..cb6d4add796
--- /dev/null
+++ b/dlls/kernel32/tests/forward4.spec
@@ -0,0 +1 @@
+@ cdecl test_func_stub()
diff --git a/dlls/kernel32/tests/iatgas.h b/dlls/kernel32/tests/iatgas.h
new file mode 100644
index 00000000000..8657c3e905a
--- /dev/null
+++ b/dlls/kernel32/tests/iatgas.h
@@ -0,0 +1,77 @@
+/*
+ * 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
+ */
+
+#ifdef __i386__
+#define SYM(x) _ ## x
+#define IMPSYM(x) _imp__ ## x
+#else
+#define SYM(x) x
+#define IMPSYM(x) __imp_ ## x
+#endif
+#define STRINGIFY_ARG(x) #x
+#define STRINGIFY(x) STRINGIFY_ARG(x)
+
+#if defined(_WIN64)
+#define PTRSIZE_STR "8"
+#elif defined(_WIN32)
+#define PTRSIZE_STR "4"
+#else
+#error unknown pointer size
+#endif
+
+#define INLINE_IMPORT_BEGIN(dll_name) \
+ __asm__( \
+ ".section \".idata$2\", \"aw\"\n" \
+ ".skip 4\n" /* OriginalFirstThunk */ \
+ ".reloc . - 4, rva32, 4f\n" \
+ ".long 0\n" /* TimeDateStamp */ \
+ ".long -1\n" /* ForwarderChain */ \
+ ".skip 4\n" /* Name */ \
+ ".reloc . - 4, rva32, 7f\n" \
+ ".skip 4\n" /* FirstThunk */ \
+ ".reloc . - 4, rva32, 5f\n" \
+ ".section \".idata$4\", \"a\"\n" \
+ "4:\n" \
+ ".section \".idata$5\", \"aw\"\n" \
+ "5:\n" \
+ ".section \".idata$7\", \"a\"\n" \
+ "7:\n" \
+ ".asciz \"" dll_name "\"\n" \
+ ".p2align 2\n"
+#define IMPORT_ENTRY(hint, name, impsym) \
+ ".section \".idata$4\", \"a\"\n" \
+ ".skip " PTRSIZE_STR "\n" \
+ ".reloc . - " PTRSIZE_STR ", rva32, 6f\n" \
+ ".section \".idata$5\", \"aw\"\n" \
+ STRINGIFY(SYM(impsym)) ":\n" \
+ ".skip " PTRSIZE_STR "\n" \
+ ".reloc . - " PTRSIZE_STR ", rva32, 6f\n" \
+ ".section \".idata$6\", \"a\"\n" \
+ "6:\n" \
+ ".short " STRINGIFY_ARG(hint) "\n" \
+ ".asciz \"" STRINGIFY_ARG(name) "\"\n" \
+ ".p2align 1\n"
+#define INLINE_IMPORT_END() \
+ ".section \".idata$4\", \"a\"\n" \
+ ".skip " PTRSIZE_STR "\n" \
+ ".section \".idata$5\", \"aw\"\n" \
+ ".skip " PTRSIZE_STR "\n" \
+ ".text\n" \
+ );
diff --git a/dlls/kernel32/tests/loader.c b/dlls/kernel32/tests/loader.c
index 308cf1a44a0..52d14562205 100644
--- a/dlls/kernel32/tests/loader.c
+++ b/dlls/kernel32/tests/loader.c
@@ -1641,6 +1641,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];
+ 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;
+
+ 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() );
+
+ FreeLibrary( forward1 );
+ FreeLibrary( forward2 );
+ FreeLibrary( forward3 );
+
+ 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 +4280,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