This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] PR middle-end/19774


SJLJ exception handling is incompatible with alloca (and hence,
variable-length arrays).  A function which calls alloca and then catches
an exception may discover upon catching that the alloca'd memory has
been corrupted.

This is occurring because SJLJ exception handling stores function
context (SP, FP, etc.) at the start of a function, so any calls to
alloca aren't taken into consideration.

The attached patch causes the function context to be updated following
every call to alloca.  It implements this by creating a NOTE_INSN during
expansion of builtin_alloca's, which the EH logic processes to induce a
re-calculation of stack context.

Regression tested with no new failures on...

Target: sh-none-elf
Configured with:
/home/josh/work/testing/builds/gcc-mainline-sh/gcc/configure
--prefix=/home/josh/work/testing/builds/gcc-mainline-sh/installed
--target=sh-none-elf --disable-nls --enable-languages=c,c++
--with-newlib --enable-sjlj-exceptions

OK for mainline?

:ADDPATCH middle-end:

- Josh

2007-03-14  Josh Conner  <jconner@apple.com>

        PR middle-end/19774
        * builtins.c (expand_builtin_alloca): Create NOTE_INSN_ALLOCA.
        * except.c (sjlj_emit_function_enter): Split into...
        (sjlj_generate_setjmp_sequence): ..., with logic to handle
        unregistering.
        (sjlj_emit_context_saves): ..., with logic for handling
        NOTE_INSN_ALLOCA.
        (sjlj_build_landing_pads): Call sjlj_emit_context_saves
        instead of sjlj_emit_function_enter.
        * final.c (final_scan_insn): Handle NOTE_INSN_ALLOCA.
        * insn-notes.def: Add NOTE_INSN_ALLOCA.

2007-03-14  Josh Conner  <jconner@apple.com>

	PR middle-end/19774
	* g++.dg/eh/alloca.C: New.
Index: gcc/builtins.c
===================================================================
--- gcc/builtins.c	(revision 122887)
+++ gcc/builtins.c	(working copy)
@@ -4940,6 +4940,9 @@ expand_builtin_alloca (tree exp, rtx tar
   result = allocate_dynamic_stack_space (op0, target, BITS_PER_UNIT);
   result = convert_memory_address (ptr_mode, result);
 
+  /* EH with sjlj needs to know when the stack layout has changed.  */
+  emit_note (NOTE_INSN_ALLOCA);
+
   return result;
 }
 
Index: gcc/except.c
===================================================================
--- gcc/except.c	(revision 122887)
+++ gcc/except.c	(working copy)
@@ -274,7 +274,8 @@ struct sjlj_lp_info;
 static bool sjlj_find_directly_reachable_regions (struct sjlj_lp_info *);
 static void sjlj_assign_call_site_values (rtx, struct sjlj_lp_info *);
 static void sjlj_mark_call_sites (struct sjlj_lp_info *);
-static void sjlj_emit_function_enter (rtx);
+static rtx sjlj_generate_setjmp_sequence (rtx, bool);
+static void sjlj_emit_context_saves (rtx);
 static void sjlj_emit_function_exit (void);
 static void sjlj_emit_dispatch_table (rtx, struct sjlj_lp_info *);
 static void sjlj_build_landing_pads (void);
@@ -1836,16 +1837,20 @@ sjlj_mark_call_sites (struct sjlj_lp_inf
 
 /* Construct the SjLj_Function_Context.  */
 
-static void
-sjlj_emit_function_enter (rtx dispatch_label)
+static rtx
+sjlj_generate_setjmp_sequence (rtx dispatch_label, bool unregister_first)
 {
-  rtx fn_begin, fc, mem, seq;
-  bool fn_begin_outside_block;
+  rtx fc, mem, seq;
 
   fc = cfun->eh->sjlj_fc;
 
   start_sequence ();
 
+  /* We're reusing an old context, unregister it.  */
+  if (unregister_first == true)
+    emit_library_call (unwind_sjlj_unregister_libfunc, LCT_NORMAL,
+		       VOIDmode, 1, XEXP (fc, 0), Pmode);
+
   /* We're storing this libcall's address into memory instead of
      calling it directly.  Thus, we must call assemble_external_libcall
      here, as we can not depend on emit_library_call to do it for us.  */
@@ -1889,25 +1894,52 @@ sjlj_emit_function_enter (rtx dispatch_l
 
   seq = get_insns ();
   end_sequence ();
+  return seq;
+}
+
+/* Insert calls to SjLj_Function_Context at the start of the function,
+   and following any calls to alloca.  */
+
+static void
+sjlj_emit_context_saves (rtx dispatch_label)
+{
+  rtx seq, insn;
+  bool fn_begin_outside_block;
+
+  /* Create a new context for this function.  */
+  seq = sjlj_generate_setjmp_sequence (dispatch_label, false);
 
   /* ??? Instead of doing this at the beginning of the function,
      do this in a block that is at loop level 0 and dominates all
      can_throw_internal instructions.  */
 
   fn_begin_outside_block = true;
-  for (fn_begin = get_insns (); ; fn_begin = NEXT_INSN (fn_begin))
-    if (NOTE_P (fn_begin))
+  for (insn = get_insns (); ; insn = NEXT_INSN (insn))
+    if (NOTE_P (insn))
       {
-	if (NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_FUNCTION_BEG)
+	if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_BEG)
 	  break;
-	else if (NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_BASIC_BLOCK)
+	else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BASIC_BLOCK)
 	  fn_begin_outside_block = false;
       }
 
   if (fn_begin_outside_block)
     insert_insn_on_edge (seq, single_succ_edge (ENTRY_BLOCK_PTR));
   else
-    emit_insn_after (seq, fn_begin);
+    emit_insn_after (seq, insn);
+
+  /* Any time we call alloca, we need to re-calculate our stack context.
+     Ideally, we would only update a minimal context (i.e., stack
+     pointer in the general case), but that is a riskier solution to
+     an uncommon problem.  */
+  if (current_function_calls_alloca)
+    for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+      if (NOTE_P (insn) && NOTE_LINE_NUMBER (insn) == NOTE_INSN_ALLOCA)
+	{
+	  /* Re-register the function context.  */
+	  seq = sjlj_generate_setjmp_sequence (dispatch_label, true);
+	  emit_insn_after (seq, insn);
+	}
 }
 
 /* Call back from expand_function_end to know where we should put
@@ -2056,7 +2088,7 @@ sjlj_build_landing_pads (void)
       sjlj_assign_call_site_values (dispatch_label, lp_info);
       sjlj_mark_call_sites (lp_info);
 
-      sjlj_emit_function_enter (dispatch_label);
+      sjlj_emit_context_saves (dispatch_label);
       sjlj_emit_dispatch_table (dispatch_label, lp_info);
       sjlj_emit_function_exit ();
     }
Index: gcc/final.c
===================================================================
--- gcc/final.c	(revision 122887)
+++ gcc/final.c	(working copy)
@@ -1697,6 +1697,7 @@ final_scan_insn (rtx insn, FILE *file, i
     case NOTE:
       switch (NOTE_LINE_NUMBER (insn))
 	{
+	case NOTE_INSN_ALLOCA:
 	case NOTE_INSN_DELETED:
 	  break;
 
Index: gcc/insn-notes.def
===================================================================
--- gcc/insn-notes.def	(revision 122887)
+++ gcc/insn-notes.def	(working copy)
@@ -71,4 +71,8 @@ INSN_NOTE (BASIC_BLOCK)
    between hot and cold text sections.  */
 INSN_NOTE (SWITCH_TEXT_SECTIONS)
 
+/* Record that we have just executed an alloca() -- used for
+   implementation of sjlj exception handling.  */
+INSN_NOTE (ALLOCA)
+
 #undef INSN_NOTE
// PR middle-end/19774
// Memory allocated with alloca() is lost when using SJLJ exception
// handling.
// { dg-do run }

#include <alloca.h>

#define ARRAY_SIZE 100

int foo (int X);
int bar (int X);

int main (void)
{
  return foo (ARRAY_SIZE);
}

int foo (int X) {
  
  int *foo_array;
  unsigned int i;

  foo_array = (int *) alloca (sizeof (int) * X);    
  for (i = 0; i < ARRAY_SIZE; i++)
    foo_array[i] = ARRAY_SIZE - i;

  try
    {
      bar(X);
    }
  catch (const char *msg)
    {
      for (i = 0; i < ARRAY_SIZE; i++)
	if (foo_array[i] != ARRAY_SIZE - i)
	  return 1;

      return 0;
    }

  return 1;
}

int bar (int X)
{
    throw "whoops";
    return 0;
}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]