#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "output.h"
#include "tree.h"
#include "flags.h"
#include "tm_p.h"
#include "toplev.h"
#include "hashtab.h"
#include "ggc.h"
static tree associated_type (tree);
static tree gen_stdcall_or_fastcall_suffix (tree, bool);
static int i386_pe_dllexport_p (tree);
static int i386_pe_dllimport_p (tree);
static void i386_pe_mark_dllexport (tree);
static void i386_pe_mark_dllimport (tree);
#ifndef DLL_IMPORT_PREFIX
#define DLL_IMPORT_PREFIX "#i."
#endif
#ifndef DLL_EXPORT_PREFIX
#define DLL_EXPORT_PREFIX "#e."
#endif
tree
ix86_handle_shared_attribute (tree *node, tree name,
tree args ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
{
if (TREE_CODE (*node) != VAR_DECL)
{
warning ("%qs attribute only applies to variables",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
return NULL_TREE;
}
static tree
associated_type (tree decl)
{
tree t = NULL_TREE;
if (TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
{
if (!DECL_ARTIFICIAL (decl) || DECL_COMDAT (decl))
t = TYPE_MAIN_VARIANT
(TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (decl)))));
}
else if (DECL_CONTEXT (decl) && TYPE_P (DECL_CONTEXT (decl)))
t = DECL_CONTEXT (decl);
return t;
}
static int
i386_pe_dllexport_p (tree decl)
{
tree exp;
if (TREE_CODE (decl) != VAR_DECL
&& TREE_CODE (decl) != FUNCTION_DECL)
return 0;
exp = lookup_attribute ("dllexport", DECL_ATTRIBUTES (decl));
if (exp)
return 1;
if (associated_type (decl))
{
exp = lookup_attribute ("dllexport",
TYPE_ATTRIBUTES (associated_type (decl)));
if (exp)
return 1;
}
return 0;
}
static int
i386_pe_dllimport_p (tree decl)
{
tree imp;
int context_imp = 0;
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_ATTRIBUTES (decl));
if (!imp && associated_type (decl))
{
imp = lookup_attribute ("dllimport",
TYPE_ATTRIBUTES (associated_type (decl)));
if (imp)
context_imp = 1;
}
if (imp)
{
if (TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl)
&& !DECL_INLINE (decl))
{
if (!DECL_ARTIFICIAL (decl))
warning ("%Jfunction '%D' is defined after prior declaration "
"as dllimport: attribute ignored", decl, decl);
return 0;
}
else if (TREE_CODE (decl) == FUNCTION_DECL && DECL_INLINE (decl))
{
if (extra_warnings)
warning ("%Jinline function '%D' is declared as dllimport: "
"attribute ignored.", decl, decl);
return 0;
}
else if (TREE_CODE (decl) == VAR_DECL
&& TREE_STATIC (decl) && TREE_PUBLIC (decl)
&& !DECL_EXTERNAL (decl) && context_imp)
{
if (!DECL_VIRTUAL_P (decl))
error ("%Jdefinition of static data member '%D' of "
"dllimport'd class.", decl, decl);
return 0;
}
else if (TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE
&& (DECL_VIRTUAL_P (decl) || DECL_ARTIFICIAL (decl)))
return 0;
return 1;
}
return 0;
}
int
i386_pe_dllexport_name_p (const char *symbol)
{
return (strncmp (DLL_EXPORT_PREFIX, symbol,
strlen (DLL_EXPORT_PREFIX)) == 0);
}
int
i386_pe_dllimport_name_p (const char *symbol)
{
return (strncmp (DLL_IMPORT_PREFIX, symbol,
strlen (DLL_IMPORT_PREFIX)) == 0);
}
static void
i386_pe_mark_dllexport (tree decl)
{
const char *oldname;
char *newname;
rtx rtlname;
rtx symref;
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))
{
warning ("%Jinconsistent dll linkage for '%D', dllexport assumed.",
decl, decl);
oldname += strlen (DLL_IMPORT_PREFIX);
DECL_NON_ADDR_CONST_P (decl) = 0;
}
else if (i386_pe_dllexport_name_p (oldname))
return;
newname = alloca (strlen (DLL_EXPORT_PREFIX) + strlen (oldname) + 1);
sprintf (newname, "%s%s", DLL_EXPORT_PREFIX, oldname);
idp = get_identifier (newname);
symref = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (idp));
SYMBOL_REF_DECL (symref) = decl;
XEXP (DECL_RTL (decl), 0) = symref;
}
static void
i386_pe_mark_dllimport (tree decl)
{
const char *oldname;
char *newname;
tree idp;
rtx rtlname, newrtl;
rtx symref;
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 ("%qs declared as both exported to and imported from a DLL",
IDENTIFIER_POINTER (DECL_NAME (decl)));
return;
}
else if (i386_pe_dllimport_name_p (oldname))
{
if (!DECL_EXTERNAL (decl) || !TREE_PUBLIC (decl))
{
error ("%Jfailure in redeclaration of '%D': dllimport'd "
"symbol lacks external linkage.", decl, decl);
abort();
}
return;
}
newname = alloca (strlen (DLL_IMPORT_PREFIX) + strlen (oldname) + 1);
sprintf (newname, "%s%s", DLL_IMPORT_PREFIX, oldname);
idp = get_identifier (newname);
symref = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (idp));
SYMBOL_REF_DECL (symref) = decl;
newrtl = gen_rtx_MEM (Pmode,symref);
XEXP (DECL_RTL (decl), 0) = newrtl;
DECL_NON_ADDR_CONST_P (decl) = 1;
}
static tree
gen_stdcall_or_fastcall_suffix (tree decl, bool fastcall)
{
int total = 0;
const char *asmname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
char *newsym;
char *p;
tree formal_type;
if (*asmname == '*' || strchr (asmname, '@'))
return DECL_ASSEMBLER_NAME (decl);
formal_type = TYPE_ARG_TYPES (TREE_TYPE (decl));
if (formal_type != NULL_TREE)
{
if (TREE_VALUE (tree_last (formal_type)) != void_type_node)
return DECL_ASSEMBLER_NAME (decl);
while (TREE_VALUE (formal_type) != void_type_node
&& COMPLETE_TYPE_P (TREE_VALUE (formal_type)))
{
int parm_size
= TREE_INT_CST_LOW (TYPE_SIZE (TREE_VALUE (formal_type)));
parm_size = ((parm_size + PARM_BOUNDARY - 1)
/ PARM_BOUNDARY * PARM_BOUNDARY);
total += parm_size;
formal_type = TREE_CHAIN (formal_type);\
}
}
newsym = alloca (1 + strlen (asmname) + 1 + 8 + 1);
p = newsym;
if (fastcall)
*p++ = FASTCALL_PREFIX;
sprintf (p, "%s@%d", asmname, total/BITS_PER_UNIT);
return get_identifier (newsym);
}
void
i386_pe_encode_section_info (tree decl, rtx rtl, int first)
{
default_encode_section_info (decl, rtl, first);
if (first && TREE_CODE (decl) == FUNCTION_DECL)
{
tree type_attributes = TYPE_ATTRIBUTES (TREE_TYPE (decl));
tree newid = NULL_TREE;
if (lookup_attribute ("stdcall", type_attributes))
newid = gen_stdcall_or_fastcall_suffix (decl, false);
else if (lookup_attribute ("fastcall", type_attributes))
newid = gen_stdcall_or_fastcall_suffix (decl, true);
if (newid != NULL_TREE)
{
rtx rtlname = XEXP (rtl, 0);
if (GET_CODE (rtlname) == MEM)
rtlname = XEXP (rtlname, 0);
XSTR (rtlname, 0) = IDENTIFIER_POINTER (newid);
change_decl_assembler_name (decl, newid);
}
}
if (i386_pe_dllexport_p (decl))
i386_pe_mark_dllexport (decl);
else if (i386_pe_dllimport_p (decl))
i386_pe_mark_dllimport (decl);
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)))
{
const char *oldname = XSTR (XEXP (XEXP (DECL_RTL (decl), 0), 0), 0);
tree idp = get_identifier (oldname + strlen (DLL_IMPORT_PREFIX));
rtx symref = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (idp));
SYMBOL_REF_DECL (symref) = decl;
XEXP (DECL_RTL (decl), 0) = symref;
DECL_NON_ADDR_CONST_P (decl) = 0;
if (DECL_INITIAL (decl) || !DECL_EXTERNAL (decl))
warning ("%J'%D' defined locally after being "
"referenced with dllimport linkage", decl, decl);
else
warning ("%J'%D' redeclared without dllimport attribute "
"after being referenced with dllimport linkage", decl, decl);
}
}
const char *
i386_pe_strip_name_encoding (const char *str)
{
if (strncmp (str, DLL_IMPORT_PREFIX, strlen (DLL_IMPORT_PREFIX))
== 0)
str += strlen (DLL_IMPORT_PREFIX);
else if (strncmp (str, DLL_EXPORT_PREFIX, strlen (DLL_EXPORT_PREFIX))
== 0)
str += strlen (DLL_EXPORT_PREFIX);
if (*str == '*')
str += 1;
return str;
}
const char *
i386_pe_strip_name_encoding_full (const char *str)
{
const char *p;
const char *name = i386_pe_strip_name_encoding (str);
if (*name == '@')
name++;
p = strchr (name, '@');
if (p)
return ggc_alloc_string (name, p - name);
return name;
}
void i386_pe_output_labelref (FILE *stream, const char *name)
{
if (strncmp (name, DLL_IMPORT_PREFIX, strlen (DLL_IMPORT_PREFIX))
== 0)
{
if (name[strlen (DLL_IMPORT_PREFIX)] == FASTCALL_PREFIX)
{
fprintf (stream, "__imp_%s",
i386_pe_strip_name_encoding (name));
}
else
{
fprintf (stream, "__imp__%s",
i386_pe_strip_name_encoding (name));
}
}
else if ((name[0] == FASTCALL_PREFIX)
|| (strncmp (name, DLL_EXPORT_PREFIX, strlen (DLL_EXPORT_PREFIX))
== 0
&& name[strlen (DLL_EXPORT_PREFIX)] == FASTCALL_PREFIX))
{
fprintf (stream, "%s",
i386_pe_strip_name_encoding (name));
}
else
{
fprintf (stream, "%s%s", USER_LABEL_PREFIX,
i386_pe_strip_name_encoding (name));
}
}
void
i386_pe_unique_section (tree decl, int reloc)
{
int len;
const char *name, *prefix;
char *string;
name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
name = i386_pe_strip_name_encoding_full (name);
if (TREE_CODE (decl) == FUNCTION_DECL)
prefix = ".text$";
else if (decl_readonly_section (decl, reloc))
prefix = ".rdata$";
else
prefix = ".data$";
len = strlen (name) + strlen (prefix);
string = alloca (len + 1);
sprintf (string, "%s%s", prefix, name);
DECL_SECTION_NAME (decl) = build_string (len, string);
}
#define SECTION_PE_SHARED SECTION_MACH_DEP
unsigned int
i386_pe_section_type_flags (tree decl, const char *name, int reloc)
{
static htab_t htab;
unsigned int flags;
unsigned int **slot;
if (!htab)
htab = htab_create (31, htab_hash_pointer, htab_eq_pointer, NULL);
if (decl && TREE_CODE (decl) == FUNCTION_DECL)
flags = SECTION_CODE;
else if (decl && decl_readonly_section (decl, reloc))
flags = 0;
else
{
flags = SECTION_WRITE;
if (decl && TREE_CODE (decl) == VAR_DECL
&& lookup_attribute ("shared", DECL_ATTRIBUTES (decl)))
flags |= SECTION_PE_SHARED;
}
if (decl && DECL_ONE_ONLY (decl))
flags |= SECTION_LINKONCE;
slot = (unsigned int **) htab_find_slot (htab, name, INSERT);
if (!*slot)
{
*slot = (unsigned int *) xmalloc (sizeof (unsigned int));
**slot = flags;
}
else
{
if (decl && **slot != flags)
error ("%J'%D' causes a section type conflict", decl, decl);
}
return flags;
}
void
i386_pe_asm_named_section (const char *name, unsigned int flags,
tree decl ATTRIBUTE_UNUSED)
{
char flagchars[8], *f = flagchars;
if ((flags & (SECTION_CODE | SECTION_WRITE)) == 0)
{
*f++ ='d';
*f++ ='r';
}
else
{
if (flags & SECTION_CODE)
*f++ = 'x';
if (flags & SECTION_WRITE)
*f++ = 'w';
if (flags & SECTION_PE_SHARED)
*f++ = 's';
}
*f = '\0';
fprintf (asm_out_file, "\t.section\t%s,\"%s\"\n", name, flagchars);
if (flags & SECTION_LINKONCE)
{
fprintf (asm_out_file, "\t.linkonce %s\n",
(flags & SECTION_CODE ? "discard" : "same_size"));
}
}
#include "gsyms.h"
void
i386_pe_declare_function_type (FILE *file, const char *name, int public)
{
fprintf (file, "\t.def\t");
assemble_name (file, name);
fprintf (file, ";\t.scl\t%d;\t.type\t%d;\t.endef\n",
public ? (int) C_EXT : (int) C_STAT,
(int) DT_FCN << N_BTSHFT);
}
struct extern_list GTY(())
{
struct extern_list *next;
const char *name;
};
static GTY(()) struct extern_list *extern_head;
void
i386_pe_record_external_function (const char *name)
{
struct extern_list *p;
p = (struct extern_list *) ggc_alloc (sizeof *p);
p->next = extern_head;
p->name = name;
extern_head = p;
}
struct export_list GTY(())
{
struct export_list *next;
const char *name;
int is_data;
};
static GTY(()) struct export_list *export_head;
void
i386_pe_record_exported_symbol (const char *name, int is_data)
{
struct export_list *p;
p = (struct export_list *) ggc_alloc (sizeof *p);
p->next = export_head;
p->name = name;
p->is_data = is_data;
export_head = p;
}
void
i386_pe_file_end (void)
{
struct extern_list *p;
ix86_file_end ();
for (p = extern_head; p != NULL; p = p->next)
{
tree decl;
decl = get_identifier (p->name);
if (! TREE_ASM_WRITTEN (decl) && TREE_SYMBOL_REFERENCED (decl))
{
TREE_ASM_WRITTEN (decl) = 1;
i386_pe_declare_function_type (asm_out_file, p->name,
TREE_PUBLIC (decl));
}
}
if (export_head)
{
struct export_list *q;
drectve_section ();
for (q = export_head; q != NULL; q = q->next)
{
fprintf (asm_out_file, "\t.ascii \" -export:%s%s\"\n",
i386_pe_strip_name_encoding (q->name),
(q->is_data) ? ",data" : "");
}
}
}
#include "gt-winnt.h"