[Updated 2] dllimport/dllexport attributes for Win32/BeOS

Mumit Khan khan@xraylith.wisc.edu
Sat Jun 27 18:02:00 GMT 1998


Updates since last posting (22-Jun-1998)
========================================

 - Undo hokey tree changes (using machine attributes in generic code) by 
   adding a new first class attribute.  non_const_addr_p and corresponding 
   DECL_NON_ADDR_CONST_P macro.

 - fixed two problems:
   - patch error. Thanks to Fred Fish for pointing that out.
   - don't issue incorrect error message for imported C++ classes.

 - Note that BeOS folks are using a patched version of this patch since
   the redefinition rules are different. Once this gets integrated, we can
   merge the two patches. Fred Fish is maintaining the changes.

dllimport and dllexport attributes for i386/pe
===============================================

This patch implements two new attributes, dllimport and dllexport, for 
i386-cygwin32, i386-mingw32* and i386-beos targets, which greatly simplify 
the process of using and creating DLLs. The attributes are specified using 
the usual __attribute__((dllimport)) and __attribute__((dllexport)), or 
via CPP macros __declspec(dllimport) and __declspec(dllexport).

This work is largely due to Doug Evans' work on ARM/PE at Cygnus.

The meat of this patch is about 4 lines of code, and the rest is really
support stuff. See i386_pe_mark_dll{import,export} for RTL mucking.

These patches do not affect code not using the new attributes, and even 
when used, can be reverted to old behavior with -fnop-fun-dllimport. 

Patches are against egcs-19980625 CVS tree. No testsuite regressions for
i386-cygwin32 and i386-mingw32.

What works (or doesn't)?
============================

The C front has gone through limited testing and apparently stable/usable.

The C++ global pointer initialization problem (to variables or functions 
imported from a DLL) is now *fixed* (see the change to gcc/tree.c). The 
last time around, the following did not work:
  
  // file scope
  __attribute__((dllimport) int imported_var;
  int *localvar_p = &imported_var;

nor did this:

  // file scope
  __attribute__((dllimport) int imported_func();
  int (*localfunc_p) = &imported_func;

Since the C++ front-end will parse '&imported_var' as a "static" 
ADDR_EXPR (cf: gcc/cp/typeck.c(build_unary_op), case ADDR_EXPR) and 
backend gets very confused. Both of these need initialization, and
can't treated as "static" as defined by staticp().

Specifying __attribute__((dllexport)) will do the right thing; however,
binutils does not yet take advantage of this information when building 
DLLs.

What doesn't work? I wouldn't know unless this gets integrated and people
start using it on real code and file bug reports.

Regards,
Mumit -- khan@xraylith.wisc.edu

=========================================================================

Thu Jun 25 22:27:04 1998  Mumit Khan <khan@xraylith.wisc.edu>

	Support for dllimport and dllexport attributes for i386-pe.

	* tree.h (DECL_NON_ADDR_CONST_P): New accessor macro.
	(struct tree_decl): Add non_addr_const_p field.
	* tree.c (staticp): Use.

	* i386/cygwin32.h (CPP_PREDEFINES): Map __declspec(x) to GCC
	attributes.
	(SUBTARGET_SWITCHES): Switches to turn on/off dllimport|export
	attributes. Also accept -mwindows option.
	(VALID_MACHINE_DECL_ATTRIBUTE): New macro.
	(MERGE_MACHINE_DECL_ATTRIBUTE): New macro.
	(REDO_SECTION_INFO_P): New macro.
	(DRECTVE_SECTION_FUNCTION): New macro.
	(drectve_section): Cover function to implement above.
	(SWITCH_TO_SECTION_FUNCTION): New macro.
	(switch_to_section): Covert function to implement above.
	(EXTRA_SECTIONS): Add in_drectve.
	(EXTRA_SECTION_FUNCTIONS): Add in_drectve and switch_to_section.
	(ENCODE_SECTION_INFO): Delete old macro and redefine as a function.
	(STRIP_NAME_ENCODING): Handle new attributes.
	(ASM_OUTPUT_LABELREF): New macro.
	(ASM_OUTPUT_FUNCTION_NAME): New macro.
	(ASM_OUTPUT_COMMON): New macro.
	(ASM_OUTPUT_DECLARE_OBJECT_NAME): New macro.

	* i386/mingw32.h (CPP_PREDEFINES): Map __declspec(x) to GCC
	attributes.

	* i386/winnt.c (i386_pe_valid_decl_attribute_p): New function.
	(i386_pe_merge_decl_attributes): New function.
	(i386_pe_check_vtable_importexport): New function.
	(i386_pe_dllexport_p): New function.
	(i386_pe_dllimport_p): New function.
	(i386_pe_dllexport_name_p): New function.
	(i386_pe_dllimport_name_p): New function.
	(i386_pe_mark_dllexport): New function.
	(i386_pe_mark_dllimport): New function.
	(i386_pe_encode_section_info): New function.
	(i386_pe_unique_section): Strip encoding from name first.

Index: gcc/tree.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/tree.c,v
retrieving revision 1.34
diff -u -3 -p -r1.34 tree.c
--- gcc/tree.c	1998/06/25 15:14:20	1.34
+++ gcc/tree.c	1998/06/28 00:04:49
@@ -2265,9 +2265,12 @@ staticp (arg)
     case FUNCTION_DECL:
       /* Nested functions aren't static, since taking their address
 	 involves a trampoline.  */
-       return decl_function_context (arg) == 0 || DECL_NO_STATIC_CHAIN (arg);
+       return (decl_function_context (arg) == 0 || DECL_NO_STATIC_CHAIN (arg))
+              && ! DECL_NON_ADDR_CONST_P (arg);
+
     case VAR_DECL:
-      return TREE_STATIC (arg) || DECL_EXTERNAL (arg);
+      return (TREE_STATIC (arg) || DECL_EXTERNAL (arg))
+             && ! DECL_NON_ADDR_CONST_P (arg);
 
     case CONSTRUCTOR:
       return TREE_STATIC (arg);
Index: gcc/tree.h
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/tree.h,v
retrieving revision 1.42
diff -u -3 -p -r1.42 tree.h
--- gcc/tree.h	1998/06/25 15:14:18	1.42
+++ gcc/tree.h	1998/06/28 00:04:49
@@ -1201,6 +1201,10 @@ struct tree_type
 #define DECL_LANG_FLAG_6(NODE) (DECL_CHECK (NODE)->decl.lang_flag_6)
 #define DECL_LANG_FLAG_7(NODE) (DECL_CHECK (NODE)->decl.lang_flag_7)
 
+/* Used to indicate that the pointer to this DECL cannot be treated as
+   an address constant.  */
+#define DECL_NON_ADDR_CONST_P(NODE) (DECL_CHECK (NODE)->decl.non_addr_const_p)
+
 struct tree_decl
 {
   char common[sizeof (struct tree_common)];
@@ -1241,6 +1245,8 @@ struct tree_decl
   unsigned lang_flag_5 : 1;
   unsigned lang_flag_6 : 1;
   unsigned lang_flag_7 : 1;
+
+  unsigned non_addr_const_p : 1;
 
   /* For a FUNCTION_DECL, if inline, this is the size of frame needed.
      If built-in, this is the code for which built-in function.
Index: gcc/config/i386/cygwin32.h
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/config/i386/cygwin32.h,v
retrieving revision 1.7
diff -u -3 -p -r1.7 cygwin32.h
--- gcc/config/i386/cygwin32.h	1998/04/04 17:38:55	1.7
+++ gcc/config/i386/cygwin32.h	1998/06/28 00:04:50
@@ -30,6 +30,14 @@ Boston, MA 02111-1307, USA. */
 #include "i386/gas.h"
 #include "dbxcoff.h"
 
+/* Support the __declspec keyword by turning them into attributes.
+   We currently only support: dllimport and dllexport.
+   Note that the current way we do this may result in a collision with
+   predefined attributes later on.  This can be solved by using one attribute,
+   say __declspec__, and passing args to it.  The problem with that approach
+   is that args are not accumulated: each new appearance would clobber any
+   existing args.  */
+
 #ifdef CPP_PREDEFINES
 #undef CPP_PREDEFINES
 #endif
@@ -38,6 +46,7 @@ Boston, MA 02111-1307, USA. */
   -D__CYGWIN32__ -DWINNT  -D_X86_=1 -D__STDC__=1\
   -D__stdcall=__attribute__((__stdcall__)) \
   -D__cdecl=__attribute__((__cdecl__)) \
+  -D__declspec(x)=__attribute__((x)) \
   -Asystem(winnt) -Acpu(i386) -Amachine(i386)"
 
 #undef CPP_SPEC
@@ -69,13 +78,51 @@ Boston, MA 02111-1307, USA. */
 #define WCHAR_TYPE "short unsigned int"
 #define HAVE_ATEXIT 1
 
+
+/* Ignore dllimport for functions.  */
+#define TARGET_NOP_FUN_DLLIMPORT (target_flags & 0x20000)
+
+#undef SUBTARGET_SWITCHES
+#define SUBTARGET_SWITCHES 			\
+  { "nop-fun-dllimport",	 0x20000 },	\
+  { "no-nop-fun-dllimport",	-0x20000 },	\
+  { "windows",			 0x0     },
+
+/* A C expression whose value is nonzero if IDENTIFIER with arguments ARGS
+   is a valid machine specific attribute for DECL.
+   The attributes in ATTRIBUTES have previously been assigned to DECL.  */
+extern int i386_pe_valid_decl_attribute_p ();
+
+#undef VALID_MACHINE_DECL_ATTRIBUTE
+#define VALID_MACHINE_DECL_ATTRIBUTE(DECL, ATTRIBUTES, IDENTIFIER, ARGS) \
+  i386_pe_valid_decl_attribute_p (DECL, ATTRIBUTES, IDENTIFIER, ARGS)
+
+extern union tree_node *i386_pe_merge_decl_attributes ();
+#define MERGE_MACHINE_DECL_ATTRIBUTES(OLD, NEW) \
+  i386_pe_merge_decl_attributes ((OLD), (NEW))
+
+/* Used to implement dllexport overriding dllimport semantics.  It's also used
+   to handle vtables - the first pass won't do anything because
+   DECL_CONTEXT (DECL) will be 0 so i386_pe_dll{ex,im}port_p will return 0.
+   It's also used to handle dllimport override semantics.  */
+#if 0
+#define REDO_SECTION_INFO_P(DECL) \
+  ((DECL_MACHINE_ATTRIBUTES (DECL) != NULL_TREE) \
+   || (TREE_CODE (DECL) == VAR_DECL && DECL_VIRTUAL_P (DECL)))
+#else
+#define REDO_SECTION_INFO_P(DECL) 1
+#endif
+
+
 #undef EXTRA_SECTIONS
-#define EXTRA_SECTIONS in_ctor, in_dtor
+#define EXTRA_SECTIONS in_ctor, in_dtor, in_drectve
 
 #undef EXTRA_SECTION_FUNCTIONS
 #define EXTRA_SECTION_FUNCTIONS					\
   CTOR_SECTION_FUNCTION						\
-  DTOR_SECTION_FUNCTION
+  DTOR_SECTION_FUNCTION						\
+  DRECTVE_SECTION_FUNCTION					\
+  SWITCH_TO_SECTION_FUNCTION
 
 #define CTOR_SECTION_FUNCTION					\
 void								\
@@ -99,6 +146,41 @@ dtor_section ()							\
     }								\
 }
 
+#define DRECTVE_SECTION_FUNCTION \
+void									\
+drectve_section ()							\
+{									\
+  if (in_section != in_drectve)						\
+    {									\
+      fprintf (asm_out_file, "%s\n", "\t.section .drectve\n");		\
+      in_section = in_drectve;						\
+    }									\
+}
+
+/* Switch to SECTION (an `enum in_section').
+
+   ??? This facility should be provided by GCC proper.
+   The problem is that we want to temporarily switch sections in
+   ASM_DECLARE_OBJECT_NAME and then switch back to the original section
+   afterwards.  */
+#define SWITCH_TO_SECTION_FUNCTION 				\
+void 								\
+switch_to_section (section, decl) 				\
+     enum in_section section; 					\
+     tree decl; 						\
+{ 								\
+  switch (section) 						\
+    { 								\
+      case in_text: text_section (); break; 			\
+      case in_data: data_section (); break; 			\
+      case in_named: named_section (decl, NULL, 0); break; 	\
+      case in_ctor: ctor_section (); break; 			\
+      case in_dtor: dtor_section (); break; 			\
+      case in_drectve: drectve_section (); break; 		\
+      default: abort (); break; 				\
+    } 								\
+}
+
 #define ASM_OUTPUT_CONSTRUCTOR(FILE,NAME)	\
   do {						\
     ctor_section ();				\
@@ -111,7 +193,7 @@ dtor_section ()							\
   do {						\
     dtor_section ();                   		\
     fprintf (FILE, "%s\t", ASM_LONG);		\
-    assemble_name (FILE, NAME);              	\
+    assemble_name (FILE, NAME);			\
     fprintf (FILE, "\n");			\
   } while (0)
 
@@ -119,44 +201,38 @@ dtor_section ()							\
    differently depending on something about the variable or
    function named by the symbol (such as what section it is in).
 
-   On i386, if using PIC, mark a SYMBOL_REF for a non-global symbol
-   so that we may access it directly in the GOT.
-
    On i386 running Windows NT, modify the assembler name with a suffix 
    consisting of an atsign (@) followed by string of digits that represents
    the number of bytes of arguments passed to the function, if it has the 
-   attribute STDCALL. */
+   attribute STDCALL.
+
+   In addition, we must mark dll symbols specially. Definitions of 
+   dllexport'd objects install some info in the .drectve section.  
+   References to dllimport'd objects are fetched indirectly via
+   _imp__.  If both are declared, dllexport overrides.  This is also 
+   needed to implement one-only vtables: they go into their own
+   section and we need to set DECL_SECTION_NAME so we do that here.
+   Note that we can be called twice on the same decl.  */
+
+extern void i386_pe_encode_section_info ();
 
 #ifdef ENCODE_SECTION_INFO
 #undef ENCODE_SECTION_INFO
-#define ENCODE_SECTION_INFO(DECL) 					\
-do									\
-  {									\
-    if (flag_pic)							\
-      {									\
-	rtx rtl = (TREE_CODE_CLASS (TREE_CODE (DECL)) != 'd'		\
-		   ? TREE_CST_RTL (DECL) : DECL_RTL (DECL));		\
-	SYMBOL_REF_FLAG (XEXP (rtl, 0))					\
-	  = (TREE_CODE_CLASS (TREE_CODE (DECL)) != 'd'			\
-	     || ! TREE_PUBLIC (DECL));					\
-      }									\
-    if (TREE_CODE (DECL) == FUNCTION_DECL) 				\
-      if (lookup_attribute ("stdcall",					\
-			    TYPE_ATTRIBUTES (TREE_TYPE (DECL))))	\
-        XEXP (DECL_RTL (DECL), 0) = 					\
-          gen_rtx (SYMBOL_REF, Pmode, gen_stdcall_suffix (DECL)); 	\
-  }									\
-while (0)
 #endif
+#define ENCODE_SECTION_INFO(DECL) i386_pe_encode_section_info (DECL)
 
-/* This macro gets just the user-specified name out of the string in a
-   SYMBOL_REF.  Discard trailing @[NUM] encoded by ENCODE_SECTION_INFO.   */
-
+/* Utility used only in this file.  */
+#define I386_PE_STRIP_ENCODING(SYM_NAME) \
+  ((SYM_NAME) + ((SYM_NAME)[0] == '@' ? 3 : 0))
+
+/* This macro gets just the user-specified name
+   out of the string in a SYMBOL_REF.  Discard
+   trailing @[NUM] encoded by ENCODE_SECTION_INFO.  */
 #undef  STRIP_NAME_ENCODING
 #define STRIP_NAME_ENCODING(VAR,SYMBOL_NAME)				\
 do {									\
   char *_p;								\
-  char *_name = ((SYMBOL_NAME) + ((SYMBOL_NAME)[0] == '*'));		\
+  char *_name = I386_PE_STRIP_ENCODING (SYMBOL_NAME);			\
   for (_p = _name; *_p && *_p != '@'; ++_p)				\
     ;									\
   if (*_p == '@')							\
@@ -170,6 +246,65 @@ do {									\
     (VAR) = _name;							\
 } while (0)
       
+
+/* Output a reference to a label.  */
+#undef ASM_OUTPUT_LABELREF
+#define ASM_OUTPUT_LABELREF(STREAM, NAME)  		\
+  fprintf (STREAM, "%s%s", USER_LABEL_PREFIX, 		\
+           I386_PE_STRIP_ENCODING (NAME))		\
+
+/* Output a function definition label.  */
+#undef ASM_DECLARE_FUNCTION_NAME
+#define ASM_DECLARE_FUNCTION_NAME(STREAM, NAME, DECL)	\
+do {							\
+  if (i386_pe_dllexport_name_p (NAME))			\
+    {							\
+      drectve_section ();				\
+      fprintf ((STREAM), "\t.ascii \" -export:%s\"\n", 	\
+               I386_PE_STRIP_ENCODING (NAME));		\
+      function_section (DECL);				\
+    }							\
+  ASM_OUTPUT_LABEL ((STREAM), (NAME));			\
+} while (0)
+
+#undef ASM_COMMENT_START
+#define ASM_COMMENT_START " #"
+
+/* Output a common block.  */
+#undef ASM_OUTPUT_COMMON
+#define ASM_OUTPUT_COMMON(STREAM, NAME, SIZE, ROUNDED)	\
+do {							\
+  if (i386_pe_dllexport_name_p (NAME))			\
+    {							\
+      drectve_section ();				\
+      fprintf ((STREAM), "\t.ascii \" -export:%s\"\n",	\
+               I386_PE_STRIP_ENCODING (NAME));		\
+    }							\
+  if (! i386_pe_dllimport_name_p (NAME))		\
+    {							\
+      fprintf ((STREAM), "\t.comm\t"); 			\
+      assemble_name ((STREAM), (NAME));			\
+      fprintf ((STREAM), ", %d\t%s %d\n",		\
+	       (ROUNDED), ASM_COMMENT_START, (SIZE));	\
+    }							\
+} while (0)
+
+/* Output the label for an initialized variable.  */
+#undef ASM_DECLARE_OBJECT_NAME
+#define ASM_DECLARE_OBJECT_NAME(STREAM, NAME, DECL) 	\
+do {							\
+  if (i386_pe_dllexport_name_p (NAME))			\
+    {							\
+      enum in_section save_section = in_section;	\
+      drectve_section ();				\
+      fprintf ((STREAM), "\t.ascii \" -export:%s\"\n",	\
+               I386_PE_STRIP_ENCODING (NAME));		\
+      switch_to_section (save_section, (DECL));		\
+    }							\
+  ASM_OUTPUT_LABEL ((STREAM), (NAME));			\
+} while (0)
+
+
 /* Emit code to check the stack when allocating more that 4000
    bytes in one go. */
 
@@ -224,11 +359,9 @@ do {								\
 	     ? "discard" : "same_size");			\
 } while (0)
 
-#undef ASM_COMMENT_START
-#define ASM_COMMENT_START " #"
-
 /* DWARF2 Unwinding doesn't work with exception handling yet. */
 #define DWARF2_UNWIND_INFO 0
 
 /* Don't assume anything about the header files. */
 #define NO_IMPLICIT_EXTERN_C
+
Index: gcc/config/i386/mingw32.h
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/config/i386/mingw32.h,v
retrieving revision 1.3
diff -u -3 -p -r1.3 mingw32.h
--- gcc/config/i386/mingw32.h	1998/04/04 17:38:59	1.3
+++ gcc/config/i386/mingw32.h	1998/06/28 00:04:50
@@ -31,6 +31,7 @@ Boston, MA 02111-1307, USA. */
   -D__MINGW32__ -DWINNT  -D_X86_=1 -D__STDC__=1\
   -D__stdcall=__attribute__((__stdcall__)) \
   -D__cdecl=__attribute__((__cdecl__)) \
+  -D__declspec(x)=__attribute__((x)) \
   -Asystem(winnt) -Acpu(i386) -Amachine(i386)"
 
 /* Specific a different directory for the standard include files.  */
Index: gcc/config/i386/winnt.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/config/i386/winnt.c,v
retrieving revision 1.3
diff -u -3 -p -r1.3 winnt.c
--- gcc/config/i386/winnt.c	1998/05/13 14:04:08	1.3
+++ gcc/config/i386/winnt.c	1998/06/28 00:41:16
@@ -28,6 +28,351 @@ Boston, MA 02111-1307, USA.  */
 #include "tree.h"
 #include "flags.h"
 
+/* i386/PE specific attribute support.
+
+   i386/PE has two new attributes:
+   dllexport - for exporting a function/variable that will live in a dll
+   dllimport - for importing a function/variable from a dll
+
+   Microsoft allows multiple declspecs in one __declspec, separating
+   them with spaces.  We do NOT support this.  Instead, use __declspec
+   multiple times.
+*/
+
+/* Return nonzero if ATTR is a valid attribute for DECL.
+   ATTRIBUTES are any existing attributes and ARGS are the arguments
+   supplied with ATTR.  */
+
+int
+i386_pe_valid_decl_attribute_p (decl, attributes, attr, args)
+     tree decl;
+     tree attributes;
+     tree attr;
+     tree args;
+{
+  if (args != NULL_TREE)
+    return 0;
+
+  if (is_attribute_p ("dllexport", attr))
+    return 1;
+  if (is_attribute_p ("dllimport", attr))
+    return 1;
+
+  return i386_valid_decl_attribute_p (decl, attributes, attr, args);
+}
+
+/* Merge attributes in decls OLD and NEW.
+
+   This handles the following situation:
+
+   __declspec (dllimport) int foo;
+   int foo;
+
+   The second instance of `foo' nullifies the dllimport.  */
+
+tree
+i386_pe_merge_decl_attributes (old, new)
+     tree old, new;
+{
+  tree a;
+  int delete_dllimport_p;
+
+  old = DECL_MACHINE_ATTRIBUTES (old);
+  new = DECL_MACHINE_ATTRIBUTES (new);
+
+  /* What we need to do here is remove from `old' dllimport if it doesn't
+     appear in `new'.  dllimport behaves like extern: if a declaration is
+     marked dllimport and a definition appears later, then the object
+     is not dllimport'd.  */
+
+  if (lookup_attribute ("dllimport", old) != NULL_TREE
+      && lookup_attribute ("dllimport", new) == NULL_TREE)
+    delete_dllimport_p = 1;
+  else
+    delete_dllimport_p = 0;
+
+  a = merge_attributes (old, new);
+
+  if (delete_dllimport_p)
+    {
+      tree prev,t;
+
+      /* Scan the list for dllimport and delete it.  */
+      for (prev = NULL_TREE, t = a; t; prev = t, t = TREE_CHAIN (t))
+	{
+	  if (is_attribute_p ("dllimport", TREE_PURPOSE (t)))
+	    {
+	      if (prev == NULL_TREE)
+		a = TREE_CHAIN (a);
+	      else
+		TREE_CHAIN (prev) = TREE_CHAIN (t);
+	      break;
+	    }
+	}
+    }
+
+  return a;
+}
+
+/* Check a type that has a virtual table, and see if any virtual methods are
+   marked for import or export, and if so, arrange for the vtable to
+   be imported or exported.  */
+
+static int
+i386_pe_check_vtable_importexport (type)
+     tree type;
+{
+  tree methods = TYPE_METHODS (type);
+  tree fndecl;
+
+  if (TREE_CODE (methods) == FUNCTION_DECL)
+    fndecl = methods;
+  else if (TREE_VEC_ELT (methods, 0) != NULL_TREE)
+    fndecl = TREE_VEC_ELT (methods, 0);
+  else
+    fndecl = TREE_VEC_ELT (methods, 1);
+
+  while (fndecl)
+    {
+      if (DECL_VIRTUAL_P (fndecl) || DECL_VINDEX (fndecl) != NULL_TREE)
+	{
+	  tree exp = lookup_attribute ("dllimport",
+				       DECL_MACHINE_ATTRIBUTES (fndecl));
+	  if (exp == 0)
+	    exp = lookup_attribute ("dllexport",
+				    DECL_MACHINE_ATTRIBUTES (fndecl));
+	  if (exp)
+	    return 1;
+	}
+
+      fndecl = TREE_CHAIN (fndecl);
+    }
+
+  return 0;
+}
+
+/* Return non-zero if DECL is a dllexport'd object.  */
+
+#if 0
+tree current_class_type; /* FIXME */
+#endif
+
+int
+i386_pe_dllexport_p (decl)
+     tree decl;
+{
+  tree exp;
+
+  if (TREE_CODE (decl) != VAR_DECL
+      && TREE_CODE (decl) != FUNCTION_DECL)
+    return 0;
+  exp = lookup_attribute ("dllexport", DECL_MACHINE_ATTRIBUTES (decl));
+  if (exp)
+    return 1;
+
+#if 0 /* This was a hack to get vtable's exported or imported since only one
+	 copy of them is ever output.  Disabled pending better solution.  */
+  /* For C++, the vtables might have to be marked.  */
+  if (TREE_CODE (decl) == VAR_DECL && DECL_VIRTUAL_P (decl))
+    {
+      if (TREE_PUBLIC (decl)
+	  && DECL_EXTERNAL (decl) == 0
+	  && (DECL_CONTEXT (decl)
+	      ? i386_pe_check_vtable_importexport (DECL_CONTEXT (decl))
+	      : current_class_type
+	      ? i386_pe_check_vtable_importexport (current_class_type)
+	      : 0)
+	  )
+	return 1;
+    }
+#endif
+
+  return 0;
+}
+
+/* Return non-zero if DECL is a dllimport'd object.  */
+
+int
+i386_pe_dllimport_p (decl)
+     tree decl;
+{
+  tree imp;
+
+  if (TREE_CODE (decl) == FUNCTION_DECL
+      && TARGET_NOP_FUN_DLLIMPORT)
+    return 0;
+
+  if (TREE_CODE (decl) != VAR_DECL
+      && TREE_CODE (decl) != FUNCTION_DECL)
+    return 0;
+  imp = lookup_attribute ("dllimport", DECL_MACHINE_ATTRIBUTES (decl));
+  if (imp)
+    return 1;
+
+#if 0 /* This was a hack to get vtable's exported or imported since only one
+	 copy of them is ever output.  Disabled pending better solution.  */
+  /* For C++, the vtables might have to be marked.  */
+  if (TREE_CODE (decl) == VAR_DECL && DECL_VIRTUAL_P (decl))
+    {
+      if (TREE_PUBLIC (decl)
+	  && DECL_EXTERNAL (decl)
+	  && (DECL_CONTEXT (decl)
+	      ? i386_pe_check_vtable_importexport (DECL_CONTEXT (decl))
+	      : current_class_type
+	      ? i386_pe_check_vtable_importexport (current_class_type)
+	      : 0)
+	  )
+	return 1;
+    }
+#endif
+
+  return 0;
+}
+
+/* Return non-zero if SYMBOL is marked as being dllexport'd.  */
+
+int
+i386_pe_dllexport_name_p (symbol)
+     char *symbol;
+{
+  return symbol[0] == '@' && symbol[1] == 'e' && symbol[2] == '.';
+}
+
+/* Return non-zero if SYMBOL is marked as being dllimport'd.  */
+
+int
+i386_pe_dllimport_name_p (symbol)
+     char *symbol;
+{
+  return symbol[0] == '@' && symbol[1] == 'i' && symbol[2] == '.';
+}
+
+/* Mark a DECL as being dllexport'd.
+   Note that we override the previous setting (eg: dllimport).  */
+
+void
+i386_pe_mark_dllexport (decl)
+     tree decl;
+{
+  char *oldname, *newname;
+  rtx rtlname;
+  tree idp;
+
+  rtlname = XEXP (DECL_RTL (decl), 0);
+  if (GET_CODE (rtlname) == SYMBOL_REF)
+    oldname = XSTR (rtlname, 0);
+  else if (GET_CODE (rtlname) == MEM
+	   && GET_CODE (XEXP (rtlname, 0)) == SYMBOL_REF)
+    oldname = XSTR (XEXP (rtlname, 0), 0);
+  else
+    abort ();
+  if (i386_pe_dllimport_name_p (oldname))
+    oldname += 9;
+  else if (i386_pe_dllexport_name_p (oldname))
+    return; /* already done */
+
+  newname = alloca (strlen (oldname) + 4);
+  sprintf (newname, "@e.%s", oldname);
+
+  /* We pass newname through get_identifier to ensure it has a unique
+     address.  RTL processing can sometimes peek inside the symbol ref
+     and compare the string's addresses to see if two symbols are
+     identical.  */
+  idp = get_identifier (newname);
+
+  XEXP (DECL_RTL (decl), 0) =
+    gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (idp));
+}
+
+/* Mark a DECL as being dllimport'd.  */
+
+void
+i386_pe_mark_dllimport (decl)
+     tree decl;
+{
+  char *oldname, *newname;
+  tree idp;
+  rtx rtlname, newrtl;
+
+  rtlname = XEXP (DECL_RTL (decl), 0);
+  if (GET_CODE (rtlname) == SYMBOL_REF)
+    oldname = XSTR (rtlname, 0);
+  else if (GET_CODE (rtlname) == MEM
+	   && GET_CODE (XEXP (rtlname, 0)) == SYMBOL_REF)
+    oldname = XSTR (XEXP (rtlname, 0), 0);
+  else
+    abort ();
+  if (i386_pe_dllexport_name_p (oldname))
+    {
+      error ("`%s' declared as both exported to and imported from a DLL.",
+             IDENTIFIER_POINTER (DECL_NAME (decl)));
+      return;
+    }
+  else if (i386_pe_dllimport_name_p (oldname))
+    {
+      /* Already done, but force correct linkage since the redeclaration 
+         might have omitted explicit extern.  Sigh.  */
+      if (TREE_CODE (decl) == VAR_DECL
+	  /* ??? Is this test for vtables needed?  */
+	  && !DECL_VIRTUAL_P (decl))
+	{
+	  DECL_EXTERNAL (decl) = 1;
+	  TREE_PUBLIC (decl) = 1;
+	}
+      return;
+    }
+
+  /* ??? One can well ask why we're making these checks here,
+     and that would be a good question.  */
+
+  /* Imported variables can't be initialized. Note that C++ classes
+     are marked initial, so we need to check.  */
+  if (TREE_CODE (decl) == VAR_DECL
+      && !DECL_VIRTUAL_P (decl)
+      && (DECL_INITIAL (decl)
+          && ! TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl))))
+    {
+      error_with_decl (decl, "initialized variable `%s' is marked dllimport");
+      return;
+    }
+  /* Nor can they be static.  */
+  if (TREE_CODE (decl) == VAR_DECL
+      /* ??? Is this test for vtables needed?  */
+      && !DECL_VIRTUAL_P (decl)
+      && 0 /*???*/)
+    {
+      error_with_decl (decl, "static variable `%s' is marked dllimport");
+      return;
+    }
+
+  /* `extern' needn't be specified with dllimport.
+     Specify `extern' now and hope for the best.  Sigh.  */
+  if (TREE_CODE (decl) == VAR_DECL
+      /* ??? Is this test for vtables needed?  */
+      && !DECL_VIRTUAL_P (decl))
+    {
+      DECL_EXTERNAL (decl) = 1;
+      TREE_PUBLIC (decl) = 1;
+    }
+
+  newname = alloca (strlen (oldname) + 11);
+  sprintf (newname, "@i._imp__%s", oldname);
+
+  /* We pass newname through get_identifier to ensure it has a unique
+     address.  RTL processing can sometimes peek inside the symbol ref
+     and compare the string's addresses to see if two symbols are
+     identical.  */
+  idp = get_identifier (newname);
+
+  newrtl = gen_rtx (MEM, Pmode,
+		    gen_rtx (SYMBOL_REF, Pmode,
+			     IDENTIFIER_POINTER (idp)));
+  XEXP (DECL_RTL (decl), 0) = newrtl;
+
+  /* Can't treat a pointer to this as a constant address */
+  DECL_NON_ADDR_CONST_P (decl) = 1;
+}
+
 /* Return string which is the former assembler name modified with a 
    suffix consisting of an atsign (@) followed by the number of bytes of 
    arguments */
@@ -66,6 +411,59 @@ gen_stdcall_suffix (decl)
   return IDENTIFIER_POINTER (get_identifier (newsym));
 }
 
+/* Cover function to implement ENCODE_SECTION_INFO.  */
+
+void
+i386_pe_encode_section_info (decl)
+     tree decl;
+{
+  /* This bit is copied from i386.h.  */
+  if (optimize > 0 && TREE_CONSTANT (decl)
+      && (!flag_writable_strings || TREE_CODE (decl) != STRING_CST))
+    {
+      rtx rtl = (TREE_CODE_CLASS (TREE_CODE (decl)) != 'd'
+                 ? TREE_CST_RTL (decl) : DECL_RTL (decl));
+      SYMBOL_REF_FLAG (XEXP (rtl, 0)) = 1;
+    }
+
+  if (TREE_CODE (decl) == FUNCTION_DECL)
+    if (lookup_attribute ("stdcall",
+			  TYPE_ATTRIBUTES (TREE_TYPE (decl))))
+      XEXP (DECL_RTL (decl), 0) = 
+	gen_rtx (SYMBOL_REF, Pmode, gen_stdcall_suffix (decl));
+
+  /* Mark the decl so we can tell from the rtl whether the object is
+     dllexport'd or dllimport'd.  */
+
+  if (i386_pe_dllexport_p (decl))
+    i386_pe_mark_dllexport (decl);
+  else if (i386_pe_dllimport_p (decl))
+    i386_pe_mark_dllimport (decl);
+  /* It might be that DECL has already been marked as dllimport, but a
+     subsequent definition nullified that.  The attribute is gone but
+     DECL_RTL still has @i._imp__foo.  We need to remove that. Ditto
+     for the DECL_NON_ADDR_CONST_P flag.  */
+  else if ((TREE_CODE (decl) == FUNCTION_DECL
+	    || TREE_CODE (decl) == VAR_DECL)
+	   && DECL_RTL (decl) != NULL_RTX
+	   && GET_CODE (DECL_RTL (decl)) == MEM
+	   && GET_CODE (XEXP (DECL_RTL (decl), 0)) == MEM
+	   && GET_CODE (XEXP (XEXP (DECL_RTL (decl), 0), 0)) == SYMBOL_REF
+	   && i386_pe_dllimport_name_p (XSTR (XEXP (XEXP (DECL_RTL (decl), 0), 0), 0)))
+    {
+      char *oldname = XSTR (XEXP (XEXP (DECL_RTL (decl), 0), 0), 0);
+      tree idp = get_identifier (oldname + 9);
+      rtx newrtl = gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (idp));
+
+      XEXP (DECL_RTL (decl), 0) = newrtl;
+
+      DECL_NON_ADDR_CONST_P (decl) = 0;
+
+      /* We previously set TREE_PUBLIC and DECL_EXTERNAL.
+	 We leave these alone for now.  */
+    }
+}
+
 /* Cover function for UNIQUE_SECTION.  */
 
 void
@@ -77,6 +475,8 @@ i386_pe_unique_section (decl, reloc)
   char *name,*string,*prefix;
 
   name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+  /* Strip off any encoding in fnname.  */
+  STRIP_NAME_ENCODING (name, name);
 
   /* The object is put in, for example, section .text$foo.
      The linker will then ultimately place them in .text



More information about the Gcc-patches mailing list