diff mbox series

plugins: Move the windows linking function to qemu

Message ID 20231109091453.589-1-gmanning@rapitasystems.com (mailing list archive)
State New, archived
Headers show
Series plugins: Move the windows linking function to qemu | expand

Commit Message

Greg Manning Nov. 9, 2023, 9:14 a.m. UTC
Previously, a plugin author needed an implementation of the
__pfnDliFailureHook2 or __pfnDliNotifyHook2 hook in the plugin. Now all
they need is a null exported pointer with the right name
(win32_common.c). If QEMU finds this, it will set it to the hook
function, which has now moved into qemu (os-win32.c).
---
 contrib/plugins/win32_linker.c | 23 +++--------------------
 include/sysemu/os-win32.h      | 25 +++++++++++++++++++++++++
 os-win32.c                     | 33 +++++++++++++++++++++++++++++++++
 plugins/loader.c               |  3 +++
 4 files changed, 64 insertions(+), 20 deletions(-)
diff mbox series

Patch

diff --git a/contrib/plugins/win32_linker.c b/contrib/plugins/win32_linker.c
index 7534b2b8bf..619fdd38c8 100644
--- a/contrib/plugins/win32_linker.c
+++ b/contrib/plugins/win32_linker.c
@@ -4,8 +4,8 @@ 
  * This hook, __pfnDliFailureHook2, is documented in the microsoft documentation here:
  * https://learn.microsoft.com/en-us/cpp/build/reference/error-handling-and-notification
  * It gets called when a delay-loaded DLL encounters various errors.
- * We handle the specific case of a DLL looking for a "qemu.exe",
- * and give it the running executable (regardless of what it is named).
+ * QEMU will set it to a function which handles the specific case of a DLL looking for
+ * a "qemu.exe", and give it the running executable (regardless of what it is named).
  *
  * This work is licensed under the terms of the GNU LGPL, version 2 or later.
  * See the COPYING.LIB file in the top-level directory.
@@ -14,21 +14,4 @@ 
 #include <windows.h>
 #include <delayimp.h>
 
-FARPROC WINAPI dll_failure_hook(unsigned dliNotify, PDelayLoadInfo pdli);
-
-
-PfnDliHook __pfnDliFailureHook2 = dll_failure_hook;
-
-FARPROC WINAPI dll_failure_hook(unsigned dliNotify, PDelayLoadInfo pdli) {
-    if (dliNotify == dliFailLoadLib) {
-        /* If the failing request was for qemu.exe, ... */
-        if (strcmp(pdli->szDll, "qemu.exe") == 0) {
-            /* Then pass back a pointer to the top level module. */
-            HMODULE top = GetModuleHandle(NULL);
-            return (FARPROC) top;
-        }
-    }
-    /* Otherwise we can't do anything special. */
-    return 0;
-}
-
+__declspec(dllexport) PfnDliHook __pfnDliNotifyHook2 = NULL;
diff --git a/include/sysemu/os-win32.h b/include/sysemu/os-win32.h
index 1047d260cb..0f698554b2 100644
--- a/include/sysemu/os-win32.h
+++ b/include/sysemu/os-win32.h
@@ -30,6 +30,8 @@ 
 #include <windows.h>
 #include <ws2tcpip.h>
 #include "qemu/typedefs.h"
+#include <delayimp.h>
+#include <gmodule.h>
 
 #ifdef HAVE_AFUNIX_H
 #include <afunix.h>
@@ -265,6 +267,29 @@  win32_close_exception_handler(struct _EXCEPTION_RECORD*, void*,
 void *qemu_win32_map_alloc(size_t size, HANDLE *h, Error **errp);
 void qemu_win32_map_free(void *ptr, HANDLE h, Error **errp);
 
+
+/* dll_delaylink_hook:
+ * @dliNotify: Type of event that caused this callback.
+ * @pdli: Extra data about the DLL being loaded
+ *
+ * For more info on the arguments and when this gets invoked see
+ * https://learn.microsoft.com/en-us/cpp/build/reference/understanding-the-helper-function
+ *
+ * This is to be used on windows as the target of a __pfnDliNotifyHook2 or __pfnDliFailureHook2
+ * hook. If the DLL being loaded is 'qemu.exe', it instead passes back a pointer to the main
+ * executable This gets set into plugins to assist with the plugins delay-linking back to the main
+ * executable, if they define an appropriate symbol. */
+FARPROC WINAPI dll_delaylink_hook(unsigned dliNotify, PDelayLoadInfo pdli);
+
+/* set_dll_delaylink_hook:
+ * @mod: pointer to the DLL being loaded
+ *
+ * takes a pointer to a loaded plugin DLL, and tries to find a __pfnDliNotifyHook2 or
+ * __pfnDliFailureHook2 hook. If it finds either one, and its value is null, it sets it to the
+ * address fo dll_delaylink_hook.
+ */
+void set_dll_delaylink_hook(GModule *mod);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/os-win32.c b/os-win32.c
index 725ad652e8..4a113d1d10 100644
--- a/os-win32.c
+++ b/os-win32.c
@@ -60,3 +60,36 @@  void os_set_line_buffering(void)
     setbuf(stdout, NULL);
     setbuf(stderr, NULL);
 }
+
+FARPROC WINAPI dll_delaylink_hook(unsigned dliNotify, PDelayLoadInfo pdli) {
+    /* If we just tried, or are about to try to load a DLL ... */
+    if (dliNotify == dliFailLoadLib || dliNotify == dliNotePreLoadLibrary) {
+        /* ... if the failing request was for qemu.exe, ... */
+        if (strcmp(pdli->szDll, "qemu.exe") == 0) {
+            /* ... then pass back a pointer to the top level module. */
+            HMODULE top = GetModuleHandle(NULL);
+            return (FARPROC) top;
+        }
+    }
+    /* Otherwise we can't do anything special. */
+    return 0;
+}
+void set_dll_delaylink_hook(GModule *mod) {
+    /* find the __pfnDliFailureHook2 symbol in the DLL.
+     * if found, set it to our handler.
+     */
+    gpointer sym;
+    PfnDliHook *hook;
+    if (g_module_symbol(mod, "__pfnDliFailureHook2", &sym)) {
+        hook = (PfnDliHook*) sym;
+        if (hook != NULL && *hook == NULL) {
+            *hook = &dll_delaylink_hook;
+        }
+    }
+    if (g_module_symbol(mod, "__pfnDliNotifyHook2", &sym)) {
+        hook = (PfnDliHook*) sym;
+        if (hook != NULL && *hook == NULL) {
+            *hook = &dll_delaylink_hook;
+        }
+    }
+}
diff --git a/plugins/loader.c b/plugins/loader.c
index 734c11cae0..7ead9b26e4 100644
--- a/plugins/loader.c
+++ b/plugins/loader.c
@@ -241,6 +241,9 @@  static int plugin_load(struct qemu_plugin_desc *desc, const qemu_info_t *info, E
     }
     QTAILQ_INSERT_TAIL(&plugin.ctxs, ctx, entry);
     ctx->installing = true;
+    #ifdef CONFIG_WIN32
+        set_dll_delaylink_hook(ctx->handle);
+    #endif
     rc = install(ctx->id, info, desc->argc, desc->argv);
     ctx->installing = false;
     if (rc) {