Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)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