windows DLL support

Fergus Henderson fjh at cs.mu.oz.au
Fri Aug 1 20:38:44 AEST 1997


Hi,

I managed to get DLLs on Windows working.  Well, it works for Hello World
at least.  And Hello World is just 3k stripped :->.

This is not ready to commit.  I will post a cleaned up version of this
sometime.  But just in case I get run over by a bus in the near future...

--------------------

Implement support for DLLs on Windows.
The changes consisted of

1.  Adding `Makefile.DLLs', which is a file I wrote contains
    GNU Make rules for building DLLs.  Making sure that
    the various Mmakefiles include that one.

2.  Making sure we #include the "libfoo_dll.h" header file
    generated by `Makefile.DLLs' before every place that
    references a global variable.

3.  Making sure that code which references the DLLs is
    compiled with -Dlibfoo_USE_DLL.  (XXX only done partially.)
 
4.  Changing various places to explicitly register the data
    segment as a root set with the conservative garbage collector.
    This was necessary to get GC working with DLLs under Windows
    (there doesn't seem to be any way of automatically finding
    the data segments of DLLs under win32).

5.  Changing the way names are mangled to ensure that the assembler
    names always contain a leading underscore, since `dlltool'
    on gnu-win32 requires this.

boehm_gc/Makefile:
	Add dependency $(OBJS) : libgc_dll.h. XXX should be conditional
	Include Mmakefile.DLLs. XXX should be conditional
	Add test_dll target.
	XXX should add a `dll' target.

boehm_gc/gc.h:
	For __CYGWIN32__, #include "libgc_dll.h".
	This is necessary to handle global variables in DLLs
	on Windows.  XXX needs documentation.
	For __CYGWIN32__, define GC_INIT() to add the
	data segment of the current DLL or executable as
	a new root set.

library/Mmakefile:
	Add stuff needed for building DLLs.

runtime/wrapper.mod:
library/io.m:
	Add call to GC_INIT() to register the root set for
	libmer.dll and libmercury.dll respectively.
	In wrapper.mod, move the call to GC_is_visible
	_after_ the calls to GC_INIT().

runtime/Mmakefile:
	Add stuff needed for building DLLs.

runtime/init.h:
runtime/imp.h:
runtime/dummy.c:
runtime/label.c:
runtime/memory.c:
runtime/table.c:
	For __CYGWIN32__, make sure we #include "libmer_dll.h".
	before defining global variables.
	This is necessary to handle global variables in DLLs
	on Windows.  XXX needs documentation.
	XXX some are unconditional, e.g. table.c

runtime/goto.h:
	Change the way names are mangled to ensure that the assembler
	names always contain a leading underscore, since `dlltool'
	on gnu-win32 requires this.  XXX need to modify
	util/mdemangle.c and prof/demangle.m.

util/mkinit.c:
	For __CYGIN32__, when using DLLs, make sure we initialize the
	_impure_ptr of the DLLs that we're using.

library/Mmake:
runtime/Mmake:
samples/Mmake:
	Compile with -Dlibfoo_USE_DLL options.
	Link with -lfoo_dll rather than -lfoo.

cvs diff: Diffing .
Index: boehm_gc/Makefile
===================================================================
RCS file: /home/staff/zs/imp/mercury/boehm_gc/Makefile,v
retrieving revision 1.29
diff -u -w -r1.29 Makefile
--- Makefile	1997/02/09 05:47:24	1.29
+++ Makefile	1997/08/01 06:41:28
@@ -180,7 +180,7 @@
 	make -f PCR-Makefile
 
 $(OBJS) test.o dyn_load.o dyn_load_sunos53.o: $(srcdir)/gc_priv.h $(srcdir)/gc_hdrs.h $(srcdir)/gc.h \
-    $(srcdir)/config.h $(srcdir)/gc_typed.h Makefile
+    $(srcdir)/config.h $(srcdir)/gc_typed.h $(srcdir)/libgc_dll.h Makefile
 # The dependency on Makefile is needed.  Changing
 # options such as -DSILENT affects the size of GC_arrays,
 # invalidating all .o files that rely on gc_priv.h
@@ -375,6 +375,15 @@
 gctest_irix_dyn_link: test.o libirixgc.so
 	$(CC) -L$(ABSDIR) -o gctest_irix_dyn_link test.o -lirixgc
 
+test_dll.o: test.c libgc_globals.h
+	$(CC) $(CFLAGS) -DGC_USE_DLL -c test.c -o test_dll.o
+
+test_dll: test_dll.o libgc_dll.a libgc.dll
+	$(CC) test_dll.o -L$(ABSDIR) -lgc_dll -o test_dll
+
+SYM_PREFIX-libgc=GC
+include ../Makefile.DLLs
+
 reserved_namespace: $(SRCS)
 	for file in $(SRCS) test.c test_cpp.cc; do \
 		sed s/GC_/_GC_/g < $$file > tmp; \
cvs diff: cannot find boehm_gc/README
cvs diff: cannot find boehm_gc/README.OS2
Index: boehm_gc/gc.h
===================================================================
RCS file: /home/staff/zs/imp/mercury/boehm_gc/gc.h,v
retrieving revision 1.14
diff -u -w -r1.14 gc.h
--- gc.h	1996/12/06 11:48:21	1.14
+++ gc.h	1997/08/01 08:07:14
@@ -32,6 +32,9 @@
 # define __GC
 # include <stddef.h>
 
+#if defined(__CYGWIN32__)
+#include "libgc_dll.h"
+#endif
 
 #if defined(_MSC_VER) && defined(_DLL)
 #ifdef GC_BUILD
@@ -615,7 +618,6 @@
 # define pthread_create GC_pthread_create
 # define pthread_sigmask GC_pthread_sigmask
 # define pthread_join GC_pthread_join
-
 #endif /* IRIX_THREADS */
 
 #if defined(SOLARIS_THREADS) || defined(IRIX_THREADS)
@@ -638,6 +640,16 @@
 #   define GC_INIT() { extern end, etext; \
 		       extern void GC_noop(void *, void *); \
 		       GC_noop(&end, &etext); }
+/*
+ * Similarly GC_INIT() is also required for gnu-win32 DLLs.
+ * I don't know any other method for figuring out the start and
+ * end of the main program's global data from inside a DLL.
+ */
+#elif defined(__CYGWIN32__) && defined(GC_USE_DLL)
+#   define GC_INIT() { \
+		extern int _bss_start__, _data_end__; \
+		GC_add_roots((void *)&_bss_start__, (void *)&_data_end__); \
+    }
 #else
 #   define GC_INIT()
 #endif
Index: library/Mmakefile
===================================================================
RCS file: /home/staff/zs/imp/mercury/library/Mmakefile,v
retrieving revision 1.11
diff -u -w -r1.11 Mmakefile
--- Mmakefile	1997/07/28 08:52:08	1.11
+++ Mmakefile	1997/08/01 04:10:04
@@ -56,12 +56,31 @@
 		--cflags "-I$(RUNTIME_DIR) -I$(BOEHM_GC_DIR) $(EXTRA_CFLAGS)" \
 		--intermodule-optimization
 MGNUC	=	MERCURY_C_INCL_DIR=$(RUNTIME_DIR) $(SCRIPTS_DIR)/mgnuc
-MGNUCFLAGS =	-I$(RUNTIME_DIR) -I$(BOEHM_GC_DIR) $(EXTRA_CFLAGS)
+MGNUCFLAGS =	-I$(RUNTIME_DIR) -I$(BOEHM_GC_DIR) \
+		$(DLL_CFLAGS) $(EXTRA_CFLAGS)
 
 MTAGS	=	$(SCRIPTS_DIR)/mtags
 
 #-----------------------------------------------------------------------------#
 
+# Stuff for Windows DLLS
+
+DLL_CFLAGS		= -DGC_USE_DLL -Dlibmer_USE_DLL
+LDFLAGS-libmercury	= -L$(BOEHM_GC_DIR) -L$(RUNTIME_DIR)
+LDLIBS-libmercury	= -lmer_dll					\
+		` case "$(GRADE)" in 					\
+		    *.gc.prof)	echo "-lgc_prof_dll" ;;			\
+		    *.gc)	echo "-lgc_dll" ;;			\
+		  esac							\
+		`
+
+include $(MERCURY_DIR)/Makefile.DLLs
+
+.PHONY: dll
+dll: libmercury.dll libmercury_dll.a
+
+#-----------------------------------------------------------------------------#
+
 NU_LIBRARY_NOS = \
 	io.nu.no require.nu.no std_util.nu.no string.nu.no term_io.nu.no \
 	int.nu.no float.nu.no char.nu.no mercury_builtin.nu.no
Index: library/io.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/library/io.m,v
retrieving revision 1.132
diff -u -w -r1.132 io.m
--- io.m	1997/07/27 15:06:50	1.132
+++ io.m	1997/08/01 09:12:36
@@ -2113,6 +2113,7 @@
    The handwritten code below is almost equivalent to
 
 	io__run :-
+		c_code("GC_INIT();"),
 		initial_external_state(IO0),
 		program_entry_point(IO0, IO),
 		final_io_state(IO).
@@ -2131,6 +2132,9 @@
 	init_label(mercury__io__run_0_0_i2);
 BEGIN_CODE
 Define_entry(mercury__io__run_0_0);
+
+	GC_INIT();
+
         mkframe(""mercury__io__run_0_0"", 0, ENTRY(do_fail));
 	r1 = initial_external_state();
 	noprof_call(ENTRY(mercury__io__init_state_2_0),
cvs diff: Diffing profiler
cvs diff: Diffing runtime
Index: runtime/Mmakefile
===================================================================
RCS file: /home/staff/zs/imp/mercury/runtime/Mmakefile,v
retrieving revision 1.8
diff -u -w -r1.8 Mmakefile
--- Mmakefile	1997/07/27 15:07:59	1.8
+++ Mmakefile	1997/08/01 04:08:24
@@ -14,7 +14,7 @@
 #-----------------------------------------------------------------------------#
 
 CFLAGS		= -I$(MERCURY_DIR)/runtime -I$(MERCURY_DIR)/boehm_gc -g \
-		  $(EXTRA_CFLAGS)
+		  $(DLL_CFLAGS) $(EXTRA_CFLAGS)
 MGNUC		= MERCURY_C_INCL_DIR=. $(SCRIPTS_DIR)/mgnuc
 MGNUCFLAGS	= --no-ansi $(CFLAGS)
 MOD2C		= $(SCRIPTS_DIR)/mod2c
@@ -22,14 +22,18 @@
 #-----------------------------------------------------------------------------#
 
 #		  keep this list in alphabetical order, please
-HDRS		= misc.h calls.h conf.h context.h \
+HDRS		= calls.h conf.h context.h \
 		  deep_copy.h dlist.h debug.h dummy.h \
 		  engine.h getopt.h goto.h heap.h imp.h init.h label.h \
 		  memory.h mercury_solver_backtrack.h \
 		  mercury_float.h mercury_string.h mercury_types.h \
-		  overflow.h prof.h prof_mem.h regorder.h regs.h \
+		  misc.h overflow.h prof.h prof_mem.h regorder.h regs.h \
 		  spinlock.h std.h stacks.h \
-		  table.h tags.h timing.h type_info.h wrapper.h
+		  table.h tags.h timing.h type_info.h wrapper.h \
+		  libmer_dll.h
+# Note that `libmer_globals.h' cannot be part of $(HDR),
+# since it depends on libmer.a, and $(OBJ) : $(HDR) would create a
+# circular dependency.
 
 MACHHDRS	= machdeps/no_regs.h machdeps/i386_regs.h \
 		  machdeps/mips_regs.h machdeps/sparc_regs.h \
@@ -50,6 +54,24 @@
 
 #-----------------------------------------------------------------------------#
 
+# Stuff for Windows DLLs
+
+DLL_CFLAGS	= -DGC_USE_DLL
+# For debugging, add `-Map libmer.map --cref' to LDFLAGS-libmer
+LDFLAGS-libmer	= -L$(BOEHM_GC_DIR)
+LDLIBS-libmer	= \
+		` case "$(GRADE)" in 					\
+		    *.gc.prof)	echo "-lgc_prof_dll" ;;			\
+		    *.gc)	echo "-lgc_dll" ;;			\
+		  esac							\
+		`
+include $(MERCURY_DIR)/Makefile.DLLs
+
+.PHONY: dll
+dll: libmer.dll libmer_dll.a libmer_dll.h libmer_globals.h
+
+#-----------------------------------------------------------------------------#
+
 $(OBJS) $(PIC_OBJS): $(HDRS) $(MACHHDRS)
 
 #-----------------------------------------------------------------------------#
@@ -106,10 +128,10 @@
 install: install_headers install_init install_lib
 
 .PHONY: install_headers
-install_headers: $(HDRS) $(MACHHDRS)
+install_headers: $(HDRS) $(MACHHDRS) libmer_globals.h
 	-[ -d $(INSTALL_INC_DIR)/machdeps ] || \
 		mkdir -p $(INSTALL_INC_DIR)/machdeps
-	cp `vpath_find $(HDRS)` $(INSTALL_INC_DIR)
+	cp `vpath_find $(HDRS) libmer_globals.h` $(INSTALL_INC_DIR)
 	chmod u+w $(INSTALL_INC_DIR)/conf.h
 	cp `vpath_find $(MACHHDRS)` $(INSTALL_INC_DIR)/machdeps
 
Index: runtime/dummy.c
===================================================================
RCS file: /home/staff/zs/imp/mercury/runtime/dummy.c,v
retrieving revision 1.10
diff -u -w -r1.10 dummy.c
--- dummy.c	1997/07/27 15:08:12	1.10
+++ dummy.c	1997/07/31 21:27:28
@@ -5,6 +5,7 @@
 */
 
 #include "dummy.h"
+#include "imp.h"	/* we need libmer_globals.h for Windows DLLs */
 
 /*
 ** This dummy function is in a file of its own to ensure
Index: runtime/goto.h
===================================================================
RCS file: /home/staff/zs/imp/mercury/runtime/goto.h,v
retrieving revision 1.31
diff -u -w -r1.31 goto.h
--- goto.h	1997/07/27 15:08:17	1.31
+++ goto.h	1997/08/01 04:26:50
@@ -40,7 +40,7 @@
 
 #define paste(a,b) a##b
 #define stringify(string) #string
-#define entry(label) paste(entry_,label)
+#define entry(label) paste(_entry_,label)
 #define skip(label) paste(skip_,label)
 
 #ifdef SPLIT_C_FILES
@@ -188,7 +188,7 @@
   */
   #ifdef __ELF__
     #define INLINE_ASM_ENTRY_LABEL_TYPE(label) \
-	"	.type entry_" stringify(label) ", at function\n"
+	"	.type _entry_" stringify(label) ", at function\n"
   #endif
 
 #elif defined (__sparc)
@@ -249,7 +249,7 @@
   ** Hence the `.type' directive below.
   */
   #define INLINE_ASM_ENTRY_LABEL_TYPE(label) \
-	"	.type entry_" stringify(label) ",#function\n"
+	"	.type _entry_" stringify(label) ",#function\n"
 
 #endif
 
@@ -290,7 +290,7 @@
 */
 #ifndef INLINE_ASM_GLOBALIZE_LABEL
 #define INLINE_ASM_GLOBALIZE_LABEL(label) \
-	"	.globl entry_" stringify(label) "\n"
+	"	.globl _entry_" stringify(label) "\n"
 #endif
 
 /*
@@ -308,7 +308,7 @@
 */
 #ifndef INLINE_ASM_ENTRY_LABEL
 #define INLINE_ASM_ENTRY_LABEL(label)	\
-	"entry_" stringify(label) ":\n"
+	"_entry_" stringify(label) ":\n"
 #endif
 
 /*
@@ -433,9 +433,9 @@
 
   #if defined(USE_ASM_LABELS)
     #define Declare_entry(label)	\
-	extern void label(void) __asm__("entry_" stringify(label))
+	extern void label(void) __asm__("_entry_" stringify(label))
     #define Declare_static(label)	\
-	static void label(void) __asm__("entry_" stringify(label))
+	static void label(void) __asm__("_entry_" stringify(label))
     #define Define_extern_entry(label)	Declare_entry(label)
     #define Define_entry(label)		\
 		ASM_ENTRY(label)	\
Index: runtime/imp.h
===================================================================
RCS file: /home/staff/zs/imp/mercury/runtime/imp.h,v
retrieving revision 1.109
diff -u -w -r1.109 imp.h
--- imp.h	1997/07/27 15:08:20	1.109
+++ imp.h	1997/08/01 03:46:02
@@ -19,6 +19,14 @@
 #ifndef IMP_H
 #define IMP_H
 
+/*
+** The following must come before any definitions of global variables.
+** This is necessary to support DLLs on Windows.
+*/
+#ifdef __CYGWIN32__
+  #include "libmer_dll.h"
+#endif
+
 #include	"regs.h"	/* must come before system headers */
 
 #include	"conf.h"
Index: runtime/init.h
===================================================================
RCS file: /home/staff/zs/imp/mercury/runtime/init.h,v
retrieving revision 1.16
diff -u -w -r1.16 init.h
--- init.h	1997/07/27 15:08:21	1.16
+++ init.h	1997/08/01 03:46:18
@@ -12,6 +12,14 @@
 #ifndef	INIT_H
 #define	INIT_H
 
+/*
+** The following must come before any definitions of global variables.
+** This is necessary to support DLLs on Windows.
+*/
+#if defined(__CYGWIN32__)
+  #include "libmer_dll.h"
+#endif
+
 #include "goto.h"		/* for Declare_entry */
 #include "mercury_types.h"	/* for `Code *' */
 #include "wrapper.h"		/* for do_init_modules() */
Index: runtime/label.c
===================================================================
RCS file: /home/staff/zs/imp/mercury/runtime/label.c,v
retrieving revision 1.21
diff -u -w -r1.21 label.c
--- label.c	1997/07/27 15:08:22	1.21
+++ label.c	1997/07/31 21:29:04
@@ -16,6 +16,7 @@
 
 #include	"label.h"
 
+#include	"imp.h"		/* we need libmer_globals.h for Windows DLLs */
 #include	"table.h"	/* for `Table' */
 #include	"prof.h"	/* for prof_output_addr_decls() */
 #include	"engine.h"	/* for `progdebug' */
Index: runtime/memory.c
===================================================================
RCS file: /home/staff/zs/imp/mercury/runtime/memory.c,v
retrieving revision 1.73
diff -u -w -r1.73 memory.c
--- memory.c	1997/07/27 15:08:25	1.73
+++ memory.c	1997/08/01 03:49:18
@@ -29,6 +29,9 @@
 
 /*---------------------------------------------------------------------------*/
 
+#ifdef __CYGWIN32__
+  #include "libmer_dll.h"
+#endif
 #include "regs.h"	/* must come first, due to global register vars */
 #include "conf.h"	/* must come second */
 
Index: runtime/table.c
===================================================================
RCS file: /home/staff/zs/imp/mercury/runtime/table.c,v
retrieving revision 1.15
diff -u -w -r1.15 table.c
--- table.c	1997/07/27 15:08:45	1.15
+++ table.c	1997/08/01 03:24:58
@@ -15,6 +15,7 @@
 
 #define	HASHDEBUG
 
+#include	"libmer_dll.h"
 #include	<stdio.h>
 #include	"std.h"
 #include	"dlist.h"
Index: runtime/wrapper.mod
===================================================================
RCS file: /home/staff/zs/imp/mercury/runtime/wrapper.mod,v
retrieving revision 1.76
diff -u -w -r1.76 wrapper.mod
--- wrapper.mod	1997/07/27 15:08:53	1.76
+++ wrapper.mod	1997/08/01 09:10:18
@@ -158,14 +158,16 @@
 #ifdef CONSERVATIVE_GC
 	GC_quiet = TRUE;
 
-	/* double-check that the garbage collector knows about
-	   global variables in shared libraries */
-	GC_is_visible(fake_reg);
+	GC_INIT();
 
 	/* call the init_gc() function defined in <foo>_init.c - */
 	/* this is to work around a Solaris 2.X (X <= 4) linker bug */
 	(*address_of_init_gc)();
 
+	/* double-check that the garbage collector knows about
+	   global variables in shared libraries */
+	GC_is_visible(fake_reg);
+
 	/* The following code is necessary to tell the conservative */
 	/* garbage collector that we are using tagged pointers */
 	{
Index: util/mkinit.c
===================================================================
RCS file: /home/staff/zs/imp/mercury/util/mkinit.c,v
retrieving revision 1.17
diff -u -w -r1.17 mkinit.c
--- mkinit.c	1997/07/27 15:09:59	1.17
+++ mkinit.c	1997/08/01 08:52:08
@@ -83,6 +83,19 @@
 	"extern char *GC_stackbottom;\n"
 	"#endif\n"
 	"\n"
+	"#if defined(__CYGWIN32__)\n"
+	"  #if defined(libmer_USE_DLL)\n"
+	"       #define libmer_impure_ptr \\\n"
+	"		(*__imp_libmer_impure_ptr)\n"
+	"	extern void *libmer_impure_ptr;\n"
+	"  #endif\n"
+	"  #if defined(libmercury_USE_DLL)\n"
+	"       #define libmercury_impure_ptr \\\n"
+	"		(*__imp_libmercury_impure_ptr)\n"
+	"	extern void *libmercury_impure_ptr;\n"
+	"  #endif\n"
+	"#endif\n"
+	"\n"
 	"int mercury_main(int argc, char **argv, char *stackbottom)\n"
 	"{\n"
 	"\n"
@@ -96,6 +109,20 @@
 	"	GC_stackbottom = stackbottom;\n"
 	"#endif\n"
 	"\n"
+	"/*\n"
+	"** If we're using DLLs on gnu-win32, then we need\n"
+	"** to take special steps to initialize _impure_ptr\n"
+	"** for the DLLs.\n"
+	"*/\n"
+	"#if defined(__CYGWIN32__)\n"
+	"  #if defined(libmer_USE_DLL)\n"
+	"	libmer_impure_ptr = _impure_ptr;\n"
+	"  #endif\n"
+	"  #if defined(libmercury_USE_DLL)\n"
+	"	libmercury_impure_ptr = _impure_ptr;\n"
+	"  #endif\n"
+	"#endif\n"
+	"\n"
 	"	address_of_mercury_init_io = mercury_init_io;\n"
 	"	address_of_init_modules = init_modules;\n"
 	"#ifdef CONSERVATIVE_GC\n"

Index: samples/Mmakefile
===================================================================
RCS file: /home/staff/zs/imp/mercury/samples/Mmakefile,v
retrieving revision 1.2
diff -u -w -r1.2 Mmakefile
--- Mmakefile	1997/07/27 15:09:15	1.2
+++ Mmakefile	1997/08/01 08:41:48
@@ -1,4 +1,63 @@
 #-----------------------------------------------------------------------------#
+# Copyright (C) 1995-1997 The University of Melbourne. 
+# This file may only be copied under the terms of the GNU General
+# Public Licence - see the file COPYING in the Mercury distribution.
+#-----------------------------------------------------------------------------#
+
+# Mmake - this is Mmake file for building the Mercury compiler 
+
+MERCURY_DIR=..
+include $(MERCURY_DIR)/Mmake.common
+
+MAIN_TARGET=mercury
+
+VPATH=$(LIBRARY_DIR)
+
+#-----------------------------------------------------------------------------#
+
+# Specify which compilers to use to compile the compiler.
+# Don't change these without good reason - if you want to
+# do a temporary change, change ../Mmake.params
+
+MCD	=	MERCURY_INT_DIR=. $(MC) --generate-dependencies
+MCI	=	MERCURY_INT_DIR=$(LIBRARY_DIR) $(MC) --make-interface
+MCG	=	MERCURY_C_INCL_DIR=$(RUNTIME_DIR) \
+		MERCURY_INT_DIR=$(LIBRARY_DIR) $(MC) --compile-to-c
+MCS	=	MERCURY_C_INCL_DIR=$(RUNTIME_DIR) \
+		MERCURY_INT_DIR=$(LIBRARY_DIR) \
+		$(MC) --cflags -I$(BOEHM_GC_DIR) --split-c-files -c 
+MGNUC	=	MERCURY_C_INCL_DIR=$(RUNTIME_DIR) $(SCRIPTS_DIR)/mgnuc
+MGNUCFLAGS =	-I$(BOEHM_GC_DIR) $(DLL_CFLAGS) $(EXTRA_CFLAGS)
+C2INIT =	\
+MERCURY_MOD_LIB_MODS="$(RUNTIME_DIR)/runtime.init $(LIBRARY_DIR)/libmercury.init" \
+		MERCURY_MKINIT=$(UTIL_DIR)/mkinit $(SCRIPTS_DIR)/c2init
+ML	=	MERCURY_C_LIB_DIR=. $(SCRIPTS_DIR)/ml
+MLFLAGS =	--mercury-libs none $(EXTRA_MLFLAGS)
+MLLIBS  =	$(EXTRA_MLLIBS) $(LIBRARY_DIR)/libmercury_dll.a \
+		$(RUNTIME_DIR)/libmer_dll.a ` \
+		    case $(GRADE) in \
+			*.gc.prof) echo $(BOEHM_GC_DIR)/libgc_prof_dll.a ;; \
+			*.gc) echo $(BOEHM_GC_DIR)/libgc_dll.a ;; \
+		    esac \
+		` -lm
+MSC	=	MERCURY_SICSTUS_COMPILER=$(LIBRARY_DIR)/sicstus_compile \
+		$(SCRIPTS_DIR)/msc
+MSL	=	MERCURY_SP_LIB_DIR=$(LIBRARY_DIR) $(SCRIPTS_DIR)/msl
+MNC	=	MERCURY_NC_BUILTIN=$(LIBRARY_DIR)/nc_builtin.nl \
+		$(SCRIPTS_DIR)/mnc
+MNL	=	MERCURY_NU_LIB_DIR=$(LIBRARY_DIR) $(SCRIPTS_DIR)/mnl
+MNLFLAGS =	-u 6000
+MTAGS	=	$(SCRIPTS_DIR)/mtags
+
+DLL_CFLAGS =	-DGC_USE_DLL -Dlibmer_USE_DLL -Dlibmercury_USE_DLL
+
+EXTRA_CFLAGS =	-g
+EXTRA_MLFLAGS =	-g
+
+#-----------------------------------------------------------------------------#
+
+#-----------------------------------------------------------------------------#
+#-----------------------------------------------------------------------------#
 # Copyright (C) 1995 The University of Melbourne.
 # This file may only be copied under the terms of the GNU General
 # Public License - see the file COPYING in the Mercury distribution.



#-----------------------------------------------------------------------------#
#-----------------------------------------------------------------------------#

# Makefile.DLLs, version 0.5.

# This Makefile contains rules for creating DLLs on Windows using gnu-win32.

#-----------------------------------------------------------------------------#

# The SYM_PREFIX is used as a prefix for the symbols in the files
# that this makefiles automatically generates.
#
# The default SYM_PREFIX for libfoo.dll is `libfoo'.
# But you can override this by setting `SYM_PREFIX-libfoo = blah'.

SYM_PREFIX = $(firstword $(SYM_PREFIX-$*) $*)

GUARD_MACRO =		$(SYM_PREFIX)_GLOBALS_H
DEFINE_DLL_MACRO =	$(SYM_PREFIX)_DEFINE_DLL
USE_DLL_MACRO =		$(SYM_PREFIX)_USE_DLL
IMP_MACRO =		$(SYM_PREFIX)_IMP
GLOBAL_MACRO =		$(SYM_PREFIX)_GLOBAL
IMPURE_PTR =		$(SYM_PREFIX)_impure_ptr

# This rule creates a `.def' file, which lists the symbols that are exported
# from the DLL.  We use `nm' to get a list of all the exported text (`T')
# symbols and data symbols -- including uninitialized data (`B'),
# initialized data (`D'), read-only data (`R'), and common blocks (`C').
# We also export `_impure_ptr', suitably renamed, so that the 
# main program can do the necessary initialization of the DLL's _impure_ptr.
# (Since there can be more than one DLL, we must rename _impure_ptr as
# $(SYM_PREFIX)_impure_ptr to prevent name collisions.)
%.def: %.a
	echo EXPORTS > $@
	echo $(IMPURE_PTR) = _impure_ptr >> $@
	nm $< | sed -n '/^........ [BCDRT] _/s/[^_]*_//p' >> $@

# We need to use macros to access global data:
# the user of the DLL must refer to `bar' as `(*__imp_bar)'.
# This rule creates a pair of files `foo_dll.h' and `foo_globals.h'
# which contains macros for doing this.
#
# The DLL may also contain some references to _impure_ptr
# (e.g. stdin is defined as a macro which expands to _impure_ptr.stdin).
# We need to provide a definition for this (otherwise it will link in
# the definition in libccrt.o, which causes lots of problems,
# eventually leading to undefined symbol `WinMain').
# The main program needs to initialize all the _impure_ptr variables
# for the DLLs with its _impure_ptr.

%_dll.h:
	echo "/* automatically generated by Makefile.DLLs */"	> $@
	echo "#ifndef $(GUARD_MACRO)"				>> $@
	echo "#define $(GUARD_MACRO)"				>> $@
	echo ""							>> $@
	echo "#if defined(__GNUC__) && defined(__CYGWIN32__)"	>> $@
	echo "  #if defined($(USE_DLL_MACRO))"			>> $@
	echo "    #define $(IMP_MACRO)(name)	__imp_##name" 	>> $@
	echo "    #define $(GLOBAL_MACRO)(name)	(*$(IMP_MACRO)(name))" >> $@
	echo "    #include \"$*_globals.h\""			>> $@
	echo "  #endif /* $(USE_DLL_MACRO) */"			>> $@
	echo "#endif /* __GNUC__ && __CYGWIN32__ */"		>> $@
	echo ""							>> $@
	echo "#endif /* $(GUARD_MACRO) */"			>> $@

%_globals.h: %.a
	echo "/* automatically generated by Makefile.DLLs */"	> $@
	for sym in $(IMPURE_PTR) \
		`nm $< | grep '^........ [BCDR] _' | sed 's/[^_]*_//'`; \
	do \
		echo "#define $$sym	$(GLOBAL_MACRO)($$sym)"	>> $@; \
	done

%_dll.c:
	echo "/* automatically generated by Makefile.DLLs */"	> $@
	echo "void *_impure_ptr;"				>> $@

# This rule creates the export object file (`foo.exp') which contains the
# jump table array; this export object file becomes part of the DLL. 
# This rule also creates the import library (`foo_dll.a') which contains small
# stubs for all the functions exported by the DLL which jump to them via the
# jump table.  Executables that will use the DLL must be linked against this
# stub library.
%.exp %_dll.a : %.def
	dlltool $(DLLTOOLFLAGS) $(DLLTOOLFLAGS-$*)		\
		--def $<					\
		--dllname $*.dll				\
		--output-exp $*.exp				\
		--output-lib $*_dll.a

# The `sed' commands below are to convert DOS-style `C:\foo\bar'
# pathnames into Unix-style `//c/foo/bar' pathnames.
CYGWIN32_LIBS = $(shell echo					\
	-L`dirname \`gcc -print-file-name=libgcc.a |		\
	sed -e 's@^\\\\([A-Za-z]\\\\):@//\\\\1 at g' -e 's@\\\\\\\\@/@g' \` ` \
	-L`dirname \`gcc -print-file-name=libcygwin.a |	\
	sed -e 's@^\\\\([A-Za-z]\\\\):@//\\\\1 at g' -e 's@\\\\\\\\@/@g' \` ` \
	-L`dirname \`gcc -print-file-name=libkernel32.a | \
	sed -e 's@^\\\\([A-Za-z]\\\\):@//\\\\1 at g' -e 's@\\\\\\\\@/@g' \` ` \
	-lgcc -lcygwin -lkernel32 -lgcc)

# Making relocatable DLLs doesn't seem to work.
# Note quite sure why.  The --image-base values below
# where chosen at random, they seem to work on my machine.
RELOCATABLE=no
LDFLAGS-libgc +=	--image-base=0x2345000
LDFLAGS-libmer +=	--image-base=0x1234000
LDFLAGS-libmercury +=	--image-base=0x3456000

ifeq "$(strip $(RELOCATABLE))" "yes"

# to create relocatable DLLs, we need to do two passes
# (warning: this is untested)
%.dll: %.exp %.a %_dll.o dll_init.o dll_fixup.o
	$(LD) $(LDFLAGS) $(LDFLAGS-$*) --dll -o $*.base			\
		-e _dll_entry at 12					\
		$*.exp $*.a $*_dll.o					\
		dll_init.o dll_fixup.o					\
		$(LDLIBS) $(LDLIBS-$*)					\
		$(CYGWIN32_LIBS)
	# untested
	dlltool $(DLLTOOLFLAGS) $(DLLTOOLFLAGS-$*)		\
		--def $*.def					\
		--dllname $*.dll				\
		--base-file $*.base				\
		--output-exp $*.exp
	$(LD) $(LDFLAGS) $(LDFLAGS-$*) --dll -o $*.base			\
		-e _dll_entry at 12					\
		$*.exp $*.a $*_dll.o					\
		dll_init.o dll_fixup.o					\
		$(LDLIBS) $(LDLIBS-$*)					\
		$(CYGWIN32_LIBS)
	dlltool $(DLLTOOLFLAGS) $(DLLTOOLFLAGS-$*)		\
		--def $*.def					\
		--dllname $*.dll				\
		--base-file $*.base				\
		--output-exp $*.exp
	# end untested stuff
	$(LD) $(LDFLAGS) $(LDFLAGS-$*) --dll --base-file $*.base -o $@	\
		-e _dll_entry at 12 					\
		$*.exp $*.a $*_dll.o 					\
		dll_init.o dll_fixup.o					\
		$(LDLIBS) $(LDLIBS-$*)					\
		$(CYGWIN32_LIBS)
	rm -f $*.base

else

%.dll: %.exp %.a %_dll.o dll_fixup.o dll_init.o
	$(LD) $(LDFLAGS) $(LDFLAGS-$*) --dll -o $@			\
		-e _dll_entry at 12 					\
		$*.exp $*.a $*_dll.o 					\
		dll_init.o dll_fixup.o					\
		$(LDLIBS) $(LDLIBS-$*)					\
		$(CYGWIN32_LIBS)

endif

# This black magic piece of assembler needs to be linked in in order to
# properly terminate the list of imported DLLs.
dll_fixup.s:
	echo '.section .idata$$3' 	> dll_fixup.s
	echo '.long 0,0,0,0, 0,0,0,0'	>> dll_fixup.s

dll_fixup.o: dll_fixup.s
	$(AS) $(ASFLAGS) -o dll_fixup.o dll_fixup.s

# Windows requires each DLL to have an initialization function
# that is called at certain points (thread/process attach/detach).
# This one just does't do anything.
dll_init.c:
	echo '__attribute__((stdcall))' > dll_init.c
	echo 'int dll_entry(int handle, int reason, void *ptr)' >> dll_init.c
	echo '{return 1; }' >> dll_init.c

# The following rule is just there to convince gcc
# to keep otherwise unused intermediate targets around.
dont_throw_away: dll_fixup.o dll_init.o

-- 
Fergus Henderson <fjh at cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh at 128.250.37.3         |     -- the last words of T. S. Garp.



More information about the developers mailing list