diff --git a/src/acconfig.h b/src/acconfig.h
index b2d63be008d4b50bbc1dbf3b1dd252d098771440..a02047153dfb0892b3545a5a198a21aa27956317 100644
--- a/src/acconfig.h
+++ b/src/acconfig.h
@@ -73,6 +73,9 @@
 /* Define this to enable the internal Pike security system */
 #undef PIKE_SECURITY
 
+/* Define this to simulate dynamic module loading with static modules. */
+#undef USE_SEMIDYNAMIC_MODULES
+
 /* Define this to enable the internal bignum conversion */
 #undef AUTO_BIGNUM
 
diff --git a/src/configure.in b/src/configure.in
index 943d3391fb7d8d83f69f00b860e7f6f1ac327e6d..0874ffdf070e3310484bbf54c449b372a2e7d580 100644
--- a/src/configure.in
+++ b/src/configure.in
@@ -8514,11 +8514,12 @@ if test x$with_dynamic_modules = xyes ; then
   POST_MODULE_BUILD_TYPE=dynamic
   STATIC_POST_MODULES_LINKOPTS=
 else
-  dmmsrc="modules/static_module_makefile.in"
+  AC_DEFINE(USE_SEMIDYNAMIC_MODULES)
+  dmmsrc="modules/semidynamic_module_makefile.in"
   PIKE_LIB="libpike.a"
   MODULE_O=module.o
   BUILD_PIKE=tpike$EXEEXT
-  POST_MODULE_BUILD_TYPE=static
+  POST_MODULE_BUILD_TYPE=dynamic
   STATIC_POST_MODULES_LINKOPTS=post_modules/linker_options
   DYNAMIC_MODULE_FLAGS=""
 fi
diff --git a/src/dynamic_load.c b/src/dynamic_load.c
index c4e51d93c176af67d38a4b44bc16bd98b09bdacb..72f88fd67c4f41a00ce75dd3217ea942749dff2e 100644
--- a/src/dynamic_load.c
+++ b/src/dynamic_load.c
@@ -36,38 +36,37 @@
 #include <string.h>
 #endif /* HAVE_STRING_H */
 
-#if !defined(HAVE_DLOPEN)
+#if !defined(HAVE_DLOPEN) || defined(USE_SEMIDYNAMIC_MODULES)
 
-#if defined(HAVE_DLD_LINK) && defined(HAVE_DLD_GET_FUNC)
+#ifdef USE_SEMIDYNAMIC_MODULES
+#undef HAVE_DLOPEN
+#define USE_STATIC_MODULES
+#define HAVE_SOME_DLOPEN
+#define EMULATE_DLOPEN
+#define USE_DYNAMIC_MODULES
+#elif defined(HAVE_DLD_LINK) && defined(HAVE_DLD_GET_FUNC)
 #define USE_DLD
 #define HAVE_SOME_DLOPEN
 #define EMULATE_DLOPEN
-#else
-#if defined(HAVE_SHL_LOAD) && defined(HAVE_DL_H)
+#elif defined(HAVE_SHL_LOAD) && defined(HAVE_DL_H)
 #define USE_HPUX_DL
 #define HAVE_SOME_DLOPEN
 #define EMULATE_DLOPEN
-#else
-
-#ifdef USE_DLL
-#if defined(HAVE_LOADLIBRARY) && defined(HAVE_FREELIBRARY) && \
-    defined(HAVE_GETPROCADDRESS) && defined(HAVE_WINBASE_H)
+#elif defined(USE_DLL) && \
+      defined(HAVE_LOADLIBRARY) && defined(HAVE_FREELIBRARY) && \
+      defined(HAVE_GETPROCADDRESS) && defined(HAVE_WINBASE_H)
 #define USE_LOADLIBRARY
 #define HAVE_SOME_DLOPEN
 #define EMULATE_DLOPEN
-#endif
-#endif
-
-#ifdef HAVE_MACH_O_DYLD_H
+#elif defined(HAVE_MACH_O_DYLD_H)
 /* MacOS X... */
 #define USE_DYLD
 #define HAVE_SOME_DLOPEN
 #define EMULATE_DLOPEN
-#endif /* HAVE_MACH_O_DYLD_H */
-
-#endif
 #endif
+
 #else
+/* HAVE_DLOPEN */
 #define HAVE_SOME_DLOPEN
 #endif
 
@@ -76,7 +75,54 @@
 
 typedef void (*modfun)(void);
 
-#ifdef USE_LOADLIBRARY
+#ifdef USE_STATIC_MODULES
+
+static void *dlopen(const char *foo, int how)
+{
+  struct pike_string *s = low_read_file(foo);
+  char *name, *end;
+  void *res;
+
+  if (!s) return NULL;
+  if (strncmp(s->str, "PMODULE=\"", 9)) {
+    free_string(s);
+    return NULL;
+  }
+  name = s->str + 9;
+  if (!(end = strchr(name, '\"'))) {
+    free_string(s);
+    return NULL;
+  }
+
+  res = find_semidynamic_module(name, end - name);
+  free_string(s);
+  return res;
+}
+
+static char *dlerror(void)
+{
+  return "Invalid dynamic module.";
+}
+
+static void *dlsym(void *module, char *function)
+{
+  if (!strcmp(function, "pike_module_init"))
+    return get_semidynamic_init_fun(module);
+  if (!strcmp(function, "pike_module_exit"))
+    return get_semidynamic_exit_fun(module);
+  return NULL;
+}
+
+static int dlinit(void)
+{
+  return 1;
+}
+
+static void dlclose(void *module)
+{
+}
+
+#elif defined(USE_LOADLIBRARY)
 #include <windows.h>
 
 static TCHAR *convert_string(const char *str, ptrdiff_t len)
diff --git a/src/module.c b/src/module.c
index 6b19542d7d33da29766c9f6c0cdb34e524735003..1f6f02293e5593621bc16ab05b4cc136ff51e1bc 100644
--- a/src/module.c
+++ b/src/module.c
@@ -373,16 +373,47 @@ struct static_module
   char *name;
   modfun init;
   modfun exit;
+  int semidynamic;
 };
 
 static const struct static_module module_list[] = {
-  { "Builtin", init_builtin_modules, exit_builtin_modules }
+  { "Builtin", init_builtin_modules, exit_builtin_modules, 0 }
 #include "modules/modlist.h"
 #ifndef PRE_PIKE
 #include "post_modules/modlist.h"
 #endif
 };
 
+/* The follwing are used to simulate dlopen() et al. */
+
+const struct static_module *find_semidynamic_module(const char *name, int namelen)
+{
+  unsigned int e;
+  for(e=0;e<NELEM(module_list);e++) {
+    if (module_list[e].semidynamic &&
+	!strncmp(module_list[e].name, name, namelen) &&
+	!module_list[e].name[namelen]) {
+      TRACE((stderr, "Found semidynamic module #%d: \"%s\"...\n",
+	     e, module_list[e].name));
+      return module_list+e;
+    }
+  }
+  return NULL;
+}
+
+void *get_semidynamic_init_fun(const struct static_module *sm)
+{
+  if (!sm) return NULL;
+  return sm->init;
+}
+
+void *get_semidynamic_exit_fun(const struct static_module *sm)
+{
+  if (!sm) return NULL;
+  return sm->exit;
+}
+
+
 /*! @decl object _static_modules
  *!
  *! This is an object containing the classes for all static
@@ -419,6 +450,7 @@ void init_modules(void)
       start_new_program();
       p = Pike_compiler->new_program;
     }
+    if (module_list[e].semidynamic) continue;
     if(SETJMP(recovery)) {
       /* FIXME: We could loop here until we find p. */
       free_program(end_program());
@@ -502,6 +534,7 @@ void exit_modules(void)
 
   for(e=NELEM(module_list)-1;e>=0;e--)
   {
+    if (module_list[e].semidynamic) continue;
     if(SETJMP(recovery))
       call_handle_error();
     else {
diff --git a/src/module.h b/src/module.h
index da04937947fb83453e1b00a273a10d31a0eafc24..97afb8680df45f9a371db99feb2ffe6e912405b8 100644
--- a/src/module.h
+++ b/src/module.h
@@ -19,6 +19,10 @@
 
 /* Prototypes begin here */
 struct static_module;
+const struct static_module *find_semidynamic_module(const char *name,
+						    int namelen);
+void *get_semidynamic_init_fun(const struct static_module *sm);
+void *get_semidynamic_exit_fun(const struct static_module *sm);
 void init_modules(void);
 void exit_modules(void);
 PIKE_MODULE_INIT;
diff --git a/src/modules/Makefile.in b/src/modules/Makefile.in
index e2ecccb801ac2956705cdd74e35003dffcceff1c..1d32c62fb25e87c5fc7eb928de5912272cf06b74 100644
--- a/src/modules/Makefile.in
+++ b/src/modules/Makefile.in
@@ -49,10 +49,10 @@ modlist.h: $(MODULE_SEGMENTS)
 	@echo 'Updating $(MY_DIR)/modlist.h'; \
 	cat $(MODULE_SEGMENTS) </dev/null >modlist.h
 
-modlist_headers.h: $(MODULE_SEGMENTS)
+modlist_headers.h: $(MODULE_SEGMENTS) Makefile
 	@echo 'Updating $(MY_DIR)/modlist_headers.h'; \
 	cat $(MODULE_SEGMENTS) </dev/null | \
-	sed -e 's/^.*{.*,\(.*\),\(.*\)}.*$$/void \1(void),\2(void);/' >modlist_headers.h
+	sed -e 's/^.*{.*,\(.*\),\(.*\),\(.*\)}.*$$/void \1(void),\2(void);/' >modlist_headers.h
 
 linker_options: $(MODULE_LINKOPTS)
 	@echo 'Updating $(MY_DIR)/linker_options'; \
diff --git a/src/modules/semidynamic_module_makefile.in b/src/modules/semidynamic_module_makefile.in
new file mode 100644
index 0000000000000000000000000000000000000000..299d10d636777fce0bbdc2b1abb2468772650477
--- /dev/null
+++ b/src/modules/semidynamic_module_makefile.in
@@ -0,0 +1,122 @@
+LC_REQ=@LC_REQ@
+
+UNIQUE_MODNAME=$(MODNAME)
+
+LINKAGE_CPPFLAGS=-Dpike_module_init=pike_module_$(UNIQUE_MODNAME)_init -Dpike_module_exit=pike_module_$(UNIQUE_MODNAME)_exit
+LINKAGE_CFLAGS=
+
+MODULE_PROGRAM=.$(MODULE_WRAPPER_PREFIX)$(MODNAME)
+MODULE_TARGET=$(TMP_MODULE_BASE)/$(MODDIR)$(MODULE_WRAPPER_PREFIX)$(MODNAME).so
+
+# The reason for this is that we can't use targets directly to specify
+# the build type; many module makefiles depend on being able to
+# override the default target.
+all:
+	@case "x$$MODULE_BUILD_TYPE" in \
+	  xstatic) $(MAKE) $(MAKE_FLAGS) static;; \
+	  xdynamic) $(MAKE) $(MAKE_FLAGS) dynamic;; \
+	  x) $(MAKE) $(MAKE_FLAGS) static dynamic;; \
+	  *) \
+	    echo "Invalid MODULE_BUILD_TYPE: $$MODULE_BUILD_TYPE" >&2; \
+	    exit 1;; \
+	esac
+
+@common_module_makefile@
+
+build_type:
+	@echo semidynamic > build_type
+
+static: Makefile $(DUMMY) module-preamble module-main
+	@:
+
+# Depend on $(DUMMY) here too for the sake of post modules, where the
+# static target never gets called.
+dynamic: $(DUMMY) module-main
+	@:
+
+linker_options: Makefile module.a $(MODULE_ARCHIVES)
+	@linkopts="";\
+	for a in "`pwd`/module.a" $(MODULE_ARCHIVES); do \
+	  if test "x$$a" = "x"; then :; else \
+	    case "$$a" in \
+	      /*) \
+		linkopts="$$linkopts $$a" \
+	      ;; \
+	      *) \
+		linkopts="$$linkopts `pwd`/$$a" \
+	      ;; \
+	    esac; \
+	  fi; \
+	done; \
+	linkopts="$$linkopts `echo '$(MODULE_LDFLAGS)' | sed -e 's@$(BASE_LDFLAGS)@@'`"; \
+	echo "echo $$linkopts > linker_options"; \
+	echo $$linkopts > linker_options
+
+modlist_segment: Makefile
+	@echo >modlist_segment " ,{ \"$(MODULE_PROGRAM)\", pike_module_$(UNIQUE_MODNAME)_init, pike_module_$(UNIQUE_MODNAME)_exit, 1 } "
+
+# Can't depend on $(SRCDIR)/$(CONFIG_HEADERS).in since
+# $(CONFIG_HEADERS) isn't always used.
+Makefile: $(MODULE_BASE)/dynamic_module_makefile $(SRCDIR)/Makefile.in $(SRCDIR)/dependencies make_variables config.status
+	CONFIG_FILES=Makefile CONFIG_HEADERS="$(CONFIG_HEADERS)" ./config.status
+	touch remake
+	@echo "Run make again" >&2
+	@exit 1
+
+$(MODULE_TARGET): module.so module.a
+	@if test "x$(OBJS)" = "x" ; then \
+	  exit 0; \
+	fi; \
+	if test "x$(PIKE_EXTERNAL_MODULE)" = "x" ; then \
+	  $(TMP_BINDIR)/install_module module.so $(MODULE_TARGET) && \
+	  if [ -f $(MODNAME).pdb ]; then \
+	    cp $(MODNAME).pdb `echo "$(MODULE_TARGET)" | sed -e 's,[^/]*$$,,'`; \
+	  else :; fi; \
+	fi
+
+module.a: $(OBJS)
+	-rm -f module.a
+	$(AR) cq module.a $(OBJS)
+	-@RANLIB@ module.a
+	if test -f linker_options ; then touch linker_options ; else :; fi
+
+module.so:
+	@echo "PMODULE=\"$(MODULE_PROGRAM)\"" >module.so
+
+$(OBJS): propagated_variables
+
+#
+# install a standard module with optional c component in the system module path
+#
+install: $(MODULE_INSTALL)
+	@if test "x$(OBJS)" != "x" ; then \
+	  $(TMP_BINDIR)/install_module module.so $(SYSTEM_MODULE_PATH)/$(MODDIR)$(MODULE_WRAPPER_PREFIX)$(MODNAME).so && \
+	fi; \
+	if test "x$(MODULE_PMOD_IN)" != "x"; then \
+	  $(TMP_BINDIR)/install_module module.pmod $(SYSTEM_MODULE_PATH)/$(MODDIR)$(MODNAME).pmod ;\
+	fi;
+
+#
+# install the module in LOCAL_MODULE_PATH, creating it if it doesn't already exist.
+#
+local_install: $(MODULE_INSTALL)
+	if test ! -d "$(LOCAL_MODULE_PATH)" ; then \
+	  mkdir -p $(LOCAL_MODULE_PATH) ; \
+	fi; if test "x$(OBJS)" != "x" ; then \
+	  $(TMP_BINDIR)/install_module module.so $(LOCAL_MODULE_PATH)/$(MODDIR)$(MODULE_WRAPPER_PREFIX)$(MODNAME).so && \
+	fi; \
+	if test "x$(MODULE_PMOD_IN)" != "x"; then \
+	  $(TMP_BINDIR)/install_module module.pmod $(LOCAL_MODULE_PATH)/$(MODDIR)$(MODNAME).pmod ;\
+	fi
+
+dump_module: install
+	-rm -f dumpmodule.log
+	args=$${args:-"--log-file --update-only=dumpversion --report-failed"}; \
+	$(FINAL_PIKE) -x dump $$args \
+	--recursive "$(SYSTEM_MODULE_PATH)/$(MODDIR)$(MODNAME).pmod"
+
+dump_local_module: install
+	-rm -f dumpmodule.log
+	args=$${args:-"--log-file --update-only=dumpversion --report-failed"}; \
+	$(FINAL_PIKE) -x dump $$args \
+	--recursive "$(LOCAL_MODULE_PATH)/$(MODDIR)$(MODNAME).pmod"
diff --git a/src/modules/static_module_makefile.in b/src/modules/static_module_makefile.in
index b14a8f27fc489c2d515c39a6a910141116718374..88b79745e5c08399c6a495e792871adc82251f40 100644
--- a/src/modules/static_module_makefile.in
+++ b/src/modules/static_module_makefile.in
@@ -49,7 +49,7 @@ linker_options: Makefile $(MODULE_ARCHIVES)
 	echo $$linkopts > linker_options
 
 modlist_segment: Makefile
-	@echo >modlist_segment " ,{ \"$(MODULE_PROGRAM)\", pike_module_$(UNIQUE_MODNAME)_init, pike_module_$(UNIQUE_MODNAME)_exit } "
+	@echo >modlist_segment " ,{ \"$(MODULE_PROGRAM)\", pike_module_$(UNIQUE_MODNAME)_init, pike_module_$(UNIQUE_MODNAME)_exit, 0 } "
 
 
 # Can't depend on $(SRC_DIR)/$(CONFIG_HEADERS).in since
diff --git a/src/object.c b/src/object.c
index 3495a012a0e52c14e2f8fb665ea07f579ff76ac5..3e79e23d3876a6ade6fbdf81b4e6003b6a11fbfe 100644
--- a/src/object.c
+++ b/src/object.c
@@ -457,7 +457,7 @@ struct object *decode_value_clone_object(struct svalue *prog)
   return o;
 }
 
-static struct pike_string *low_read_file(const char *file)
+struct pike_string *low_read_file(const char *file)
 {
   struct pike_string *s;
   PIKE_OFF_T len;
diff --git a/src/object.h b/src/object.h
index 2d214113430a20586efab4d6dbe93233b4a485aa..83108e2d265d976798cf886511c3e30b87c18b33 100644
--- a/src/object.h
+++ b/src/object.h
@@ -92,6 +92,7 @@ PMOD_EXPORT struct object *parent_clone_object(struct program *p,
 					       int args);
 PMOD_EXPORT struct object *clone_object_from_object(struct object *o, int args);
 struct object *decode_value_clone_object(struct svalue *prog);
+struct pike_string *low_read_file(const char *file);
 PMOD_EXPORT struct object *get_master(void);
 PMOD_EXPORT struct object *debug_master(void);
 struct destroy_called_mark;