#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 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 (OPT_Wattributes, "%qs attribute only applies to variables",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
return NULL_TREE;
}
tree
ix86_handle_selectany_attribute (tree *node, tree name,
tree args ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED,
bool *no_add_attrs)
{
if (TREE_CODE (*node) != VAR_DECL || !TREE_PUBLIC (*node))
{
error ("%qs attribute applies only to initialized variables"
" with external linkage", IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
return NULL_TREE;
}
static tree
associated_type (tree decl)
{
return (DECL_CONTEXT (decl) && TYPE_P (DECL_CONTEXT (decl)))
? DECL_CONTEXT (decl) : NULL_TREE;
}
bool
i386_pe_dllexport_p (tree decl)
{
if (TREE_CODE (decl) != VAR_DECL
&& TREE_CODE (decl) != FUNCTION_DECL)
return false;
if (lookup_attribute ("dllexport", DECL_ATTRIBUTES (decl)))
return true;
if (associated_type (decl)
&& lookup_attribute ("dllexport",
TYPE_ATTRIBUTES (associated_type (decl))))
return i386_pe_type_dllexport_p (decl);
return false;
}
bool
i386_pe_dllimport_p (tree decl)
{
if (TREE_CODE (decl) != VAR_DECL
&& TREE_CODE (decl) != FUNCTION_DECL)
return false;
if (DECL_DLLIMPORT_P (decl)
&& lookup_attribute ("dllimport", DECL_ATTRIBUTES (decl)))
{
if (!DECL_EXTERNAL (decl))
{
error ("%q+D: definition is marked as dllimport", decl);
DECL_DLLIMPORT_P (decl) = 0;
return false;
}
return true;
}
else if (associated_type (decl)
&& lookup_attribute ("dllimport",
TYPE_ATTRIBUTES (associated_type (decl))))
return i386_pe_type_dllimport_p (decl);
return false;
}
bool
i386_pe_valid_dllimport_attribute_p (tree decl)
{
if (TARGET_NOP_FUN_DLLIMPORT && TREE_CODE (decl) == FUNCTION_DECL)
return false;
return true;
}
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) == MEM)
rtlname = XEXP (rtlname, 0);
gcc_assert (GET_CODE (rtlname) == SYMBOL_REF);
oldname = XSTR (rtlname, 0);
if (i386_pe_dllimport_name_p (oldname))
{
warning (0, "inconsistent dll linkage for %q+D, dllexport assumed",
decl);
oldname += strlen (DLL_IMPORT_PREFIX);
}
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));
SET_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) == MEM)
rtlname = XEXP (rtlname, 0);
gcc_assert (GET_CODE (rtlname) == SYMBOL_REF);
oldname = XSTR (rtlname, 0);
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))
{
gcc_assert (DECL_EXTERNAL (decl) && TREE_PUBLIC (decl)
&& DECL_DLLIMPORT_P (decl));
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));
SET_SYMBOL_REF_DECL (symref, decl);
newrtl = gen_rtx_MEM (Pmode,symref);
XEXP (DECL_RTL (decl), 0) = newrtl;
DECL_DLLIMPORT_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);
}
}
else if (TREE_CODE (decl) == VAR_DECL
&& lookup_attribute ("selectany", DECL_ATTRIBUTES (decl)))
{
if (DECL_INITIAL (decl)
|| TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl)))
make_decl_one_only (decl);
else
error ("%q+D:'selectany' attribute applies only to initialized objects",
decl);
}
if (i386_pe_dllexport_p (decl))
i386_pe_mark_dllexport (decl);
else if (i386_pe_dllimport_p (decl))
i386_pe_mark_dllimport (decl);
else
gcc_assert (!((TREE_CODE (decl) == FUNCTION_DECL
|| TREE_CODE (decl) == VAR_DECL)
&& rtl != NULL_RTX
&& GET_CODE (rtl) == MEM
&& GET_CODE (XEXP (rtl, 0)) == MEM
&& GET_CODE (XEXP (XEXP (rtl, 0), 0)) == SYMBOL_REF
&& i386_pe_dllimport_name_p (XSTR (XEXP (XEXP (rtl, 0), 0), 0))));
}
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 ("%q+D causes a section type conflict", decl);
}
return flags;
}
void
i386_pe_asm_named_section (const char *name, unsigned int flags,
tree decl)
{
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)
{
bool discard = (flags & SECTION_CODE)
|| lookup_attribute ("selectany",
DECL_ATTRIBUTES (decl));
fprintf (asm_out_file, "\t.linkonce %s\n",
(discard ? "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;
tree decl;
const char *name;
};
static GTY(()) struct extern_list *extern_head;
void
i386_pe_record_external_function (tree decl, const char *name)
{
struct extern_list *p;
p = (struct extern_list *) ggc_alloc (sizeof *p);
p->next = extern_head;
p->decl = decl;
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 = p->decl;
if (! TREE_ASM_WRITTEN (decl)
&& TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (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"