diff options
author | Mumit Khan <khan@xraylith.wisc.edu> | 1998-07-07 00:05:27 +0000 |
---|---|---|
committer | Jeff Law <law@gcc.gnu.org> | 1998-07-06 18:05:27 -0600 |
commit | 27da1b4d5a1c30a60a38f725f2119fda88854d88 (patch) | |
tree | 75c3f6b8a5b39fd43548abba6d1860adc4b7c3fe /gcc/config/i386/winnt.c | |
parent | bceb30e77bd19db8debc5182b75018c169acd6ff (diff) | |
download | gcc-27da1b4d5a1c30a60a38f725f2119fda88854d88.zip gcc-27da1b4d5a1c30a60a38f725f2119fda88854d88.tar.gz gcc-27da1b4d5a1c30a60a38f725f2119fda88854d88.tar.bz2 |
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.
From-SVN: r20983
Diffstat (limited to 'gcc/config/i386/winnt.c')
-rw-r--r-- | gcc/config/i386/winnt.c | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/gcc/config/i386/winnt.c b/gcc/config/i386/winnt.c index 2f4cc88..95daf14 100644 --- a/gcc/config/i386/winnt.c +++ b/gcc/config/i386/winnt.c @@ -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 |