diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile | 29 | ||||
-rw-r--r-- | Makefile.objs | 19 | ||||
-rw-r--r-- | Makefile.target | 23 | ||||
-rw-r--r-- | block/Makefile.objs | 13 | ||||
-rwxr-xr-x | configure | 156 | ||||
-rw-r--r-- | include/qemu-common.h | 2 | ||||
-rw-r--r-- | include/qemu/module.h | 23 | ||||
-rw-r--r-- | include/qemu/osdep.h | 9 | ||||
-rw-r--r-- | module-common.c | 10 | ||||
-rw-r--r-- | os-posix.c | 42 | ||||
-rw-r--r-- | os-win32.c | 21 | ||||
-rw-r--r-- | qemu-img.c | 1 | ||||
-rw-r--r-- | qemu-io.c | 1 | ||||
-rw-r--r-- | qemu-nbd.c | 1 | ||||
-rw-r--r-- | rules.mak | 80 | ||||
-rwxr-xr-x | scripts/create_config | 3 | ||||
-rw-r--r-- | util/module.c | 145 | ||||
-rw-r--r-- | util/oslib-posix.c | 54 | ||||
-rw-r--r-- | util/oslib-win32.c | 30 | ||||
-rw-r--r-- | vl.c | 3 |
21 files changed, 541 insertions, 127 deletions
@@ -66,6 +66,9 @@ libuser *.cp *.dvi *.exe +*.dll +*.so +*.mo *.fn *.ky *.log @@ -127,13 +127,29 @@ defconfig: ifneq ($(wildcard config-host.mak),) include $(SRC_PATH)/Makefile.objs +endif + +dummy := $(call unnest-vars,, \ + stub-obj-y \ + util-obj-y \ + qga-obj-y \ + block-obj-y \ + block-obj-m \ + common-obj-y \ + common-obj-m) + +ifneq ($(wildcard config-host.mak),) include $(SRC_PATH)/tests/Makefile endif ifeq ($(CONFIG_SMARTCARD_NSS),y) include $(SRC_PATH)/libcacard/Makefile endif -all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all +all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules + +vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) + +vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS) config-host.h: config-host.h-timestamp config-host.h-timestamp: config-host.mak @@ -192,6 +208,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 @@ -247,6 +266,8 @@ clean: rm -f qemu-options.def find . -name '*.[oda]' -type f -exec rm -f {} + find . -name '*.l[oa]' -type f -exec rm -f {} + + find . -name '*$(DSOSUF)' -type f -exec rm -f {} + + find . -name '*.mo' -type f -exec rm -f {} + rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~ rm -f fsdev/*.pod rm -rf .libs */.libs @@ -354,6 +375,12 @@ install-datadir install-localstatedir ifneq ($(TOOLS),) $(INSTALL_PROG) $(STRIP_OPT) $(TOOLS) "$(DESTDIR)$(bindir)" endif +ifneq ($(CONFIG_MODULES),) + $(INSTALL_DIR) "$(DESTDIR)$(qemu_moddir)" + for s in $(patsubst %.mo,%$(DSOSUF),$(modules-m)); do \ + $(INSTALL_PROG) $(STRIP_OPT) $$s "$(DESTDIR)$(qemu_moddir)/$${s//\//-}"; \ + done +endif ifneq ($(HELPERS-y),) $(INSTALL_DIR) "$(DESTDIR)$(libexecdir)" $(INSTALL_PROG) $(STRIP_OPT) $(HELPERS-y) "$(DESTDIR)$(libexecdir)" diff --git a/Makefile.objs b/Makefile.objs index ac1d0e1..4a62913 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -19,6 +19,8 @@ block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o block-obj-y += qemu-coroutine-sleep.o block-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o +block-obj-m = block/ + ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy) # Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add. # only pull in the actual virtio-9p device if we also enabled virtio. @@ -41,7 +43,7 @@ libcacard-y += libcacard/vcardt.o # single QEMU executable should support all CPUs and machines. ifeq ($(CONFIG_SOFTMMU),y) -common-obj-y = $(block-obj-y) blockdev.o blockdev-nbd.o block/ +common-obj-y = blockdev.o blockdev-nbd.o block/ common-obj-y += net/ common-obj-y += qdev-monitor.o device-hotplug.o common-obj-$(CONFIG_WIN32) += os-win32.o @@ -111,18 +113,3 @@ version-lobj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.lo # by libqemuutil.a. These should be moved to a separate .json schema. qga-obj-y = qga/ qapi-types.o qapi-visit.o qga-vss-dll-obj-y = qga/ - -vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) - -vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS) - -QEMU_CFLAGS+=$(GLIB_CFLAGS) - -nested-vars += \ - stub-obj-y \ - util-obj-y \ - qga-obj-y \ - qga-vss-dll-obj-y \ - block-obj-y \ - common-obj-y -dummy := $(call unnest-vars) diff --git a/Makefile.target b/Makefile.target index af6ac7e..ba12340 100644 --- a/Makefile.target +++ b/Makefile.target @@ -130,8 +130,6 @@ else obj-y += hw/$(TARGET_BASE_ARCH)/ endif -main.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) - GENERATED_HEADERS += hmp-commands.h qmp-commands-old.h endif # CONFIG_SOFTMMU @@ -139,13 +137,26 @@ endif # CONFIG_SOFTMMU # Workaround for http://gcc.gnu.org/PR55489, see configure. %/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS) -nested-vars += obj-y +dummy := $(call unnest-vars,,obj-y) + +# we are making another call to unnest-vars with different vars, protect obj-y, +# it can be overriden in subdir Makefile.objs +obj-y-save := $(obj-y) -# This resolves all nested paths, so it must come last +block-obj-y := +common-obj-y := include $(SRC_PATH)/Makefile.objs +dummy := $(call unnest-vars,.., \ + block-obj-y \ + block-obj-m \ + common-obj-y \ + common-obj-m) + +# Now restore obj-y +obj-y := $(obj-y-save) -all-obj-y = $(obj-y) -all-obj-y += $(addprefix ../, $(common-obj-y)) +all-obj-y = $(obj-y) $(common-obj-y) +all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) ifndef CONFIG_HAIKU LIBS+=-lm diff --git a/block/Makefile.objs b/block/Makefile.objs index e254a21..673aa7c 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -24,4 +24,15 @@ common-obj-y += commit.o common-obj-y += mirror.o common-obj-y += backup.o -$(obj)/curl.o: QEMU_CFLAGS+=$(CURL_CFLAGS) +iscsi.o-cflags := $(LIBISCSI_CFLAGS) +iscsi.o-libs := $(LIBISCSI_LIBS) +curl.o-cflags := $(CURL_CFLAGS) +curl.o-libs := $(CURL_LIBS) +rbd.o-cflags := $(RBD_CFLAGS) +rbd.o-libs := $(RBD_LIBS) +gluster.o-cflags := $(GLUSTERFS_CFLAGS) +gluster.o-libs := $(GLUSTERFS_LIBS) +ssh.o-cflags := $(LIBSSH2_CFLAGS) +ssh.o-libs := $(LIBSSH2_LIBS) +qcow.o-libs := -lz +linux-aio.o-libs := -laio @@ -12,7 +12,10 @@ else fi TMPC="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.c" -TMPO="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.o" +TMPB="qemu-conf-${RANDOM}-$$-${RANDOM}" +TMPO="${TMPDIR1}/${TMPB}.o" +TMPL="${TMPDIR1}/${TMPB}.lo" +TMPA="${TMPDIR1}/lib${TMPB}.la" TMPE="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.exe" # NB: do not call "exit" in the trap handler; this is buggy with some shells; @@ -86,6 +89,38 @@ compile_prog() { do_cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags } +do_libtool() { + local mode=$1 + shift + # Run the compiler, capturing its output to the log. + echo $libtool $mode --tag=CC $cc "$@" >> config.log + $libtool $mode --tag=CC $cc "$@" >> config.log 2>&1 || return $? + # Test passed. If this is an --enable-werror build, rerun + # the test with -Werror and bail out if it fails. This + # makes warning-generating-errors in configure test code + # obvious to developers. + if test "$werror" != "yes"; then + return 0 + fi + # Don't bother rerunning the compile if we were already using -Werror + case "$*" in + *-Werror*) + return 0 + ;; + esac + echo $libtool $mode --tag=CC $cc -Werror "$@" >> config.log + $libtool $mode --tag=CC $cc -Werror "$@" >> config.log 2>&1 && return $? + error_exit "configure test passed without -Werror but failed with -Werror." \ + "This is probably a bug in the configure script. The failing command" \ + "will be at the bottom of config.log." \ + "You can run configure with --disable-werror to bypass this check." +} + +libtool_prog() { + do_libtool --mode=compile $QEMU_CFLAGS -c -fPIE -DPIE -o $TMPO $TMPC || return $? + do_libtool --mode=link $LDFLAGS -o $TMPA $TMPL -rpath /usr/local/lib +} + # symbolically link $1 to $2. Portable version of "ln -sf". symlink() { rm -rf "$2" @@ -205,6 +240,9 @@ mingw32="no" gcov="no" gcov_tool="gcov" EXESUF="" +DSOSUF=".so" +LDFLAGS_SHARED="-shared" +modules="no" prefix="/usr/local" mandir="\${prefix}/share/man" datadir="\${prefix}/share" @@ -515,11 +553,10 @@ OpenBSD) Darwin) bsd="yes" darwin="yes" + LDFLAGS_SHARED="-bundle -undefined dynamic_lookup" if [ "$cpu" = "x86_64" ] ; then QEMU_CFLAGS="-arch x86_64 $QEMU_CFLAGS" LDFLAGS="-arch x86_64 $LDFLAGS" - else - QEMU_CFLAGS="-mdynamic-no-pic $QEMU_CFLAGS" fi cocoa="yes" audio_drv_list="coreaudio" @@ -610,6 +647,7 @@ fi if test "$mingw32" = "yes" ; then EXESUF=".exe" + DSOSUF=".dll" QEMU_CFLAGS="-DWIN32_LEAN_AND_MEAN -DWINVER=0x501 $QEMU_CFLAGS" # enable C99/POSIX format strings (needs mingw32-runtime 3.15 or later) QEMU_CFLAGS="-D__USE_MINGW_ANSI_STDIO=1 $QEMU_CFLAGS" @@ -676,6 +714,9 @@ for opt do ;; --disable-debug-info) ;; + --enable-modules) + modules="yes" + ;; --cpu=*) ;; --target-list=*) target_list="$optarg" @@ -1131,7 +1172,8 @@ 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) --enable-debug-info enable debugging information (default) @@ -1428,6 +1470,32 @@ EOF fi fi +# check for broken gcc and libtool in RHEL5 +if test -n "$libtool" -a "$pie" != "no" ; then + cat > $TMPC <<EOF + +void *f(unsigned char *buf, int len); +void *g(unsigned char *buf, int len); + +void * +f(unsigned char *buf, int len) +{ + return (void*)0L; +} + +void * +g(unsigned char *buf, int len) +{ + return f(buf, len); +} + +EOF + if ! libtool_prog; then + echo "Disabling libtool due to broken toolchain support" + libtool= + fi +fi + ########################################## # __sync_fetch_and_and requires at least -march=i486. Many toolchains # use i686 as default anyway, but for those that don't, an explicit @@ -2347,8 +2415,6 @@ EOF curl_libs=`$curlconfig --libs 2>/dev/null` if compile_prog "$curl_cflags" "$curl_libs" ; then curl=yes - libs_tools="$curl_libs $libs_tools" - libs_softmmu="$curl_libs $libs_softmmu" else if test "$curl" = "yes" ; then feature_not_found "curl" "Install libcurl devel" @@ -2386,13 +2452,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` - 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 ########################################## @@ -2503,8 +2588,6 @@ EOF rbd_libs="-lrbd -lrados" if compile_prog "" "$rbd_libs" ; then rbd=yes - libs_tools="$rbd_libs $libs_tools" - libs_softmmu="$rbd_libs $libs_softmmu" else if test "$rbd" = "yes" ; then feature_not_found "rados block device" "Install librbd/ceph devel" @@ -2521,9 +2604,6 @@ if test "$libssh2" != "no" ; then libssh2_cflags=`$pkg_config libssh2 --cflags` libssh2_libs=`$pkg_config libssh2 --libs` libssh2=yes - libs_tools="$libssh2_libs $libs_tools" - libs_softmmu="$libssh2_libs $libs_softmmu" - QEMU_CFLAGS="$QEMU_CFLAGS $libssh2_cflags" else if test "$libssh2" = "yes" ; then error_exit "libssh2 >= $min_libssh2_version required for --enable-libssh2" @@ -2569,8 +2649,6 @@ int main(void) { io_setup(0, NULL); io_set_eventfd(NULL, 0); eventfd(0, 0); retu EOF if compile_prog "" "-laio" ; then linux_aio=yes - libs_softmmu="$libs_softmmu -laio" - libs_tools="$libs_tools -laio" else if test "$linux_aio" = "yes" ; then feature_not_found "linux AIO" "Install libaio devel" @@ -2739,9 +2817,6 @@ if test "$glusterfs" != "no" ; then glusterfs="yes" glusterfs_cflags=`$pkg_config --cflags glusterfs-api` glusterfs_libs=`$pkg_config --libs glusterfs-api` - CFLAGS="$CFLAGS $glusterfs_cflags" - libs_tools="$glusterfs_libs $libs_tools" - libs_softmmu="$glusterfs_libs $libs_softmmu" if $pkg_config --atleast-version=5 glusterfs-api; then glusterfs_discard="yes" fi @@ -3112,11 +3187,9 @@ EOF libiscsi="yes" libiscsi_cflags=$($pkg_config --cflags libiscsi) libiscsi_libs=$($pkg_config --libs libiscsi) - CFLAGS="$CFLAGS $libiscsi_cflags" - LIBS="$LIBS $libiscsi_libs" elif compile_prog "" "-liscsi" ; then libiscsi="yes" - LIBS="$LIBS -liscsi" + libiscsi_libs="-liscsi" else if test "$libiscsi" = "yes" ; then feature_not_found "libiscsi" "Install libiscsi devel" @@ -3717,6 +3790,7 @@ if test "$mingw32" = "yes" ; then fi qemu_confdir=$sysconfdir$confsuffix +qemu_moddir=$libdir$confsuffix qemu_datadir=$datadir$confsuffix qemu_localedir="$datadir/locale" @@ -3807,6 +3881,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`" @@ -3833,6 +3908,7 @@ echo "python $python" if test "$slirp" = "yes" ; then echo "smbd $smbd" fi +echo "module support $modules" echo "host CPU $cpu" echo "host big endian $bigendian" echo "target list $target_list" @@ -3945,6 +4021,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 @@ -4077,6 +4154,12 @@ echo "TARGET_DIRS=$target_list" >> $config_host_mak 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 echo "CONFIG_SDL=y" >> $config_host_mak echo "SDL_CFLAGS=$sdl_cflags" >> $config_host_mak @@ -4148,8 +4231,9 @@ if test "$bswap_h" = "yes" ; then echo "CONFIG_MACHINE_BSWAP_H=y" >> $config_host_mak fi if test "$curl" = "yes" ; then - echo "CONFIG_CURL=y" >> $config_host_mak + echo "CONFIG_CURL=m" >> $config_host_mak echo "CURL_CFLAGS=$curl_cflags" >> $config_host_mak + echo "CURL_LIBS=$curl_libs" >> $config_host_mak fi if test "$brlapi" = "yes" ; then echo "CONFIG_BRLAPI=y" >> $config_host_mak @@ -4238,10 +4322,12 @@ if test "$glx" = "yes" ; then fi if test "$libiscsi" = "yes" ; then - echo "CONFIG_LIBISCSI=y" >> $config_host_mak + echo "CONFIG_LIBISCSI=m" >> $config_host_mak if test "$libiscsi_version" = "1.4.0"; then echo "CONFIG_LIBISCSI_1_4=y" >> $config_host_mak fi + echo "LIBISCSI_CFLAGS=$libiscsi_cflags" >> $config_host_mak + echo "LIBISCSI_LIBS=$libiscsi_libs" >> $config_host_mak fi if test "$libnfs" = "yes" ; then @@ -4266,7 +4352,9 @@ if test "$qom_cast_debug" = "yes" ; then echo "CONFIG_QOM_CAST_DEBUG=y" >> $config_host_mak fi if test "$rbd" = "yes" ; then - echo "CONFIG_RBD=y" >> $config_host_mak + echo "CONFIG_RBD=m" >> $config_host_mak + echo "RBD_CFLAGS=$rbd_cflags" >> $config_host_mak + echo "RBD_LIBS=$rbd_libs" >> $config_host_mak fi echo "CONFIG_COROUTINE_BACKEND=$coroutine" >> $config_host_mak @@ -4309,7 +4397,9 @@ if test "$getauxval" = "yes" ; then fi if test "$glusterfs" = "yes" ; then - echo "CONFIG_GLUSTERFS=y" >> $config_host_mak + echo "CONFIG_GLUSTERFS=m" >> $config_host_mak + echo "GLUSTERFS_CFLAGS=$glusterfs_cflags" >> $config_host_mak + echo "GLUSTERFS_LIBS=$glusterfs_libs" >> $config_host_mak fi if test "$glusterfs_discard" = "yes" ; then @@ -4321,7 +4411,9 @@ if test "$glusterfs_zerofill" = "yes" ; then fi if test "$libssh2" = "yes" ; then - echo "CONFIG_LIBSSH2=y" >> $config_host_mak + echo "CONFIG_LIBSSH2=m" >> $config_host_mak + echo "LIBSSH2_CFLAGS=$libssh2_cflags" >> $config_host_mak + echo "LIBSSH2_LIBS=$libssh2_libs" >> $config_host_mak fi if test "$virtio_blk_data_plane" = "yes" ; then @@ -4452,6 +4544,8 @@ echo "LIBTOOLFLAGS=$LIBTOOLFLAGS" >> $config_host_mak echo "LIBS+=$LIBS" >> $config_host_mak echo "LIBS_TOOLS+=$libs_tools" >> $config_host_mak echo "EXESUF=$EXESUF" >> $config_host_mak +echo "DSOSUF=$DSOSUF" >> $config_host_mak +echo "LDFLAGS_SHARED=$LDFLAGS_SHARED" >> $config_host_mak echo "LIBS_QGA+=$libs_qga" >> $config_host_mak echo "POD2MAN=$POD2MAN" >> $config_host_mak echo "TRANSLATE_OPT_CFLAGS=$TRANSLATE_OPT_CFLAGS" >> $config_host_mak diff --git a/include/qemu-common.h b/include/qemu-common.h index b0e34b2..46c2bea 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -352,7 +352,7 @@ char *qemu_find_file(int type, const char *name); /* OS specific functions */ void os_setup_early_signal_handling(void); -char *os_find_datadir(const char *argv0); +char *os_find_datadir(void); void os_parse_cmd_args(int index, const char *optarg); void os_pidfile_error(void); diff --git a/include/qemu/module.h b/include/qemu/module.h index c4ccd57..72d9498 100644 --- a/include/qemu/module.h +++ b/include/qemu/module.h @@ -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); diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index eac7172..ffb2966 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -215,6 +215,15 @@ bool fips_get_state(void); */ char *qemu_get_local_state_pathname(const char *relative_pathname); +/* Find program directory, and save it for later usage with + * qemu_get_exec_dir(). + * Try OS specific API first, if not working, parse from argv0. */ +void qemu_init_exec_dir(const char *argv0); + +/* Get the saved exec dir. + * Caller needs to release the returned string by g_free() */ +char *qemu_get_exec_dir(void); + /** * qemu_getauxval: * @type: the auxiliary vector key to lookup diff --git a/module-common.c b/module-common.c new file mode 100644 index 0000000..50c6750 --- /dev/null +++ b/module-common.c @@ -0,0 +1,10 @@ +#include "config-host.h" +#include "qemu/module.h" + +void qemu_module_dummy(void) +{ +} + +void DSO_STAMP_FUN(void) +{ +} @@ -84,46 +84,17 @@ void os_setup_signal_handling(void) running from the build tree this will be "$bindir/../pc-bios". */ #define SHARE_SUFFIX "/share/qemu" #define BUILD_SUFFIX "/pc-bios" -char *os_find_datadir(const char *argv0) +char *os_find_datadir(void) { - char *dir; - char *p = NULL; + char *dir, *exec_dir; char *res; - char buf[PATH_MAX]; size_t max_len; -#if defined(__linux__) - { - int len; - len = readlink("/proc/self/exe", buf, sizeof(buf) - 1); - if (len > 0) { - buf[len] = 0; - p = buf; - } - } -#elif defined(__FreeBSD__) - { - static int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; - size_t len = sizeof(buf) - 1; - - *buf = '\0'; - if (!sysctl(mib, ARRAY_SIZE(mib), buf, &len, NULL, 0) && - *buf) { - buf[sizeof(buf) - 1] = '\0'; - p = buf; - } - } -#endif - /* If we don't have any way of figuring out the actual executable - location then try argv[0]. */ - if (!p) { - p = realpath(argv0, buf); - if (!p) { - return NULL; - } + exec_dir = qemu_get_exec_dir(); + if (exec_dir == NULL) { + return NULL; } - dir = dirname(p); - dir = dirname(dir); + dir = dirname(exec_dir); max_len = strlen(dir) + MAX(strlen(SHARE_SUFFIX), strlen(BUILD_SUFFIX)) + 1; @@ -137,6 +108,7 @@ char *os_find_datadir(const char *argv0) } } + g_free(exec_dir); return res; } #undef SHARE_SUFFIX @@ -84,26 +84,9 @@ void os_setup_early_signal_handling(void) } /* Look for support files in the same directory as the executable. */ -char *os_find_datadir(const char *argv0) +char *os_find_datadir(void) { - char *p; - char buf[MAX_PATH]; - DWORD len; - - len = GetModuleFileName(NULL, buf, sizeof(buf) - 1); - if (len == 0) { - return NULL; - } - - buf[len] = 0; - p = buf + len - 1; - while (p != buf && *p != '\\') - p--; - *p = 0; - if (access(buf, R_OK) == 0) { - return g_strdup(buf); - } - return NULL; + return qemu_get_exec_dir(); } void os_set_line_buffering(void) @@ -2719,6 +2719,7 @@ int main(int argc, char **argv) #endif error_set_progname(argv[0]); + qemu_init_exec_dir(argv[0]); qemu_init_main_loop(); bdrv_init(); @@ -381,6 +381,7 @@ int main(int argc, char **argv) #endif progname = basename(argv[0]); + qemu_init_exec_dir(argv[0]); while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) { switch (c) { @@ -376,6 +376,7 @@ int main(int argc, char **argv) memset(&sa_sigterm, 0, sizeof(sa_sigterm)); sa_sigterm.sa_handler = termsig_handler; sigaction(SIGTERM, &sa_sigterm, NULL); + qemu_init_exec_dir(argv[0]); while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { switch (ch) { @@ -22,8 +22,15 @@ QEMU_DGFLAGS += -MMD -MP -MT $@ -MF $(*D)/$(*F).d # Same as -I$(SRC_PATH) -I., but for the nested source/object directories QEMU_INCLUDES += -I$(<D) -I$(@D) +maybe-add = $(filter-out $1, $2) $1 +extract-libs = $(strip $(sort $(foreach o,$1,$($o-libs)) \ + $(foreach o,$(call expand-objs,$1),$($o-libs)))) +expand-objs = $(strip $(sort $(filter %.o,$1)) \ + $(foreach o,$(filter %.mo,$1),$($o-objs)) \ + $(filter-out %.o %.mo,$1)) + %.o: %.c - $(call quiet-command,$(CC) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," CC $(TARGET_DIR)$@") + $(call quiet-command,$(CC) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) $($@-cflags) -c -o $@ $<," CC $(TARGET_DIR)$@") %.o: %.rc $(call quiet-command,$(WINDRES) -I. -o $@ $<," RC $(TARGET_DIR)$@") @@ -33,8 +40,8 @@ LINKPROG = $(or $(CXX),$(CC)) ifeq ($(LIBTOOL),) LINK = $(call quiet-command,$(LINKPROG) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ \ - $(sort $(filter %.o, $1)) $(filter-out %.o, $1) $(version-obj-y) \ - $(LIBS)," LINK $(TARGET_DIR)$@") + $(call expand-objs,$1) $(version-obj-y) \ + $(call extract-libs,$1) $(LIBS)," LINK $(TARGET_DIR)$@") else LIBTOOL += $(if $(V),,--quiet) %.lo: %.c @@ -45,12 +52,12 @@ LIBTOOL += $(if $(V),,--quiet) $(call quiet-command,$(LIBTOOL) --mode=compile --tag=CC dtrace -o $@ -G -s $<, " lt GEN $(TARGET_DIR)$@") LINK = $(call quiet-command,\ - $(if $(filter %.lo %.la,$^),$(LIBTOOL) --mode=link --tag=CC \ + $(if $(filter %.lo %.la,$1),$(LIBTOOL) --mode=link --tag=CC \ )$(LINKPROG) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ \ - $(sort $(filter %.o, $1)) $(filter-out %.o, $1) \ - $(if $(filter %.lo %.la,$^),$(version-lobj-y),$(version-obj-y)) \ - $(if $(filter %.lo %.la,$^),$(LIBTOOLFLAGS)) \ - $(LIBS),$(if $(filter %.lo %.la,$^),"lt LINK ", " LINK ")"$(TARGET_DIR)$@") + $(call expand-objs,$1) \ + $(if $(filter %.lo %.la,$1),$(version-lobj-y),$(version-obj-y)) \ + $(if $(filter %.lo %.la,$1),$(LIBTOOLFLAGS)) \ + $(call extract-libs,$1) $(LIBS),$(if $(filter %.lo %.la,$1),"lt LINK ", " LINK ")"$(TARGET_DIR)$@") endif %.asm: %.S @@ -71,6 +78,16 @@ endif %.o: %.dtrace $(call quiet-command,dtrace -o $@ -G -s $<, " GEN $(TARGET_DIR)$@") +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: + %$(EXESUF): %.o $(call LINK,$^) @@ -146,9 +163,6 @@ clean: clean-timestamp # magic to descend into other directories -obj := . -old-nested-dirs := - define push-var $(eval save-$2-$1 = $(value $1)) $(eval $1 :=) @@ -160,11 +174,27 @@ $(eval $1 = $(value save-$2-$1) $$(subdir-$2-$1)) $(eval save-$2-$1 :=) endef +define fix-obj-vars +$(foreach v,$($1), \ + $(if $($v-cflags), \ + $(eval $2$v-cflags := $($v-cflags)) \ + $(eval $v-cflags := )) \ + $(if $($v-libs), \ + $(eval $2$v-libs := $($v-libs)) \ + $(eval $v-libs := )) \ + $(if $($v-objs), \ + $(eval $2$v-objs := $(addprefix $2,$($v-objs))) \ + $(eval $v-objs := ))) +endef + define unnest-dir $(foreach var,$(nested-vars),$(call push-var,$(var),$1/)) -$(eval obj := $(obj)/$1) +$(eval obj-parent-$1 := $(obj)) +$(eval obj := $(if $(obj),$(obj)/$1,$1)) $(eval include $(SRC_PATH)/$1/Makefile.objs) -$(eval obj := $(patsubst %/$1,%,$(obj))) +$(foreach v,$(nested-vars),$(call fix-obj-vars,$v,$(if $(obj),$(obj)/))) +$(eval obj := $(obj-parent-$1)) +$(eval obj-parent-$1 := ) $(foreach var,$(nested-vars),$(call pop-var,$(var),$1/)) endef @@ -178,10 +208,34 @@ $(if $(nested-dirs), $(call unnest-vars-1)) endef +define process-modules +$(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) + $(eval $o:; $$(call quiet-command,touch $$@," GEN $$(TARGET_DIR)$$@")) + $(if $(CONFIG_MODULES),$(eval modules: $(patsubst %.mo,%$(DSOSUF),$o)))) \ +$(eval modules-objs-m := $(sort $(modules-objs-m))) +$(foreach o,$(modules-objs-m), \ + $(if $(CONFIG_MODULES),$(eval $o-cflags := $(call maybe-add, $(DSO_CFLAGS), $($o-cflags))))) +$(eval $(patsubst %-m,%-$(call lnot,$(CONFIG_MODULES)),$1) += $($1)) +endef + define unnest-vars +$(eval obj := $1) +$(eval nested-vars := $2) +$(eval old-nested-dirs := ) $(call unnest-vars-1) +$(if $1,$(foreach v,$(nested-vars),$(eval \ + $v := $(addprefix $1/,$($v))))) $(foreach var,$(nested-vars),$(eval $(var) := $(filter-out %/, $($(var))))) $(shell mkdir -p $(sort $(foreach var,$(nested-vars),$(dir $($(var)))))) $(foreach var,$(nested-vars), $(eval \ -include $(addsuffix *.d, $(sort $(dir $($(var))))))) +$(foreach v,$(filter %-m,$(nested-vars)), \ + $(call process-modules,$v)) endef diff --git a/scripts/create_config b/scripts/create_config index 06f5316..546f889 100755 --- a/scripts/create_config +++ b/scripts/create_config @@ -108,6 +108,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..42bc373 100644 --- a/util/module.c +++ b/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 +} diff --git a/util/oslib-posix.c b/util/oslib-posix.c index d5dca47..c2eeb4f 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -57,6 +57,7 @@ extern int daemon(int, int); #include "trace.h" #include "qemu/sockets.h" #include <sys/mman.h> +#include <libgen.h> #ifdef CONFIG_LINUX #include <sys/syscall.h> @@ -274,3 +275,56 @@ void qemu_set_tty_echo(int fd, bool echo) tcsetattr(fd, TCSANOW, &tty); } + +static char exec_dir[PATH_MAX]; + +void qemu_init_exec_dir(const char *argv0) +{ + char *dir; + char *p = NULL; + char buf[PATH_MAX]; + + assert(!exec_dir[0]); + +#if defined(__linux__) + { + int len; + len = readlink("/proc/self/exe", buf, sizeof(buf) - 1); + if (len > 0) { + buf[len] = 0; + p = buf; + } + } +#elif defined(__FreeBSD__) + { + static int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; + size_t len = sizeof(buf) - 1; + + *buf = '\0'; + if (!sysctl(mib, ARRAY_SIZE(mib), buf, &len, NULL, 0) && + *buf) { + buf[sizeof(buf) - 1] = '\0'; + p = buf; + } + } +#endif + /* If we don't have any way of figuring out the actual executable + location then try argv[0]. */ + if (!p) { + if (!argv0) { + return; + } + p = realpath(argv0, buf); + if (!p) { + return; + } + } + dir = dirname(p); + + pstrcpy(exec_dir, sizeof(exec_dir), dir); +} + +char *qemu_get_exec_dir(void) +{ + return g_strdup(exec_dir); +} diff --git a/util/oslib-win32.c b/util/oslib-win32.c index 50be044..93f7d35 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -208,3 +208,33 @@ void qemu_set_tty_echo(int fd, bool echo) dwMode & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT)); } } + +static char exec_dir[PATH_MAX]; + +void qemu_init_exec_dir(const char *argv0) +{ + + char *p; + char buf[MAX_PATH]; + DWORD len; + + len = GetModuleFileName(NULL, buf, sizeof(buf) - 1); + if (len == 0) { + return; + } + + buf[len] = 0; + p = buf + len - 1; + while (p != buf && *p != '\\') { + p--; + } + *p = 0; + if (access(buf, R_OK) == 0) { + pstrcpy(exec_dir, sizeof(exec_dir), buf); + } +} + +char *qemu_get_exec_dir(void) +{ + return g_strdup(exec_dir); +} @@ -2827,6 +2827,7 @@ int main(int argc, char **argv, char **envp) atexit(qemu_run_exit_notifiers); error_set_progname(argv[0]); + qemu_init_exec_dir(argv[0]); g_mem_set_vtable(&mem_trace); if (!g_thread_supported()) { @@ -3865,7 +3866,7 @@ int main(int argc, char **argv, char **envp) /* If no data_dir is specified then try to find it relative to the executable path. */ if (data_dir_idx < ARRAY_SIZE(data_dir)) { - data_dir[data_dir_idx] = os_find_datadir(argv[0]); + data_dir[data_dir_idx] = os_find_datadir(); if (data_dir[data_dir_idx] != NULL) { data_dir_idx++; } |