mirror of
				https://github.com/qemu/qemu.git
				synced 2025-10-31 04:06:46 +00:00 
			
		
		
		
	module: implement module loading
This patch adds loading, stamp checking and initialization of modules.
The init function of dynamic module is no longer directly called as
__attribute__((constructor)) in static linked version, it is called
only after passed the checking of presense of stamp symbol:
    qemu_stamp_$RELEASEHASH
where $RELEASEHASH is generated by hashing version strings and content
of configure script.
With this, modules built from a different tree/version/configure will
not be loaded.
The module loading code requires gmodule-2.0.
Modules are searched under
 - CONFIG_MODDIR
 - executable folder (to allow running qemu-{img,io} in the build
   directory)
 - ../ of executable folder (to allow running system emulator in the
   build directory)
Modules are linked under their subdir respectively, then copied to top
level of build directory for above convinience, e.g.:
    $(BUILD_DIR)/block/curl.so -> $(BUILD_DIR)/block-curl.so
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
			
			
This commit is contained in:
		
							parent
							
								
									17969268f5
								
							
						
					
					
						commit
						e26110cfc6
					
				
							
								
								
									
										3
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								Makefile
									
									
									
									
									
								
							| @ -201,6 +201,9 @@ Makefile: $(version-obj-y) $(version-lobj-y) | ||||
| libqemustub.a: $(stub-obj-y) | ||||
| libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o | ||||
| 
 | ||||
| block-modules = $(foreach o,$(block-obj-m),"$(basename $(subst /,-,$o))",) NULL | ||||
| util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)' | ||||
| 
 | ||||
| ######################################################################
 | ||||
| 
 | ||||
| qemu-img.o: qemu-img-cmds.h | ||||
|  | ||||
							
								
								
									
										42
									
								
								configure
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										42
									
								
								configure
									
									
									
									
										vendored
									
									
								
							| @ -1137,7 +1137,7 @@ Advanced options (experts only): | ||||
|   --libdir=PATH            install libraries in PATH | ||||
|   --sysconfdir=PATH        install config in PATH$confsuffix | ||||
|   --localstatedir=PATH     install local state in PATH (set at runtime on win32) | ||||
|   --with-confsuffix=SUFFIX suffix for QEMU data inside datadir and sysconfdir [$confsuffix] | ||||
|   --with-confsuffix=SUFFIX suffix for QEMU data inside datadir/libdir/sysconfdir [$confsuffix] | ||||
|   --enable-modules         enable modules support | ||||
|   --enable-debug-tcg       enable TCG debugging | ||||
|   --disable-debug-tcg      disable TCG debugging (default) | ||||
| @ -2362,14 +2362,32 @@ 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" | ||||
| 
 | ||||
| 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 $i is required to compile QEMU" | ||||
|     fi | ||||
| done | ||||
| 
 | ||||
| ########################################## | ||||
| # SHA command probe for modules | ||||
| if test "$modules" = yes; then | ||||
|     shacmd_probe="sha1sum sha1 shasum" | ||||
|     for c in $shacmd_probe; do | ||||
|         if which $c &>/dev/null; then | ||||
|             shacmd="$c" | ||||
|             break | ||||
|         fi | ||||
|     done | ||||
|     if test "$shacmd" = ""; then | ||||
|         error_exit "one of the checksum commands is required to enable modules: $shacmd_probe" | ||||
|     fi | ||||
| fi | ||||
| 
 | ||||
| ########################################## | ||||
| @ -3661,6 +3679,7 @@ if test "$mingw32" = "yes" ; then | ||||
| fi | ||||
| 
 | ||||
| qemu_confdir=$sysconfdir$confsuffix | ||||
| qemu_moddir=$libdir$confsuffix | ||||
| qemu_datadir=$datadir$confsuffix | ||||
| qemu_localedir="$datadir/locale" | ||||
| 
 | ||||
| @ -3751,6 +3770,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 $qemu_moddir`" | ||||
| echo "libexec directory `eval echo $libexecdir`" | ||||
| echo "include directory `eval echo $includedir`" | ||||
| echo "config directory  `eval echo $sysconfdir`" | ||||
| @ -3890,6 +3910,7 @@ echo "sysconfdir=$sysconfdir" >> $config_host_mak | ||||
| echo "qemu_confdir=$qemu_confdir" >> $config_host_mak | ||||
| echo "qemu_datadir=$qemu_datadir" >> $config_host_mak | ||||
| echo "qemu_docdir=$qemu_docdir" >> $config_host_mak | ||||
| echo "qemu_moddir=$qemu_moddir" >> $config_host_mak | ||||
| if test "$mingw32" = "no" ; then | ||||
|   echo "qemu_localstatedir=$local_statedir" >> $config_host_mak | ||||
| fi | ||||
| @ -4023,6 +4044,9 @@ if [ "$docs" = "yes" ] ; then | ||||
|   echo "BUILD_DOCS=yes" >> $config_host_mak | ||||
| fi | ||||
| if test "$modules" = "yes"; then | ||||
|   # $shacmd can generate a hash started with digit, which the compiler doesn't | ||||
|   # like as an symbol. So prefix it with an underscore | ||||
|   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 "$sdl" = "yes" ; then | ||||
|  | ||||
| @ -14,11 +14,31 @@ | ||||
| #ifndef QEMU_MODULE_H | ||||
| #define QEMU_MODULE_H | ||||
| 
 | ||||
| #include "qemu/osdep.h" | ||||
| 
 | ||||
| #define DSO_STAMP_FUN         glue(qemu_stamp, CONFIG_STAMP) | ||||
| #define DSO_STAMP_FUN_STR     stringify(DSO_STAMP_FUN) | ||||
| 
 | ||||
| #ifdef BUILD_DSO | ||||
| void DSO_STAMP_FUN(void); | ||||
| /* This is a dummy symbol to identify a loaded DSO as a QEMU module, so we can
 | ||||
|  * distinguish "version mismatch" from "not a QEMU module", when the stamp | ||||
|  * check fails during module loading */ | ||||
| void qemu_module_dummy(void); | ||||
| 
 | ||||
| #define module_init(function, type)                                         \ | ||||
| static void __attribute__((constructor)) do_qemu_init_ ## function(void)    \ | ||||
| {                                                                           \ | ||||
|     register_dso_module_init(function, type);                               \ | ||||
| } | ||||
| #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) {  \ | ||||
| static void __attribute__((constructor)) do_qemu_init_ ## function(void)    \ | ||||
| {                                                                           \ | ||||
|     register_module_init(function, type);                                   \ | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| typedef enum { | ||||
|     MODULE_INIT_BLOCK, | ||||
| @ -34,6 +54,7 @@ typedef enum { | ||||
| #define type_init(function) module_init(function, MODULE_INIT_QOM) | ||||
| 
 | ||||
| void register_module_init(void (*fn)(void), module_init_type type); | ||||
| void register_dso_module_init(void (*fn)(void), module_init_type type); | ||||
| 
 | ||||
| void module_call_init(module_init_type type); | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										10
									
								
								module-common.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								module-common.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| #include "config-host.h" | ||||
| #include "qemu/module.h" | ||||
| 
 | ||||
| void qemu_module_dummy(void) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void DSO_STAMP_FUN(void) | ||||
| { | ||||
| } | ||||
| @ -82,6 +82,8 @@ DSO_CFLAGS := -fPIC -DBUILD_DSO | ||||
| %$(DSOSUF): LDFLAGS += $(LDFLAGS_SHARED) | ||||
| %$(DSOSUF): %.mo libqemustub.a | ||||
| 	$(call LINK,$^) | ||||
| 	@# Copy to build root so modules can be loaded when program started without install | ||||
| 	$(if $(findstring /,$@),$(call quiet-command,cp $@ $(subst /,-,$@), "  CP    $(subst /,-,$@)")) | ||||
| 
 | ||||
| .PHONY: modules | ||||
| modules: | ||||
| @ -211,6 +213,7 @@ $(foreach o,$(filter %.o,$($1)), | ||||
| 	$(eval $(patsubst %.o,%.mo,$o): $o) \
 | ||||
| 	$(eval $(patsubst %.o,%.mo,$o)-objs := $o)) | ||||
| $(foreach o,$(filter-out $(modules-m), $(patsubst %.o,%.mo,$($1))), \ | ||||
|     $(eval $o-objs += module-common.o) | ||||
|     $(eval $o: $($o-objs)) | ||||
|     $(eval modules-objs-m += $($o-objs)) | ||||
|     $(eval modules-m += $o) | ||||
|  | ||||
| @ -108,6 +108,9 @@ case $line in | ||||
|     value=${line#*=} | ||||
|     echo "#define $name $value" | ||||
|     ;; | ||||
|  DSOSUF=*) | ||||
|     echo "#define HOST_DSOSUF \"${line#*=}\"" | ||||
|     ;; | ||||
| esac | ||||
| 
 | ||||
| done # read | ||||
|  | ||||
							
								
								
									
										145
									
								
								util/module.c
									
									
									
									
									
								
							
							
						
						
									
										145
									
								
								util/module.c
									
									
									
									
									
								
							| @ -13,6 +13,8 @@ | ||||
|  * GNU GPL, version 2 or (at your option) any later version. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| #include <gmodule.h> | ||||
| #include "qemu-common.h" | ||||
| #include "qemu/queue.h" | ||||
| #include "qemu/module.h" | ||||
| @ -21,13 +23,16 @@ typedef struct ModuleEntry | ||||
| { | ||||
|     void (*init)(void); | ||||
|     QTAILQ_ENTRY(ModuleEntry) node; | ||||
|     module_init_type type; | ||||
| } ModuleEntry; | ||||
| 
 | ||||
| typedef QTAILQ_HEAD(, ModuleEntry) ModuleTypeList; | ||||
| 
 | ||||
| static ModuleTypeList init_type_list[MODULE_INIT_MAX]; | ||||
| 
 | ||||
| static void init_types(void) | ||||
| static ModuleTypeList dso_init_list; | ||||
| 
 | ||||
| static void init_lists(void) | ||||
| { | ||||
|     static int inited; | ||||
|     int i; | ||||
| @ -40,6 +45,8 @@ static void init_types(void) | ||||
|         QTAILQ_INIT(&init_type_list[i]); | ||||
|     } | ||||
| 
 | ||||
|     QTAILQ_INIT(&dso_init_list); | ||||
| 
 | ||||
|     inited = 1; | ||||
| } | ||||
| 
 | ||||
| @ -48,7 +55,7 @@ static ModuleTypeList *find_type(module_init_type type) | ||||
| { | ||||
|     ModuleTypeList *l; | ||||
| 
 | ||||
|     init_types(); | ||||
|     init_lists(); | ||||
| 
 | ||||
|     l = &init_type_list[type]; | ||||
| 
 | ||||
| @ -62,20 +69,154 @@ void register_module_init(void (*fn)(void), module_init_type type) | ||||
| 
 | ||||
|     e = g_malloc0(sizeof(*e)); | ||||
|     e->init = fn; | ||||
|     e->type = type; | ||||
| 
 | ||||
|     l = find_type(type); | ||||
| 
 | ||||
|     QTAILQ_INSERT_TAIL(l, e, node); | ||||
| } | ||||
| 
 | ||||
| void register_dso_module_init(void (*fn)(void), module_init_type type) | ||||
| { | ||||
|     ModuleEntry *e; | ||||
| 
 | ||||
|     init_lists(); | ||||
| 
 | ||||
|     e = g_malloc0(sizeof(*e)); | ||||
|     e->init = fn; | ||||
|     e->type = type; | ||||
| 
 | ||||
|     QTAILQ_INSERT_TAIL(&dso_init_list, e, node); | ||||
| } | ||||
| 
 | ||||
| static void module_load(module_init_type type); | ||||
| 
 | ||||
| void module_call_init(module_init_type type) | ||||
| { | ||||
|     ModuleTypeList *l; | ||||
|     ModuleEntry *e; | ||||
| 
 | ||||
|     module_load(type); | ||||
|     l = find_type(type); | ||||
| 
 | ||||
|     QTAILQ_FOREACH(e, l, node) { | ||||
|         e->init(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_MODULES | ||||
| static int module_load_file(const char *fname) | ||||
| { | ||||
|     GModule *g_module; | ||||
|     void (*sym)(void); | ||||
|     const char *dsosuf = HOST_DSOSUF; | ||||
|     int len = strlen(fname); | ||||
|     int suf_len = strlen(dsosuf); | ||||
|     ModuleEntry *e, *next; | ||||
|     int ret; | ||||
| 
 | ||||
|     if (len <= suf_len || strcmp(&fname[len - suf_len], dsosuf)) { | ||||
|         /* wrong suffix */ | ||||
|         ret = -EINVAL; | ||||
|         goto out; | ||||
|     } | ||||
|     if (access(fname, F_OK)) { | ||||
|         ret = -ENOENT; | ||||
|         goto out; | ||||
|     } | ||||
| 
 | ||||
|     assert(QTAILQ_EMPTY(&dso_init_list)); | ||||
| 
 | ||||
|     g_module = g_module_open(fname, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); | ||||
|     if (!g_module) { | ||||
|         fprintf(stderr, "Failed to open module: %s\n", | ||||
|                 g_module_error()); | ||||
|         ret = -EINVAL; | ||||
|         goto out; | ||||
|     } | ||||
|     if (!g_module_symbol(g_module, DSO_STAMP_FUN_STR, (gpointer *)&sym)) { | ||||
|         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_dummy", (gpointer *)&sym)) { | ||||
|             fprintf(stderr, | ||||
|                     "Note: only modules from the same build can be loaded.\n"); | ||||
|         } | ||||
|         g_module_close(g_module); | ||||
|         ret = -EINVAL; | ||||
|     } else { | ||||
|         QTAILQ_FOREACH(e, &dso_init_list, node) { | ||||
|             register_module_init(e->init, e->type); | ||||
|         } | ||||
|         ret = 0; | ||||
|     } | ||||
| 
 | ||||
|     QTAILQ_FOREACH_SAFE(e, &dso_init_list, node, next) { | ||||
|         QTAILQ_REMOVE(&dso_init_list, e, node); | ||||
|         g_free(e); | ||||
|     } | ||||
| out: | ||||
|     return ret; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| void module_load(module_init_type type) | ||||
| { | ||||
| #ifdef CONFIG_MODULES | ||||
|     char *fname = NULL; | ||||
|     const char **mp; | ||||
|     static const char *block_modules[] = { | ||||
|         CONFIG_BLOCK_MODULES | ||||
|     }; | ||||
|     char *exec_dir; | ||||
|     char *dirs[3]; | ||||
|     int i = 0; | ||||
|     int ret; | ||||
| 
 | ||||
|     if (!g_module_supported()) { | ||||
|         fprintf(stderr, "Module is not supported by system.\n"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     switch (type) { | ||||
|     case MODULE_INIT_BLOCK: | ||||
|         mp = block_modules; | ||||
|         break; | ||||
|     default: | ||||
|         /* no other types have dynamic modules for now*/ | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     exec_dir = qemu_get_exec_dir(); | ||||
|     dirs[i++] = g_strdup_printf("%s", CONFIG_QEMU_MODDIR); | ||||
|     dirs[i++] = g_strdup_printf("%s/..", exec_dir ? : ""); | ||||
|     dirs[i++] = g_strdup_printf("%s", exec_dir ? : ""); | ||||
|     assert(i == ARRAY_SIZE(dirs)); | ||||
|     g_free(exec_dir); | ||||
|     exec_dir = NULL; | ||||
| 
 | ||||
|     for ( ; *mp; mp++) { | ||||
|         for (i = 0; i < ARRAY_SIZE(dirs); i++) { | ||||
|             fname = g_strdup_printf("%s/%s%s", dirs[i], *mp, HOST_DSOSUF); | ||||
|             ret = module_load_file(fname); | ||||
|             /* Try loading until loaded a module file */ | ||||
|             if (!ret) { | ||||
|                 break; | ||||
|             } | ||||
|             g_free(fname); | ||||
|             fname = NULL; | ||||
|         } | ||||
|         if (ret == -ENOENT) { | ||||
|             fprintf(stderr, "Can't find module: %s\n", *mp); | ||||
|         } | ||||
| 
 | ||||
|         g_free(fname); | ||||
|     } | ||||
| 
 | ||||
|     for (i = 0; i < ARRAY_SIZE(dirs); i++) { | ||||
|         g_free(dirs[i]); | ||||
|     } | ||||
| 
 | ||||
| #endif | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Fam Zheng
						Fam Zheng