Message ID | 152819516675.30857.9162557650483931182.stgit@pasha-ThinkPad-T60 (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru> writes: > This patch adds support for dynamically loaded plugins. > Every plugin is a dynamic library with a set of optional exported > functions that will be called from QEMU. > > Signed-off-by: Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru> > --- <snip> > qemu-options.hx | 10 +++++ > vl.c | 8 ++++ There are a couple of trivial conflicts vs master here, simple to fix. > 7 files changed, 143 insertions(+), 1 deletion(-) > create mode 100644 include/qemu/plugins.h > create mode 100644 plugins/include/plugins.h > create mode 100644 plugins/plugins.c > > diff --git a/Makefile.target b/Makefile.target > index dad2cf8..4cffd96 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -93,6 +93,7 @@ all: $(PROGS) stap > # cpu emulator library > obj-y += exec.o > obj-y += accel/ > +obj-$(CONFIG_PLUGINS) += plugins/plugins.o > obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o > obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o > obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o > diff --git a/configure b/configure > index a71bf9b..34e6f00 100755 > --- a/configure > +++ b/configure > @@ -373,6 +373,7 @@ EXESUF="" > DSOSUF=".so" > LDFLAGS_SHARED="-shared" > modules="no" > +plugins="no" > prefix="/usr/local" > mandir="\${prefix}/share/man" > datadir="\${prefix}/share" > @@ -922,6 +923,12 @@ for opt do > --disable-modules) > modules="no" > ;; > + --enable-plugins) > + plugins="yes" > + ;; > + --disable-plugins) > + plugins="no" > + ;; > --cpu=*) > ;; > --target-list=*) target_list="$optarg" > @@ -1567,6 +1574,7 @@ disabled with --disable-FEATURE, default is enabled if available: > guest-agent-msi build guest agent Windows MSI installation package > pie Position Independent Executables > modules modules support > + plugins plugins support > debug-tcg TCG debugging (default is disabled) > debug-info debugging information > sparse sparse checker > @@ -3392,7 +3400,7 @@ else > glib_req_ver=2.22 > fi > glib_modules=gthread-2.0 > -if test "$modules" = yes; then > +if test "$modules" = yes || test "$plugins" = yes; then > glib_modules="$glib_modules gmodule-export-2.0" > fi > > @@ -5777,6 +5785,7 @@ if test "$slirp" = "yes" ; then > echo "smbd $smbd" > fi > echo "module support $modules" > +echo "plugin support $plugins" > echo "host CPU $cpu" > echo "host big endian $bigendian" > echo "target list $target_list" > @@ -6111,6 +6120,9 @@ if test "$modules" = "yes"; then > echo "CONFIG_STAMP=_$( (echo $qemu_version; echo $pkgversion; cat $0) | $shacmd - | cut -f1 -d\ )" >> $config_host_mak > echo "CONFIG_MODULES=y" >> $config_host_mak > fi > +if test "$plugins" = "yes"; then > + echo "CONFIG_PLUGINS=y" >> $config_host_mak > +fi > if test "$have_x11" = "yes" -a "$need_x11" = "yes"; then > echo "CONFIG_X11=y" >> $config_host_mak > echo "X11_CFLAGS=$x11_cflags" >> $config_host_mak > diff --git a/include/qemu/plugins.h b/include/qemu/plugins.h > new file mode 100644 > index 0000000..4464822 > --- /dev/null > +++ b/include/qemu/plugins.h > @@ -0,0 +1,8 @@ > +#ifndef PLUGINS_H > +#define PLUGINS_H > + > +void qemu_plugin_parse_cmd_args(const char *optarg); > +void qemu_plugin_load(const char *filename, const char *args); > +void qemu_plugins_init(void); > + > +#endif /* PLUGINS_H */ > diff --git a/plugins/include/plugins.h b/plugins/include/plugins.h > new file mode 100644 > index 0000000..100a786 > --- /dev/null > +++ b/plugins/include/plugins.h > @@ -0,0 +1,12 @@ > +#ifndef PLUGINS_INTERFACE_H > +#define PLUGINS_INTERFACE_H > + > +#include <stdbool.h> > + > +/* Plugin interface */ > + > +bool plugin_init(const char *args); > +bool plugin_needs_before_insn(uint64_t pc, void *cpu); > +void plugin_before_insn(uint64_t pc, void *cpu); > + > +#endif /* PLUGINS_INTERFACE_H */ > diff --git a/plugins/plugins.c b/plugins/plugins.c > new file mode 100644 > index 0000000..eabc931 > --- /dev/null > +++ b/plugins/plugins.c > @@ -0,0 +1,91 @@ > +#include "qemu/osdep.h" > +#include "qemu-common.h" > +#include "qemu/error-report.h" > +#include "qemu/plugins.h" > +#include "qemu/queue.h" > +#include <gmodule.h> > + > +typedef bool (*PluginInitFunc)(const char *); > +typedef bool (*PluginNeedsBeforeInsnFunc)(uint64_t, void *); > +typedef void (*PluginBeforeInsnFunc)(uint64_t, void *); > + > +typedef struct QemuPluginInfo { > + const char *filename; > + const char *args; > + GModule *g_module; > + > + PluginInitFunc init; > + PluginNeedsBeforeInsnFunc needs_before_insn; It seems a bit heavyweight to have a query function here. Is this dynamic state the plugin might change during execution? If not could we not better report plugin requirements during initialisation? > + PluginBeforeInsnFunc before_insn; > + > + QLIST_ENTRY(QemuPluginInfo) next; > +} QemuPluginInfo; > + > +static QLIST_HEAD(, QemuPluginInfo) qemu_plugins > + = QLIST_HEAD_INITIALIZER(qemu_plugins); > + > +static QemuOptsList qemu_plugin_opts = { > + .name = "plugin", > + .head = QTAILQ_HEAD_INITIALIZER(qemu_plugin_opts.head), > + .desc = { > + { > + .name = "file", > + .type = QEMU_OPT_STRING, > + },{ > + .name = "args", > + .type = QEMU_OPT_STRING, > + }, > + { /* end of list */ } > + }, > +}; > + > +void qemu_plugin_parse_cmd_args(const char *optarg) > +{ > + QemuOpts *opts = qemu_opts_parse_noisily(&qemu_plugin_opts, optarg, false); > + qemu_plugin_load(qemu_opt_get(opts, "file"), > + qemu_opt_get(opts, "args")); > +} > + > +void qemu_plugin_load(const char *filename, const char *args) > +{ > + GModule *g_module; > + QemuPluginInfo *info = NULL; > + if (!filename) { > + error_report("plugin name was not specified"); > + return; > + } > + g_module = g_module_open(filename, > + G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); > + if (!g_module) { > + error_report("can't load plugin '%s'", filename); > + return; > + } > + info = g_new0(QemuPluginInfo, 1); > + info->filename = g_strdup(filename); > + info->g_module = g_module; > + if (args) { > + info->args = g_strdup(args); > + } > + > + g_module_symbol(g_module, "plugin_init", (gpointer*)&info->init); > + > + /* Get the instrumentation callbacks */ > + g_module_symbol(g_module, "plugin_needs_before_insn", > + (gpointer*)&info->needs_before_insn); > + g_module_symbol(g_module, "plugin_before_insn", > + (gpointer*)&info->before_insn); In fact could the presence of an "optional" symbol imply it's need here? > + > + QLIST_INSERT_HEAD(&qemu_plugins, info, next); > + > + return; > +} > + > +void qemu_plugins_init(void) > +{ > + QemuPluginInfo *info; > + QLIST_FOREACH(info, &qemu_plugins, next) { > + if (info->init) { > + info->init(info->args); > + } > + } > +} > diff --git a/qemu-options.hx b/qemu-options.hx > index c0d3951..d171544 100644 > --- a/qemu-options.hx > +++ b/qemu-options.hx > @@ -3950,6 +3950,16 @@ Dump json-encoded vmstate information for current machine type to file > in @var{file} > ETEXI > > +#ifdef CONFIG_PLUGINS > +DEF("plugin", HAS_ARG, QEMU_OPTION_plugin, \ > + "-plugin file=<file>[,args=<args>] load <dso> plugin with <args>\n", QEMU_ARCH_ALL) > +STEXI > +@item -plugin file=@var{file}[,args=@var{args}] > +@findex -plugin > +Load @var{file} plugin passing @var{args} arguments. > +ETEXI > +#endif > + > STEXI > @end table > ETEXI > diff --git a/vl.c b/vl.c > index 0603171..05420bf 100644 > --- a/vl.c > +++ b/vl.c > @@ -129,6 +129,7 @@ int main(int argc, char **argv) > #include "qapi/qapi-commands-run-state.h" > #include "qapi/qmp/qerror.h" > #include "sysemu/iothread.h" > +#include "qemu/plugins.h" > > #define MAX_VIRTIO_CONSOLES 1 > > @@ -3925,6 +3926,11 @@ int main(int argc, char **argv, char **envp) > exit(1); > } > break; > +#ifdef CONFIG_PLUGINS > + case QEMU_OPTION_plugin: > + qemu_plugin_parse_cmd_args(optarg); > + break; > +#endif > case QEMU_OPTION_nodefconfig: > case QEMU_OPTION_nouserconfig: > /* Nothing to be parsed here. Especially, do not error out below. */ > @@ -4470,6 +4476,8 @@ int main(int argc, char **argv, char **envp) > } > parse_numa_opts(current_machine); > > + qemu_plugins_init(); > + > /* do monitor/qmp handling at preconfig state if requested */ > main_loop(); > -- Alex Bennée
Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru> writes: > This patch adds support for dynamically loaded plugins. > Every plugin is a dynamic library with a set of optional exported > functions that will be called from QEMU. > > Signed-off-by: Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru> > --- > Makefile.target | 1 > configure | 14 ++++++- > include/qemu/plugins.h | 8 ++++ > plugins/include/plugins.h | 12 ++++++ > plugins/plugins.c | 91 +++++++++++++++++++++++++++++++++++++++++++++ > qemu-options.hx | 10 +++++ > vl.c | 8 ++++ > 7 files changed, 143 insertions(+), 1 deletion(-) > create mode 100644 include/qemu/plugins.h > create mode 100644 plugins/include/plugins.h > create mode 100644 plugins/plugins.c > > diff --git a/Makefile.target b/Makefile.target > index dad2cf8..4cffd96 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -93,6 +93,7 @@ all: $(PROGS) stap > # cpu emulator library > obj-y += exec.o > obj-y += accel/ > +obj-$(CONFIG_PLUGINS) += plugins/plugins.o > obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o > obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o > obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o > diff --git a/configure b/configure > index a71bf9b..34e6f00 100755 > --- a/configure > +++ b/configure > @@ -373,6 +373,7 @@ EXESUF="" > DSOSUF=".so" > LDFLAGS_SHARED="-shared" > modules="no" > +plugins="no" > prefix="/usr/local" > mandir="\${prefix}/share/man" > datadir="\${prefix}/share" > @@ -922,6 +923,12 @@ for opt do > --disable-modules) > modules="no" > ;; > + --enable-plugins) > + plugins="yes" > + ;; > + --disable-plugins) > + plugins="no" > + ;; > --cpu=*) > ;; > --target-list=*) target_list="$optarg" > @@ -1567,6 +1574,7 @@ disabled with --disable-FEATURE, default is enabled if available: > guest-agent-msi build guest agent Windows MSI installation package > pie Position Independent Executables > modules modules support > + plugins plugins support > debug-tcg TCG debugging (default is disabled) > debug-info debugging information > sparse sparse checker > @@ -3392,7 +3400,7 @@ else > glib_req_ver=2.22 > fi > glib_modules=gthread-2.0 > -if test "$modules" = yes; then > +if test "$modules" = yes || test "$plugins" = yes; then > glib_modules="$glib_modules gmodule-export-2.0" > fi > > @@ -5777,6 +5785,7 @@ if test "$slirp" = "yes" ; then > echo "smbd $smbd" > fi > echo "module support $modules" > +echo "plugin support $plugins" > echo "host CPU $cpu" > echo "host big endian $bigendian" > echo "target list $target_list" > @@ -6111,6 +6120,9 @@ if test "$modules" = "yes"; then > echo "CONFIG_STAMP=_$( (echo $qemu_version; echo $pkgversion; cat $0) | $shacmd - | cut -f1 -d\ )" >> $config_host_mak > echo "CONFIG_MODULES=y" >> $config_host_mak > fi > +if test "$plugins" = "yes"; then > + echo "CONFIG_PLUGINS=y" >> $config_host_mak > +fi > if test "$have_x11" = "yes" -a "$need_x11" = "yes"; then > echo "CONFIG_X11=y" >> $config_host_mak > echo "X11_CFLAGS=$x11_cflags" >> $config_host_mak > diff --git a/include/qemu/plugins.h b/include/qemu/plugins.h > new file mode 100644 > index 0000000..4464822 > --- /dev/null > +++ b/include/qemu/plugins.h > @@ -0,0 +1,8 @@ > +#ifndef PLUGINS_H > +#define PLUGINS_H > + > +void qemu_plugin_parse_cmd_args(const char *optarg); > +void qemu_plugin_load(const char *filename, const char *args); > +void qemu_plugins_init(void); I think you want to have an #ifdef CONFIG_PLUGINS here and some empty inlines for the non CONFIG_PLUGINS case so you don't need to ifdef so much later. > + > +#endif /* PLUGINS_H */ > diff --git a/plugins/include/plugins.h b/plugins/include/plugins.h > new file mode 100644 > index 0000000..100a786 > --- /dev/null > +++ b/plugins/include/plugins.h > @@ -0,0 +1,12 @@ > +#ifndef PLUGINS_INTERFACE_H > +#define PLUGINS_INTERFACE_H > + > +#include <stdbool.h> > + > +/* Plugin interface */ > + > +bool plugin_init(const char *args); > +bool plugin_needs_before_insn(uint64_t pc, void *cpu); > +void plugin_before_insn(uint64_t pc, void *cpu); > + > +#endif /* PLUGINS_INTERFACE_H */ > diff --git a/plugins/plugins.c b/plugins/plugins.c > new file mode 100644 > index 0000000..eabc931 > --- /dev/null > +++ b/plugins/plugins.c > @@ -0,0 +1,91 @@ > +#include "qemu/osdep.h" > +#include "qemu-common.h" > +#include "qemu/error-report.h" > +#include "qemu/plugins.h" > +#include "qemu/queue.h" > +#include <gmodule.h> > + > +typedef bool (*PluginInitFunc)(const char *); > +typedef bool (*PluginNeedsBeforeInsnFunc)(uint64_t, void *); > +typedef void (*PluginBeforeInsnFunc)(uint64_t, void *); > + > +typedef struct QemuPluginInfo { > + const char *filename; > + const char *args; > + GModule *g_module; > + > + PluginInitFunc init; > + PluginNeedsBeforeInsnFunc needs_before_insn; > + PluginBeforeInsnFunc before_insn; > + > + QLIST_ENTRY(QemuPluginInfo) next; > +} QemuPluginInfo; > + > +static QLIST_HEAD(, QemuPluginInfo) qemu_plugins > + = QLIST_HEAD_INITIALIZER(qemu_plugins); > + > +static QemuOptsList qemu_plugin_opts = { > + .name = "plugin", > + .head = QTAILQ_HEAD_INITIALIZER(qemu_plugin_opts.head), > + .desc = { > + { > + .name = "file", > + .type = QEMU_OPT_STRING, > + },{ > + .name = "args", > + .type = QEMU_OPT_STRING, > + }, > + { /* end of list */ } > + }, > +}; > + > +void qemu_plugin_parse_cmd_args(const char *optarg) > +{ > + QemuOpts *opts = qemu_opts_parse_noisily(&qemu_plugin_opts, optarg, false); > + qemu_plugin_load(qemu_opt_get(opts, "file"), > + qemu_opt_get(opts, "args")); > +} > + > +void qemu_plugin_load(const char *filename, const char *args) > +{ > + GModule *g_module; > + QemuPluginInfo *info = NULL; > + if (!filename) { > + error_report("plugin name was not specified"); > + return; > + } > + g_module = g_module_open(filename, > + G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); > + if (!g_module) { > + error_report("can't load plugin '%s'", filename); > + return; > + } > + info = g_new0(QemuPluginInfo, 1); > + info->filename = g_strdup(filename); > + info->g_module = g_module; > + if (args) { > + info->args = g_strdup(args); > + } > + > + g_module_symbol(g_module, "plugin_init", (gpointer*)&info->init); > + > + /* Get the instrumentation callbacks */ > + g_module_symbol(g_module, "plugin_needs_before_insn", > + (gpointer*)&info->needs_before_insn); > + g_module_symbol(g_module, "plugin_before_insn", > + (gpointer*)&info->before_insn); > + > + QLIST_INSERT_HEAD(&qemu_plugins, info, next); > + > + return; > +} > + > +void qemu_plugins_init(void) > +{ > + QemuPluginInfo *info; > + QLIST_FOREACH(info, &qemu_plugins, next) { > + if (info->init) { > + info->init(info->args); > + } > + } > +} > diff --git a/qemu-options.hx b/qemu-options.hx > index c0d3951..d171544 100644 > --- a/qemu-options.hx > +++ b/qemu-options.hx > @@ -3950,6 +3950,16 @@ Dump json-encoded vmstate information for current machine type to file > in @var{file} > ETEXI > > +#ifdef CONFIG_PLUGINS > +DEF("plugin", HAS_ARG, QEMU_OPTION_plugin, \ > + "-plugin file=<file>[,args=<args>] load <dso> plugin with <args>\n", QEMU_ARCH_ALL) > +STEXI > +@item -plugin file=@var{file}[,args=@var{args}] > +@findex -plugin > +Load @var{file} plugin passing @var{args} arguments. > +ETEXI > +#endif > + > STEXI > @end table > ETEXI > diff --git a/vl.c b/vl.c > index 0603171..05420bf 100644 > --- a/vl.c > +++ b/vl.c > @@ -129,6 +129,7 @@ int main(int argc, char **argv) > #include "qapi/qapi-commands-run-state.h" > #include "qapi/qmp/qerror.h" > #include "sysemu/iothread.h" > +#include "qemu/plugins.h" > > #define MAX_VIRTIO_CONSOLES 1 > > @@ -3925,6 +3926,11 @@ int main(int argc, char **argv, char **envp) > exit(1); > } > break; > +#ifdef CONFIG_PLUGINS > + case QEMU_OPTION_plugin: > + qemu_plugin_parse_cmd_args(optarg); > + break; > +#endif > case QEMU_OPTION_nodefconfig: > case QEMU_OPTION_nouserconfig: > /* Nothing to be parsed here. Especially, do not error out below. */ > @@ -4470,6 +4476,8 @@ int main(int argc, char **argv, char **envp) > } > parse_numa_opts(current_machine); > > + qemu_plugins_init(); > + For example this fails to build currently. > /* do monitor/qmp handling at preconfig state if requested */ > main_loop(); > -- Alex Bennée
Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru> writes: > This patch adds support for dynamically loaded plugins. > Every plugin is a dynamic library with a set of optional exported > functions that will be called from QEMU. > <snip> > + > +static QLIST_HEAD(, QemuPluginInfo) qemu_plugins > + = QLIST_HEAD_INITIALIZER(qemu_plugins); > + > +static QemuOptsList qemu_plugin_opts = { > + .name = "plugin", > + .head = QTAILQ_HEAD_INITIALIZER(qemu_plugin_opts.head), > + .desc = { > + { > + .name = "file", > + .type = QEMU_OPT_STRING, > + },{ > + .name = "args", > + .type = QEMU_OPT_STRING, > + }, > + { /* end of list */ } > + }, > +}; > + > +void qemu_plugin_parse_cmd_args(const char *optarg) > +{ > + QemuOpts *opts = qemu_opts_parse_noisily(&qemu_plugin_opts, optarg, false); > + qemu_plugin_load(qemu_opt_get(opts, "file"), > + qemu_opt_get(opts, "args")); > +} Currently this is only available to system mode emulation. Can it be extended to include linux-user as well? > + > +void qemu_plugin_load(const char *filename, const char *args) > +{ > + GModule *g_module; > + QemuPluginInfo *info = NULL; > + if (!filename) { > + error_report("plugin name was not specified"); > + return; > + } > + g_module = g_module_open(filename, > + G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); > + if (!g_module) { > + error_report("can't load plugin '%s'", filename); > + return; > + } > + info = g_new0(QemuPluginInfo, 1); > + info->filename = g_strdup(filename); > + info->g_module = g_module; > + if (args) { > + info->args = g_strdup(args); > + } > + > + g_module_symbol(g_module, "plugin_init", (gpointer*)&info->init); > + > + /* Get the instrumentation callbacks */ > + g_module_symbol(g_module, "plugin_needs_before_insn", > + (gpointer*)&info->needs_before_insn); > + g_module_symbol(g_module, "plugin_before_insn", > + (gpointer*)&info->before_insn); > + > + QLIST_INSERT_HEAD(&qemu_plugins, info, next); > + > + return; > +} > + > +void qemu_plugins_init(void) > +{ > + QemuPluginInfo *info; > + QLIST_FOREACH(info, &qemu_plugins, next) { > + if (info->init) { > + info->init(info->args); > + } > + } > +} > diff --git a/qemu-options.hx b/qemu-options.hx > index c0d3951..d171544 100644 > --- a/qemu-options.hx > +++ b/qemu-options.hx > @@ -3950,6 +3950,16 @@ Dump json-encoded vmstate information for current machine type to file > in @var{file} > ETEXI > > +#ifdef CONFIG_PLUGINS > +DEF("plugin", HAS_ARG, QEMU_OPTION_plugin, \ > + "-plugin file=<file>[,args=<args>] load <dso> plugin with <args>\n", QEMU_ARCH_ALL) > +STEXI > +@item -plugin file=@var{file}[,args=@var{args}] > +@findex -plugin > +Load @var{file} plugin passing @var{args} arguments. > +ETEXI > +#endif > + > STEXI > @end table > ETEXI > diff --git a/vl.c b/vl.c > index 0603171..05420bf 100644 > --- a/vl.c > +++ b/vl.c > @@ -129,6 +129,7 @@ int main(int argc, char **argv) > #include "qapi/qapi-commands-run-state.h" > #include "qapi/qmp/qerror.h" > #include "sysemu/iothread.h" > +#include "qemu/plugins.h" > > #define MAX_VIRTIO_CONSOLES 1 > > @@ -3925,6 +3926,11 @@ int main(int argc, char **argv, char **envp) > exit(1); > } > break; > +#ifdef CONFIG_PLUGINS > + case QEMU_OPTION_plugin: > + qemu_plugin_parse_cmd_args(optarg); > + break; > +#endif > case QEMU_OPTION_nodefconfig: > case QEMU_OPTION_nouserconfig: > /* Nothing to be parsed here. Especially, do not error out below. */ > @@ -4470,6 +4476,8 @@ int main(int argc, char **argv, char **envp) > } > parse_numa_opts(current_machine); > > + qemu_plugins_init(); > + > /* do monitor/qmp handling at preconfig state if requested */ > main_loop(); > -- Alex Bennée
> From: Alex Bennée [mailto:alex.bennee@linaro.org] > Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru> writes: > > > This patch adds support for dynamically loaded plugins. > > Every plugin is a dynamic library with a set of optional exported > > functions that will be called from QEMU. > > > > Signed-off-by: Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru> > > --- > > Makefile.target | 1 > > configure | 14 ++++++- > > include/qemu/plugins.h | 8 ++++ > > plugins/include/plugins.h | 12 ++++++ > > plugins/plugins.c | 91 +++++++++++++++++++++++++++++++++++++++++++++ > > qemu-options.hx | 10 +++++ > > vl.c | 8 ++++ > > 7 files changed, 143 insertions(+), 1 deletion(-) > > create mode 100644 include/qemu/plugins.h > > create mode 100644 plugins/include/plugins.h > > create mode 100644 plugins/plugins.c > > > > diff --git a/Makefile.target b/Makefile.target > > index dad2cf8..4cffd96 100644 > > --- a/Makefile.target > > +++ b/Makefile.target > > @@ -93,6 +93,7 @@ all: $(PROGS) stap > > # cpu emulator library > > obj-y += exec.o > > obj-y += accel/ > > +obj-$(CONFIG_PLUGINS) += plugins/plugins.o > > obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o > > obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o > > obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o > > diff --git a/configure b/configure > > index a71bf9b..34e6f00 100755 > > --- a/configure > > +++ b/configure > > @@ -373,6 +373,7 @@ EXESUF="" > > DSOSUF=".so" > > LDFLAGS_SHARED="-shared" > > modules="no" > > +plugins="no" > > prefix="/usr/local" > > mandir="\${prefix}/share/man" > > datadir="\${prefix}/share" > > @@ -922,6 +923,12 @@ for opt do > > --disable-modules) > > modules="no" > > ;; > > + --enable-plugins) > > + plugins="yes" > > + ;; > > + --disable-plugins) > > + plugins="no" > > + ;; > > --cpu=*) > > ;; > > --target-list=*) target_list="$optarg" > > @@ -1567,6 +1574,7 @@ disabled with --disable-FEATURE, default is enabled if available: > > guest-agent-msi build guest agent Windows MSI installation package > > pie Position Independent Executables > > modules modules support > > + plugins plugins support > > debug-tcg TCG debugging (default is disabled) > > debug-info debugging information > > sparse sparse checker > > @@ -3392,7 +3400,7 @@ else > > glib_req_ver=2.22 > > fi > > glib_modules=gthread-2.0 > > -if test "$modules" = yes; then > > +if test "$modules" = yes || test "$plugins" = yes; then > > glib_modules="$glib_modules gmodule-export-2.0" > > fi > > > > @@ -5777,6 +5785,7 @@ if test "$slirp" = "yes" ; then > > echo "smbd $smbd" > > fi > > echo "module support $modules" > > +echo "plugin support $plugins" > > echo "host CPU $cpu" > > echo "host big endian $bigendian" > > echo "target list $target_list" > > @@ -6111,6 +6120,9 @@ if test "$modules" = "yes"; then > > echo "CONFIG_STAMP=_$( (echo $qemu_version; echo $pkgversion; cat $0) | $shacmd - | cut - > f1 -d\ )" >> $config_host_mak > > echo "CONFIG_MODULES=y" >> $config_host_mak > > fi > > +if test "$plugins" = "yes"; then > > + echo "CONFIG_PLUGINS=y" >> $config_host_mak > > +fi > > if test "$have_x11" = "yes" -a "$need_x11" = "yes"; then > > echo "CONFIG_X11=y" >> $config_host_mak > > echo "X11_CFLAGS=$x11_cflags" >> $config_host_mak > > diff --git a/include/qemu/plugins.h b/include/qemu/plugins.h > > new file mode 100644 > > index 0000000..4464822 > > --- /dev/null > > +++ b/include/qemu/plugins.h > > @@ -0,0 +1,8 @@ > > +#ifndef PLUGINS_H > > +#define PLUGINS_H > > + > > +void qemu_plugin_parse_cmd_args(const char *optarg); > > +void qemu_plugin_load(const char *filename, const char *args); > > +void qemu_plugins_init(void); > > I think you want to have an #ifdef CONFIG_PLUGINS here and some empty > inlines for the non CONFIG_PLUGINS case so you don't need to ifdef so > much later. Thanks. I'll better fix init, because all other invocations are command-line dependent and still should be put under ifdef. > > @@ -4470,6 +4476,8 @@ int main(int argc, char **argv, char **envp) > > } > > parse_numa_opts(current_machine); > > > > + qemu_plugins_init(); > > + > > For example this fails to build currently. > Pavel Dovgalyuk
> From: Alex Bennée [mailto:alex.bennee@linaro.org] > Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru> writes: > > > This patch adds support for dynamically loaded plugins. > > Every plugin is a dynamic library with a set of optional exported > > functions that will be called from QEMU. > > > <snip> > > + > > +static QLIST_HEAD(, QemuPluginInfo) qemu_plugins > > + = QLIST_HEAD_INITIALIZER(qemu_plugins); > > + > > +static QemuOptsList qemu_plugin_opts = { > > + .name = "plugin", > > + .head = QTAILQ_HEAD_INITIALIZER(qemu_plugin_opts.head), > > + .desc = { > > + { > > + .name = "file", > > + .type = QEMU_OPT_STRING, > > + },{ > > + .name = "args", > > + .type = QEMU_OPT_STRING, > > + }, > > + { /* end of list */ } > > + }, > > +}; > > + > > +void qemu_plugin_parse_cmd_args(const char *optarg) > > +{ > > + QemuOpts *opts = qemu_opts_parse_noisily(&qemu_plugin_opts, optarg, false); > > + qemu_plugin_load(qemu_opt_get(opts, "file"), > > + qemu_opt_get(opts, "args")); > > +} > > Currently this is only available to system mode emulation. Can it be > extended to include linux-user as well? Why not? I'm not familiar with linux-user, so I'll need some time to try. Pavel Dovgalyuk
> From: Alex Bennée [mailto:alex.bennee@linaro.org] > Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru> writes: > > > This patch adds support for dynamically loaded plugins. > > Every plugin is a dynamic library with a set of optional exported > > functions that will be called from QEMU. > > > > Signed-off-by: Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru> > > --- > <snip> > > qemu-options.hx | 10 +++++ > > vl.c | 8 ++++ > > There are a couple of trivial conflicts vs master here, simple to fix. > > > 7 files changed, 143 insertions(+), 1 deletion(-) > > create mode 100644 include/qemu/plugins.h > > create mode 100644 plugins/include/plugins.h > > create mode 100644 plugins/plugins.c > > > > diff --git a/Makefile.target b/Makefile.target > > index dad2cf8..4cffd96 100644 > > --- a/Makefile.target > > +++ b/Makefile.target > > @@ -93,6 +93,7 @@ all: $(PROGS) stap > > # cpu emulator library > > obj-y += exec.o > > obj-y += accel/ > > +obj-$(CONFIG_PLUGINS) += plugins/plugins.o > > obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o > > obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o > > obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o > > diff --git a/configure b/configure > > index a71bf9b..34e6f00 100755 > > --- a/configure > > +++ b/configure > > @@ -373,6 +373,7 @@ EXESUF="" > > DSOSUF=".so" > > LDFLAGS_SHARED="-shared" > > modules="no" > > +plugins="no" > > prefix="/usr/local" > > mandir="\${prefix}/share/man" > > datadir="\${prefix}/share" > > @@ -922,6 +923,12 @@ for opt do > > --disable-modules) > > modules="no" > > ;; > > + --enable-plugins) > > + plugins="yes" > > + ;; > > + --disable-plugins) > > + plugins="no" > > + ;; > > --cpu=*) > > ;; > > --target-list=*) target_list="$optarg" > > @@ -1567,6 +1574,7 @@ disabled with --disable-FEATURE, default is enabled if available: > > guest-agent-msi build guest agent Windows MSI installation package > > pie Position Independent Executables > > modules modules support > > + plugins plugins support > > debug-tcg TCG debugging (default is disabled) > > debug-info debugging information > > sparse sparse checker > > @@ -3392,7 +3400,7 @@ else > > glib_req_ver=2.22 > > fi > > glib_modules=gthread-2.0 > > -if test "$modules" = yes; then > > +if test "$modules" = yes || test "$plugins" = yes; then > > glib_modules="$glib_modules gmodule-export-2.0" > > fi > > > > @@ -5777,6 +5785,7 @@ if test "$slirp" = "yes" ; then > > echo "smbd $smbd" > > fi > > echo "module support $modules" > > +echo "plugin support $plugins" > > echo "host CPU $cpu" > > echo "host big endian $bigendian" > > echo "target list $target_list" > > @@ -6111,6 +6120,9 @@ if test "$modules" = "yes"; then > > echo "CONFIG_STAMP=_$( (echo $qemu_version; echo $pkgversion; cat $0) | $shacmd - | cut - > f1 -d\ )" >> $config_host_mak > > echo "CONFIG_MODULES=y" >> $config_host_mak > > fi > > +if test "$plugins" = "yes"; then > > + echo "CONFIG_PLUGINS=y" >> $config_host_mak > > +fi > > if test "$have_x11" = "yes" -a "$need_x11" = "yes"; then > > echo "CONFIG_X11=y" >> $config_host_mak > > echo "X11_CFLAGS=$x11_cflags" >> $config_host_mak > > diff --git a/include/qemu/plugins.h b/include/qemu/plugins.h > > new file mode 100644 > > index 0000000..4464822 > > --- /dev/null > > +++ b/include/qemu/plugins.h > > @@ -0,0 +1,8 @@ > > +#ifndef PLUGINS_H > > +#define PLUGINS_H > > + > > +void qemu_plugin_parse_cmd_args(const char *optarg); > > +void qemu_plugin_load(const char *filename, const char *args); > > +void qemu_plugins_init(void); > > + > > +#endif /* PLUGINS_H */ > > diff --git a/plugins/include/plugins.h b/plugins/include/plugins.h > > new file mode 100644 > > index 0000000..100a786 > > --- /dev/null > > +++ b/plugins/include/plugins.h > > @@ -0,0 +1,12 @@ > > +#ifndef PLUGINS_INTERFACE_H > > +#define PLUGINS_INTERFACE_H > > + > > +#include <stdbool.h> > > + > > +/* Plugin interface */ > > + > > +bool plugin_init(const char *args); > > +bool plugin_needs_before_insn(uint64_t pc, void *cpu); > > +void plugin_before_insn(uint64_t pc, void *cpu); > > + > > +#endif /* PLUGINS_INTERFACE_H */ > > diff --git a/plugins/plugins.c b/plugins/plugins.c > > new file mode 100644 > > index 0000000..eabc931 > > --- /dev/null > > +++ b/plugins/plugins.c > > @@ -0,0 +1,91 @@ > > +#include "qemu/osdep.h" > > +#include "qemu-common.h" > > +#include "qemu/error-report.h" > > +#include "qemu/plugins.h" > > +#include "qemu/queue.h" > > +#include <gmodule.h> > > + > > +typedef bool (*PluginInitFunc)(const char *); > > +typedef bool (*PluginNeedsBeforeInsnFunc)(uint64_t, void *); > > +typedef void (*PluginBeforeInsnFunc)(uint64_t, void *); > > + > > +typedef struct QemuPluginInfo { > > + const char *filename; > > + const char *args; > > + GModule *g_module; > > + > > + PluginInitFunc init; > > + PluginNeedsBeforeInsnFunc needs_before_insn; > > It seems a bit heavyweight to have a query function here. Is this > dynamic state the plugin might change during execution? If not could we > not better report plugin requirements during initialisation? Yes, it is dynamic. For example, we created a plugin, which detects dynamic module loading in the guest. Then it can add tracepoints to the entry addresses of the loaded functions. These addresses are not known in advance. Pavel Dovgalyuk
On Sep 10 14:41, Pavel Dovgalyuk wrote: > From: Alex Bennée [mailto:alex.bennee@linaro.org] > > Currently this is only available to system mode emulation. Can it be > > extended to include linux-user as well? > > Why not? I'm not familiar with linux-user, so I'll need some time to try. Yes. In fact, your existing setup made it quite simple: Subject: [PATCH] plugin: Add linux-user support Signed-off-by: Aaron Lindsay <aclindsa@gmail.com> --- linux-user/main.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/linux-user/main.c b/linux-user/main.c index 923cbb753a..4c70f9f8c5 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -37,6 +37,7 @@ #include "trace/control.h" #include "target_elf.h" #include "cpu_loop-common.h" +#include "qemu/plugins.h" char *exec_path; @@ -385,6 +386,11 @@ static void handle_arg_trace(const char *arg) trace_file = trace_opt_parse(arg); } +static void handle_arg_plugin(const char *arg) +{ + qemu_plugin_parse_cmd_args(arg); +} + struct qemu_argument { const char *argv; const char *env; @@ -436,6 +442,10 @@ static const struct qemu_argument arg_table[] = { "", "Seed for pseudo-random number generator"}, {"trace", "QEMU_TRACE", true, handle_arg_trace, "", "[[enable=]<pattern>][,events=<file>][,file=<file>]"}, +#ifdef CONFIG_PLUGINS + {"plugin", "QEMU_PLUGIN", true, handle_arg_plugin, + "", "file=<plugin_so>[,args=<args>]"}, +#endif {"version", "QEMU_VERSION", false, handle_arg_version, "", "display version information and exit"}, {NULL, NULL, false, NULL, NULL, NULL} @@ -816,6 +826,9 @@ int main(int argc, char **argv, char **envp) } gdb_handlesig(cpu, 0); } + + qemu_plugins_init(); + cpu_loop(env); /* never exits */ return 0;
> From: Aaron Lindsay [mailto:aclindsa@gmail.com] > On Sep 10 14:41, Pavel Dovgalyuk wrote: > > From: Alex Bennée [mailto:alex.bennee@linaro.org] > > > Currently this is only available to system mode emulation. Can it be > > > extended to include linux-user as well? > > > > Why not? I'm not familiar with linux-user, so I'll need some time to try. > > Yes. In fact, your existing setup made it quite simple: Thank you. Pavel Dovgalyuk > > > Subject: [PATCH] plugin: Add linux-user support > > Signed-off-by: Aaron Lindsay <aclindsa@gmail.com> > --- > linux-user/main.c | 13 +++++++++++++ > 1 file changed, 13 insertions(+) > > diff --git a/linux-user/main.c b/linux-user/main.c > index 923cbb753a..4c70f9f8c5 100644 > --- a/linux-user/main.c > +++ b/linux-user/main.c > @@ -37,6 +37,7 @@ > #include "trace/control.h" > #include "target_elf.h" > #include "cpu_loop-common.h" > +#include "qemu/plugins.h" > > char *exec_path; > > @@ -385,6 +386,11 @@ static void handle_arg_trace(const char *arg) > trace_file = trace_opt_parse(arg); > } > > +static void handle_arg_plugin(const char *arg) > +{ > + qemu_plugin_parse_cmd_args(arg); > +} > + > struct qemu_argument { > const char *argv; > const char *env; > @@ -436,6 +442,10 @@ static const struct qemu_argument arg_table[] = { > "", "Seed for pseudo-random number generator"}, > {"trace", "QEMU_TRACE", true, handle_arg_trace, > "", "[[enable=]<pattern>][,events=<file>][,file=<file>]"}, > +#ifdef CONFIG_PLUGINS > + {"plugin", "QEMU_PLUGIN", true, handle_arg_plugin, > + "", "file=<plugin_so>[,args=<args>]"}, > +#endif > {"version", "QEMU_VERSION", false, handle_arg_version, > "", "display version information and exit"}, > {NULL, NULL, false, NULL, NULL, NULL} > @@ -816,6 +826,9 @@ int main(int argc, char **argv, char **envp) > } > gdb_handlesig(cpu, 0); > } > + > + qemu_plugins_init(); > + > cpu_loop(env); > /* never exits */ > return 0; > -- > 2.19.0
diff --git a/Makefile.target b/Makefile.target index dad2cf8..4cffd96 100644 --- a/Makefile.target +++ b/Makefile.target @@ -93,6 +93,7 @@ all: $(PROGS) stap # cpu emulator library obj-y += exec.o obj-y += accel/ +obj-$(CONFIG_PLUGINS) += plugins/plugins.o obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o diff --git a/configure b/configure index a71bf9b..34e6f00 100755 --- a/configure +++ b/configure @@ -373,6 +373,7 @@ EXESUF="" DSOSUF=".so" LDFLAGS_SHARED="-shared" modules="no" +plugins="no" prefix="/usr/local" mandir="\${prefix}/share/man" datadir="\${prefix}/share" @@ -922,6 +923,12 @@ for opt do --disable-modules) modules="no" ;; + --enable-plugins) + plugins="yes" + ;; + --disable-plugins) + plugins="no" + ;; --cpu=*) ;; --target-list=*) target_list="$optarg" @@ -1567,6 +1574,7 @@ disabled with --disable-FEATURE, default is enabled if available: guest-agent-msi build guest agent Windows MSI installation package pie Position Independent Executables modules modules support + plugins plugins support debug-tcg TCG debugging (default is disabled) debug-info debugging information sparse sparse checker @@ -3392,7 +3400,7 @@ else glib_req_ver=2.22 fi glib_modules=gthread-2.0 -if test "$modules" = yes; then +if test "$modules" = yes || test "$plugins" = yes; then glib_modules="$glib_modules gmodule-export-2.0" fi @@ -5777,6 +5785,7 @@ if test "$slirp" = "yes" ; then echo "smbd $smbd" fi echo "module support $modules" +echo "plugin support $plugins" echo "host CPU $cpu" echo "host big endian $bigendian" echo "target list $target_list" @@ -6111,6 +6120,9 @@ if test "$modules" = "yes"; then echo "CONFIG_STAMP=_$( (echo $qemu_version; echo $pkgversion; cat $0) | $shacmd - | cut -f1 -d\ )" >> $config_host_mak echo "CONFIG_MODULES=y" >> $config_host_mak fi +if test "$plugins" = "yes"; then + echo "CONFIG_PLUGINS=y" >> $config_host_mak +fi if test "$have_x11" = "yes" -a "$need_x11" = "yes"; then echo "CONFIG_X11=y" >> $config_host_mak echo "X11_CFLAGS=$x11_cflags" >> $config_host_mak diff --git a/include/qemu/plugins.h b/include/qemu/plugins.h new file mode 100644 index 0000000..4464822 --- /dev/null +++ b/include/qemu/plugins.h @@ -0,0 +1,8 @@ +#ifndef PLUGINS_H +#define PLUGINS_H + +void qemu_plugin_parse_cmd_args(const char *optarg); +void qemu_plugin_load(const char *filename, const char *args); +void qemu_plugins_init(void); + +#endif /* PLUGINS_H */ diff --git a/plugins/include/plugins.h b/plugins/include/plugins.h new file mode 100644 index 0000000..100a786 --- /dev/null +++ b/plugins/include/plugins.h @@ -0,0 +1,12 @@ +#ifndef PLUGINS_INTERFACE_H +#define PLUGINS_INTERFACE_H + +#include <stdbool.h> + +/* Plugin interface */ + +bool plugin_init(const char *args); +bool plugin_needs_before_insn(uint64_t pc, void *cpu); +void plugin_before_insn(uint64_t pc, void *cpu); + +#endif /* PLUGINS_INTERFACE_H */ diff --git a/plugins/plugins.c b/plugins/plugins.c new file mode 100644 index 0000000..eabc931 --- /dev/null +++ b/plugins/plugins.c @@ -0,0 +1,91 @@ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/error-report.h" +#include "qemu/plugins.h" +#include "qemu/queue.h" +#include <gmodule.h> + +typedef bool (*PluginInitFunc)(const char *); +typedef bool (*PluginNeedsBeforeInsnFunc)(uint64_t, void *); +typedef void (*PluginBeforeInsnFunc)(uint64_t, void *); + +typedef struct QemuPluginInfo { + const char *filename; + const char *args; + GModule *g_module; + + PluginInitFunc init; + PluginNeedsBeforeInsnFunc needs_before_insn; + PluginBeforeInsnFunc before_insn; + + QLIST_ENTRY(QemuPluginInfo) next; +} QemuPluginInfo; + +static QLIST_HEAD(, QemuPluginInfo) qemu_plugins + = QLIST_HEAD_INITIALIZER(qemu_plugins); + +static QemuOptsList qemu_plugin_opts = { + .name = "plugin", + .head = QTAILQ_HEAD_INITIALIZER(qemu_plugin_opts.head), + .desc = { + { + .name = "file", + .type = QEMU_OPT_STRING, + },{ + .name = "args", + .type = QEMU_OPT_STRING, + }, + { /* end of list */ } + }, +}; + +void qemu_plugin_parse_cmd_args(const char *optarg) +{ + QemuOpts *opts = qemu_opts_parse_noisily(&qemu_plugin_opts, optarg, false); + qemu_plugin_load(qemu_opt_get(opts, "file"), + qemu_opt_get(opts, "args")); +} + +void qemu_plugin_load(const char *filename, const char *args) +{ + GModule *g_module; + QemuPluginInfo *info = NULL; + if (!filename) { + error_report("plugin name was not specified"); + return; + } + g_module = g_module_open(filename, + G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); + if (!g_module) { + error_report("can't load plugin '%s'", filename); + return; + } + info = g_new0(QemuPluginInfo, 1); + info->filename = g_strdup(filename); + info->g_module = g_module; + if (args) { + info->args = g_strdup(args); + } + + g_module_symbol(g_module, "plugin_init", (gpointer*)&info->init); + + /* Get the instrumentation callbacks */ + g_module_symbol(g_module, "plugin_needs_before_insn", + (gpointer*)&info->needs_before_insn); + g_module_symbol(g_module, "plugin_before_insn", + (gpointer*)&info->before_insn); + + QLIST_INSERT_HEAD(&qemu_plugins, info, next); + + return; +} + +void qemu_plugins_init(void) +{ + QemuPluginInfo *info; + QLIST_FOREACH(info, &qemu_plugins, next) { + if (info->init) { + info->init(info->args); + } + } +} diff --git a/qemu-options.hx b/qemu-options.hx index c0d3951..d171544 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3950,6 +3950,16 @@ Dump json-encoded vmstate information for current machine type to file in @var{file} ETEXI +#ifdef CONFIG_PLUGINS +DEF("plugin", HAS_ARG, QEMU_OPTION_plugin, \ + "-plugin file=<file>[,args=<args>] load <dso> plugin with <args>\n", QEMU_ARCH_ALL) +STEXI +@item -plugin file=@var{file}[,args=@var{args}] +@findex -plugin +Load @var{file} plugin passing @var{args} arguments. +ETEXI +#endif + STEXI @end table ETEXI diff --git a/vl.c b/vl.c index 0603171..05420bf 100644 --- a/vl.c +++ b/vl.c @@ -129,6 +129,7 @@ int main(int argc, char **argv) #include "qapi/qapi-commands-run-state.h" #include "qapi/qmp/qerror.h" #include "sysemu/iothread.h" +#include "qemu/plugins.h" #define MAX_VIRTIO_CONSOLES 1 @@ -3925,6 +3926,11 @@ int main(int argc, char **argv, char **envp) exit(1); } break; +#ifdef CONFIG_PLUGINS + case QEMU_OPTION_plugin: + qemu_plugin_parse_cmd_args(optarg); + break; +#endif case QEMU_OPTION_nodefconfig: case QEMU_OPTION_nouserconfig: /* Nothing to be parsed here. Especially, do not error out below. */ @@ -4470,6 +4476,8 @@ int main(int argc, char **argv, char **envp) } parse_numa_opts(current_machine); + qemu_plugins_init(); + /* do monitor/qmp handling at preconfig state if requested */ main_loop();
This patch adds support for dynamically loaded plugins. Every plugin is a dynamic library with a set of optional exported functions that will be called from QEMU. Signed-off-by: Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru> --- Makefile.target | 1 configure | 14 ++++++- include/qemu/plugins.h | 8 ++++ plugins/include/plugins.h | 12 ++++++ plugins/plugins.c | 91 +++++++++++++++++++++++++++++++++++++++++++++ qemu-options.hx | 10 +++++ vl.c | 8 ++++ 7 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 include/qemu/plugins.h create mode 100644 plugins/include/plugins.h create mode 100644 plugins/plugins.c