From patchwork Fri Oct 22 17:49:14 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Henderson X-Patchwork-Id: 68892 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 2536DB7043 for ; Sat, 23 Oct 2010 04:49:45 +1100 (EST) Received: (qmail 12895 invoked by alias); 22 Oct 2010 17:49:41 -0000 Received: (qmail 12856 invoked by uid 22791); 22 Oct 2010 17:49:31 -0000 X-SWARE-Spam-Status: No, hits=-4.7 required=5.0 tests=AWL, BAYES_40, RCVD_IN_DNSWL_HI, SPF_HELO_PASS, TW_AV, TW_CX, TW_FN, TW_JL, TW_MK, T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Fri, 22 Oct 2010 17:49:14 +0000 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o9MHnCA1018940 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Fri, 22 Oct 2010 13:49:12 -0400 Received: from stone.twiddle.home ([10.3.113.17]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id o9MHnBM8030109 for ; Fri, 22 Oct 2010 13:49:11 -0400 Message-ID: <4CC1CE9A.3090904@redhat.com> Date: Fri, 22 Oct 2010 10:49:14 -0700 From: Richard Henderson User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.9) Gecko/20100921 Fedora/3.1.4-1.fc13 Thunderbird/3.1.4 MIME-Version: 1.0 To: GCC Patches Subject: [RFC] Win64 SEH support X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org This is a consolidated patch for the work I've been doing on a (git) branch for SEH support. I've been intending to mash the branch patches together and separate them back out into logical units. To some extent I've already begun that with some of the patches that have already been applied to mainline, however, I realize that I'm running out of time to officially submit this before the end of stage1. Logically, there are 3 different portions in this patch: (1) SEH unwind info generation, (2) Changes to prologue/epilogue generation forced by limitations in unwind info representation, (3) SEH unwind runtime, and the symbol name changes and preprocessor defines within the compiler to support that. I think the only real question that's open is if we should have a configure switch a-la --enable-sjlj-exceptions in order to switch between dwarf2 and seh based exceptions. I personally don't think that's very useful. The main reason to want to use the x64 seh unwind info is that it is supported by the object format and the system similar to how PT_GNU_FRAME is supported under ELF -- one no longer has to register and de-register eh_frame data during shared library load and unload. Comments? r~ gcc/ada/raise-gcc.c | 15 gcc/config/i386/cygming.h | 40 + gcc/config/i386/i386-protos.h | 11 gcc/config/i386/i386.c | 197 ++++++ gcc/config/i386/i386.h | 9 gcc/config/i386/i386.md | 115 +--- gcc/config/i386/libgcc-cygming.ver | 28 gcc/config/i386/t-cygming | 3 gcc/config/i386/t-mingw-w64 | 5 gcc/config/i386/winnt.c | 460 ++++++++++++++++ gcc/coretypes.h | 3 gcc/dwarf2out.c | 10 gcc/expr.c | 3 gcc/opts.c | 6 gcc/testsuite/g++.dg/torture/stackalign/stackalign.exp | 12 gcc/testsuite/gcc.dg/torture/stackalign/stackalign.exp | 11 gcc/testsuite/lib/target-supports.exp | 11 gcc/unwind-c.c | 15 gcc/unwind-seh.c | 483 +++++++++++++++++ gcc/unwind-seh.h | 258 +++++++++ libjava/exception.cc | 20 libjava/libgcj.ver | 1 libobjc/exception.c | 15 libstdc++-v3/config/abi/pre/gnu.ver | 1 libstdc++-v3/libsupc++/eh_personality.cc | 20 25 files changed, 1612 insertions(+), 140 deletions(-) diff --git a/gcc/ada/raise-gcc.c b/gcc/ada/raise-gcc.c index 512ff36..d23ecf5 100644 --- a/gcc/ada/raise-gcc.c +++ b/gcc/ada/raise-gcc.c @@ -1029,6 +1029,8 @@ extern void __gnat_notify_unhandled_exception (void); #ifdef __USING_SJLJ_EXCEPTIONS__ #define PERSONALITY_FUNCTION __gnat_personality_sj0 +#elif defined(__SEH__) +#define PERSONALITY_FUNCTION __gnat_personality_imp #else #define PERSONALITY_FUNCTION __gnat_personality_v0 #endif @@ -1064,6 +1066,9 @@ typedef int version_arg_t; typedef _Unwind_Action phases_arg_t; #endif +#ifdef __SEH__ +static +#endif _Unwind_Reason_Code PERSONALITY_FUNCTION (version_arg_t version_arg, phases_arg_t phases_arg, @@ -1211,6 +1216,16 @@ __gnat_Unwind_ForcedUnwind (_Unwind_Exception *e, #endif /* __USING_SJLJ_EXCEPTIONS__ */ +#ifdef __SEH__ +EXCEPTION_DISPOSITION +__gnat_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame, + PCONTEXT ms_orig_context, + PDISPATCHER_CONTEXT ms_disp) +{ + return _GCC_specific_handler (ms_exc, this_frame, ms_orig_context, + ms_disp, __gnat_personality_imp); +} +#endif /* SEH */ #else /* ! IN_RTS */ diff --git a/gcc/config/i386/cygming.h b/gcc/config/i386/cygming.h index bb1a420..ed6b459 100644 --- a/gcc/config/i386/cygming.h +++ b/gcc/config/i386/cygming.h @@ -33,6 +33,29 @@ along with GCC; see the file COPYING3. If not see #define PREFERRED_DEBUGGING_TYPE DBX_DEBUG #endif +#undef TARGET_SEH +#define TARGET_SEH TARGET_64BIT_MS_ABI + +/* Win64 with SEH cannot represent DRAP stack frames. Disable its use. + Force the use of different mechanisms to allocate aligned local data. */ +#undef MAX_STACK_ALIGNMENT +#define MAX_STACK_ALIGNMENT (TARGET_SEH ? 128 : MAX_OFILE_ALIGNMENT) + +/* Support hooks for SEH. */ +#undef TARGET_EXCEPT_UNWIND_INFO +#define TARGET_EXCEPT_UNWIND_INFO i386_pe_seh_unwind_info +#undef TARGET_ASM_UNWIND_EMIT +#define TARGET_ASM_UNWIND_EMIT i386_pe_seh_unwind_emit +#undef TARGET_ASM_UNWIND_EMIT_BEFORE_INSN +#define TARGET_ASM_UNWIND_EMIT_BEFORE_INSN false +#undef TARGET_ASM_FUNCTION_END_PROLOGUE +#define TARGET_ASM_FUNCTION_END_PROLOGUE i386_pe_seh_end_prologue +#undef TARGET_ASM_EMIT_EXCEPT_PERSONALITY +#define TARGET_ASM_EMIT_EXCEPT_PERSONALITY i386_pe_seh_emit_except_personality +#undef TARGET_ASM_INIT_SECTIONS +#define TARGET_ASM_INIT_SECTIONS i386_pe_seh_init_sections +#define SUBTARGET_ASM_UNWIND_INIT i386_pe_seh_init + #undef DEFAULT_ABI #define DEFAULT_ABI (TARGET_64BIT ? MS_ABI : SYSV_ABI) @@ -104,6 +127,8 @@ along with GCC; see the file COPYING3. If not see { \ if (!TARGET_64BIT) \ builtin_define ("_X86_=1"); \ + if (TARGET_SEH) \ + builtin_define ("__SEH__"); \ builtin_assert ("system=winnt"); \ builtin_define ("__stdcall=__attribute__((__stdcall__))"); \ builtin_define ("__fastcall=__attribute__((__fastcall__))"); \ @@ -281,15 +306,12 @@ do { \ properly. If we are generating SDB debugging information, this will happen automatically, so we only need to handle other cases. */ #undef ASM_DECLARE_FUNCTION_NAME -#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \ - do \ - { \ - i386_pe_maybe_record_exported_symbol (DECL, NAME, 0); \ - if (write_symbols != SDB_DEBUG) \ - i386_pe_declare_function_type (FILE, NAME, TREE_PUBLIC (DECL)); \ - ASM_OUTPUT_FUNCTION_LABEL (FILE, NAME, DECL); \ - } \ - while (0) +#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \ + i386_pe_start_function (FILE, NAME, DECL) + +#undef ASM_DECLARE_FUNCTION_SIZE +#define ASM_DECLARE_FUNCTION_SIZE(FILE,NAME,DECL) \ + i386_pe_end_function (FILE, NAME, DECL) /* Add an external function to the list of functions to be declared at the end of the file. */ diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h index 9c10103..b2f4e63 100644 --- a/gcc/config/i386/i386-protos.h +++ b/gcc/config/i386/i386-protos.h @@ -225,8 +225,17 @@ extern void i386_pe_asm_output_aligned_decl_common (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT); extern void i386_pe_file_end (void); +extern void i386_pe_start_function (FILE *, const char *, tree); +extern void i386_pe_end_function (FILE *, const char *, tree); extern tree i386_pe_mangle_decl_assembler_name (tree, tree); +extern void i386_pe_seh_init (FILE *); +extern void i386_pe_seh_end_prologue (FILE *); +extern void i386_pe_seh_unwind_emit (FILE *, rtx); +extern void i386_pe_seh_emit_except_personality (rtx); +extern void i386_pe_seh_init_sections (void); +extern enum unwind_info_type i386_pe_seh_unwind_info (void); + /* In winnt-cxx.c and winnt-stubs.c */ extern void i386_pe_adjust_class_at_definition (tree); extern bool i386_pe_type_dllimport_p (tree); @@ -263,3 +272,5 @@ extern int asm_preferred_eh_data_format (int, int); #ifdef HAVE_ATTR_cpu extern enum attr_cpu ix86_schedule; #endif + +extern const char * ix86_output_call_insn (rtx insn, rtx call_op, int addr_op); diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 6668a62..a327b65 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -56,6 +56,7 @@ along with GCC; see the file COPYING3. If not see #include "debug.h" #include "dwarf2out.h" #include "sched-int.h" + static rtx legitimize_dllimport_symbol (rtx, bool); #ifndef CHECK_STACK_LIMIT @@ -1864,6 +1865,7 @@ struct ix86_frame HOST_WIDE_INT frame_pointer_offset; HOST_WIDE_INT hard_frame_pointer_offset; HOST_WIDE_INT stack_pointer_offset; + HOST_WIDE_INT hfp_save_offset; HOST_WIDE_INT reg_save_offset; HOST_WIDE_INT sse_reg_save_offset; @@ -3489,10 +3491,19 @@ ix86_option_override_internal (bool main_args_p) ix86_preferred_stack_boundary = PREFERRED_STACK_BOUNDARY_DEFAULT; if (ix86_preferred_stack_boundary_string) { + int min = (TARGET_64BIT ? 4 : 2); + int max = (TARGET_SEH ? 4 : 12); + i = atoi (ix86_preferred_stack_boundary_string); - if (i < (TARGET_64BIT ? 4 : 2) || i > 12) - error ("%spreferred-stack-boundary=%d%s is not between %d and 12", - prefix, i, suffix, TARGET_64BIT ? 4 : 2); + if (i < min || i > max) + { + if (min == max) + error ("%spreferred-stack-boundary%s is not supported " + "for this target", prefix, suffix); + else + error ("%spreferred-stack-boundary=%d%s is not between %d and %d", + prefix, i, suffix, min, max); + } else ix86_preferred_stack_boundary = (1 << i) * BITS_PER_UNIT; } @@ -3699,7 +3710,13 @@ ix86_option_override_internal (bool main_args_p) sorry ("-mfentry isn't supported for 32-bit in combination with -fpic"); flag_fentry = 0; } - if (flag_fentry < 0) + else if (TARGET_SEH) + { + if (flag_fentry == 0) + sorry ("-mno-fentry isn't compatible with SEH"); + flag_fentry = 1; + } + else if (flag_fentry < 0) { #if defined(PROFILE_BEFORE_PROLOGUE) flag_fentry = 1; @@ -5188,6 +5205,10 @@ ix86_asm_output_function_label (FILE *asm_out_file, const char *fname, fprintf (asm_out_file, ASM_LONG " %#x\n", filler_cc); } +#ifdef SUBTARGET_ASM_UNWIND_INIT + SUBTARGET_ASM_UNWIND_INIT (asm_out_file); +#endif + ASM_OUTPUT_LABEL (asm_out_file, fname); /* Output magic byte marker, if hot-patch attribute is set. */ @@ -8436,17 +8457,25 @@ ix86_compute_frame_layout (struct ix86_frame *frame) gcc_assert (preferred_alignment >= STACK_BOUNDARY / BITS_PER_UNIT); gcc_assert (preferred_alignment <= stack_alignment_needed); + /* For SEH we have to limit the amount of code movement into the prologue. + At present we do this via a BLOCKAGE, at which point there's very little + scheduling that can be done, which means that there's very little point + in doing anything except PUSHs. */ + if (TARGET_SEH) + cfun->machine->use_fast_prologue_epilogue = false; + /* During reload iteration the amount of registers saved can change. Recompute the value as needed. Do not recompute when amount of registers didn't change as reload does multiple calls to the function and does not expect the decision to change within single iteration. */ - if (!optimize_function_for_size_p (cfun) - && cfun->machine->use_fast_prologue_epilogue_nregs != frame->nregs) + else if (!optimize_function_for_size_p (cfun) + && cfun->machine->use_fast_prologue_epilogue_nregs != frame->nregs) { int count = frame->nregs; struct cgraph_node *node = cgraph_node (current_function_decl); cfun->machine->use_fast_prologue_epilogue_nregs = count; + /* The fast prologue uses move instead of push to save registers. This is significantly longer, but also executes faster as modern hardware can execute the moves in parallel, but can't do that for push/pop. @@ -8488,7 +8517,9 @@ ix86_compute_frame_layout (struct ix86_frame *frame) /* Skip saved base pointer. */ if (frame_pointer_needed) offset += UNITS_PER_WORD; + frame->hfp_save_offset = offset; + /* The traditional frame pointer location is at the top of the frame. */ frame->hard_frame_pointer_offset = offset; /* Register save area */ @@ -8571,6 +8602,27 @@ ix86_compute_frame_layout (struct ix86_frame *frame) else frame->red_zone_size = 0; frame->stack_pointer_offset -= frame->red_zone_size; + + /* The SEH frame pointer location is near the bottom of the frame. + This is enforced by the fact that the difference between the + stack pointer and the frame pointer is limited to 240 bytes in + the unwind data structure. */ + if (TARGET_SEH) + { + HOST_WIDE_INT diff; + + /* If we can leave the frame pointer where it is, do so. */ + diff = frame->stack_pointer_offset - frame->hard_frame_pointer_offset; + if (diff > 240 || (diff & 15) != 0) + { + /* Ideally we'd determine what portion of the local stack frame + (within the constraint of the lowest 240) is most heavily used. + But without that complication, simply bias the frame pointer + by 128 bytes so as to maximize the amount of the local stack + frame that is addressable with 8-bit offsets. */ + frame->hard_frame_pointer_offset = frame->stack_pointer_offset - 128; + } + } } /* This is semi-inlined memory_address_length, but simplified @@ -9503,7 +9555,8 @@ ix86_expand_prologue (void) /* Check if profiling is active and we shall use profiling before prologue variant. If so sorry. */ if (crtl->profile && flag_fentry != 0) - sorry ("ms_hook_prologue attribute isn't compatible with -mfentry for 32-bit"); + sorry ("ms_hook_prologue attribute isn't compatible " + "with -mfentry for 32-bit"); /* In ix86_asm_output_function_label we emitted: 8b ff movl.s %edi,%edi @@ -9632,14 +9685,16 @@ ix86_expand_prologue (void) insn = emit_insn (gen_push (hard_frame_pointer_rtx)); RTX_FRAME_RELATED_P (insn) = 1; - insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx); - RTX_FRAME_RELATED_P (insn) = 1; + if (m->fs.sp_offset == frame.hard_frame_pointer_offset) + { + insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx); + RTX_FRAME_RELATED_P (insn) = 1; - if (m->fs.cfa_reg == stack_pointer_rtx) - m->fs.cfa_reg = hard_frame_pointer_rtx; - gcc_assert (m->fs.sp_offset == frame.hard_frame_pointer_offset); - m->fs.fp_offset = m->fs.sp_offset; - m->fs.fp_valid = true; + if (m->fs.cfa_reg == stack_pointer_rtx) + m->fs.cfa_reg = hard_frame_pointer_rtx; + m->fs.fp_offset = m->fs.sp_offset; + m->fs.fp_valid = true; + } } int_registers_saved = (frame.nregs == 0); @@ -9792,12 +9847,15 @@ ix86_expand_prologue (void) insn = emit_insn (adjust_stack_insn (stack_pointer_rtx, stack_pointer_rtx, eax)); - if (m->fs.cfa_reg == stack_pointer_rtx) + /* Note that SEH directives need to continue tracking the stack + pointer even after the frame pointer has been set up. */ + if (m->fs.cfa_reg == stack_pointer_rtx || TARGET_SEH) { - m->fs.cfa_offset += allocate; + if (m->fs.cfa_reg == stack_pointer_rtx) + m->fs.cfa_offset += allocate; RTX_FRAME_RELATED_P (insn) = 1; - add_reg_note (insn, REG_CFA_ADJUST_CFA, + add_reg_note (insn, REG_FRAME_RELATED_EXPR, gen_rtx_SET (VOIDmode, stack_pointer_rtx, plus_constant (stack_pointer_rtx, -allocate))); @@ -9819,6 +9877,22 @@ ix86_expand_prologue (void) } gcc_assert (m->fs.sp_offset == frame.stack_pointer_offset); + /* If we havn't already set up the frame pointer, do so now. */ + if (frame_pointer_needed && !m->fs.fp_valid) + { + insn = ix86_gen_add3 (hard_frame_pointer_rtx, stack_pointer_rtx, + GEN_INT (frame.stack_pointer_offset + - frame.hard_frame_pointer_offset)); + insn = emit_insn (insn); + RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_CFA_ADJUST_CFA, NULL); + + if (m->fs.cfa_reg == stack_pointer_rtx) + m->fs.cfa_reg = hard_frame_pointer_rtx; + m->fs.fp_offset = frame.hard_frame_pointer_offset; + m->fs.fp_valid = true; + } + if (!int_registers_saved) ix86_emit_save_regs_using_mov (frame.reg_save_offset); if (frame.nsseregs) @@ -9888,6 +9962,11 @@ ix86_expand_prologue (void) /* Emit cld instruction if stringops are used in the function. */ if (TARGET_CLD && ix86_current_function_needs_cld) emit_insn (gen_cld ()); + + /* SEH requires that the prologue end within 256 bytes of the start of + the function. Prevent instruction schedules that would extend that. */ + if (TARGET_SEH) + emit_insn (gen_blockage ()); } /* Emit code to restore REG using a POP insn. */ @@ -10112,13 +10191,16 @@ ix86_expand_epilogue (int style) if (crtl->calls_eh_return && style != 2) frame.reg_save_offset -= 2 * UNITS_PER_WORD; + /* EH_RETURN requires the use of moves to function properly. */ + if (crtl->calls_eh_return) + restore_regs_via_mov = true; + /* SEH requires the use of pops to identify the epilogue. */ + else if (TARGET_SEH) + restore_regs_via_mov = false; /* If we're only restoring one register and sp is not valid then using a move instruction to restore the register since it's less work than reloading sp and popping the register. */ - if (!m->fs.sp_valid && frame.nregs <= 1) - restore_regs_via_mov = true; - /* EH_RETURN requires the use of moves to function properly. */ - else if (crtl->calls_eh_return) + else if (!m->fs.sp_valid && frame.nregs <= 1) restore_regs_via_mov = true; else if (TARGET_EPILOGUE_USING_MOVE && cfun->machine->use_fast_prologue_epilogue @@ -10230,6 +10312,23 @@ ix86_expand_epilogue (int style) } else { + /* SEH requires that the function end with (1) a stack adjustment + if necessary, (2) a sequence of pops, and (3) a return or + jump instruction. Prevent insns from the function body from + being scheduled into this sequence. */ + if (TARGET_SEH) + { + /* Prevent a catch region from being adjacent to the standard + epilogue sequence. Unfortuantely crtl->uses_eh_lsda nor + several other flags that would be interesting to test are + not yet set up. If we want to elide the nop when it isn't + strictly needed, we'll have to do it in the output template. */ + if (flag_exceptions) + emit_insn (gen_epilogue_nop ()); + else + emit_insn (gen_blockage ()); + } + /* First step is to deallocate the stack frame so that we can pop the registers. */ if (!m->fs.sp_valid) @@ -10257,7 +10356,7 @@ ix86_expand_epilogue (int style) { /* If the stack pointer is valid and pointing at the frame pointer store address, then we only need a pop. */ - if (m->fs.sp_valid && m->fs.sp_offset == frame.hard_frame_pointer_offset) + if (m->fs.sp_valid && m->fs.sp_offset == frame.hfp_save_offset) ix86_emit_restore_reg_using_pop (hard_frame_pointer_rtx); /* Leave results in shorter dependency chains on CPUs that are able to grok it fast. */ @@ -14987,6 +15086,13 @@ ix86_expand_binary_operator (enum rtx_code code, enum machine_mode mode, gcc_assert (code == PLUS); emit_insn (op); } + else if (reload_completed + && code == PLUS + && !rtx_equal_p (dst, src1)) + { + /* This is going to be an LEA; avoid splitting it later. */ + emit_insn (op); + } else { clob = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, FLAGS_REG)); @@ -20891,6 +20997,53 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1, return call; } +/* Output the assembly for a call instruction. */ + +const char * +ix86_output_call_insn (rtx insn, rtx call_op, int addr_op) +{ + bool direct_p = constant_call_address_operand (call_op, Pmode); + bool seh_nop_p = false; + + gcc_assert (addr_op == 0 || addr_op == 1); + + if (SIBLING_CALL_P (insn)) + { + if (direct_p) + return addr_op ? "jmp\t%P1" : "jmp\t%P0"; + /* SEH epilogue detection requires the indirect branch case + to include REX.W. */ + else if (TARGET_SEH) + return addr_op ? "rex.W jmp %A1" : "rex.W jmp %A0"; + else + return addr_op ? "jmp\t%A1" : "jmp\t%A0"; + } + + /* SEH unwinding can require an extra nop to be emitted in several + circumstances. Determine if we have one of those. */ + if (TARGET_SEH) + { + if (find_reg_note (insn, REG_NORETURN, NULL_RTX)) + seh_nop_p = true; + + /* else if epilogue follows */ + } + + if (direct_p) + { + if (seh_nop_p) + return addr_op ? "call\t%P1\n\tnop" : "call\t%P0\n\tnop"; + else + return addr_op ? "call\t%P1" : "call\t%P0"; + } + else + { + if (seh_nop_p) + return addr_op ? "call\t%A1\n\tnop" : "call\t%A0\n\tnop"; + else + return addr_op ? "call\t%A1" : "call\t%A0"; + } +} /* Clear stack slot assignments remembered from previous functions. This is called from INIT_EXPANDERS once before RTL is emitted for each diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h index 25463a5..30062a7 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -490,6 +490,9 @@ extern tree x86_mfence; /* For the Windows 64-bit ABI. */ #define TARGET_64BIT_MS_ABI (TARGET_64BIT && ix86_cfun_abi () == MS_ABI) +/* This is re-defined by cygming.h. */ +#define TARGET_SEH 0 + /* Available call abi. */ enum calling_abi { @@ -2242,6 +2245,9 @@ struct GTY(()) machine_frame_state BOOL_BITFIELD realigned : 1; }; +/* Private to winnt.c. */ +struct seh_frame_state; + struct GTY(()) machine_function { struct stack_local_entry *stack_locals; const char *some_ld_name; @@ -2292,6 +2298,9 @@ struct GTY(()) machine_function { /* During prologue/epilogue generation, the current frame state. Otherwise, the frame state at the end of the prologue. */ struct machine_frame_state fs; + + /* During SEH output, this is non-null. */ + struct seh_frame_state * GTY((skip(""))) seh; }; #endif diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index ae52746..9f10ba5 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -245,6 +245,7 @@ UNSPECV_XCHG UNSPECV_LOCK UNSPECV_PROLOGUE_USE + UNSPECV_EPILOGUE_NOP UNSPECV_CLD UNSPECV_NOPS UNSPECV_VZEROALL @@ -11275,32 +11276,21 @@ [(call (mem:QI (match_operand 0 "constant_call_address_operand" "")) (match_operand 1 "" ""))] "" -{ - if (SIBLING_CALL_P (insn)) - return "jmp\t%P0"; - else - return "call\t%P0"; -} + { return ix86_output_call_insn (insn, operands[0], 0); } [(set_attr "type" "call")]) (define_insn "*call_1" [(call (mem:QI (match_operand:SI 0 "call_insn_operand" "lsm")) (match_operand 1 "" ""))] "!TARGET_64BIT && !SIBLING_CALL_P (insn)" -{ - if (constant_call_address_operand (operands[0], Pmode)) - return "call\t%P0"; - return "call\t%A0"; -} + { return ix86_output_call_insn (insn, operands[0], 0); } [(set_attr "type" "call")]) (define_insn "*sibcall_1" [(call (mem:QI (match_operand:SI 0 "sibcall_insn_operand" "s,U")) (match_operand 1 "" ""))] "!TARGET_64BIT && SIBLING_CALL_P (insn)" - "@ - jmp\t%P0 - jmp\t%A0" + { return ix86_output_call_insn (insn, operands[0], 0); } [(set_attr "type" "call")]) (define_insn "*call_1_rex64" @@ -11308,11 +11298,7 @@ (match_operand 1 "" ""))] "TARGET_64BIT && !SIBLING_CALL_P (insn) && ix86_cmodel != CM_LARGE && ix86_cmodel != CM_LARGE_PIC" -{ - if (constant_call_address_operand (operands[0], Pmode)) - return "call\t%P0"; - return "call\t%A0"; -} + { return ix86_output_call_insn (insn, operands[0], 0); } [(set_attr "type" "call")]) (define_insn "*call_1_rex64_ms_sysv" @@ -11332,27 +11318,21 @@ (clobber (reg:DI SI_REG)) (clobber (reg:DI DI_REG))] "TARGET_64BIT && !SIBLING_CALL_P (insn)" -{ - if (constant_call_address_operand (operands[0], Pmode)) - return "call\t%P0"; - return "call\t%A0"; -} + { return ix86_output_call_insn (insn, operands[0], 0); } [(set_attr "type" "call")]) (define_insn "*call_1_rex64_large" [(call (mem:QI (match_operand:DI 0 "call_insn_operand" "rm")) (match_operand 1 "" ""))] "TARGET_64BIT && !SIBLING_CALL_P (insn)" - "call\t%A0" + { return ix86_output_call_insn (insn, operands[0], 0); } [(set_attr "type" "call")]) (define_insn "*sibcall_1_rex64" [(call (mem:QI (match_operand:DI 0 "sibcall_insn_operand" "s,U")) (match_operand 1 "" ""))] "TARGET_64BIT && SIBLING_CALL_P (insn)" - "@ - jmp\t%P0 - jmp\t%A0" + { return ix86_output_call_insn (insn, operands[0], 0); } [(set_attr "type" "call")]) ;; Call subroutine, returning value in operand 0 @@ -11478,6 +11458,19 @@ "" [(set_attr "length" "0")]) +;; For SEH, if a call to a function that throws immediately preceeds +;; the epilogue, the system unwinder will assume the caller is *in* +;; the epilogue and therefore not run the catch handlers. Therefore +;; if the current function has catch handlers we may need to force a +;; nop in between the body of the function and the epilogue. +(define_insn "epilogue_nop" + [(unspec_volatile [(const_int 0)] UNSPECV_EPILOGUE_NOP)] + "TARGET_SEH" + ; TODO: actually look back in the insn stream to see if the previous + ; real insn is in fact a call. For now be lazy and waste a byte. + "nop" + [(set_attr "length" "1")]) + ;; Insn emitted into the body of a function to return from a function. ;; This is only done if the function's epilogue is known to be simple. ;; See comments for ix86_can_use_return_insn_p in i386.c. @@ -17099,12 +17092,7 @@ (plus:SI (reg:SI SP_REG) (match_operand:SI 3 "immediate_operand" "")))] "!TARGET_64BIT" -{ - if (SIBLING_CALL_P (insn)) - return "jmp\t%P1"; - else - return "call\t%P1"; -} + { return ix86_output_call_insn (insn, operands[1], 1); } [(set_attr "type" "callv")]) (define_insn "*call_value_pop_1" @@ -17115,11 +17103,7 @@ (plus:SI (reg:SI SP_REG) (match_operand:SI 3 "immediate_operand" "i")))] "!TARGET_64BIT && !SIBLING_CALL_P (insn)" -{ - if (constant_call_address_operand (operands[1], Pmode)) - return "call\t%P1"; - return "call\t%A1"; -} + { return ix86_output_call_insn (insn, operands[1], 1); } [(set_attr "type" "callv")]) (define_insn "*sibcall_value_pop_1" @@ -17130,9 +17114,7 @@ (plus:SI (reg:SI SP_REG) (match_operand:SI 3 "immediate_operand" "i,i")))] "!TARGET_64BIT && SIBLING_CALL_P (insn)" - "@ - jmp\t%P1 - jmp\t%A1" + { return ix86_output_call_insn (insn, operands[1], 1); } [(set_attr "type" "callv")]) (define_insn "*call_value_0" @@ -17140,12 +17122,7 @@ (call (mem:QI (match_operand:SI 1 "constant_call_address_operand" "")) (match_operand:SI 2 "" "")))] "!TARGET_64BIT" -{ - if (SIBLING_CALL_P (insn)) - return "jmp\t%P1"; - else - return "call\t%P1"; -} + { return ix86_output_call_insn (insn, operands[1], 1); } [(set_attr "type" "callv")]) (define_insn "*call_value_0_rex64" @@ -17153,12 +17130,7 @@ (call (mem:QI (match_operand:DI 1 "constant_call_address_operand" "")) (match_operand:DI 2 "const_int_operand" "")))] "TARGET_64BIT" -{ - if (SIBLING_CALL_P (insn)) - return "jmp\t%P1"; - else - return "call\t%P1"; -} + { return ix86_output_call_insn (insn, operands[1], 1); } [(set_attr "type" "callv")]) (define_insn "*call_value_0_rex64_ms_sysv" @@ -17179,12 +17151,7 @@ (clobber (reg:DI SI_REG)) (clobber (reg:DI DI_REG))] "TARGET_64BIT && !SIBLING_CALL_P (insn)" -{ - if (SIBLING_CALL_P (insn)) - return "jmp\t%P1"; - else - return "call\t%P1"; -} + { return ix86_output_call_insn (insn, operands[1], 1); } [(set_attr "type" "callv")]) (define_insn "*call_value_1" @@ -17192,11 +17159,7 @@ (call (mem:QI (match_operand:SI 1 "call_insn_operand" "lsm")) (match_operand:SI 2 "" "")))] "!TARGET_64BIT && !SIBLING_CALL_P (insn)" -{ - if (constant_call_address_operand (operands[1], Pmode)) - return "call\t%P1"; - return "call\t%A1"; -} + { return ix86_output_call_insn (insn, operands[1], 1); } [(set_attr "type" "callv")]) (define_insn "*sibcall_value_1" @@ -17204,9 +17167,7 @@ (call (mem:QI (match_operand:SI 1 "sibcall_insn_operand" "s,U")) (match_operand:SI 2 "" "")))] "!TARGET_64BIT && SIBLING_CALL_P (insn)" - "@ - jmp\t%P1 - jmp\t%A1" + { return ix86_output_call_insn (insn, operands[1], 1); } [(set_attr "type" "callv")]) (define_insn "*call_value_1_rex64" @@ -17215,11 +17176,7 @@ (match_operand:DI 2 "" "")))] "TARGET_64BIT && !SIBLING_CALL_P (insn) && ix86_cmodel != CM_LARGE && ix86_cmodel != CM_LARGE_PIC" -{ - if (constant_call_address_operand (operands[1], Pmode)) - return "call\t%P1"; - return "call\t%A1"; -} + { return ix86_output_call_insn (insn, operands[1], 1); } [(set_attr "type" "callv")]) (define_insn "*call_value_1_rex64_ms_sysv" @@ -17240,11 +17197,7 @@ (clobber (reg:DI SI_REG)) (clobber (reg:DI DI_REG))] "TARGET_64BIT && !SIBLING_CALL_P (insn)" -{ - if (constant_call_address_operand (operands[1], Pmode)) - return "call\t%P1"; - return "call\t%A1"; -} + { return ix86_output_call_insn (insn, operands[1], 1); } [(set_attr "type" "callv")]) (define_insn "*call_value_1_rex64_large" @@ -17252,7 +17205,7 @@ (call (mem:QI (match_operand:DI 1 "call_insn_operand" "rm")) (match_operand:DI 2 "" "")))] "TARGET_64BIT && !SIBLING_CALL_P (insn)" - "call\t%A1" + { return ix86_output_call_insn (insn, operands[1], 1); } [(set_attr "type" "callv")]) (define_insn "*sibcall_value_1_rex64" @@ -17260,9 +17213,7 @@ (call (mem:QI (match_operand:DI 1 "sibcall_insn_operand" "s,U")) (match_operand:DI 2 "" "")))] "TARGET_64BIT && SIBLING_CALL_P (insn)" - "@ - jmp\t%P1 - jmp\t%A1" + { return ix86_output_call_insn (insn, operands[1], 1); } [(set_attr "type" "callv")]) ;; We used to use "int $5", in honor of #BR which maps to interrupt vector 5. diff --git a/gcc/config/i386/libgcc-cygming.ver b/gcc/config/i386/libgcc-cygming.ver new file mode 100644 index 0000000..399e52c --- /dev/null +++ b/gcc/config/i386/libgcc-cygming.ver @@ -0,0 +1,28 @@ +# Copyright (C) 2008, 2010 Free Software Foundation, Inc. +# +# This file is part of GCC. +# +# GCC 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, or (at your option) +# any later version. +# +# GCC 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 GCC; see the file COPYING3. If not see +# . + +# In order to work around the very problems that force us to now generally +# create a libgcc.so, glibc reexported a number of routines from libgcc.a. +# By now choosing the same version tags for these specific routines, we +# maintain enough binary compatibility to allow future versions of glibc +# to defer implementation of these routines to libgcc.so via DT_AUXILIARY. + +GCC_4.6 { + _GCC_specific_handler + __gcc_personality_seh0 +} diff --git a/gcc/config/i386/t-cygming b/gcc/config/i386/t-cygming index 183e545..2669935 100644 --- a/gcc/config/i386/t-cygming +++ b/gcc/config/i386/t-cygming @@ -100,4 +100,5 @@ SHLIB_MKMAP = $(srcdir)/mkmap-flat.awk # We'd like to use SHLIB_SONAME here too, but shlib_base_name # does not get substituted before mkmap-flat.awk is run. SHLIB_MKMAP_OPTS = -v pe_dll=libgcc_s_$(EH_MODEL)-$(SHLIB_SOVERSION)$(SHLIB_EXT) -SHLIB_MAPFILES = $(srcdir)/libgcc-std.ver +SHLIB_MAPFILES = $(srcdir)/libgcc-std.ver \ + $(srcdir)/config/i386/libgcc-cygming.ver diff --git a/gcc/config/i386/t-mingw-w64 b/gcc/config/i386/t-mingw-w64 index d5b2d0e..18357b1 100644 --- a/gcc/config/i386/t-mingw-w64 +++ b/gcc/config/i386/t-mingw-w64 @@ -10,3 +10,8 @@ SHLIB_LC = -lmingw32 -lmingwex -lmoldname -lmsvcrt -ladvapi32 -lshell32 -luser32 LIBGCC = stmp-multilib INSTALL_LIBGCC = install-multilib + +# Use SEH exception handling. +LIB2ADDEH = $(srcdir)/unwind-seh.c $(srcdir)/unwind-sjlj.c \ + $(srcdir)/unwind-c.c $(srcdir)/gthr-gnat.c +UNWIND_H = $(srcdir)/unwind-seh.h diff --git a/gcc/config/i386/winnt.c b/gcc/config/i386/winnt.c index 60a8b79..f117b41 100644 --- a/gcc/config/i386/winnt.c +++ b/gcc/config/i386/winnt.c @@ -36,6 +36,7 @@ along with GCC; see the file COPYING3. If not see #include "langhooks.h" #include "ggc.h" #include "target.h" +#include "except.h" #include "lto-streamer.h" /* i386/PE specific attribute support. @@ -730,4 +731,463 @@ i386_pe_file_end (void) } } + +/* x64 Structured Exception Handling unwind info. */ + +struct seh_frame_state +{ + /* SEH records saves relative to the "current" stack pointer, whether + or not there's a frame pointer in place. This tracks the current + stack pointer offset from the CFA. */ + HOST_WIDE_INT sp_offset; + + /* The CFA is located at CFA_REG + CFA_OFFSET. */ + HOST_WIDE_INT cfa_offset; + rtx cfa_reg; + + /* True if .seh_setframe has been emitted. */ + bool have_setframe; +}; + +static void seh_force_setframe (FILE *f, struct seh_frame_state *seh); + +/* Set up data structures beginning output for SEH. */ + +void +i386_pe_seh_init (FILE *f) +{ + struct seh_frame_state *seh; + + if (!TARGET_SEH) + return; + if (cfun->is_thunk) + return; + + /* We cannot support DRAP with SEH. We turned off support for it by + re-defining MAX_STACK_ALIGNMENT when SEH is enabled. */ + gcc_assert (!stack_realign_drap); + + seh = XCNEW (struct seh_frame_state); + cfun->machine->seh = seh; + + seh->sp_offset = INCOMING_FRAME_SP_OFFSET; + seh->cfa_offset = INCOMING_FRAME_SP_OFFSET; + seh->cfa_reg = stack_pointer_rtx; + seh->have_setframe = false; + + fputs ("\t.seh_proc\t", f); + assemble_name (f, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (cfun->decl))); + fputc ('\n', f); +} + +void +i386_pe_seh_end_prologue (FILE *f) +{ + if (!TARGET_SEH) + return; + if (cfun->is_thunk) + return; + + /* If nothing else has forced it, emit setframe last. */ + seh_force_setframe (f, cfun->machine->seh); + + XDELETE (cfun->machine->seh); + cfun->machine->seh = NULL; + + fputs ("\t.seh_endprologue\n", f); +} + +static void +i386_pe_seh_fini (FILE *f) +{ + if (!TARGET_SEH) + return; + if (cfun->is_thunk) + return; + fputs ("\t.seh_endproc\n", f); +} + +/* Emit an assembler directive to save REG via a PUSH. */ + +static void +seh_emit_push (FILE *f, struct seh_frame_state *seh, rtx reg) +{ + unsigned int regno = REGNO (reg); + + gcc_checking_assert (GENERAL_REGNO_P (regno)); + + seh->sp_offset += UNITS_PER_WORD; + if (seh->cfa_reg == stack_pointer_rtx) + seh->cfa_offset += UNITS_PER_WORD; + + fputs ("\t.seh_pushreg\t", f); + print_reg (reg, 0, f); + fputc ('\n', f); +} + +/* Emit an assembler directive to save REG at CFA - CFA_OFFSET. */ + +static void +seh_emit_save (FILE *f, struct seh_frame_state *seh, + rtx reg, HOST_WIDE_INT cfa_offset) +{ + unsigned int regno = REGNO (reg); + HOST_WIDE_INT offset; + + /* Negative save offsets are of course not supported, since that + would be a store below the stack pointer and thus clobberable. */ + gcc_assert (seh->sp_offset >= cfa_offset); + offset = seh->sp_offset - cfa_offset; + + fputs ((SSE_REGNO_P (regno) ? "\t.seh_savexmm\t" + : GENERAL_REGNO_P (regno) ? "\t.seh_savereg\t" + : (gcc_unreachable (), "")), f); + print_reg (reg, 0, f); + fprintf (f, ", " HOST_WIDE_INT_PRINT_DEC "\n", offset); +} + +/* Emit an assembler directive to set up the frame pointer. */ + +static void +seh_emit_setframe (FILE *f, struct seh_frame_state *seh, + rtx reg, HOST_WIDE_INT offset) +{ + gcc_assert ((offset & 15) == 0); + gcc_assert (IN_RANGE (offset, 0, 240)); + gcc_assert (!seh->have_setframe); + seh->have_setframe = true; + + fputs ("\t.seh_setframe\t", f); + print_reg (reg, 0, f); + fprintf (f, ", " HOST_WIDE_INT_PRINT_DEC "\n", offset); +} + +/* Force a setframe to be done if it needs doing. */ + +static void +seh_force_setframe (FILE *f, struct seh_frame_state *seh) +{ + if (!seh->have_setframe && seh->cfa_reg != stack_pointer_rtx) + seh_emit_setframe (f, seh, seh->cfa_reg, seh->sp_offset - seh->cfa_offset); +} + +/* Emit an assembler directive to adjust RSP by OFFSET. */ + +static void +seh_emit_stackalloc (FILE *f, struct seh_frame_state *seh, + HOST_WIDE_INT offset) +{ + /* We're only concerned with prologue stack allocations, which all + are subtractions from the stack pointer. */ + gcc_assert (offset < 0); + offset = -offset; + + if (seh->cfa_reg == stack_pointer_rtx) + seh->cfa_offset += offset; + seh->sp_offset += offset; + + fprintf (f, "\t.seh_stackalloc\t" HOST_WIDE_INT_PRINT_DEC "\n", offset); +} + +/* Process REG_CFA_ADJUST_CFA for SEH. */ + +static void +seh_cfa_adjust_cfa (FILE *f, struct seh_frame_state *seh, rtx pat) +{ + rtx dest, src; + HOST_WIDE_INT reg_offset = 0; + unsigned int dest_regno; + + dest = SET_DEST (pat); + src = SET_SRC (pat); + + if (GET_CODE (src) == PLUS) + { + reg_offset = INTVAL (XEXP (src, 1)); + src = XEXP (src, 0); + } + else if (GET_CODE (src) == MINUS) + { + reg_offset = -INTVAL (XEXP (src, 1)); + src = XEXP (src, 0); + } + gcc_assert (src == stack_pointer_rtx); + gcc_assert (seh->cfa_reg == stack_pointer_rtx); + dest_regno = REGNO (dest); + + if (dest_regno == STACK_POINTER_REGNUM) + seh_emit_stackalloc (f, seh, reg_offset); + else if (dest_regno == HARD_FRAME_POINTER_REGNUM) + { + gcc_assert (!seh->have_setframe); + seh->cfa_reg = dest; + seh->cfa_offset -= reg_offset; + } + else + gcc_unreachable (); +} + +/* Process REG_CFA_OFFSET for SEH. */ + +static void +seh_cfa_offset (FILE *f, struct seh_frame_state *seh, rtx pat) +{ + rtx dest, src; + HOST_WIDE_INT reg_offset; + + dest = SET_DEST (pat); + src = SET_SRC (pat); + + gcc_assert (MEM_P (dest)); + dest = XEXP (dest, 0); + if (REG_P (dest)) + reg_offset = 0; + else + { + gcc_assert (GET_CODE (dest) == PLUS); + reg_offset = INTVAL (XEXP (dest, 1)); + dest = XEXP (dest, 0); + } + gcc_assert (dest == seh->cfa_reg); + + seh_emit_save (f, seh, src, seh->cfa_offset - reg_offset); +} + +/* Process a FRAME_RELATED_EXPR for SEH. */ + +static void +seh_frame_related_expr (FILE *f, struct seh_frame_state *seh, rtx pat) +{ + rtx dest, src; + HOST_WIDE_INT addend; + + /* See the full loop in dwarf2out_frame_debug_expr. */ + if (GET_CODE (pat) == PARALLEL || GET_CODE (pat) == SEQUENCE) + { + int i, n = XVECLEN (pat, 0), pass, npass; + + npass = (GET_CODE (pat) == PARALLEL ? 2 : 1); + for (pass = 0; pass < npass; ++pass) + for (i = 0; i < n; ++i) + { + rtx ele = XVECEXP (pat, 0, i); + + if (GET_CODE (ele) != SET) + continue; + dest = SET_DEST (ele); + + /* Process each member of the PARALLEL independently. The first + member is always processed; others only if they are marked. */ + if (i == 0 || RTX_FRAME_RELATED_P (ele)) + { + /* Evaluate all register saves in the first pass and all + register updates in the second pass. */ + if ((MEM_P (dest) ^ pass) || npass == 1) + seh_frame_related_expr (f, seh, ele); + } + } + return; + } + + dest = SET_DEST (pat); + src = SET_SRC (pat); + + switch (GET_CODE (dest)) + { + case REG: + switch (GET_CODE (src)) + { + case REG: + /* REG = REG: This should be establishing a frame pointer. */ + gcc_assert (src == stack_pointer_rtx); + gcc_assert (dest == hard_frame_pointer_rtx); + seh_cfa_adjust_cfa (f, seh, pat); + break; + + case PLUS: + addend = INTVAL (XEXP (src, 1)); + src = XEXP (src, 0); + if (dest == hard_frame_pointer_rtx) + seh_cfa_adjust_cfa (f, seh, pat); + else if (dest == stack_pointer_rtx) + { + gcc_assert (src == stack_pointer_rtx); + seh_emit_stackalloc (f, seh, addend); + } + else + gcc_unreachable (); + break; + + default: + gcc_unreachable (); + } + break; + + case MEM: + /* A save of some kind. */ + dest = XEXP (dest, 0); + if (GET_CODE (dest) == PRE_DEC) + { + gcc_checking_assert (GET_MODE (src) == Pmode); + gcc_checking_assert (REG_P (src)); + seh_emit_push (f, seh, src); + } + else + seh_cfa_offset (f, seh, pat); + break; + + default: + gcc_unreachable (); + } +} + +/* This function looks at a single insn and emits any SEH directives + required for unwind of this insn. */ + +void +i386_pe_seh_unwind_emit (FILE *asm_out_file, rtx insn) +{ + rtx note, pat; + bool handled_one = false; + struct seh_frame_state *seh; + + if (!TARGET_SEH) + return; + + /* We free the SEH data once done with the prologue. Ignore those + RTX_FRAME_RELATED_P insns that are associated with the epilogue. */ + seh = cfun->machine->seh; + if (seh == NULL) + return; + + if (NOTE_P (insn) || !RTX_FRAME_RELATED_P (insn)) + return; + + for (note = REG_NOTES (insn); note ; note = XEXP (note, 1)) + { + pat = XEXP (note, 0); + switch (REG_NOTE_KIND (note)) + { + case REG_FRAME_RELATED_EXPR: + goto found; + + case REG_CFA_DEF_CFA: + case REG_CFA_EXPRESSION: + /* Only emitted with DRAP, which we disable. */ + gcc_unreachable (); + break; + + case REG_CFA_REGISTER: + /* Only emitted in epilogues, which we skip. */ + gcc_unreachable (); + + case REG_CFA_ADJUST_CFA: + if (pat == NULL) + { + pat = PATTERN (insn); + if (GET_CODE (pat) == PARALLEL) + pat = XVECEXP (pat, 0, 0); + } + seh_cfa_adjust_cfa (asm_out_file, seh, pat); + handled_one = true; + break; + + case REG_CFA_OFFSET: + if (pat == NULL) + pat = single_set (insn); + seh_cfa_offset (asm_out_file, seh, pat); + handled_one = true; + break; + + default: + break; + } + } + if (handled_one) + return; + pat = PATTERN (insn); + found: + seh_frame_related_expr (asm_out_file, seh, pat); +} + +void +i386_pe_seh_emit_except_personality (rtx personality) +{ + int flags = 0; + + if (!TARGET_SEH) + return; + + fputs ("\t.seh_handler\t", asm_out_file); + output_addr_const (asm_out_file, personality); + +#if 0 + /* ??? The current implementation of _GCC_specific_handler requires + both except and unwind handling, regardless of which sorts the + user-level function requires. */ + eh_region r; + FOR_ALL_EH_REGION(r) + { + if (r->type == ERT_CLEANUP) + flags |= 1; + else + flags |= 2; + } +#else + flags = 3; +#endif + + if (flags & 1) + fputs (", @unwind", asm_out_file); + if (flags & 2) + fputs (", @except", asm_out_file); + fputc ('\n', asm_out_file); +} + +void +i386_pe_seh_init_sections (void) +{ + if (TARGET_SEH) + exception_section = get_unnamed_section (0, output_section_asm_op, + "\t.seh_handlerdata"); +} + +/* Implement TARGET_EXCEPT_UNWIND_INFO. */ + +enum unwind_info_type +i386_pe_seh_unwind_info (void) +{ + /* Honor the --enable-sjlj-exceptions configure switch. */ +#ifdef CONFIG_UNWIND_EXCEPTIONS + if (CONFIG_UNWIND_EXCEPTIONS) + return UI_SJLJ; +#endif + + /* Prefer SEH exceptions over anything else. */ + if (TARGET_SEH) + return UI_SEH; + + if (DWARF2_UNWIND_INFO) + return UI_DWARF2; + + return UI_SJLJ; +} + +void +i386_pe_start_function (FILE *f, const char *name, tree decl) +{ + i386_pe_maybe_record_exported_symbol (decl, name, 0); + if (write_symbols != SDB_DEBUG) + i386_pe_declare_function_type (f, name, TREE_PUBLIC (decl)); + ASM_OUTPUT_FUNCTION_LABEL (f, name, decl); +} + +void +i386_pe_end_function (FILE *f, const char *name ATTRIBUTE_UNUSED, + tree decl ATTRIBUTE_UNUSED) +{ + i386_pe_seh_fini (f); +} + + #include "gt-winnt.h" diff --git a/gcc/coretypes.h b/gcc/coretypes.h index 3c63684..38f37a2 100644 --- a/gcc/coretypes.h +++ b/gcc/coretypes.h @@ -118,7 +118,8 @@ enum unwind_info_type UI_NONE, UI_SJLJ, UI_DWARF2, - UI_TARGET + UI_TARGET, + UI_SEH }; struct edge_def; diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index d19a5cd..0821d3c 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -2552,7 +2552,9 @@ dwarf2out_frame_debug_expr (rtx expr, const char *label) regno = REGNO (XEXP (XEXP (dest, 0), 0)); - if (cfa_store.reg == (unsigned) regno) + if (cfa.reg == (unsigned) regno) + offset -= cfa.offset; + else if (cfa_store.reg == (unsigned) regno) offset -= cfa_store.offset; else { @@ -2568,7 +2570,9 @@ dwarf2out_frame_debug_expr (rtx expr, const char *label) { int regno = REGNO (XEXP (dest, 0)); - if (cfa_store.reg == (unsigned) regno) + if (cfa.reg == (unsigned) regno) + offset = -cfa.offset; + else if (cfa_store.reg == (unsigned) regno) offset = -cfa_store.offset; else { @@ -3985,7 +3989,7 @@ dwarf2out_begin_prologue (unsigned int line ATTRIBUTE_UNUSED, call-site information. We must emit this label if it might be used. */ if (!do_frame && (!flag_exceptions - || targetm.except_unwind_info () != UI_TARGET)) + || targetm.except_unwind_info () == UI_SJLJ)) return; fnsec = function_section (current_function_decl); diff --git a/gcc/expr.c b/gcc/expr.c index 0050518..6ae55c7 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -10275,6 +10275,9 @@ build_personality_function (const char *lang) case UI_TARGET: unwind_and_version = "_v0"; break; + case UI_SEH: + unwind_and_version = "_seh0"; + break; default: gcc_unreachable (); } diff --git a/gcc/opts.c b/gcc/opts.c index 52f8c6d..3dc0347 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -1066,7 +1066,7 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set) if (flag_exceptions && flag_reorder_blocks_and_partition - && (ui_except == UI_SJLJ || ui_except == UI_TARGET)) + && (ui_except == UI_SJLJ || ui_except >= UI_TARGET)) { inform (input_location, "-freorder-blocks-and-partition does not work " @@ -1081,7 +1081,7 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set) if (flag_unwind_tables && !targetm.unwind_tables_default && flag_reorder_blocks_and_partition - && (ui_except == UI_SJLJ || ui_except == UI_TARGET)) + && (ui_except == UI_SJLJ || ui_except >= UI_TARGET)) { inform (input_location, "-freorder-blocks-and-partition does not support " @@ -1098,7 +1098,7 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set) && (!targetm.have_named_sections || (flag_unwind_tables && targetm.unwind_tables_default - && (ui_except == UI_SJLJ || ui_except == UI_TARGET)))) + && (ui_except == UI_SJLJ || ui_except >= UI_TARGET)))) { inform (input_location, "-freorder-blocks-and-partition does not work " diff --git a/gcc/testsuite/g++.dg/torture/stackalign/stackalign.exp b/gcc/testsuite/g++.dg/torture/stackalign/stackalign.exp index bfa413e..e68337d 100644 --- a/gcc/testsuite/g++.dg/torture/stackalign/stackalign.exp +++ b/gcc/testsuite/g++.dg/torture/stackalign/stackalign.exp @@ -18,19 +18,15 @@ # This harness is for tests that should be run at all optimisation levels. load_lib g++-dg.exp - -# Only run on targets which support automatic stack alignment. -if { ![check_effective_target_automatic_stack_alignment] } then { - return -} - +dg-init set additional_flags "" -if { [istarget i?86*-*-*] || [istarget x86_64-*-*] } then { + +# If automatic stack alignment is supported, force it on. +if { [check_effective_target_automatic_stack_alignment] } then { lappend additional_flags "-mstackrealign" lappend additional_flags "-mpreferred-stack-boundary=5" } -dg-init gcc-dg-runtest [lsort [glob $srcdir/$subdir/*.C]] $additional_flags if { [check_effective_target_fpic] } then { lappend additional_flags "-fpic" diff --git a/gcc/testsuite/gcc.dg/torture/stackalign/stackalign.exp b/gcc/testsuite/gcc.dg/torture/stackalign/stackalign.exp index ef45dbe..83c6239 100644 --- a/gcc/testsuite/gcc.dg/torture/stackalign/stackalign.exp +++ b/gcc/testsuite/gcc.dg/torture/stackalign/stackalign.exp @@ -19,15 +19,12 @@ load_lib gcc-dg.exp -# Only run on targets which support automatic stack alignment. -if { ![check_effective_target_automatic_stack_alignment] } then { - return -} - set additional_flags "" -if { [istarget i?86*-*-*] || [istarget x86_64-*-*] } then { +if { [check_effective_target_automatic_stack_alignment] } then { lappend additional_flags "-mstackrealign" lappend additional_flags "-mpreferred-stack-boundary=5" +} +if { [istarget i?86*-*-*] || [istarget x86_64-*-*] } then { lappend additional_flags "-mno-mmx" } @@ -40,7 +37,7 @@ if { [check_effective_target_fpic] } then { gcc-dg-runtest [lsort [glob $srcdir/$subdir/*.c]] $pic_additional_flags } -if { [istarget i?86*-*-*] || [istarget x86_64-*-*] } then { +if { [check_effective_target_automatic_stack_alignment] } then { lappend additional_flags "-mforce-drap" gcc-dg-runtest [lsort [glob $srcdir/$subdir/*.c]] $additional_flags if { [check_effective_target_fpic] } then { diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp index 0ae003a..87b234d 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -3551,8 +3551,15 @@ proc check_effective_target_4byte_wchar_t { } { # Return 1 if the target supports automatic stack alignment. proc check_effective_target_automatic_stack_alignment { } { - # Not "stack alignment" per se, but proper stack alignment of decls. - return 1; + # Ordinarily x86 supports automatic stack alignment ... + if { [istarget i?86*-*-*] || [istarget x86_64-*-*] } then { + if { [istarget *-*-mingw*] || [istarget *-*-cygwin*] } { + # ... except Win64 SEH doesn't. Succeed for Win32 though. + return [check_effective_target_ilp32]; + } + return 1; + } + return 0; } # Return 1 if avx instructions can be compiled. diff --git a/gcc/unwind-c.c b/gcc/unwind-c.c index 86b9f55..32ff0e6 100644 --- a/gcc/unwind-c.c +++ b/gcc/unwind-c.c @@ -93,6 +93,8 @@ parse_lsda_header (struct _Unwind_Context *context, const unsigned char *p, #ifdef __USING_SJLJ_EXCEPTIONS__ #define PERSONALITY_FUNCTION __gcc_personality_sj0 #define __builtin_eh_return_data_regno(x) x +#elif defined(__SEH__) +#define PERSONALITY_FUNCTION __gcc_personality_imp #else #define PERSONALITY_FUNCTION __gcc_personality_v0 #endif @@ -107,6 +109,9 @@ PERSONALITY_FUNCTION (_Unwind_State state, struct _Unwind_Exception * ue_header, struct _Unwind_Context * context) #else +#ifdef __SEH__ +static +#endif _Unwind_Reason_Code PERSONALITY_FUNCTION (int, _Unwind_Action, _Unwind_Exception_Class, struct _Unwind_Exception *, struct _Unwind_Context *); @@ -227,3 +232,13 @@ PERSONALITY_FUNCTION (int version, _Unwind_SetIP (context, landing_pad); return _URC_INSTALL_CONTEXT; } + +#ifdef __SEH__ +EXCEPTION_DISPOSITION +__gcc_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame, + PCONTEXT ms_orig_context, PDISPATCHER_CONTEXT ms_disp) +{ + return _GCC_specific_handler (ms_exc, this_frame, ms_orig_context, + ms_disp, __gcc_personality_imp); +} +#endif /* SEH */ diff --git a/gcc/unwind-seh.c b/gcc/unwind-seh.c new file mode 100644 index 0000000..67bcd90 --- /dev/null +++ b/gcc/unwind-seh.c @@ -0,0 +1,483 @@ +/* Structured Exception Handling (SEH) runtime interface routines. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of GCC. + + GCC 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, or (at your option) + any later version. + + GCC 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. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + . */ + +#include "tconfig.h" +#include "tsystem.h" +#include "coretypes.h" +#include "tm.h" +#include "unwind.h" +#include "gthr.h" + +#ifdef __SEH__ + +/* At the moment everything is written for x64, but in theory this could + also be used for i386, arm, mips and other extant embedded Windows. */ +#ifndef __x86_64__ +#error "Unsupported architecture." +#endif + +/* Define GCC's exception codes. See + http://msdn.microsoft.com/en-us/library/het71c37(v=VS.80).aspx + In particular, MS defines bits: + [31:30] = 3 (error), 2 (warning), 1 (info), 0 (success) + [29] = 1 (user-defined) + [28] = 0 (reserved) + We define bits: + [24:27] = type + [0:23] = magic + We set "magic" to "GCC", which is similar to MVC++ which uses "msc" + as the low 3 bytes of its user-defined codes for C++ exceptions. + + We define the ExceptionInformation entries as follows: + [0] = _Unwind_Exception pointer + [1] = target frame + [2] = target ip + [3] = target rdx +*/ + +#define STATUS_USER_DEFINED (1U << 29) + +#define GCC_MAGIC (('G' << 16) | ('C' << 8) | 'C') +#define GCC_EXCEPTION(TYPE) \ + (STATUS_USER_DEFINED | ((TYPE) << 24) | GCC_MAGIC) + +#define STATUS_GCC_THROW GCC_EXCEPTION (0) +#define STATUS_GCC_UNWIND GCC_EXCEPTION (1) +#define STATUS_GCC_FORCED GCC_EXCEPTION (2) + + +struct _Unwind_Context +{ + _Unwind_Word cfa; + _Unwind_Word ra; + _Unwind_Word reg[2]; + PDISPATCHER_CONTEXT disp; +}; + +/* Get the value of register INDEX as saved in CONTEXT. */ + +_Unwind_Word +_Unwind_GetGR (struct _Unwind_Context *c, int index) +{ + if (index < 0 || index > 2) + abort (); + return c->reg[index]; +} + +/* Overwrite the saved value for register INDEX in CONTEXT with VAL. */ + +void +_Unwind_SetGR (struct _Unwind_Context *c, int index, _Unwind_Word val) +{ + if (index < 0 || index > 2) + abort (); + c->reg[index] = val; +} + +/* Get the value of the CFA as saved in CONTEXT. */ + +_Unwind_Word +_Unwind_GetCFA (struct _Unwind_Context *c) +{ + return c->cfa; +} + +/* Retrieve the return address for CONTEXT. */ + +_Unwind_Ptr +_Unwind_GetIP (struct _Unwind_Context *c) +{ + return c->ra; +} + +/* Retrieve the return address and flag whether that IP is before + or after first not yet fully executed instruction. */ + +_Unwind_Ptr +_Unwind_GetIPInfo (struct _Unwind_Context *c, int *ip_before_insn) +{ + /* ??? Is there a concept of a signal context properly? There's + obviously an UNWP_PUSH_MACHFRAME opcode, but the runtime might + have arranged for that not to matter, really. */ + *ip_before_insn = 0; + return c->ra; +} + +/* Overwrite the return address for CONTEXT with VAL. */ + +inline void +_Unwind_SetIP (struct _Unwind_Context *c, _Unwind_Ptr val) +{ + c->ra = val; +} + +void * +_Unwind_GetLanguageSpecificData (struct _Unwind_Context *c) +{ + return c->disp->HandlerData; +} + +_Unwind_Ptr +_Unwind_GetRegionStart (struct _Unwind_Context *c) +{ + return c->disp->FunctionEntry->BeginAddress + c->disp->ImageBase; +} + +void * +_Unwind_FindEnclosingFunction (void *pc) +{ + PRUNTIME_FUNCTION entry; + ULONG64 ImageBase; + + entry = RtlLookupFunctionEntry ((ULONG64)pc, &ImageBase, NULL); + + return (entry ? (void *)(entry->BeginAddress + ImageBase) : NULL); +} + +_Unwind_Ptr +_Unwind_GetDataRelBase (struct _Unwind_Context *c ATTRIBUTE_UNUSED) +{ + return 0; +} + +_Unwind_Ptr +_Unwind_GetTextRelBase (struct _Unwind_Context *c) +{ + return c->disp->ImageBase; +} + + +/* The two-phase unwind process that GCC uses is ordered differently + from the two-phase unwind process that SEH uses. The mechansism + that GCC uses is to have the filter return _URC_HANDER_FOUND; the + mechanism that SEH uses is for the filter function call back into + the unwinder. + + An Ideal port to SEH would have GCC emit handler functions that + can be called, given a pointer to the "EstablisherFrame" (i.e. + the frame pointer base of the user-level function) can manipulate + the user-level variables within the user-level function's stack + frame. Once done manipulating the variables, it would return + a ExceptionContinueSearch, and the unwind process would continue. + + GCC has always done things a bit differently. We continue to + transfer control back into the user-level function which, once + done manipulating the user-level variables, re-throws the exception. */ + +/* The "real" language-specific personality handler forwards to here + where we handle the MS SEH state and transforms it into the GCC + unwind state as per GCC's , at which point we defer to + the regular language-specfic exception handler, which is passed in. */ + +EXCEPTION_DISPOSITION +_GCC_specific_handler (PEXCEPTION_RECORD ms_exc, void *this_frame, + PCONTEXT ms_orig_context, PDISPATCHER_CONTEXT ms_disp, + _Unwind_Personality_Fn gcc_per) +{ + DWORD ms_flags = ms_exc->ExceptionFlags; + DWORD ms_code = ms_exc->ExceptionCode; + void *ms_data = ms_disp->HandlerData; + + struct _Unwind_Exception *gcc_exc + = (struct _Unwind_Exception *) ms_exc->ExceptionInformation[0]; + struct _Unwind_Context gcc_context; + _Unwind_Action gcc_action; + _Unwind_Reason_Code gcc_reason; + + if (ms_flags & EXCEPTION_TARGET_UNWIND) + { + /* This frame is known to be the target frame. We've already + "installed" the target_ip and RAX value via the arguments + to RtlUnwindEx. All that's left is to set the RDX value + and "continue" to have the context installed. */ + ms_disp->ContextRecord->Rdx = ms_exc->ExceptionInformation[3]; + return ExceptionContinueSearch; + } + + if (ms_code == STATUS_GCC_UNWIND) + { + /* This is a colliding exception that we threw so that we could + cancel the already in-flight exception and stop in a frame + that wanted to perform some unwind action. The only relevant + test is that we're the target frame. */ + if (ms_exc->ExceptionInformation[1] == (_Unwind_Ptr) this_frame) + { + RtlUnwindEx (this_frame, ms_exc->ExceptionInformation[2], + ms_exc, gcc_exc, ms_orig_context, + ms_disp->HistoryTable); + abort (); + } + return ExceptionContinueSearch; + } + + gcc_context.cfa = ms_disp->ContextRecord->Rsp; + gcc_context.ra = ms_disp->ControlPc; + gcc_context.reg[0] = 0xdeadbeef; /* These are write-only. */ + gcc_context.reg[1] = 0xdeadbeef; + gcc_context.disp = ms_disp; + + if (ms_code == STATUS_GCC_FORCED) + { + _Unwind_Stop_Fn stop = (_Unwind_Stop_Fn) gcc_exc->private_[0]; + void *stop_argument = (void *) gcc_exc->private_[4]; + + gcc_action = _UA_FORCE_UNWIND | _UA_CLEANUP_PHASE; + + stop (1, gcc_action, gcc_exc->exception_class, gcc_exc, + &gcc_context, stop_argument); + + goto phase2; + } + + /* ??? TODO: handling non-gcc user-defined exceptions as foreign. */ + if (ms_code != STATUS_GCC_THROW) + return ExceptionContinueSearch; + + if (ms_flags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND)) + { + /* This is Phase 2. */ + /* We know this isn't the target frame because we've already tested + EXCEPTION_TARGET_UNWIND. The remaining possibility is that the + gcc personality has unwind code to run. */ + + gcc_action = _UA_CLEANUP_PHASE; + phase2: + gcc_reason = gcc_per(1, gcc_action, gcc_exc->exception_class, + gcc_exc, &gcc_context); + + if (gcc_reason == _URC_CONTINUE_UNWIND) + return ExceptionContinueSearch; + + if (gcc_reason == _URC_INSTALL_CONTEXT) + { + /* Scratch space for the bits for the unwind catch. */ + ms_exc->ExceptionInformation[1] = (_Unwind_Ptr) this_frame; + ms_exc->ExceptionInformation[2] = gcc_context.ra; + ms_exc->ExceptionInformation[3] = gcc_context.reg[1]; + + /* Cancel the current exception by raising another. */ + RaiseException (STATUS_GCC_UNWIND, EXCEPTION_NONCONTINUABLE, + 4, ms_exc->ExceptionInformation); + + /* Is RaiseException declared noreturn? */ + } + + /* In _Unwind_RaiseException_Phase2 we return _URC_FATAL_PHASE2_ERROR. */ + } + else + { + /* This is Phase 1. */ + gcc_reason = gcc_per(1, _UA_SEARCH_PHASE, gcc_exc->exception_class, + gcc_exc, &gcc_context); + + if (gcc_reason == _URC_CONTINUE_UNWIND) + return ExceptionContinueSearch; + + if (gcc_reason == _URC_HANDLER_FOUND) + { + /* We really need some of the information that GCC's personality + routines compute during phase 2 right now, like the target IP. + Go ahead and ask for it now, and cache it. */ + gcc_reason = gcc_per(1, _UA_CLEANUP_PHASE | _UA_HANDLER_FRAME, + gcc_exc->exception_class, gcc_exc, + &gcc_context); + if (gcc_reason != _URC_INSTALL_CONTEXT) + abort (); + + gcc_exc->private_[1] = (_Unwind_Ptr) this_frame; + gcc_exc->private_[2] = gcc_context.ra; + gcc_exc->private_[3] = gcc_context.reg[1]; + + ms_exc->NumberParameters = 4; + ms_exc->ExceptionInformation[1] = (_Unwind_Ptr) this_frame; + ms_exc->ExceptionInformation[2] = gcc_context.ra; + ms_exc->ExceptionInformation[3] = gcc_context.reg[1]; + + /* Begin phase 2. Perform the unwinding. */ + RtlUnwindEx (this_frame, gcc_context.ra, ms_exc, gcc_exc, + ms_orig_context, ms_disp->HistoryTable); + } + + /* In _Unwind_RaiseException we return _URC_FATAL_PHASE1_ERROR. */ + } + abort (); +} + +/* Raise an exception, passing along the given exception object. */ + +_Unwind_Reason_Code +_Unwind_RaiseException(struct _Unwind_Exception *exc) +{ + memset (exc->private_, 0, sizeof (exc->private_)); + + RaiseException (STATUS_GCC_THROW, 0, 1, (ULONG_PTR *)&exc); + + /* The exception handler installed in crt0 will continue any GCC + exception that reaches there (and isn't marked non-continuable). + Returning allows the C++ runtime to call std::terminate. */ + return _URC_END_OF_STACK; +} + +/* Resume propagation of an existing exception. This is used after + e.g. executing cleanup code, and not to implement rethrowing. */ + +void +_Unwind_Resume (struct _Unwind_Exception *gcc_exc) +{ + UNWIND_HISTORY_TABLE ms_history; + EXCEPTION_RECORD ms_exc; + CONTEXT ms_context; + + memset (&ms_exc, 0, sizeof(ms_exc)); + memset (&ms_history, 0, sizeof(ms_history)); + + /* ??? Not 100% perfect, since we aren't passing on the *original* + exception context, but should be good enough. */ + ms_exc.ExceptionCode = STATUS_GCC_THROW; + ms_exc.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + ms_exc.NumberParameters = 4; + ms_exc.ExceptionInformation[0] = (ULONG_PTR) gcc_exc; + ms_exc.ExceptionInformation[1] = gcc_exc->private_[1]; + ms_exc.ExceptionInformation[2] = gcc_exc->private_[2]; + ms_exc.ExceptionInformation[3] = gcc_exc->private_[3]; + + ms_context.ContextFlags = CONTEXT_ALL; + RtlCaptureContext (&ms_context); + + RtlUnwindEx((void *) gcc_exc->private_[1], gcc_exc->private_[2], + &ms_exc, gcc_exc, &ms_context, &ms_history); + + /* Is RtlUnwindEx declared noreturn? */ + abort (); +} + +static _Unwind_Reason_Code +_Unwind_ForcedUnwind_Phase2 (struct _Unwind_Exception *exc) +{ + _Unwind_Stop_Fn stop; + void * stop_argument; + + RaiseException (STATUS_GCC_FORCED, 0, 1, (ULONG_PTR *)&exc); + + /* If we get here, we got to top-of-stack. */ + /* ??? We no longer have a context pointer to pass in. */ + + stop = (_Unwind_Stop_Fn) exc->private_[0]; + stop_argument = (void *) exc->private_[4]; + stop (1, _UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK, + exc->exception_class, exc, NULL, stop_argument); + + return _UA_END_OF_STACK; +} + +_Unwind_Reason_Code +_Unwind_Resume_or_Rethrow (struct _Unwind_Exception *exc) +{ + if (exc->private_[0] == 0) + _Unwind_RaiseException (exc); + else + _Unwind_ForcedUnwind_Phase2 (exc); + abort (); +} + +/* Raise an exception for forced unwinding. */ + +_Unwind_Reason_Code +_Unwind_ForcedUnwind (struct _Unwind_Exception *exc, + _Unwind_Stop_Fn stop, void * stop_argument) +{ + /* ??? This is a hack that only works with _GCC_specific_handler. + There's no way to invoke STOP within frames that use a different + exception handler. This is essentially just good enough to run + the code within the gcc testsuite. */ + + memset (exc->private_, 0, sizeof (exc->private_)); + exc->private_[0] = (_Unwind_Ptr) stop; + exc->private_[4] = (_Unwind_Ptr) stop_argument; + + return _Unwind_ForcedUnwind_Phase2 (exc); +} + +/* A convenience function that calls the exception_cleanup field. */ + +void +_Unwind_DeleteException (struct _Unwind_Exception *exc) +{ + if (exc->exception_cleanup) + (*exc->exception_cleanup) (_URC_FOREIGN_EXCEPTION_CAUGHT, exc); +} + +/* Perform stack backtrace through unwind data. */ + +_Unwind_Reason_Code +_Unwind_Backtrace(_Unwind_Trace_Fn trace, void * trace_argument) +{ +#if 0 + UNWIND_HISTORY_TABLE ms_history; + CONTEXT ms_context; + struct _Unwind_Context gcc_context; + + memset (&ms_history, 0, sizeof(ms_history)); + memset (&gcc_context, 0, sizeof(gcc_context)); + + ms_context.ContextFlags = CONTEXT_ALL; + RtlCaptureContext (&ms_context); + + gcc_context.disp.ContextRecord = &ms_context; + gcc_context.disp.HistoryTable = &ms_history; + + while (1) + { + gcc_context.disp.ControlPc = ms_context.Rip; + gcc_context.disp.FunctionEntry + = RtlLookupFunctionEntry (ms_context.Rip, &gcc_context.disp.ImageBase, + &ms_history); + + if (gcc_context.disp.FunctionEntry) + { + gcc_context.disp.LanguageHandler + = RtlVirtualUnwind (0, gcc_context.disp.ImageBase, ms_context.Rip, + gcc_context.disp.FunctionEntry, &ms_context, + &gcc_context.disp.HandlerData, + &gcc_context.disp.EstablisherFrame, NULL); + } + else + { + ms_context.Rip = *(ULONG_PTR *)ms_context.Rsp; + ms_context.Rsp += 8; + } + + /* Call trace function. */ + if (trace (&gcc_context, trace_argument) != _URC_NO_REASON) + return _URC_FATAL_PHASE1_ERROR; + + /* ??? Check for invalid stack pointer. */ + if (ms_context.Rip == 0) + return _URC_END_OF_STACK; + } +#else + return _URC_END_OF_STACK; +#endif +} +#endif /* __SEH__ */ diff --git a/gcc/unwind-seh.h b/gcc/unwind-seh.h new file mode 100644 index 0000000..4caf785 --- /dev/null +++ b/gcc/unwind-seh.h @@ -0,0 +1,258 @@ +/* Exception handling and frame unwind runtime interface routines. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of GCC. + + GCC 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, or (at your option) + any later version. + + GCC 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. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + . */ + +/* This is derived from the C++ ABI for IA-64. Where we diverge + for cross-architecture compatibility are noted with "@@@". */ + +#ifndef _UNWIND_H +#define _UNWIND_H + +#ifdef __SEH__ +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Level 1: Base ABI */ + +/* @@@ The IA-64 ABI uses uint64 throughout. Most places this is + inefficient for 32-bit and smaller machines. */ +typedef unsigned _Unwind_Word __attribute__((__mode__(__unwind_word__))); +typedef signed _Unwind_Sword __attribute__((__mode__(__unwind_word__))); +typedef unsigned _Unwind_Ptr __attribute__((__mode__(__pointer__))); +typedef unsigned _Unwind_Internal_Ptr __attribute__((__mode__(__pointer__))); + +/* @@@ The IA-64 ABI uses a 64-bit word to identify the producer and + consumer of an exception. We'll go along with this for now even on + 32-bit machines. We'll need to provide some other option for + 16-bit machines and for machines with > 8 bits per byte. */ +typedef unsigned _Unwind_Exception_Class __attribute__((__mode__(__DI__))); + +/* The unwind interface uses reason codes in several contexts to + identify the reasons for failures or other actions. */ +typedef enum +{ + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8 +} _Unwind_Reason_Code; + + +/* The unwind interface uses a pointer to an exception header object + as its representation of an exception being thrown. In general, the + full representation of an exception object is language- and + implementation-specific, but it will be prefixed by a header + understood by the unwind interface. */ + +struct _Unwind_Exception; + +typedef void (*_Unwind_Exception_Cleanup_Fn) (_Unwind_Reason_Code, + struct _Unwind_Exception *); + +struct _Unwind_Exception +{ + _Unwind_Exception_Class exception_class; + _Unwind_Exception_Cleanup_Fn exception_cleanup; + +#ifdef __USING_SJLJ_EXCEPTIONS__ + _Unwind_Word private_1; + _Unwind_Word private_2; +#else + _Unwind_Word private_[6]; +#endif + + /* @@@ The IA-64 ABI says that this structure must be double-word aligned. + Taking that literally does not make much sense generically. Instead we + provide the maximum alignment required by any type for the machine. */ +} __attribute__((__aligned__)); + + +/* The ACTIONS argument to the personality routine is a bitwise OR of one + or more of the following constants. */ +typedef int _Unwind_Action; + +#define _UA_SEARCH_PHASE 1 +#define _UA_CLEANUP_PHASE 2 +#define _UA_HANDLER_FRAME 4 +#define _UA_FORCE_UNWIND 8 +#define _UA_END_OF_STACK 16 + +/* The target can override this macro to define any back-end-specific + attributes required for the lowest-level stack frame. */ +#ifndef LIBGCC2_UNWIND_ATTRIBUTE +#define LIBGCC2_UNWIND_ATTRIBUTE +#endif + +/* This is an opaque type used to refer to a system-specific data + structure used by the system unwinder. This context is created and + destroyed by the system, and passed to the personality routine + during unwinding. */ +struct _Unwind_Context; + +/* Raise an exception, passing along the given exception object. */ +extern _Unwind_Reason_Code LIBGCC2_UNWIND_ATTRIBUTE +_Unwind_RaiseException (struct _Unwind_Exception *); + +/* Raise an exception for forced unwinding. */ + +typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) + (int, _Unwind_Action, _Unwind_Exception_Class, + struct _Unwind_Exception *, struct _Unwind_Context *, void *); + +extern _Unwind_Reason_Code LIBGCC2_UNWIND_ATTRIBUTE +_Unwind_ForcedUnwind (struct _Unwind_Exception *, _Unwind_Stop_Fn, void *); + +/* Helper to invoke the exception_cleanup routine. */ +extern void _Unwind_DeleteException (struct _Unwind_Exception *); + +/* Resume propagation of an existing exception. This is used after + e.g. executing cleanup code, and not to implement rethrowing. */ +extern void LIBGCC2_UNWIND_ATTRIBUTE +_Unwind_Resume (struct _Unwind_Exception *); + +/* @@@ Resume propagation of a FORCE_UNWIND exception, or to rethrow + a normal exception that was handled. */ +extern _Unwind_Reason_Code LIBGCC2_UNWIND_ATTRIBUTE +_Unwind_Resume_or_Rethrow (struct _Unwind_Exception *); + +/* @@@ Use unwind data to perform a stack backtrace. The trace callback + is called for every stack frame in the call chain, but no cleanup + actions are performed. */ +typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn) + (struct _Unwind_Context *, void *); + +extern _Unwind_Reason_Code LIBGCC2_UNWIND_ATTRIBUTE +_Unwind_Backtrace (_Unwind_Trace_Fn, void *); + +/* These functions are used for communicating information about the unwind + context (i.e. the unwind descriptors and the user register state) between + the unwind library and the personality routine and landing pad. Only + selected registers may be manipulated. */ + +extern _Unwind_Word _Unwind_GetGR (struct _Unwind_Context *, int); +extern void _Unwind_SetGR (struct _Unwind_Context *, int, _Unwind_Word); + +extern _Unwind_Ptr _Unwind_GetIP (struct _Unwind_Context *); +extern _Unwind_Ptr _Unwind_GetIPInfo (struct _Unwind_Context *, int *); +extern void _Unwind_SetIP (struct _Unwind_Context *, _Unwind_Ptr); + +/* @@@ Retrieve the CFA of the given context. */ +extern _Unwind_Word _Unwind_GetCFA (struct _Unwind_Context *); + +extern void *_Unwind_GetLanguageSpecificData (struct _Unwind_Context *); + +extern _Unwind_Ptr _Unwind_GetRegionStart (struct _Unwind_Context *); + + +/* The personality routine is the function in the C++ (or other language) + runtime library which serves as an interface between the system unwind + library and language-specific exception handling semantics. It is + specific to the code fragment described by an unwind info block, and + it is always referenced via the pointer in the unwind info block, and + hence it has no ABI-specified name. + + Note that this implies that two different C++ implementations can + use different names, and have different contents in the language + specific data area. Moreover, that the language specific data + area contains no version info because name of the function invoked + provides more effective versioning by detecting at link time the + lack of code to handle the different data format. */ + +typedef _Unwind_Reason_Code (*_Unwind_Personality_Fn) + (int, _Unwind_Action, _Unwind_Exception_Class, + struct _Unwind_Exception *, struct _Unwind_Context *); + +/* @@@ The following alternate entry points are for setjmp/longjmp + based unwinding. */ + +struct SjLj_Function_Context; +extern void _Unwind_SjLj_Register (struct SjLj_Function_Context *); +extern void _Unwind_SjLj_Unregister (struct SjLj_Function_Context *); + +extern _Unwind_Reason_Code LIBGCC2_UNWIND_ATTRIBUTE +_Unwind_SjLj_RaiseException (struct _Unwind_Exception *); +extern _Unwind_Reason_Code LIBGCC2_UNWIND_ATTRIBUTE +_Unwind_SjLj_ForcedUnwind (struct _Unwind_Exception *, _Unwind_Stop_Fn, void *); +extern void LIBGCC2_UNWIND_ATTRIBUTE +_Unwind_SjLj_Resume (struct _Unwind_Exception *); +extern _Unwind_Reason_Code LIBGCC2_UNWIND_ATTRIBUTE +_Unwind_SjLj_Resume_or_Rethrow (struct _Unwind_Exception *); + +/* @@@ The following provide access to the base addresses for text + and data-relative addressing in the LDSA. In order to stay link + compatible with the standard ABI for IA-64, we inline these. */ + +extern _Unwind_Ptr _Unwind_GetDataRelBase (struct _Unwind_Context *); +extern _Unwind_Ptr _Unwind_GetTextRelBase (struct _Unwind_Context *); + +/* @@@ Given an address, return the entry point of the function that + contains it. */ +extern void * _Unwind_FindEnclosingFunction (void *pc); + +#ifndef __SIZEOF_LONG__ + #error "__SIZEOF_LONG__ macro not defined" +#endif + +#ifndef __SIZEOF_POINTER__ + #error "__SIZEOF_POINTER__ macro not defined" +#endif + +/* leb128 type numbers have a potentially unlimited size. + The target of the following definitions of _sleb128_t and _uleb128_t + is to have efficient data types large enough to hold the leb128 type + numbers used in the unwind code. + Mostly these types will simply be defined to long and unsigned long + except when a unsigned long data type on the target machine is not + capable of storing a pointer. */ + +#if __SIZEOF_LONG__ >= __SIZEOF_POINTER__ + typedef long _sleb128_t; + typedef unsigned long _uleb128_t; +#elif __SIZEOF_LONG_LONG__ >= __SIZEOF_POINTER__ + typedef long long _sleb128_t; + typedef unsigned long long _uleb128_t; +#else +# error "What type shall we use for _sleb128_t?" +#endif + +#ifdef __SEH__ +/* Handles the mapping from SEH to GCC interfaces. */ +EXCEPTION_DISPOSITION _GCC_specific_handler (PEXCEPTION_RECORD, void *, + PCONTEXT, PDISPATCHER_CONTEXT, + _Unwind_Personality_Fn); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* unwind.h */ diff --git a/libjava/exception.cc b/libjava/exception.cc index 76f1451..d356f35 100644 --- a/libjava/exception.cc +++ b/libjava/exception.cc @@ -208,6 +208,8 @@ get_ttype_entry (_Unwind_Context *context, lsda_header_info *info, long i) #ifdef SJLJ_EXCEPTIONS #define PERSONALITY_FUNCTION __gcj_personality_sj0 #define __builtin_eh_return_data_regno(x) x +#elif defined (__SEH__) +#define PERSONALITY_FUNCTION __gcj_personality_imp #else #define PERSONALITY_FUNCTION __gcj_personality_v0 #endif @@ -231,7 +233,12 @@ PERSONALITY_FUNCTION (_Unwind_State state, #define CONTINUE_UNWINDING return _URC_CONTINUE_UNWIND -extern "C" _Unwind_Reason_Code +#ifdef __SEH__ +static +#else +extern "C" +#endif +_Unwind_Reason_Code PERSONALITY_FUNCTION (int version, _Unwind_Action actions, _Unwind_Exception_Class exception_class, @@ -495,3 +502,14 @@ PERSONALITY_FUNCTION (int version, #endif return _URC_INSTALL_CONTEXT; } + +#ifdef __SEH__ +extern "C" +EXCEPTION_DISPOSITION +__gcj_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame, + PCONTEXT ms_orig_context, PDISPATCHER_CONTEXT ms_disp) +{ + return _GCC_specific_handler (ms_exc, this_frame, ms_orig_context, + ms_disp, __gcj_personality_imp); +} +#endif /* SEH */ diff --git a/libjava/libgcj.ver b/libjava/libgcj.ver index 4e90d9d..142c6fb 100644 --- a/libjava/libgcj.ver +++ b/libjava/libgcj.ver @@ -7,6 +7,7 @@ _Jv_*; __gcj_personality_v0; __gcj_personality_sj0; + __gcj_personality_seh0; _Z*; local: *; diff --git a/libobjc/exception.c b/libobjc/exception.c index 4883448..ef80c23 100644 --- a/libobjc/exception.c +++ b/libobjc/exception.c @@ -226,6 +226,8 @@ get_ttype_entry (struct lsda_header_info *info, _Unwind_Word i) #ifdef SJLJ_EXCEPTIONS #define PERSONALITY_FUNCTION __gnu_objc_personality_sj0 #define __builtin_eh_return_data_regno(x) x +#elif defined(__SEH__) +#define PERSONALITY_FUNCTION __gnu_objc_personality_imp #else #define PERSONALITY_FUNCTION __gnu_objc_personality_v0 #endif @@ -249,6 +251,9 @@ PERSONALITY_FUNCTION (_Unwind_State state, #define CONTINUE_UNWINDING return _URC_CONTINUE_UNWIND +#ifdef __SEH__ +static +#endif _Unwind_Reason_Code PERSONALITY_FUNCTION (int version, _Unwind_Action actions, @@ -549,3 +554,13 @@ objc_exception_throw (id exception) abort (); } +#ifdef __SEH__ +EXCEPTION_DISPOSITION +__gnu_objc_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame, + PCONTEXT ms_orig_context, + PDISPATCHER_CONTEXT ms_disp) +{ + return _GCC_specific_handler (ms_exc, this_frame, ms_orig_context, + ms_disp, __gnu_objc_personality_imp); +} +#endif /* SEH */ diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver index 4981ca9..f2762b4 100644 --- a/libstdc++-v3/config/abi/pre/gnu.ver +++ b/libstdc++-v3/config/abi/pre/gnu.ver @@ -1225,6 +1225,7 @@ CXXABI_1.3 { __cxa_vec_new; __gxx_personality_v0; __gxx_personality_sj0; + __gxx_personality_seh0; __dynamic_cast; # *_type_info classes, ctor and dtor diff --git a/libstdc++-v3/libsupc++/eh_personality.cc b/libstdc++-v3/libsupc++/eh_personality.cc index 2b194f2..2cd2152 100644 --- a/libstdc++-v3/libsupc++/eh_personality.cc +++ b/libstdc++-v3/libsupc++/eh_personality.cc @@ -342,11 +342,18 @@ namespace __cxxabiv1 #ifdef _GLIBCXX_SJLJ_EXCEPTIONS #define PERSONALITY_FUNCTION __gxx_personality_sj0 #define __builtin_eh_return_data_regno(x) x +#elif defined(__SEH__) +#define PERSONALITY_FUNCTION __gxx_personality_imp #else #define PERSONALITY_FUNCTION __gxx_personality_v0 #endif -extern "C" _Unwind_Reason_Code +#ifdef __SEH__ +static +#else +extern "C" +#endif +_Unwind_Reason_Code #ifdef __ARM_EABI_UNWINDER__ PERSONALITY_FUNCTION (_Unwind_State state, struct _Unwind_Exception* ue_header, @@ -785,4 +792,15 @@ __cxa_call_unexpected (void *exc_obj_in) } #endif +#ifdef __SEH__ +extern "C" +EXCEPTION_DISPOSITION +__gxx_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame, + PCONTEXT ms_orig_context, PDISPATCHER_CONTEXT ms_disp) +{ + return _GCC_specific_handler (ms_exc, this_frame, ms_orig_context, + ms_disp, __gxx_personality_imp); +} +#endif /* SEH */ + } // namespace __cxxabiv1