/*****************************************************************************/ /* */ /* midipix target interfaces */ /* */ /* Copyright (C) 2014,2015,2019 Z. Gilboa */ /* */ /* This program is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ /* the Free Software Foundation, either version 3 of the License, or */ /* (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program. If not, see . */ /* */ /*****************************************************************************/ #include "config.h" #include "system.h" #include "coretypes.h" #include "tm.h" #include "rtl.h" #include "output.h" #include "tree.h" #include "flags.h" #include "target.h" #include "i386-protos.h" #include "function.h" #include "cgraph.h" /* common specs */ const int TARGET_NOP_FUN_DLLIMPORT = 0; const int use_pe_aligned_common = 1; const int flag_writable_rel_rdata = 0; /* pointer annotation */ static const char winnt_ptrsize_long[] = ".long"; static const char winnt_ptrsize_quad[] = ".quad"; /* target SEH providers */ #define TARGET_SEH_WINNT (TARGET_SEH && TARGET_NT64) #define TARGET_SEH_MIDIPIX (TARGET_SEH && TARGET_NT32) /* 64-bit seh provided by winnt */ void winnt_x86_64_pe_seh_init(FILE *); void winnt_x86_64_pe_seh_end_prologue(FILE *); void winnt_x86_64_pe_seh_unwind_emit(FILE *, rtx); /* 32-bit seh provided by midipix */ void midipix_i386_pe_seh_init(FILE *); void midipix_i386_pe_seh_end_prologue(FILE *); void midipix_i386_pe_seh_unwind_emit(FILE *, rtx); /* debugging */ unsigned int midipix_dbx_get_register_number(unsigned int n) { if (TARGET_NT64 ) return dbx64_register_map[n]; else if (write_symbols == DWARF2_DEBUG) return svr4_dbx_register_map[n]; else return dbx_register_map[n]; } unsigned int midipix_dbx_get_dwarf_frame_register_number(unsigned int n) { if (TARGET_NT64) return dbx64_register_map[n]; else return svr4_dbx_register_map[n]; } void midipix_asm_output_dwarf_offset( FILE * gas_exhaled, unsigned size, const char * label, section * section __attribute__((unused))) { #define PE_SIZE_IDENTIFIER 4 #define PEP_SIZE_IDENTIFIER 8 extern void assemble_name (FILE *, const char *); if ((size == PE_SIZE_IDENTIFIER) || (size == PEP_SIZE_IDENTIFIER)) { fputs ("\t.secrel32\t", gas_exhaled); assemble_name(gas_exhaled, label); /* in PE32+, section relocation need to be zero-extended */ if (size == PEP_SIZE_IDENTIFIER) fputs ("\n\t.int\t0", gas_exhaled); } else gcc_unreachable(); } /* assembler labels */ void midipix_asm_output_label_ref (FILE * gas_exhaled, const char * name) { extern const char * user_label_prefix; /* in __fastcall labels the @ prefix is already present */ if (*name != FASTCALL_PREFIX) fputs(user_label_prefix, gas_exhaled); fputs(name, gas_exhaled); } void midipix_asm_generate_internal_label ( char * strbuf, const char * prefix, unsigned long number) { sprintf (strbuf, "*%s%s%ld", LOCAL_LABEL_PREFIX, prefix, number); } static void midipix_pe_create_got_entry(FILE * stream, const char * name, tree decl) { int visibility; if (flag_assume_static_linking) return; const char * ptrsize = TARGET_64BIT ? winnt_ptrsize_quad : winnt_ptrsize_long; visibility = (decl->decl_with_vis.visibility == VISIBILITY_DEFAULT) ? default_visibility : decl->decl_with_vis.visibility; if ((visibility == VISIBILITY_PROTECTED) || (visibility == VISIBILITY_INTERNAL)) return; /* dsostrs$... */ fputs("\n",stream); fputs(GAS_SECTION,stream); fputs(GOTSTRS_SECTION_NAME,stream); fputs("$",stream); assemble_name(stream,name); fputs(GOT_SECTION_ATTR,stream); /* .symstr_... */ fputs("\n.symstr_",stream); assemble_name(stream,name); fputs(":\n\t",stream); fputs(".ascii\t\"",stream); assemble_name(stream,name); fputs("\\0\"\n",stream); fputs("\t.linkonce discard\n\n",stream); /* dsosyms$... */ fputs("\n",stream); fputs(GAS_SECTION,stream); fputs(GOTSYMS_SECTION_NAME,stream); fputs("$",stream); assemble_name(stream,name); fputs(GOT_SECTION_ATTR,stream); fputs("\n\t.linkonce discard\n\n",stream); /* __imp_... */ fputs("\n\t.global __imp_",stream); assemble_name(stream,name); fputs("\n__imp_",stream); assemble_name(stream,name); fputs(":\n\t",stream); fputs(ptrsize,stream); fputs("\t",stream); assemble_name(stream,name); fputs("\n\t",stream); fputs(".rva",stream); fputs("\t.symstr_",stream); assemble_name(stream,name); fputs("\n",stream); fputs(TARGET_64BIT ? "\t.long\t" "0" "\n" : "",stream); fputs("\n\n",stream); } static void midipix_asm_reestablish_section(FILE * asmout, section * sect) { section * tree_section; tree_section = in_section; in_section = 0; /* already in section? */ if (tree_section) switch_to_section(tree_section); /* re-establish .bss section */ else if (sect == bss_noswitch_section) switch_to_section(bss_section); /* re-establish .data section */ else if (sect == data_section) switch_to_section(data_section); /* no-switch section */ else if (sect && (sect->common.flags & SECTION_NOSWITCH)) (void)0; /* re-establish generic section */ else if (sect) switch_to_section(sect); /* re-establish .text section (default) */ else fputs("\t.text\n",asmout); } void midipix_asm_output_got_entry( FILE * asmout, const char * name, tree decl, section * sect) { if (!decl->base.public_flag) return; /* .got entry */ midipix_pe_create_got_entry(asmout,name,decl); /* re-establish current section */ midipix_asm_reestablish_section(asmout,sect); } void midipix_asm_declare_object_name(FILE * gas_exhaled, const char * name, tree decl) { extern void assemble_name (FILE *, const char *); extern void i386_pe_maybe_record_exported_symbol (tree, const char *, int); i386_pe_maybe_record_exported_symbol(decl, name, true); assemble_name(gas_exhaled, name); fputs (":\n", gas_exhaled); } void midipix_asm_drectve_section(void) { extern FILE * asm_out_file; extern section * in_section; in_section = (section *)0; fprintf (asm_out_file, "%s%s\n", GAS_SECTION, DRECTVE_SECTION_NAME); } int midipix_target_use_local_thunk_alias(tree decl) { return !DECL_ONE_ONLY(decl); } void midipix_asm_output_external( FILE * asmout __attribute__((unused)), tree decl, const char * name) { if (decl->base.code == FUNCTION_DECL) i386_pe_record_external_function( decl,name); } void midipix_asm_output_external_libcall (FILE * asmout, rtx fn) { i386_pe_declare_function_type( asmout, XSTR(fn,0), true); } void midipix_asm_output_def_from_decls( FILE * asmout, tree decl, tree target) { const char * sym; sym = (const char *)decl->decl_with_vis.assembler_name->identifier.id.str; i386_pe_maybe_record_exported_symbol( decl,sym,false); if (decl->base.code == FUNCTION_DECL) i386_pe_declare_function_type( asmout,sym,decl->base.public_flag); ASM_OUTPUT_DEF( asmout, sym, IDENTIFIER_POINTER(target)); if (!decl->decl_with_vis.weak_flag) midipix_asm_output_got_entry(asmout,sym,decl,0); } /* visibility */ void midipix_i386_pe_assemble_visibility ( tree decl __attribute__((unused)), int visible __attribute__((unused))) { /* cf. midipix_pe_create_got_entry */ } /* seh hook selectors */ void midipix_seh_hook__pe_seh_init(FILE * f) { if (TARGET_SEH_WINNT) winnt_x86_64_pe_seh_init(f); else if (TARGET_SEH_MIDIPIX) midipix_i386_pe_seh_init(f); } void midipix_seh_hook__pe_seh_end_prologue(FILE * f) { if (TARGET_SEH_WINNT) winnt_x86_64_pe_seh_end_prologue(f); else if (TARGET_SEH_MIDIPIX) midipix_i386_pe_seh_end_prologue(f); } void midipix_seh_hook__pe_seh_unwind_emit(FILE * f, rtx r) { if (TARGET_SEH_WINNT) winnt_x86_64_pe_seh_unwind_emit(f,r); else if (TARGET_SEH_MIDIPIX) midipix_i386_pe_seh_unwind_emit(f,r); } void midipix_seh_hook__pe_seh_emit_except_personality(rtx personality) { /* .__lang_personality_seh0 thunk */ fputs("\t.seh_handler\t",asm_out_file); fputs(".",asm_out_file); output_addr_const(asm_out_file,personality); fputs(", @unwind, @except\n",asm_out_file); } void midipix_seh_hook__pe_seh_init_sections(void) { exception_section = get_unnamed_section( 0,output_section_asm_op, "\t.seh_handlerdata"); } /* 64-bit seh hooks (winnt) */ void winnt_x86_64_pe_seh_init(FILE * f) { i386_pe_seh_init(f); } void winnt_x86_64_pe_seh_end_prologue(FILE * f) { i386_pe_seh_end_prologue(f); } void winnt_x86_64_pe_seh_unwind_emit(FILE * f, rtx r) { i386_pe_seh_unwind_emit(f,r); } /* 32-bit seh hooks (midipix) */ void midipix_i386_pe_seh_init(FILE * f __attribute__((unused))) { } void midipix_i386_pe_seh_end_prologue(FILE * f __attribute__((unused))) { } void midipix_i386_pe_seh_unwind_emit( FILE * f __attribute__((unused)), rtx r __attribute__((unused))) { } enum unwind_info_type midipix_except_unwind_info(struct gcc_options * opts) { (void)opts; return UI_SEH; } section * midipix_i386_pe_function_section ( tree decl, enum node_frequency freq, bool startup, bool exit) { extern section * default_function_section ( tree decl, enum node_frequency freq, bool startup, bool exit); const char * fname; bool weak; section * asm_section; char * secname; size_t size; /* .got entry */ fname = (const char *)decl->decl_with_vis.assembler_name->identifier.id.str; weak = (decl->decl_with_vis.dllimport_flag && decl->decl_with_vis.weak_flag); /* section entry */ #ifdef MIDIPIX_TARGET_DEBUG fputs ("\t# <", asm_out_file); assemble_name (asm_out_file, fname); fputs (": section>\n", asm_out_file); #endif if (weak) { #ifdef MIDIPIX_TARGET_DEBUG fputs("\t# (weak symbol section)\n",asm_out_file); #endif size = strlen(fname) + 6; /* .text$ */ if (!(secname = (char *)xmalloc(size))) gcc_unreachable(); sprintf(secname,".text$%s",fname); asm_section = get_named_section(decl, secname, 1); free(secname); } else if (decl) { #ifdef MIDIPIX_TARGET_DEBUG fputs("\t# (strong symbol section)\n",asm_out_file); #endif asm_section = default_function_section(decl, freq, startup, exit); } else { fputs("\t# should never get here?\n",asm_out_file); asm_section = (section *)0; } #ifdef MIDIPIX_TARGET_DEBUG fputs ("\t# \n", asm_out_file); #endif return asm_section; } void midipix_i386_pe_asm_output_aligned_decl_common( FILE * stream, tree decl, const char * name, HOST_WIDE_INT size, HOST_WIDE_INT align) { i386_pe_asm_output_aligned_decl_common( stream,decl,name,size,align); } void midipix_i386_pe_start_function (FILE * gas_exhaled, const char *name, tree decl) { #ifdef MIDIPIX_TARGET_DEBUG fputs ("\t# <", gas_exhaled); assemble_name (gas_exhaled, name); fputs (">\n", gas_exhaled); #endif i386_pe_start_function (gas_exhaled, name, decl); } void midipix_i386_pe_end_function (FILE * gas_exhaled, const char * name, tree decl) { if (cfun->is_thunk) { midipix_asm_output_got_entry(gas_exhaled,name,decl,0); return; } if (TARGET_SEH) fputs ("\t.seh_endproc\n", gas_exhaled); /* avoid duplicate GOT entries */ if (decl && !decl->decl_with_vis.weak_flag) midipix_asm_output_got_entry(gas_exhaled,name,decl,0); } void midipix_i386_pe_asm_weaken_decl( FILE * gas_exhaled, tree decl, const char * name, const char * alias __attribute__((unused))) { midipix_asm_output_got_entry(gas_exhaled,name,decl,0); fputs ("\t.weak\t", gas_exhaled); assemble_name (gas_exhaled, name); fputs ("\n", gas_exhaled); } void midipix_i386_pe_asm_weaken_label(FILE * gas_exhaled, const char * name) { midipix_i386_pe_asm_weaken_decl( gas_exhaled, (tree)0, name, (const char *)0); } int midipix_symbol_ref_dllimport_p(rtx symbol) { tree decl = SYMBOL_REF_DECL(symbol); if (!decl || !decl->base.public_flag || flag_assume_static_linking) return 0; return (decl->decl_with_vis.visibility == VISIBILITY_DEFAULT) || !decl->decl_with_vis.visibility_specified; } bool midipix_asm_assemble_integer(rtx x, unsigned int size, int aligned_p) { bool falt; int visibility; rtx xref; rtx xoff; tree decl; section * sect; const char * name; const char * ptrsize; const char * secname; const char * sortname; char namebuf[2048]; /* filter out c++ constructors */ if (flag_assume_static_linking) return default_assemble_integer(x,size,aligned_p); /* xref, xoff */ if (GET_CODE(x) == CONST) { xoff = XEXP(x, 0); /* --> PLUS */ xref = XEXP(xoff,0); /* --> ref */ xoff = XEXP(xoff,1); /* --> val */ } else { xref = x; xoff = 0; } /* extern symbol ref? */ falt = (GET_CODE(xref) == SYMBOL_REF) && ((decl = SYMBOL_REF_DECL(xref))) && (DECL_P(decl)) && (DECL_EXTERNAL(decl)) && (TREE_PUBLIC(decl)); /* visibility */ visibility = falt && decl && decl->decl_with_vis.visibility_specified ? decl->decl_with_vis.visibility : VISIBILITY_DEFAULT; /* defer? */ if (!falt || (visibility != VISIBILITY_DEFAULT)) return default_assemble_integer(x,size,aligned_p); /* name, ptrsize, sect */ name = XSTR(xref,0); ptrsize = TARGET_64BIT ? winnt_ptrsize_quad : winnt_ptrsize_long; sect = xoff ? data_section : in_section ? get_variable_section(decl,false) : 0; /* in-section mark */ fputs("1:\n",asm_out_file); /* in-section address reference */ fputs("\t",asm_out_file); fputs(ptrsize,asm_out_file); fputs("\t" "0",asm_out_file); /* array offset */ if (xoff && (INTVAL(xoff) < 0)) { fprintf(asm_out_file,HOST_WIDE_INT_PRINT_DEC,INTVAL(xoff)); } else if (xoff && INTVAL(xoff)) { fputs("+",asm_out_file); fprintf (asm_out_file,HOST_WIDE_INT_PRINT_DEC,INTVAL(xoff)); } /* back reference: section name */ if (!in_section) gcc_unreachable(); switch (SECTION_STYLE(in_section)) { case SECTION_NAMED: secname = (const char *)in_section->named.name; break; case SECTION_UNNAMED: if (in_section->common.flags & SECTION_BSS) gcc_unreachable(); else if (in_section->common.flags & SECTION_CODE) gcc_unreachable(); else if (in_section->common.flags & SECTION_LINKONCE) gcc_unreachable(); else if (in_section->common.flags & SECTION_WRITE) secname = ".data"; else secname = ".rdata"; break; default: gcc_unreachable(); } if (!(sortname = strchr(secname,'$'))) { sortname = secname; } else if (sortname-secname >= sizeof(namebuf)) { gcc_unreachable(); } else { memcpy(namebuf,secname,sortname-secname); namebuf[sortname-secname] = 0; sortname = namebuf; } /* .gotrefs section */ fputs("\n\n",asm_out_file); fputs(GAS_SECTION,asm_out_file); fputs(GOTREFS_SECTION_NAME,asm_out_file); fputs("$",asm_out_file); fputs(sortname,asm_out_file); fputs("\n",asm_out_file); /* back reference: section rva */ fputs("\t.rva",asm_out_file); fprintf(asm_out_file,"\t%s\n",secname); /* back reference: in-section offset */ fprintf(asm_out_file,"\t.long\t" "1b - %s" "\n",secname); /* sym reference */ fputs("\t.rva",asm_out_file); fputs("\t" "__imp_",asm_out_file); assemble_name(asm_out_file,name); fputs("\n\n",asm_out_file); /* re-establish current section */ midipix_asm_reestablish_section(asm_out_file,sect); /* all done */ return true; }