diff mbox

[v9,5/8] module: implement module loading

Message ID 1379066356-14986-6-git-send-email-famz@redhat.com
State New
Headers show

Commit Message

Fam Zheng Sept. 13, 2013, 9:59 a.m. UTC
Added three types of modules:

    typedef enum {
        MODULE_LOAD_BLOCK = 0,
        MODULE_LOAD_UI,
        MODULE_LOAD_NET,
        MODULE_LOAD_MAX,
    } module_load_type;

and their loading function:

    void module_load(module_load_type).

which loads whitelisted ".so" files in a subdir under configured
${MODDIR}, e.g.  "/usr/lib/qemu/block". Modules of each type should be
loaded in respective subsystem initialization code.

The init function of dynamic module is no longer with
__attribute__((constructor)) as static linked version, and need to be
explicitly called once loaded. The function name is mangled with per
configure fingerprint as:

    init_$(date +%s$$$RANDOM)

Which is known to module_load function, and the loading fails if this
symbol is not there. With this, modules built from a different
tree/version/configure will not be loaded.

The module loading code requires gmodule-2.0.

Configure option "--enable-modules=L" can be used to restrict qemu to
only build/load some whitelisted modules.

Signed-off-by: Fam Zheng <famz@redhat.com>
---
 Makefile              |  3 +++
 block.c               |  1 +
 configure             | 43 ++++++++++++++++++++++--------
 include/qemu/module.h | 23 ++++++++++++++++
 rules.mak             |  9 +++++--
 scripts/create_config | 21 +++++++++++++++
 util/module.c         | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++
 vl.c                  |  2 ++
 8 files changed, 163 insertions(+), 13 deletions(-)

Comments

Daniel P. Berrangé Sept. 13, 2013, 2:07 p.m. UTC | #1
On Fri, Sep 13, 2013 at 05:59:13PM +0800, Fam Zheng wrote:
> +void module_load(module_load_type type)
> +{
> +#ifdef CONFIG_MODULES
> +    const char *path;
> +    char *fname = NULL;
> +    const char **mp;
> +    const char *module_whitelist[] = {
> +        CONFIG_MODULE_WHITELIST
> +    };
> +
> +    if (!g_module_supported()) {
> +        return;
> +    }
> +
> +    switch (type) {
> +    case MODULE_LOAD_BLOCK:
> +        path = CONFIG_MODDIR "/block/";
> +        break;
> +    case MODULE_LOAD_UI:
> +        path = CONFIG_MODDIR "/ui/";
> +        break;
> +    case MODULE_LOAD_NET:
> +        path = CONFIG_MODDIR "/net/";
> +        break;
> +    default:
> +        return;
> +    }
> +
> +    for (mp = &module_whitelist[0]; *mp; mp++) {
> +        fname = g_strdup_printf("%s%s" HOST_DSOSUF, path, *mp);
> +        module_load_file(fname);
> +        g_free(fname);
> +    }

You need a separate whitelist for block, ui, net, etc. This code just
spews errors to stderr.

Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/ui/iscsi.so: cannot open shared object file: No such file or directory
Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/ui/curl.so: cannot open shared object file: No such file or directory
Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/ui/rbd.so: cannot open shared object file: No such file or directory
Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/ui/gluster.so: cannot open shared object file: No such file or directory
Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/ui/ssh.so: cannot open shared object file: No such file or directory
Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/net/iscsi.so: cannot open shared object file: No such file or directory
Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/net/curl.so: cannot open shared object file: No such file or directory
Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/net/rbd.so: cannot open shared object file: No such file or directory
Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/net/gluster.so: cannot open shared object file: No such file or directory
Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/net/ssh.so: cannot open shared object file: No such file or directory

was this even tested before being posted ?

Daniel
Richard Henderson Sept. 13, 2013, 2:52 p.m. UTC | #2
On 09/13/2013 02:59 AM, Fam Zheng wrote:
> +    const char *module_whitelist[] = {
> +        CONFIG_MODULE_WHITELIST
> +    };

  static const char * const module_whitelist[] = ...

> +    switch (type) {
> +    case MODULE_LOAD_BLOCK:
> +        path = CONFIG_MODDIR "/block/";
> +        break;
> +    case MODULE_LOAD_UI:
> +        path = CONFIG_MODDIR "/ui/";
> +        break;
> +    case MODULE_LOAD_NET:
> +        path = CONFIG_MODDIR "/net/";
> +        break;

Also, separate the whitelists by type.  I.e.

  static const char * const modules_block[] = ...
  static const char * const modules_ui[] = ...
  static const char * const modules_net[] = ...

  switch (type) {
  case MODULE_LOAD_BLOCK:
    list = modules_block;
    n = ARRAY_SIZE(modules_block);
    break;
  ...
  }

No need for null termination of the array, as you're currently using.

> +    for (mp = &module_whitelist[0]; *mp; mp++) {
> +        fname = g_strdup_printf("%s%s" HOST_DSOSUF, path, *mp);
> +        module_load_file(fname);
> +        g_free(fname);
> +    }

Why this bizzare mix of g_strdup_printf and compile-time string concatenation?
 Certainly you could have arranged for HOST_DSOSUF to be built into the module
name as seen in the arrays.

Then we're back to the subdirectory vs filename prefix and CONFIG_MODDIR vs any
of several module search path options, the debate of which I don't believe has
concluded.


r~
Michael Tokarev Sept. 13, 2013, 4:20 p.m. UTC | #3
13.09.2013 18:52, Richard Henderson wrote:

> Also, separate the whitelists by type.  I.e.

What for?

Thanks,

/mjt
Richard Henderson Sept. 13, 2013, 7:43 p.m. UTC | #4
On 09/13/2013 09:20 AM, Michael Tokarev wrote:
> 13.09.2013 18:52, Richard Henderson wrote:
> 
>> Also, separate the whitelists by type.  I.e.
> 
> What for?

We are talking about a function whose interface is to
only load modules of a particular type, right?

Given that we know the set of all modules, and the type
to which it belongs, how can it not be most efficient
to separate them into different lists, to avoid having
to perform some kind of runtime test on each module?


r~
Fam Zheng Sept. 16, 2013, 1:30 a.m. UTC | #5
On Fri, 09/13 15:07, Daniel P. Berrange wrote:
> On Fri, Sep 13, 2013 at 05:59:13PM +0800, Fam Zheng wrote:
> > +void module_load(module_load_type type)
> > +{
> > +#ifdef CONFIG_MODULES
> > +    const char *path;
> > +    char *fname = NULL;
> > +    const char **mp;
> > +    const char *module_whitelist[] = {
> > +        CONFIG_MODULE_WHITELIST
> > +    };
> > +
> > +    if (!g_module_supported()) {
> > +        return;
> > +    }
> > +
> > +    switch (type) {
> > +    case MODULE_LOAD_BLOCK:
> > +        path = CONFIG_MODDIR "/block/";
> > +        break;
> > +    case MODULE_LOAD_UI:
> > +        path = CONFIG_MODDIR "/ui/";
> > +        break;
> > +    case MODULE_LOAD_NET:
> > +        path = CONFIG_MODDIR "/net/";
> > +        break;
> > +    default:
> > +        return;
> > +    }
> > +
> > +    for (mp = &module_whitelist[0]; *mp; mp++) {
> > +        fname = g_strdup_printf("%s%s" HOST_DSOSUF, path, *mp);
> > +        module_load_file(fname);
> > +        g_free(fname);
> > +    }
> 
> You need a separate whitelist for block, ui, net, etc. This code just
> spews errors to stderr.
> 
> Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/ui/iscsi.so: cannot open shared object file: No such file or directory
> Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/ui/curl.so: cannot open shared object file: No such file or directory
> Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/ui/rbd.so: cannot open shared object file: No such file or directory
> Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/ui/gluster.so: cannot open shared object file: No such file or directory
> Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/ui/ssh.so: cannot open shared object file: No such file or directory
> Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/net/iscsi.so: cannot open shared object file: No such file or directory
> Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/net/curl.so: cannot open shared object file: No such file or directory
> Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/net/rbd.so: cannot open shared object file: No such file or directory
> Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/net/gluster.so: cannot open shared object file: No such file or directory
> Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/net/ssh.so: cannot open shared object file: No such file or directory
> 

Given that we should accept non-existence of a whitelisted module (i.e. user
didn't install it), I think it's these -ENOENT error messges are unnecessary.

Fam
Fam Zheng Sept. 16, 2013, 1:41 a.m. UTC | #6
On Fri, 09/13 07:52, Richard Henderson wrote:
> On 09/13/2013 02:59 AM, Fam Zheng wrote:
> > +    const char *module_whitelist[] = {
> > +        CONFIG_MODULE_WHITELIST
> > +    };
> 
>   static const char * const module_whitelist[] = ...
> 

OK, thanks.


> > +    switch (type) {
> > +    case MODULE_LOAD_BLOCK:
> > +        path = CONFIG_MODDIR "/block/";
> > +        break;
> > +    case MODULE_LOAD_UI:
> > +        path = CONFIG_MODDIR "/ui/";
> > +        break;
> > +    case MODULE_LOAD_NET:
> > +        path = CONFIG_MODDIR "/net/";
> > +        break;
> 
> Also, separate the whitelists by type.  I.e.
> 
>   static const char * const modules_block[] = ...
>   static const char * const modules_ui[] = ...
>   static const char * const modules_net[] = ...
> 
>   switch (type) {
>   case MODULE_LOAD_BLOCK:
>     list = modules_block;
>     n = ARRAY_SIZE(modules_block);
>     break;
>   ...
>   }
> 
> No need for null termination of the array, as you're currently using.
> 

It's that I'd like to be consistent with block whitelist code style as in
bdrv_is_whitelisted().

> > +    for (mp = &module_whitelist[0]; *mp; mp++) {
> > +        fname = g_strdup_printf("%s%s" HOST_DSOSUF, path, *mp);
> > +        module_load_file(fname);
> > +        g_free(fname);
> > +    }
> 
> Why this bizzare mix of g_strdup_printf and compile-time string concatenation?
>  Certainly you could have arranged for HOST_DSOSUF to be built into the module
> name as seen in the arrays.
> 
> Then we're back to the subdirectory vs filename prefix and CONFIG_MODDIR vs any
> of several module search path options, the debate of which I don't believe has
> concluded.
> 

Perhaps using module type as prefix and building into whitelist can save us
creating subdirs for each type and also the separation of the list as you
suggested above:

    switch (type) {
    case MODULE_LOAD_BLOCK:
        prefix = "block-";
        break;
        ...
    }

    for (mp = &module_whitelist[0]; *mp; mp++) {
        if (!strncmp(prefix, *mp, sizeof(prefix))) {
            /* load */
        }
    }
diff mbox

Patch

diff --git a/Makefile b/Makefile
index ef76967..766b309 100644
--- a/Makefile
+++ b/Makefile
@@ -196,6 +196,9 @@  Makefile: $(version-obj-y) $(version-lobj-y)
 libqemustub.a: $(stub-obj-y)
 libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o
 
+default-whitelist = $(foreach o,$(modules-m),"$(notdir $(basename $o))",) NULL
+util/module.o-cflags = -D'CONFIG_MODULE_WHITELIST=$(default-whitelist)'
+
 ######################################################################
 
 qemu-img.o: qemu-img-cmds.h
diff --git a/block.c b/block.c
index 26639e8..16ceaaf 100644
--- a/block.c
+++ b/block.c
@@ -4008,6 +4008,7 @@  BlockDriverAIOCB *bdrv_aio_discard(BlockDriverState *bs,
 
 void bdrv_init(void)
 {
+    module_load(MODULE_LOAD_BLOCK);
     module_call_init(MODULE_INIT_BLOCK);
 }
 
diff --git a/configure b/configure
index 5bc7256..05aa964 100755
--- a/configure
+++ b/configure
@@ -199,6 +199,7 @@  datadir="\${prefix}/share"
 qemu_docdir="\${prefix}/share/doc/qemu"
 bindir="\${prefix}/bin"
 libdir="\${prefix}/lib"
+moddir="\${prefix}/lib/qemu"
 libexecdir="\${prefix}/libexec"
 includedir="\${prefix}/include"
 sysconfdir="\${prefix}/etc"
@@ -651,7 +652,9 @@  for opt do
   ;;
   --disable-debug-info)
   ;;
-  --enable-modules) modules="yes"
+  --enable-modules|--enable-modules=*)
+      modules="yes"
+      module_list=`echo "$optarg" | sed -e 's/,/ /g'`
   ;;
   --cpu=*)
   ;;
@@ -676,6 +679,8 @@  for opt do
   ;;
   --libdir=*) libdir="$optarg"
   ;;
+  --moddir=*) moddir="$optarg"
+  ;;
   --libexecdir=*) libexecdir="$optarg"
   ;;
   --includedir=*) includedir="$optarg"
@@ -1052,10 +1057,13 @@  echo "  --datadir=PATH           install firmware in PATH$confsuffix"
 echo "  --docdir=PATH            install documentation in PATH$confsuffix"
 echo "  --bindir=PATH            install binaries in PATH"
 echo "  --libdir=PATH            install libraries in PATH"
+echo "  --moddir=PATH            install modules in PATH"
 echo "  --sysconfdir=PATH        install config in PATH$confsuffix"
 echo "  --localstatedir=PATH     install local state in PATH (set at runtime on win32)"
 echo "  --with-confsuffix=SUFFIX suffix for QEMU data inside datadir and sysconfdir [$confsuffix]"
-echo "  --enable-modules         enable modules support"
+echo "  --enable-modules         enable modules support and whitelist all modules"
+echo "  --enable-modules=L       enable modules and provide a whitelist"
+echo "                           Available modules: curl iscsi gluster ssh rbd"
 echo "  --enable-debug-tcg       enable TCG debugging"
 echo "  --disable-debug-tcg      disable TCG debugging (default)"
 echo "  --enable-debug-info       enable debugging information (default)"
@@ -2256,15 +2264,19 @@  if test "$mingw32" = yes; then
 else
     glib_req_ver=2.12
 fi
-if $pkg_config --atleast-version=$glib_req_ver gthread-2.0; then
-    glib_cflags=`$pkg_config --cflags gthread-2.0`
-    glib_libs=`$pkg_config --libs gthread-2.0`
-    CFLAGS="$glib_cflags $CFLAGS"
-    LIBS="$glib_libs $LIBS"
-    libs_qga="$glib_libs $libs_qga"
-else
-    error_exit "glib-$glib_req_ver required to compile QEMU"
-fi
+
+for i in gthread-2.0 gmodule-2.0; do
+    if $pkg_config --atleast-version=$glib_req_ver $i; then
+        glib_cflags=`$pkg_config --cflags $i`
+        glib_libs=`$pkg_config --libs $i`
+        CFLAGS="$glib_cflags $CFLAGS"
+        LIBS="$glib_libs $LIBS"
+        libs_qga="$glib_libs $libs_qga"
+    else
+        error_exit "glib-$glib_req_ver required to compile QEMU"
+    fi
+done
+
 
 ##########################################
 # pixman support probe
@@ -3557,6 +3569,7 @@  echo "Install prefix    $prefix"
 echo "BIOS directory    `eval echo $qemu_datadir`"
 echo "binary directory  `eval echo $bindir`"
 echo "library directory `eval echo $libdir`"
+echo "module directory  `eval echo $moddir`"
 echo "libexec directory `eval echo $libexecdir`"
 echo "include directory `eval echo $includedir`"
 echo "config directory  `eval echo $sysconfdir`"
@@ -3581,6 +3594,9 @@  if test "$slirp" = "yes" ; then
     echo "smbd              $smbd"
 fi
 echo "module support    $modules"
+if test -n "$module_list"; then
+    echo "module whitelist  $module_list"
+fi
 echo "host CPU          $cpu"
 echo "host big endian   $bigendian"
 echo "target list       $target_list"
@@ -3680,6 +3696,7 @@  echo all: >> $config_host_mak
 echo "prefix=$prefix" >> $config_host_mak
 echo "bindir=$bindir" >> $config_host_mak
 echo "libdir=$libdir" >> $config_host_mak
+echo "moddir=$moddir" >> $config_host_mak
 echo "libexecdir=$libexecdir" >> $config_host_mak
 echo "includedir=$includedir" >> $config_host_mak
 echo "mandir=$mandir" >> $config_host_mak
@@ -3698,8 +3715,12 @@  echo "libs_softmmu=$libs_softmmu" >> $config_host_mak
 
 echo "ARCH=$ARCH" >> $config_host_mak
 
+echo "CONFIG_FINGERPRINT=$(date +%s$$$RANDOM)" >> $config_host_mak
 if test "$modules" = "yes"; then
   echo "CONFIG_MODULES=y" >> $config_host_mak
+  if test -n "$module_list"; then
+      echo "CONFIG_MODULE_WHITELIST=$module_list" >> $config_host_mak
+  fi
 fi
 case "$cpu" in
   arm|i386|x86_64|x32|ppc|aarch64)
diff --git a/include/qemu/module.h b/include/qemu/module.h
index c4ccd57..6458d8f 100644
--- a/include/qemu/module.h
+++ b/include/qemu/module.h
@@ -14,11 +14,25 @@ 
 #ifndef QEMU_MODULE_H
 #define QEMU_MODULE_H
 
+#ifdef BUILD_DSO
+
+/* For error message, this function is an identification of qemu module */
+void qemu_module_do_init(void (*init)(void));
+
+/* To restrict loading of arbitrary DSO, The init function name is changed per
+ * "./configure" to refuse unknown DSO file */
+void DSO_INIT_FUN(void);
+
+#define module_init(function, type)                                         \
+void qemu_module_do_init(void (*init)(void)) { init(); }                    \
+void DSO_INIT_FUN(void) { qemu_module_do_init(function); }
+#else
 /* This should not be used directly.  Use block_init etc. instead.  */
 #define module_init(function, type)                                         \
 static void __attribute__((constructor)) do_qemu_init_ ## function(void) {  \
     register_module_init(function, type);                                   \
 }
+#endif
 
 typedef enum {
     MODULE_INIT_BLOCK,
@@ -37,4 +51,13 @@  void register_module_init(void (*fn)(void), module_init_type type);
 
 void module_call_init(module_init_type type);
 
+typedef enum {
+    MODULE_LOAD_BLOCK = 0,
+    MODULE_LOAD_UI,
+    MODULE_LOAD_NET,
+    MODULE_LOAD_MAX,
+} module_load_type;
+
+void module_load(module_load_type type);
+
 #endif
diff --git a/rules.mak b/rules.mak
index 43a502e..e5529da 100644
--- a/rules.mak
+++ b/rules.mak
@@ -62,7 +62,7 @@  endif
 %.o: %.dtrace
 	$(call quiet-command,dtrace -o $@ -G -s $<, "  GEN   $(TARGET_DIR)$@")
 
-%$(DSOSUF): QEMU_CFLAGS += -fPIC
+%$(DSOSUF): QEMU_CFLAGS += -fPIC -DBUILD_DSO
 %$(DSOSUF): LDFLAGS += $(LDFLAGS_SHARED)
 %$(DSOSUF): %.mo libqemustub.a
 	$(call LINK,$^)
@@ -165,13 +165,18 @@  $(if $(nested-dirs),
   $(call unnest-vars-1))
 endef
 
+is-whitelisted = $(if $(CONFIG_MODULE_WHITELIST),$(strip \
+    $(filter $(CONFIG_MODULE_WHITELIST),$(basename $(notdir $1)))),\
+	yes)
 define add-modules
 $(foreach o,$(filter %.o,$($1)),
 	$(eval $(patsubst %.o,%.mo,$o): $o) \
 	$(eval $(patsubst %.o,%.mo,$o)-objs := $o))
 $(foreach o,$(filter %.mo,$($1)),$(eval \
     $o: $($o-objs)))
-$(eval modules-m += $(patsubst %.o,%.mo,$($1)))
+$(eval t := $(patsubst %.o,%.mo,$($1)))
+$(foreach o,$t,$(if $(call is-whitelisted,$o),$(eval \
+	modules-m += $o)))
 endef
 
 define unnest-vars
diff --git a/scripts/create_config b/scripts/create_config
index b1adbf5..ab430c7 100755
--- a/scripts/create_config
+++ b/scripts/create_config
@@ -26,6 +26,24 @@  case $line in
     # save for the next definitions
     prefix=${line#*=}
     ;;
+ moddir=*)
+    eval "moddir=\"${line#*=}\""
+    echo "#define CONFIG_MODDIR \"$moddir\""
+    ;;
+ CONFIG_FINGERPRINT=*)
+    echo "#define DSO_INIT_FUN init_${line#*=}"
+    echo "#define DSO_INIT_FUN_STR \"init_${line#*=}\""
+    ;;
+ CONFIG_MODULES=*)
+    echo "#define CONFIG_MODULES \"${line#*=}\""
+    ;;
+ CONFIG_MODULE_WHITELIST=*)
+    echo "#define CONFIG_MODULE_WHITELIST\\"
+    for mod in ${line#*=}; do
+      echo "    \"${mod}\",\\"
+    done
+    echo "    NULL"
+    ;;
  CONFIG_AUDIO_DRIVERS=*)
     drivers=${line#*=}
     echo "#define CONFIG_AUDIO_DRIVERS \\"
@@ -104,6 +122,9 @@  case $line in
     value=${line#*=}
     echo "#define $name $value"
     ;;
+ DSOSUF=*)
+    echo "#define HOST_DSOSUF \"${line#*=}\""
+    ;;
 esac
 
 done # read
diff --git a/util/module.c b/util/module.c
index 7acc33d..1f38311 100644
--- a/util/module.c
+++ b/util/module.c
@@ -13,6 +13,7 @@ 
  * GNU GPL, version 2 or (at your option) any later version.
  */
 
+#include <gmodule.h>
 #include "qemu-common.h"
 #include "qemu/queue.h"
 #include "qemu/module.h"
@@ -79,3 +80,76 @@  void module_call_init(module_init_type type)
         e->init();
     }
 }
+
+#ifdef CONFIG_MODULES
+static void module_load_file(const char *fname)
+{
+    GModule *g_module;
+    void (*init_fun)(void);
+    const char *dsosuf = HOST_DSOSUF;
+    int len = strlen(fname);
+    int suf_len = strlen(dsosuf);
+
+    if (len <= suf_len || strcmp(&fname[len - suf_len], dsosuf)) {
+        /* wrong suffix */
+        return;
+    }
+    g_module = g_module_open(fname, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
+    if (!g_module) {
+        fprintf(stderr, "Failed to open module file %s\n",
+                g_module_error());
+        return;
+    }
+    if (!g_module_symbol(g_module, DSO_INIT_FUN_STR, (gpointer *)&init_fun)) {
+        fprintf(stderr, "Failed to initialize module: %s\n",
+                fname);
+        /* Print some info if this is a QEMU module (but from different build),
+         * this will make debugging user problems easier. */
+        if (g_module_symbol(g_module, "qemu_module_do_init",
+                            (gpointer *)&init_fun)) {
+            fprintf(stderr,
+                    "Note: only modules from the same build can be loaded.\n");
+        }
+        g_module_close(g_module);
+        return;
+    }
+    init_fun();
+}
+#endif
+
+void module_load(module_load_type type)
+{
+#ifdef CONFIG_MODULES
+    const char *path;
+    char *fname = NULL;
+    const char **mp;
+    const char *module_whitelist[] = {
+        CONFIG_MODULE_WHITELIST
+    };
+
+    if (!g_module_supported()) {
+        return;
+    }
+
+    switch (type) {
+    case MODULE_LOAD_BLOCK:
+        path = CONFIG_MODDIR "/block/";
+        break;
+    case MODULE_LOAD_UI:
+        path = CONFIG_MODDIR "/ui/";
+        break;
+    case MODULE_LOAD_NET:
+        path = CONFIG_MODDIR "/net/";
+        break;
+    default:
+        return;
+    }
+
+    for (mp = &module_whitelist[0]; *mp; mp++) {
+        fname = g_strdup_printf("%s%s" HOST_DSOSUF, path, *mp);
+        module_load_file(fname);
+        g_free(fname);
+    }
+
+#endif
+}
diff --git a/vl.c b/vl.c
index b4b119a..659e53a 100644
--- a/vl.c
+++ b/vl.c
@@ -2940,6 +2940,8 @@  int main(int argc, char **argv, char **envp)
 #endif
     }
 
+    module_load(MODULE_LOAD_UI);
+    module_load(MODULE_LOAD_NET);
     module_call_init(MODULE_INIT_QOM);
 
     qemu_add_opts(&qemu_drive_opts);