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();
 
