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]

PATCH:throwing exceptions with no memory


Hi,
here's a patch for http://www.cygnus.com/ml/egcs-bugs/1998-Sep/0229.html
and http://www.cygnus.com/ml/egcs/1998-Oct/0773.html

To recap, exception throwing currently invokes malloc, this is obviously
a problem when malloc has just failed (throw bad_alloc). This patch
fixes this to a greater or lesser degree.

First, on the first exception thrown by a thread, an exception context
needs to be allocated. I've altered new_eh_context (libgcc2.c) to allow
fallback to a single static eh_context variable, should malloc fail.
This means that a single threaded application can't die here, but a
multi threaded application could, if two threads throw exceptions with
malloc failing. The only way to fix this properly is in the thread
creation to allocate the eh_context. A workaround is to insert
	try{throw 1;}catch(int){}
at the beginning of a thread, when there is known to be sufficient
memory.

Second, when throwing objects by value, space must be allocated for the
object thrown. This is done by __eh_alloc (exception.cc), which again
calls malloc. I've added a small (~120 byte) buffer to the eh_context
struct, which __eh_alloc can use as a fallback malloc arena, should
malloc fail. This buffer is of limited size, because the intention is
that it'll be used when throwing bad_alloc, a small structure. __eh_free
is similarly altered to return space to this fallback arena.

__eh_alloc is also used to allocate an eh_info structure for that
particular exception. The same applies. Because __eh_alloc and __eh_free
are now tied up with the eh_context struct, I've moved them into
libgcc2.c. Because an exception handler can throw a different exception,
there can be two live eh_info's and two live exception objects. The
fallback buffer is large enough to hold these for reasonable sized
objects, and doesn't assume anything about the ordering of alloc and
free calls.

Locating the call frame __frame_state_for (frame.c) can also call malloc
to sort a module FDE's. I've altered this to check for malloc failing,
and if so do a linear search on the unsorted chain. There are two
mallocs involved, if the second fails, it is still possible to sort the
FDEs, but they're not split into linear and erratic sets. To indicate
that the FDE's are unsorted, but previously seen, I swap the values of
pc_begin and pc_end in the struct object (frame.h) structure. I can't
set fde_array to NULL, because that can hold the original unsorted list.
This is a bit of a kludge, but I couldn't add a field to the structure.
Later exceptions from the same module, reattempt the sorting.

Finally, locating the unwind information for an instruction,
execute_cfa_instruction can also malloc when a DW_CFA_remember_state
record is found. This is checked, and if it fails, we return NULL
(rather than seg fault). A quick grep of the gcc sources indicates that
CFA_remember_state is not used, and I would suggest that it is kept this
way. Using them adds an unbounded runtime memory requirement, because
they can be arbitrarily nested. There was also a memory leak in
__frame_state_for, regarding these records. The design of the
remember_state record is rather daft, if you ask me, what's really
needed is a PC skip offset and a record increment offset, so you can
directly hop over the dwarf fields, if they're not appropriate for your
unwind PC. Then no pushing or popping would be needed.

As Martin von Loewis points out, a better solution to the DFE's would be
have the linker sort them in the first place.

I've not added exception specifications to __eh_alloc and the associated
functions, because although I believe they cannot throw (they call
__terminate in the event of failure), I cannot locate a paragraph in the
CD2 which categorically states this. Such exception specifications would
reduce redundant exception code.

frame.c still has a number of calls to abort(), some are checking
frame's program logic, and some are checking FDE consistency. At a
minimum I would suggest commenting which are which, but it might be
better to replace the latter with calls to __terminate().

I include a test program which throws and rethrows exceptions in the
presence of a malloc which always returns NULL. The current egcs blows
up with this program.

NB. A copyright assignment will shortly be in the post.

nathan
-- 
Dr Nathan Sidwell :: Computer Science Department :: Bristol University
      You can up the bandwidth, but you can't up the speed of light      
nathan@acm.org  http://www.cs.bris.ac.uk/~nathan/  nathan@cs.bris.ac.uk
egcs/gcc/ChangeLog:
Sat Oct 24 15:48:13 BST 1998  Nathan Sidwell  <nathan@acm.org>

	* libgcc2.c (single_eh_context, single_eh_alloc, eh_mutex): New
	static variables.
	(new_eh_context): Fallback on static location, if malloc fails.
	(eh_context_free): Release static location, if allocated.
	(eh_threads_initialize): Initialize eh_mutex.
	(eh_context_static): Use new_eh_context.
	(__eh_alloc): Moved from cp/exception.cc. Fallback on emergency
	eh_context buffer, if malloc fails.
	(__eh_free): Moved from cp/exception.cc. Release to emergency
	eh_context buffer, if appropriate.
	
	* eh-common.h (EH_ALLOC_BLOCKS): New #define.
	(eh_context): Add alloc_mask and alloc_buffer emergency fallback
	space.
	
	* frame.c (start_fde_sort): Only allocate erratic array, if
	linear one is ok.
	(fde_insert): Only insert, if there's a valid array.
	(fde_end_sort): Split, sort and merge if linear and erratic
	arrays exist, else just sort linear one.
	(search_fdes): New function. Linear search through original fde
	structure.
	(frame_init): If failed to sort, swap pc_begin and pc_end to
	indicate this.
	(find_fde): Fallback on linear search, if failed to sort array.
	(execute_cfa_insn): Check malloc for DW_CFA_remember_state.
	(__frame_state_for): Check execute_cfa_insn return. Free any
	saved state.

egcs/gcc/cp/ChangeLog:
Sat Oct 24 15:48:13 BST 1998  Nathan Sidwell  <nathan@acm.org>

	* exception.cc: include tconfig.h, needed by eh-common.h.
	(__eh_alloc, __eh_free): Moved to libgcc2.c

Index: egcs/gcc/libgcc2.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/libgcc2.c,v
retrieving revision 1.53
diff -c -3 -p -r1.53 libgcc2.c
*** libgcc2.c	1998/10/19 10:44:52	1.53
--- libgcc2.c	1998/10/24 14:48:06
*************** __empty ()
*** 3071,3091 ****
  
  static void *top_elt[2];
  
! /* Allocate and return a new EH context structure. */
  
- extern void __throw ();
- 
  static void *
  new_eh_context ()
  {
!   struct eh_context *eh = (struct eh_context *) malloc (sizeof *eh);
    if (! eh)
!     __terminate ();
  
    memset (eh, 0, sizeof *eh);
  
    eh->dynamic_handler_chain = top_elt;
- 
    return eh;
  }
  
--- 3071,3113 ----
  
  static void *top_elt[2];
  
! /* Allocate and return a new EH context structure. If malloc fails us, we
!  * attempt to fall back on static variables. This helps us throw bad_alloc.
!  * It is not a total fix, because it only works for one thread at a time.
!  */
! static struct eh_context single_eh_context;
! static int single_eh_alloc = 0;
! #ifdef __GTHREAD_MUTEX_INIT
! static __gthread_mutex_t eh_mutex = __GTHREAD_MUTEX_INIT;
! #else
! static __gthread_mutex_t eh_mutex;
! #endif /* ! __GTHREAD_MUTEX_INIT */
  
  static void *
  new_eh_context ()
  {
!   struct eh_context *eh = NULL;
! #if __GTHREADS
!   eh = (struct eh_context *) malloc (sizeof *eh);
! #endif /* __GTHREADS */
! 
    if (! eh)
!     {
!       __gthread_mutex_lock (&eh_mutex);
!       if (! single_eh_alloc)
! 	{
! 	  single_eh_alloc = 1;
! 	  eh = &single_eh_context;
! 	}
!       __gthread_mutex_unlock (&eh_mutex);
!       /* call terminate outside the mutex */
!       if (! eh)
! 	__terminate ();
!     }
  
    memset (eh, 0, sizeof *eh);
  
    eh->dynamic_handler_chain = top_elt;
    return eh;
  }
  
*************** eh_context_free (void *ptr)
*** 3098,3106 ****
  {
    __gthread_key_dtor (eh_context_key, ptr);
    if (ptr)
!     free (ptr);
  }
! #endif
  
  /* Pointer to function to return EH context. */
  
--- 3120,3135 ----
  {
    __gthread_key_dtor (eh_context_key, ptr);
    if (ptr)
!     {
!       __gthread_mutex_lock (&eh_mutex);
!       if (ptr == &single_eh_context)
! 	single_eh_alloc = 0;
!       else
! 	free (ptr);
!       __gthread_mutex_unlock (&eh_mutex);
!     }
  }
! #endif /* __GTHREADS */
  
  /* Pointer to function to return EH context. */
  
*************** __get_eh_info ()
*** 3134,3139 ****
--- 3163,3171 ----
  static void
  eh_threads_initialize ()
  {
+ #ifdef __GTHREAD_MUTEX_INIT_FUNCTION
+   __GTHREAD_MUTEX_INIT_FUNCTION (&eh_mutex);
+ #endif
    /* Try to create the key.  If it fails, revert to static method,
       otherwise start using thread specific EH contexts. */
    if (__gthread_key_create (&eh_context_key, &eh_context_free) == 0)
*************** eh_threads_initialize ()
*** 3141,3147 ****
    else
      get_eh_context = &eh_context_static;
  }
! #endif /* no __GTHREADS */
  
  /* Initialize EH context.
     This will be called only once, since we change GET_EH_CONTEXT
--- 3173,3179 ----
    else
      get_eh_context = &eh_context_static;
  }
! #endif /* __GTHREADS */
  
  /* Initialize EH context.
     This will be called only once, since we change GET_EH_CONTEXT
*************** eh_context_initialize ()
*** 3178,3192 ****
  static struct eh_context *
  eh_context_static ()
  {
!   static struct eh_context eh;
!   static int initialized;
!   if (! initialized)
!     {
!       initialized = 1;
!       memset (&eh, 0, sizeof eh);
!       eh.dynamic_handler_chain = top_elt;
!     }
!   return &eh;
  }
  
  #if __GTHREADS
--- 3210,3220 ----
  static struct eh_context *
  eh_context_static ()
  {
!   static struct eh_context *eh;
!   
!   if (! eh)
!     eh = new_eh_context();
!   return eh;
  }
  
  #if __GTHREADS
*************** eh_context_specific ()
*** 3205,3212 ****
      }
  
    return eh;
  }
- #endif __GTHREADS
  
  /* Support routines for setjmp/longjmp exception handling.  */
  
--- 3233,3308 ----
      }
  
    return eh;
+ }
+ #endif /* __GTHREADS */
+ 
+ /* Support routines for alloc/free during exception handling */
+ 
+ /* __eh_alloc and __eh_free attempt allocation using malloc, but fall back to
+  * the small arena in the eh_context. This is needed because throwing an
+  * out-of-memory exception would fail otherwise. The emergency space is
+  * allocated in blocks of size BIGGEST_ALIGNMENT / BITS_PER_UNIT, the
+  * minimum allocation being two blocks. A bitmask indicates which blocks
+  * have been allocated. To indicate the size of an allocation, the bit for
+  * the final block is not set. Hence each allocation is a run of 1s followed
+  * by a zero.
+  */
+ /* extern "C" void * malloc (size_t);*/
+ extern void *
+ __eh_alloc (size_t size)
+ {
+   void *p = malloc (size);
+   if (p == 0)
+     {
+       struct eh_context *eh = __get_eh_context ();
+       unsigned blocks = (size + BIGGEST_ALIGNMENT / BITS_PER_UNIT) /
+ 	  (BIGGEST_ALIGNMENT / BITS_PER_UNIT);
+       unsigned real_mask = eh->alloc_mask | (eh->alloc_mask << 1);
+       unsigned our_mask;
+       unsigned ix;
+       
+       blocks -= blocks > 1;
+       our_mask = (2 << blocks) - 1; /* avoid shift by word size */
+       
+       for (ix = EH_ALLOC_BLOCKS - blocks; ix--;)
+ 	if (! ((real_mask >> ix) & our_mask))
+ 	  {
+ 	    /* found some space */
+ 	    p = &eh->alloc_buffer[ix * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)];
+ 	    eh->alloc_mask |= (our_mask >> 1) << ix;
+ 	    return p;
+ 	  }
+       __terminate ();
+     }
+   return p;
+ }
+ 
+ /* Free the memory for an cp_eh_info and associated exception, given
+    a pointer to the cp_eh_info.  */
+ 
+ /* extern "C" void free (void *); */
+ extern void
+ __eh_free (void *p)
+ {
+   struct eh_context *eh = __get_eh_context ();
+ 
+   size_t  diff = (size_t)((char *)p - &eh->alloc_buffer[0]);
+   if (diff < EH_ALLOC_BLOCKS * (BIGGEST_ALIGNMENT / BITS_PER_UNIT))
+     {
+       unsigned mask = eh->alloc_mask;
+       unsigned bit = 1 << (diff / (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
+       
+       do
+ 	{
+ 	  mask ^= bit;
+ 	  bit <<= 1;
+ 	}
+       while (mask & bit);
+       eh->alloc_mask = mask;
+     }
+   else
+     free (p);
  }
  
  /* Support routines for setjmp/longjmp exception handling.  */
  
Index: egcs/gcc/eh-common.h
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/eh-common.h,v
retrieving revision 1.5
diff -c -3 -p -r1.5 eh-common.h
*** eh-common.h	1998/06/25 14:11:45	1.5
--- eh-common.h	1998/10/24 14:48:06
***************
*** 24,36 ****
     The routine get_dynamic_handler_chain() also has a dependancy on
     the location of 'dynamic_handler_chain'. If its location is changed, 
     that routine must be modified as well. */
! 
  struct eh_context
  {
    void *handler_label;
    void **dynamic_handler_chain;
    /* This is language dependent part of the eh context. */
    void *info;
  };
  
  #ifndef EH_TABLE_LOOKUP
--- 24,50 ----
     The routine get_dynamic_handler_chain() also has a dependancy on
     the location of 'dynamic_handler_chain'. If its location is changed, 
     that routine must be modified as well. */
! #ifndef EH_ALLOC_BLOCKS
! /* 216 bytes means the entire eh_context plus malloc overhead fits in 256
!  * bytes. 216 bytes gives an eh_info and object size limit of 108 bytes.
!  * This should be sufficient for throwing bad_alloc.
!  */
! #if INT_TYPE_SIZE <= (216 / (BIGGEST_ALIGNMENT / BITS_PER_UNIT))
! #define EH_ALLOC_BLOCKS INT_TYPE_SIZE
! #else
! #define EH_ALLOC_BLOCKS (216 / (BIGGEST_ALIGNMENT / BITS_PER_UNIT))
! #endif
! #endif
  struct eh_context
  {
    void *handler_label;
    void **dynamic_handler_chain;
    /* This is language dependent part of the eh context. */
    void *info;
+   /* emergency fallback space, if malloc fails during handling */
+   unsigned alloc_mask;
+   char alloc_buffer[EH_ALLOC_BLOCKS * BIGGEST_ALIGNMENT / BITS_PER_UNIT]
+       __attribute__((__aligned__(BIGGEST_ALIGNMENT / BITS_PER_UNIT)));
  };
  
  #ifndef EH_TABLE_LOOKUP
Index: egcs/gcc/frame.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/frame.c,v
retrieving revision 1.22
diff -c -3 -p -r1.22 frame.c
*** frame.c	1998/08/27 14:51:54	1.22
--- frame.c	1998/10/24 14:48:07
*************** static inline void
*** 260,266 ****
  start_fde_sort (fde_accumulator *accu, size_t count)
  {
    accu->linear.array = (fde **) malloc (sizeof (fde *) * count);
!   accu->erratic.array = (fde **) malloc (sizeof (fde *) * count);
    accu->linear.count = 0;
    accu->erratic.count = 0;
  }
--- 260,267 ----
  start_fde_sort (fde_accumulator *accu, size_t count)
  {
    accu->linear.array = (fde **) malloc (sizeof (fde *) * count);
!   accu->erratic.array = accu->linear.array ?
!       (fde **) malloc (sizeof (fde *) * count) : NULL;
    accu->linear.count = 0;
    accu->erratic.count = 0;
  }
*************** start_fde_sort (fde_accumulator *accu, s
*** 268,274 ****
  static inline void
  fde_insert (fde_accumulator *accu, fde *this_fde)
  {
!   accu->linear.array[accu->linear.count++] = this_fde;
  }
  
  /* Split LINEAR into a linear sequence with low values and an erratic
--- 269,276 ----
  static inline void
  fde_insert (fde_accumulator *accu, fde *this_fde)
  {
!   if (accu->linear.array)
!     accu->linear.array[accu->linear.count++] = this_fde;
  }
  
  /* Split LINEAR into a linear sequence with low values and an erratic
*************** fde_merge (fde_vector *v1, const fde_vec
*** 395,408 ****
  static fde **
  end_fde_sort (fde_accumulator *accu, size_t count)
  {
!   if (accu->linear.count != count)
      abort ();
!   fde_split (&accu->linear, &accu->erratic);
!   if (accu->linear.count + accu->erratic.count != count)
!     abort ();
!   frame_heapsort (&accu->erratic);
!   fde_merge (&accu->linear, &accu->erratic);
!   free (accu->erratic.array);
    return accu->linear.array;
  }
  
--- 397,420 ----
  static fde **
  end_fde_sort (fde_accumulator *accu, size_t count)
  {
!   if (accu->linear.array && accu->linear.count != count)
      abort ();
!   
!   if (accu->erratic.array)
!     {
!       fde_split (&accu->linear, &accu->erratic);
!       if (accu->linear.count + accu->erratic.count != count)
! 	abort ();
!       frame_heapsort (&accu->erratic);
!       fde_merge (&accu->linear, &accu->erratic);
!       free (accu->erratic.array);
!     }
!   else
!     {
!       /* we've not managed to malloc an erratic array, so heap sort in the
!        * linear one */
!       frame_heapsort (&accu->linear);
!     }
    return accu->linear.array;
  }
  
*************** add_fdes (fde *this_fde, fde_accumulator
*** 447,452 ****
--- 459,480 ----
    *end_ptr = pc_end;
  }
  
+ /* search this fde table for the one containing the pc */
+ static fde *
+ search_fdes (fde *this_fde, void *pc)
+ {
+   for (; this_fde->length != 0; this_fde = next_fde (this_fde))
+     {
+       /* Skip CIEs and linked once FDE entries.  */
+       if (this_fde->CIE_delta == 0 || this_fde->pc_begin == 0)
+ 	continue;
+ 
+       if ((uaddr)((char *)pc - (char *)this_fde->pc_begin) < this_fde->pc_range)
+ 	return this_fde;
+     }
+   return NULL;
+ }
+ 
  /* Set up a sorted array of pointers to FDEs for a loaded object.  We
     count up the entries before allocating the array because it's likely to
     be faster.  */
*************** frame_init (struct object* ob)
*** 457,462 ****
--- 485,491 ----
    size_t count;
    fde_accumulator accu;
    void *pc_begin, *pc_end;
+   fde **array;
  
    if (ob->fde_array)
      {
*************** frame_init (struct object* ob)
*** 481,488 ****
      }
    else
      add_fdes (ob->fde_begin, &accu, &pc_begin, &pc_end);
  
-   ob->fde_array = end_fde_sort (&accu, count);
    ob->pc_begin = pc_begin;
    ob->pc_end = pc_end;
  }
--- 510,532 ----
      }
    else
      add_fdes (ob->fde_begin, &accu, &pc_begin, &pc_end);
+   
+   array = end_fde_sort (&accu, count);
+   if(array)
+     ob->fde_array = array;
+   else
+     {
+       /* Swap pc_begin and pc_end to signify that we've not managed to sort
+        * the fde array. we can't have fde_array being NULL, as it's holding
+        * the unsorted table
+        */
+       void *tmp;
+       
+       tmp = pc_begin;
+       pc_begin = pc_end;
+       pc_end = tmp;
+     }
  
    ob->pc_begin = pc_begin;
    ob->pc_end = pc_end;
  }
*************** find_fde (void *pc)
*** 498,530 ****
    init_object_mutex_once ();
    __gthread_mutex_lock (&object_mutex);
  
    for (ob = objects; ob; ob = ob->next)
      {
!       if (ob->pc_begin == 0)
  	frame_init (ob);
        if (pc >= ob->pc_begin && pc < ob->pc_end)
  	break;
      }
  
-   __gthread_mutex_unlock (&object_mutex);
  
    if (ob == 0)
!     return 0;
  
!   /* Standard binary search algorithm.  */
!   for (lo = 0, hi = ob->count; lo < hi; )
      {
!       size_t i = (lo + hi) / 2;
!       fde *f = ob->fde_array[i];
  
!       if (pc < f->pc_begin)
! 	hi = i;
!       else if (pc >= f->pc_begin + f->pc_range)
! 	lo = i + 1;
        else
! 	return f;
      }
- 
    return 0;
  }
  
--- 542,612 ----
    init_object_mutex_once ();
    __gthread_mutex_lock (&object_mutex);
  
+   /* Linear sort through the objects, to find the one containing the pc. */
    for (ob = objects; ob; ob = ob->next)
      {
!       int init = ob->pc_begin == 0;
!       if (init)
  	frame_init (ob);
+       if (pc >= ob->pc_end && pc < ob->pc_begin)
+ 	{
+ 	  /* we had insufficient memory to sort the fde array, but we're in
+ 	   * this block */
+ 	  if (! init)
+ 	    /* Have another go, if it was a previous time */
+ 	    frame_init (ob);
+ 	  break;
+ 	}
        if (pc >= ob->pc_begin && pc < ob->pc_end)
  	break;
      }
  
  
    if (ob == 0)
!     {
!       return 0;
!       __gthread_mutex_unlock (&object_mutex);
!     }
  
!   if (ob->pc_end > ob->pc_begin)
      {
!       __gthread_mutex_unlock (&object_mutex);
!       
!       /* Standard binary search algorithm.  */
!       for (lo = 0, hi = ob->count; lo < hi; )
! 	{
! 	  size_t i = (lo + hi) / 2;
! 	  fde *f = ob->fde_array[i];
  
! 	  if (pc < f->pc_begin)
! 	    hi = i;
! 	  else if (pc >= f->pc_begin + f->pc_range)
! 	    lo = i + 1;
! 	  else
! 	    return f;
! 	}
!     }
!   else
!     {
!       /* Long slow labourious linear search, cos we've no memory. */
!       fde *f;
!       
!       if (ob->fde_array)
! 	{
! 	  fde **p = ob->fde_array;
! 	  
! 	  for (; *p; ++p)
! 	    {
! 	      f = search_fdes (*p, pc);
! 	      if (f)
! 		break;
! 	    }
! 	}
        else
! 	f = search_fdes (ob->fde_begin, pc);
!       __gthread_mutex_unlock (&object_mutex);
!       return f;
      }
    return 0;
  }
  
*************** execute_cfa_insn (void *p, struct frame_
*** 665,675 ****
--- 747,763 ----
        state->s.cfa_offset = offset;
        break;
        
+       /* remember_state adds unbounded runtime memory requirement. This is
+        * fatal for exception handling. GCC doesn't currently emit these. If
+        * it ever does exception unwind tables will be broken.
+        */
      case DW_CFA_remember_state:
        {
  	struct frame_state_internal *save =
  	  (struct frame_state_internal *)
  	  malloc (sizeof (struct frame_state_internal));
+ 	if (! save)
+ 	  return NULL;
  	memcpy (save, state, sizeof (struct frame_state_internal));
  	state->saved_state = save;
        }
*************** __frame_state_for (void *pc_target, stru
*** 822,828 ****
  
    /* First decode all the insns in the CIE.  */
    end = next_fde ((fde*) get_cie (f));
!   while (insn < end)
      insn = execute_cfa_insn (insn, &state, &info, 0);
  
    insn = ((fde *)f) + 1;
--- 910,916 ----
  
    /* First decode all the insns in the CIE.  */
    end = next_fde ((fde*) get_cie (f));
!   while (insn != 0 && insn < end)
      insn = execute_cfa_insn (insn, &state, &info, 0);
  
    insn = ((fde *)f) + 1;
*************** __frame_state_for (void *pc_target, stru
*** 837,846 ****
    /* Then the insns in the FDE up to our target PC.  */
    end = next_fde (f);
    pc = f->pc_begin;
!   while (insn < end && pc <= pc_target)
      insn = execute_cfa_insn (insn, &state, &info, &pc);
  
    memcpy (state_in, &state.s, sizeof (state.s));
    return state_in;
  }
  #endif /* DWARF2_UNWIND_INFO */
--- 925,944 ----
    /* Then the insns in the FDE up to our target PC.  */
    end = next_fde (f);
    pc = f->pc_begin;
!   while (insn != 0 && insn < end && pc <= pc_target)
      insn = execute_cfa_insn (insn, &state, &info, &pc);
  
    memcpy (state_in, &state.s, sizeof (state.s));
+   
+   while (state.saved_state != 0)
+     {
+       struct frame_state_internal *saved = state.saved_state;
+       state.saved_state = saved->saved_state;
+       free (saved);
+     }
+   
+   if (insn == 0)
+     return NULL;
    return state_in;
  }
  #endif /* DWARF2_UNWIND_INFO */
Index: egcs/gcc/cp/exception.cc
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/exception.cc,v
retrieving revision 1.18
diff -c -3 -p -r1.18 exception.cc
*** exception.cc	1998/09/17 13:55:51	1.18
--- exception.cc	1998/10/24 14:48:07
***************
*** 30,35 ****
--- 30,36 ----
  #include "typeinfo"
  #include "exception"
  #include <stddef.h>
+ #include "tconfig.h"
  #include "eh-common.h"
  
  /* Define terminate, unexpected, set_terminate, set_unexpected as
*************** struct cp_eh_info
*** 100,105 ****
--- 101,110 ----
  
  extern "C" cp_eh_info **__get_eh_info (); 	// actually void **
  
+ /* Exception allocate and free, defined in libgcc2. */
+ extern "C" void *__eh_alloc(size_t);
+ extern "C" void __eh_free();
+ 
  /* Is P the type_info node for a pointer of some kind?  */
  
  extern bool __is_pointer (void *);
*************** __cp_eh_info (void)
*** 124,153 ****
  {
    return *__get_eh_info ();
  }
- 
- /* Allocate a buffer for a cp_eh_info and an exception object of size SIZE,
-    and return a pointer to the beginning of the object's space.  */
- 
- extern "C" void * malloc (size_t);
- extern "C" void *
- __eh_alloc (size_t size)
- {
-   void *p = malloc (size);
-   if (p == 0)
-     terminate ();
-   return p;
- }
- 
- /* Free the memory for an cp_eh_info and associated exception, given
-    a pointer to the cp_eh_info.  */
- 
- extern "C" void free (void *);
- extern "C" void
- __eh_free (void *p)
- {
-   free (p);
- }
- 
  
  typedef void * (* rtimetype) (void);
  
--- 129,134 ----
// If malloc fails, exceptions blow up. Compile, link and run this. With no
// args, we blow up at the second exception exception. With one arg we blow up
// at the first exception.
// nathan@acm.org

#include <stdio.h>

// simulate malloc failing (think bad_alloc)
static int fail;
static char arena[20000]; // so things can initialize
static unsigned pos;

extern "C" void *malloc(unsigned int size)
{
  void *p = &arena[pos];
  if(fail)
    return 0;
  size = (size + 15u) & ~15u; // Yes, this is a hack
  pos += size;
  return p;
}
extern "C" void free(void *)
{
  
}

void fn_throw() throw(int)
{
  fputs("throwing int\n", stdout);
  throw 1;
}

void fn_rethrow() throw(int)
{
  try{fn_throw();}
  catch(int a){
    fputs("caught int, rethrowing\n", stdout);
    throw;}
}

void fn_catchthrow() throw(int)
{
  try{fn_throw();}
  catch(int a){
    fputs("caught int, throwing another\n", stdout);
    throw a + 1;}
}

int main(int argc, char **argv)
{
  unsigned failat = 0;
  unsigned startat = 0;
  unsigned ix;
  
  fputs("two numeric args, first at what stage to start, second at what stage to fail\n", stdout);
  
  if(argc > 1)
    {
      failat = startat = argv[1][0] - '0';
      if(argv[1][1])
        failat = argv[1][1] - '0';
    }
  
  for(ix = startat; ix < 3; ix++)
    {
      if(ix >= failat)
        fail = 1;
      switch(ix)
        {
          case 0:
            fputs("Attempting throw, catch\n", stdout);
            try{fn_throw();}
            catch(int a){fputs("caught\n", stdout);}
            fputs("OK\n", stdout);
            break;
          case 1:
            fputs("Attempting throw, catch, rethrow, catch\n", stdout);
            try{fn_rethrow();}
            catch(int a){fputs("caught\n", stdout);}
            fputs("OK\n", stdout);
            break;
          case 2:
            fputs("Attempting throw, catch, throw, catch\n", stdout);
            try{fn_catchthrow();}
            catch(int a){fputs("caught\n", stdout);}
            fputs("OK\n", stdout);
            break;
        }
    }
  
  return 0;
}

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