* [PATCH v2 1/4] plugins: add dllexport and dllimport to api funcs
2023-11-02 17:19 [PATCH v2 0/4] Enable plugin support for windows Greg Manning
@ 2023-11-02 17:19 ` Greg Manning
2023-11-02 18:10 ` Alex Bennée
2023-11-02 17:19 ` [PATCH v2 2/4] plugins: make test/example plugins work on windows Greg Manning
` (3 subsequent siblings)
4 siblings, 1 reply; 10+ messages in thread
From: Greg Manning @ 2023-11-02 17:19 UTC (permalink / raw)
To: qemu-devel
Cc: luoyonggang, richard.henderson, Alex Bennée, Paolo Bonzini,
Greg Manning
In qemu-plugin.h, mark all API functions as __declspec(dllexport) when
compiling the executables, and as __declspec(dllimport) when being used
to compile plugins against.
Signed-off-by: Greg Manning <gmanning@rapitasystems.com>
---
include/qemu/qemu-plugin.h | 50 +++++++++++++++++++++++++++++++++++---
1 file changed, 47 insertions(+), 3 deletions(-)
diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index 50a9957279..4daab6efd2 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -22,15 +22,18 @@
* https://gcc.gnu.org/wiki/Visibility
*/
#if defined _WIN32 || defined __CYGWIN__
- #ifdef BUILDING_DLL
- #define QEMU_PLUGIN_EXPORT __declspec(dllexport)
- #else
+ #ifdef CONFIG_PLUGIN
#define QEMU_PLUGIN_EXPORT __declspec(dllimport)
+ #define QEMU_PLUGIN_API __declspec(dllexport)
+ #else
+ #define QEMU_PLUGIN_EXPORT __declspec(dllexport)
+ #define QEMU_PLUGIN_API __declspec(dllimport)
#endif
#define QEMU_PLUGIN_LOCAL
#else
#define QEMU_PLUGIN_EXPORT __attribute__((visibility("default")))
#define QEMU_PLUGIN_LOCAL __attribute__((visibility("hidden")))
+ #define QEMU_PLUGIN_API
#endif
/**
@@ -147,6 +150,7 @@ typedef void (*qemu_plugin_vcpu_udata_cb_t)(unsigned int vcpu_index,
*
* Note: Calling this function from qemu_plugin_install() is a bug.
*/
+QEMU_PLUGIN_API
void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb);
/**
@@ -160,6 +164,7 @@ void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb);
* Plugins are reset asynchronously, and therefore the given plugin receives
* callbacks until @cb is called.
*/
+QEMU_PLUGIN_API
void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb);
/**
@@ -171,6 +176,7 @@ void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb);
*
* See also: qemu_plugin_register_vcpu_exit_cb()
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_init_cb(qemu_plugin_id_t id,
qemu_plugin_vcpu_simple_cb_t cb);
@@ -183,6 +189,7 @@ void qemu_plugin_register_vcpu_init_cb(qemu_plugin_id_t id,
*
* See also: qemu_plugin_register_vcpu_init_cb()
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id,
qemu_plugin_vcpu_simple_cb_t cb);
@@ -193,6 +200,7 @@ void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id,
*
* The @cb function is called every time a vCPU idles.
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id,
qemu_plugin_vcpu_simple_cb_t cb);
@@ -203,6 +211,7 @@ void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id,
*
* The @cb function is called every time a vCPU resumes execution.
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id,
qemu_plugin_vcpu_simple_cb_t cb);
@@ -253,6 +262,7 @@ typedef void (*qemu_plugin_vcpu_tb_trans_cb_t)(qemu_plugin_id_t id,
* callbacks to be triggered when the block or individual instruction
* executes.
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id,
qemu_plugin_vcpu_tb_trans_cb_t cb);
@@ -265,6 +275,7 @@ void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id,
*
* The @cb function is called every time a translated unit executes.
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb,
qemu_plugin_vcpu_udata_cb_t cb,
enum qemu_plugin_cb_flags flags,
@@ -296,6 +307,7 @@ enum qemu_plugin_op {
* Note: ops are not atomic so in multi-threaded/multi-smp situations
* you will get inexact results.
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb,
enum qemu_plugin_op op,
void *ptr, uint64_t imm);
@@ -309,6 +321,7 @@ void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb,
*
* The @cb function is called every time an instruction is executed
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn,
qemu_plugin_vcpu_udata_cb_t cb,
enum qemu_plugin_cb_flags flags,
@@ -324,6 +337,7 @@ void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn,
* Insert an inline op to every time an instruction executes. Useful
* if you just want to increment a single counter somewhere in memory.
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn,
enum qemu_plugin_op op,
void *ptr, uint64_t imm);
@@ -334,6 +348,7 @@ void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn,
*
* Returns: number of instructions in this block
*/
+QEMU_PLUGIN_API
size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb);
/**
@@ -342,6 +357,7 @@ size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb);
*
* Returns: virtual address of block start
*/
+QEMU_PLUGIN_API
uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb);
/**
@@ -355,6 +371,7 @@ uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb);
*
* Returns: opaque handle to instruction
*/
+QEMU_PLUGIN_API
struct qemu_plugin_insn *
qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx);
@@ -368,6 +385,7 @@ qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx);
* Returns: pointer to a stream of bytes containing the value of this
* instructions opcode.
*/
+QEMU_PLUGIN_API
const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn);
/**
@@ -376,6 +394,7 @@ const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn);
*
* Returns: size of instruction in bytes
*/
+QEMU_PLUGIN_API
size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn);
/**
@@ -384,6 +403,7 @@ size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn);
*
* Returns: virtual address of instruction
*/
+QEMU_PLUGIN_API
uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn);
/**
@@ -392,6 +412,7 @@ uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn);
*
* Returns: hardware (physical) target address of instruction
*/
+QEMU_PLUGIN_API
void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn);
/**
@@ -410,6 +431,7 @@ struct qemu_plugin_hwaddr;
*
* Returns: size of access in ^2 (0=byte, 1=16bit, 2=32bit etc...)
*/
+QEMU_PLUGIN_API
unsigned int qemu_plugin_mem_size_shift(qemu_plugin_meminfo_t info);
/**
* qemu_plugin_mem_is_sign_extended() - was the access sign extended
@@ -417,6 +439,7 @@ unsigned int qemu_plugin_mem_size_shift(qemu_plugin_meminfo_t info);
*
* Returns: true if it was, otherwise false
*/
+QEMU_PLUGIN_API
bool qemu_plugin_mem_is_sign_extended(qemu_plugin_meminfo_t info);
/**
* qemu_plugin_mem_is_big_endian() - was the access big endian
@@ -424,6 +447,7 @@ bool qemu_plugin_mem_is_sign_extended(qemu_plugin_meminfo_t info);
*
* Returns: true if it was, otherwise false
*/
+QEMU_PLUGIN_API
bool qemu_plugin_mem_is_big_endian(qemu_plugin_meminfo_t info);
/**
* qemu_plugin_mem_is_store() - was the access a store
@@ -431,6 +455,7 @@ bool qemu_plugin_mem_is_big_endian(qemu_plugin_meminfo_t info);
*
* Returns: true if it was, otherwise false
*/
+QEMU_PLUGIN_API
bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info);
/**
@@ -446,6 +471,7 @@ bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info);
* information about the handle should be recovered before the
* callback returns.
*/
+QEMU_PLUGIN_API
struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info,
uint64_t vaddr);
@@ -462,6 +488,7 @@ struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info,
* Returns true if the handle's memory operation is to memory-mapped IO, or
* false if it is to RAM
*/
+QEMU_PLUGIN_API
bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr);
/**
@@ -473,12 +500,14 @@ bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr);
* Note that the returned physical address may not be unique if you are dealing
* with multiple address spaces.
*/
+QEMU_PLUGIN_API
uint64_t qemu_plugin_hwaddr_phys_addr(const struct qemu_plugin_hwaddr *haddr);
/*
* Returns a string representing the device. The string is valid for
* the lifetime of the plugin.
*/
+QEMU_PLUGIN_API
const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h);
/**
@@ -513,6 +542,7 @@ typedef void (*qemu_plugin_vcpu_mem_cb_t) (unsigned int vcpu_index,
* callback so the plugin is responsible for ensuring it doesn't get
* confused by making appropriate use of locking if required.
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn,
qemu_plugin_vcpu_mem_cb_t cb,
enum qemu_plugin_cb_flags flags,
@@ -531,6 +561,7 @@ void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn,
* instruction. This provides for a lightweight but not thread-safe
* way of counting the number of operations done.
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_mem_inline(struct qemu_plugin_insn *insn,
enum qemu_plugin_mem_rw rw,
enum qemu_plugin_op op, void *ptr,
@@ -544,6 +575,7 @@ typedef void
uint64_t a3, uint64_t a4, uint64_t a5,
uint64_t a6, uint64_t a7, uint64_t a8);
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_syscall_cb(qemu_plugin_id_t id,
qemu_plugin_vcpu_syscall_cb_t cb);
@@ -551,6 +583,7 @@ typedef void
(*qemu_plugin_vcpu_syscall_ret_cb_t)(qemu_plugin_id_t id, unsigned int vcpu_idx,
int64_t num, int64_t ret);
+QEMU_PLUGIN_API
void
qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id,
qemu_plugin_vcpu_syscall_ret_cb_t cb);
@@ -563,6 +596,7 @@ qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id,
* Returns an allocated string containing the disassembly
*/
+QEMU_PLUGIN_API
char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn);
/**
@@ -572,6 +606,7 @@ char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn);
* Return a static string referring to the symbol. This is dependent
* on the binary QEMU is running having provided a symbol table.
*/
+QEMU_PLUGIN_API
const char *qemu_plugin_insn_symbol(const struct qemu_plugin_insn *insn);
/**
@@ -583,9 +618,11 @@ const char *qemu_plugin_insn_symbol(const struct qemu_plugin_insn *insn);
*
* See also: qemu_plugin_register_vcpu_init_cb()
*/
+QEMU_PLUGIN_API
void qemu_plugin_vcpu_for_each(qemu_plugin_id_t id,
qemu_plugin_vcpu_simple_cb_t cb);
+QEMU_PLUGIN_API
void qemu_plugin_register_flush_cb(qemu_plugin_id_t id,
qemu_plugin_simple_cb_t cb);
@@ -602,6 +639,7 @@ void qemu_plugin_register_flush_cb(qemu_plugin_id_t id,
* In user-mode it is possible a few un-instrumented instructions from
* child threads may run before the host kernel reaps the threads.
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_atexit_cb(qemu_plugin_id_t id,
qemu_plugin_udata_cb_t cb, void *userdata);
@@ -615,6 +653,7 @@ int qemu_plugin_n_max_vcpus(void);
* qemu_plugin_outs() - output string via QEMU's logging system
* @string: a string
*/
+QEMU_PLUGIN_API
void qemu_plugin_outs(const char *string);
/**
@@ -628,6 +667,7 @@ void qemu_plugin_outs(const char *string);
* returns true if the combination @name=@val parses correctly to a boolean
* argument, and false otherwise
*/
+QEMU_PLUGIN_API
bool qemu_plugin_bool_parse(const char *name, const char *val, bool *ret);
/**
@@ -638,6 +678,7 @@ bool qemu_plugin_bool_parse(const char *name, const char *val, bool *ret);
* return NULL. The user should g_free() the string once no longer
* needed.
*/
+QEMU_PLUGIN_API
const char *qemu_plugin_path_to_binary(void);
/**
@@ -646,6 +687,7 @@ const char *qemu_plugin_path_to_binary(void);
* Returns the nominal start address of the main text segment in
* user-mode. Currently returns 0 for system emulation.
*/
+QEMU_PLUGIN_API
uint64_t qemu_plugin_start_code(void);
/**
@@ -654,6 +696,7 @@ uint64_t qemu_plugin_start_code(void);
* Returns the nominal end address of the main text segment in
* user-mode. Currently returns 0 for system emulation.
*/
+QEMU_PLUGIN_API
uint64_t qemu_plugin_end_code(void);
/**
@@ -662,6 +705,7 @@ uint64_t qemu_plugin_end_code(void);
* Returns the nominal entry address of the main text segment in
* user-mode. Currently returns 0 for system emulation.
*/
+QEMU_PLUGIN_API
uint64_t qemu_plugin_entry_code(void);
#endif /* QEMU_QEMU_PLUGIN_H */
--
2.42.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v2 2/4] plugins: make test/example plugins work on windows
2023-11-02 17:19 [PATCH v2 0/4] Enable plugin support for windows Greg Manning
2023-11-02 17:19 ` [PATCH v2 1/4] plugins: add dllexport and dllimport to api funcs Greg Manning
@ 2023-11-02 17:19 ` Greg Manning
2023-11-02 18:11 ` Alex Bennée
2023-11-02 17:19 ` [PATCH v2 3/4] plugins: disable lockstep plugin " Greg Manning
` (2 subsequent siblings)
4 siblings, 1 reply; 10+ messages in thread
From: Greg Manning @ 2023-11-02 17:19 UTC (permalink / raw)
To: qemu-devel
Cc: luoyonggang, richard.henderson, Alex Bennée, Paolo Bonzini,
Greg Manning
Generate a qemu_plugin_api.lib delay import lib on windows, for
windows qemu plugins to link against.
Implement an example dll load fail hook to link up the API functions
correctly when a plugin is loaded on windows.
Update the build scripts for the test and example plugins to use these
things.
Signed-off-by: Greg Manning <gmanning@rapitasystems.com>
---
configure | 3 +++
contrib/plugins/Makefile | 20 ++++++++++++++++----
contrib/plugins/win32_linker.c | 34 ++++++++++++++++++++++++++++++++++
plugins/meson.build | 17 +++++++++++++++++
tests/plugin/meson.build | 14 +++++++++++---
5 files changed, 81 insertions(+), 7 deletions(-)
create mode 100644 contrib/plugins/win32_linker.c
diff --git a/configure b/configure
index f1456f6123..04f2cdd166 100755
--- a/configure
+++ b/configure
@@ -1662,6 +1662,9 @@ echo "CFLAGS=${CFLAGS-$default_cflags} $EXTRA_CFLAGS" >> contrib/plugins/$config
if test "$targetos" = darwin; then
echo "CONFIG_DARWIN=y" >> contrib/plugins/$config_host_mak
fi
+if test "$targetos" = windows; then
+ echo "CONFIG_WIN32=y" >> contrib/plugins/$config_host_mak
+fi
# tests/tcg configuration
(config_host_mak=tests/tcg/config-host.mak
diff --git a/contrib/plugins/Makefile b/contrib/plugins/Makefile
index 8ba78c7a32..751fa38619 100644
--- a/contrib/plugins/Makefile
+++ b/contrib/plugins/Makefile
@@ -22,7 +22,14 @@ NAMES += hwprofile
NAMES += cache
NAMES += drcov
-SONAMES := $(addsuffix .so,$(addprefix lib,$(NAMES)))
+ifeq ($(CONFIG_WIN32),y)
+SO_SUFFIX := .dll
+LDLIBS += $(shell $(PKG_CONFIG) --libs glib-2.0)
+else
+SO_SUFFIX := .so
+endif
+
+SONAMES := $(addsuffix $(SO_SUFFIX),$(addprefix lib,$(NAMES)))
# The main QEMU uses Glib extensively so it's perfectly fine to use it
# in plugins (which many example do).
@@ -35,15 +42,20 @@ all: $(SONAMES)
%.o: %.c
$(CC) $(CFLAGS) $(PLUGIN_CFLAGS) -c -o $@ $<
-lib%.so: %.o
-ifeq ($(CONFIG_DARWIN),y)
+ifeq ($(CONFIG_WIN32),y)
+lib%$(SO_SUFFIX): %.o win32_linker.o ../../plugins/qemu_plugin_api.lib
+ $(CC) -shared -o $@ $^ $(LDLIBS)
+else ifeq ($(CONFIG_DARWIN),y)
+lib%$(SO_SUFFIX): %.o
$(CC) -bundle -Wl,-undefined,dynamic_lookup -o $@ $^ $(LDLIBS)
else
+lib%$(SO_SUFFIX): %.o
$(CC) -shared -o $@ $^ $(LDLIBS)
endif
+
clean:
- rm -f *.o *.so *.d
+ rm -f *.o *$(SO_SUFFIX) *.d
rm -Rf .libs
.PHONY: all clean
diff --git a/contrib/plugins/win32_linker.c b/contrib/plugins/win32_linker.c
new file mode 100644
index 0000000000..50797d616e
--- /dev/null
+++ b/contrib/plugins/win32_linker.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023, Greg Manning <gmanning@rapitasystems.com>
+ *
+ * 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).
+ *
+ * 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.
+ */
+
+#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;
+}
+
diff --git a/plugins/meson.build b/plugins/meson.build
index 71ed996ed3..8ed9fa270c 100644
--- a/plugins/meson.build
+++ b/plugins/meson.build
@@ -14,6 +14,23 @@ if not enable_modules
endif
if get_option('plugins')
+ if targetos == 'windows'
+ # Generate a .lib file for plugins to link against.
+ # First, create a .def file listing all the symbols a plugin should expect to have
+ # available in qemu
+ win32_plugin_def = configure_file(
+ input: files('qemu-plugins.symbols'),
+ output: 'qemu_plugin_api.def',
+ capture: true,
+ command: ['sed', '-e', '0,/^/s//EXPORTS/; s/[{};]//g', '@INPUT@'])
+ # then use dlltool to assemble a delaylib.
+ win32_qemu_plugin_api_lib = configure_file(
+ input: win32_plugin_def,
+ output: 'qemu_plugin_api.lib',
+ command: ['dlltool', '--input-def', '@INPUT@',
+ '--output-delaylib', '@OUTPUT@', '--dllname', 'qemu.exe']
+ )
+ endif
specific_ss.add(files(
'loader.c',
'core.c',
diff --git a/tests/plugin/meson.build b/tests/plugin/meson.build
index 322cafcdf6..528bb9d86c 100644
--- a/tests/plugin/meson.build
+++ b/tests/plugin/meson.build
@@ -1,9 +1,17 @@
t = []
if get_option('plugins')
foreach i : ['bb', 'empty', 'insn', 'mem', 'syscall']
- t += shared_module(i, files(i + '.c'),
- include_directories: '../../include/qemu',
- dependencies: glib)
+ if targetos == 'windows'
+ t += shared_module(i, files(i + '.c') + '../../contrib/plugins/win32_linker.c',
+ include_directories: '../../include/qemu',
+ objects: [win32_qemu_plugin_api_lib],
+ dependencies: glib)
+
+ else
+ t += shared_module(i, files(i + '.c'),
+ include_directories: '../../include/qemu',
+ dependencies: glib)
+ endif
endforeach
endif
if t.length() > 0
--
2.42.0
^ permalink raw reply related [flat|nested] 10+ messages in thread