summaryrefslogtreecommitdiff
path: root/gcc/libgcc2.c
diff options
context:
space:
mode:
authorjason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>1997-09-16 02:07:50 +0000
committerjason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>1997-09-16 02:07:50 +0000
commit447a9eb9a6224483e22ea816ace798558a37c74d (patch)
treed3f3fa9225d4040922a030638ba72ca3349ef34e /gcc/libgcc2.c
parent241e4f8babf0f4f354d81bc32e0f156fec5346af (diff)
downloadgcc-447a9eb9a6224483e22ea816ace798558a37c74d.tar.gz
dwarf2 EH support
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@15464 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/libgcc2.c')
-rw-r--r--gcc/libgcc2.c460
1 files changed, 349 insertions, 111 deletions
diff --git a/gcc/libgcc2.c b/gcc/libgcc2.c
index 0e9252e12a3..26a0e05d1ad 100644
--- a/gcc/libgcc2.c
+++ b/gcc/libgcc2.c
@@ -3107,11 +3107,9 @@ int _exit_dummy_decl = 0; /* prevent compiler & linker warnings */
#ifdef L_eh
-#ifdef EH_TABLE_LOOKUP
-
-EH_TABLE_LOOKUP
+/* Shared exception handling support routines. */
-#else
+extern void *__eh_type;
void
__default_terminate ()
@@ -3127,14 +3125,31 @@ __terminate ()
(*__terminate_func)();
}
+void *
+__throw_type_match (void *catch_type, void *throw_type, void *obj)
+{
+#if 0
+ printf ("__throw_type_match (): catch_type = %s, throw_type = %s\n",
+ catch_type, throw_type);
+#endif
+ if (strcmp ((const char *)catch_type, (const char *)throw_type) == 0)
+ return obj;
+ return 0;
+}
+
+void
+__empty ()
+{
+}
+
+/* Support routines for setjmp/longjmp exception handling. */
+
/* Calls to __sjthrow are generated by the compiler when an exception
is raised when using the setjmp/longjmp exception handling codegen
method. */
extern void longjmp (void *, int);
-void *__eh_type;
-
static void *top_elt[2];
void **__dynamic_handler_chain = top_elt;
@@ -3286,120 +3301,104 @@ __sjpopnthrow ()
__sjthrow ();
}
+
+/* Support code for all exception region-based exception handling. */
+
+/* This value identifies the place from which an exception is being
+ thrown. */
+
+extern void *__eh_pc;
+
+#ifdef EH_TABLE_LOOKUP
+
+EH_TABLE_LOOKUP
-typedef struct {
+#else
+
+typedef struct exception_table {
void *start;
void *end;
void *exception_handler;
} exception_table;
-struct exception_table_node {
+/* This routine takes a PC and a pointer to the exception region TABLE for
+ its translation unit, and returns the address of the exception handler
+ associated with the closest exception table handler entry associated
+ with that PC, or 0 if there are no table entries the PC fits in.
+
+ In the advent of a tie, we have to give the last entry, as it represents
+ an inner block. */
+
+static void *
+find_exception_handler (void *pc, exception_table *table)
+{
+ if (table)
+ {
+ int pos;
+ int best = 0;
+
+ /* We can't do a binary search because the table isn't guaranteed
+ to be sorted from function to function. */
+ for (pos = 0; table[pos].exception_handler != (void *) -1; ++pos)
+ {
+ if (table[pos].start <= pc && table[pos].end >= pc)
+ {
+ /* This can apply. Make sure it is at least as small as
+ the previous best. */
+ if (best == 0 || (table[pos].end <= table[best].end
+ && table[pos].start >= table[best].start))
+ best = pos;
+ }
+ /* But it is sorted by starting PC within a function. */
+ else if (best && table[pos].start > pc)
+ break;
+ }
+ if (best != 0)
+ return table[best].exception_handler;
+ }
+
+ return (void *) 0;
+}
+#endif /* EH_TABLE_LOOKUP */
+
+#ifndef DWARF2_UNWIND_INFO
+/* Support code for exception handling using inline unwinders or
+ __unwind_function. */
+
+#ifndef EH_TABLE_LOOKUP
+typedef struct exception_table_node {
exception_table *table;
void *start;
void *end;
struct exception_table_node *next;
-};
+} exception_table_node;
static struct exception_table_node *exception_table_list;
-/* this routine takes a pc, and the address of the exception handler associated
- with the closest exception table handler entry associated with that PC,
- or 0 if there are no table entries the PC fits in. The algorithm works
- something like this:
-
- while(current_entry exists) {
- if(current_entry.start < pc )
- current_entry = next_entry;
- else {
- if(prev_entry.start <= pc && prev_entry.end > pc) {
- save pointer to prev_entry;
- return prev_entry.exception_handler;
- }
- else return 0;
- }
- }
- return 0;
-
- Assuming a correctly sorted table (ascending order) this routine should
- return the tightest match...
-
- In the advent of a tie, we have to give the last entry, as it represents
- an inner block. */
-
void *
__find_first_exception_table_match (void *pc)
{
- register struct exception_table_node *tnp;
- register exception_table *table;
- int pos;
- int best;
-
-#if 0
- printf ("find_first_exception_table_match (): pc = %x!\n", pc);
-#endif
+ register exception_table_node *tnp;
for (tnp = exception_table_list; tnp != 0; tnp = tnp->next)
{
- if (tnp->start > pc || tnp->end <= pc)
- continue;
-
- table = tnp->table;
-
- pos = 0;
- best = 0;
-#if 0
- /* We can't do this yet, as we don't know that the table is sorted. */
- do {
- ++pos;
- if (table[pos].start > pc)
- /* found the first table[pos].start > pc, so the previous
- entry better be the one we want! */
- break;
- } while (table[pos].exception_handler != (void *) -1);
-
- --pos;
- if (table[pos].start <= pc && table[pos].end > pc)
- {
-#if 0
- printf ("find_first_eh_table_match (): found match: %x\n", table[pos].exception_handler);
-#endif
- return table[pos].exception_handler;
- }
-#else
- while (table[++pos].exception_handler != (void *) -1) {
- if (table[pos].start <= pc && table[pos].end > pc)
- {
- /* This can apply. Make sure it is better or as good as
- the previous best. */
- /* The best one ends first. */
- if (best == 0 || (table[pos].end <= table[best].end
- /* The best one starts last. */
- && table[pos].start >= table[best].start))
- best = pos;
- }
- }
- if (best != 0)
- return table[best].exception_handler;
-#endif
+ if (tnp->start <= pc && tnp->end >= pc)
+ return find_exception_handler (pc, tnp->table);
}
-#if 0
- printf ("find_first_eh_table_match (): else: returning NULL!\n");
-#endif
return (void *) 0;
}
void
__register_exceptions (exception_table *table)
{
- struct exception_table_node *node;
+ exception_table_node *node;
exception_table *range = table + 1;
if (range->start == (void *) -1)
return;
- node = (struct exception_table_node *)
- malloc (sizeof (struct exception_table_node));
+ node = (exception_table_node *) malloc (sizeof (exception_table_node));
node->table = table;
/* This look can be optimized away either if the table
@@ -3417,19 +3416,7 @@ __register_exceptions (exception_table *table)
node->next = exception_table_list;
exception_table_list = node;
}
-#endif
-
-void *
-__throw_type_match (void *catch_type, void *throw_type, void *obj)
-{
-#if 0
- printf ("__throw_type_match (): catch_type = %s, throw_type = %s\n",
- catch_type, throw_type);
-#endif
- if (strcmp ((const char *)catch_type, (const char *)throw_type) == 0)
- return obj;
- return 0;
-}
+#endif /* !EH_TABLE_LOOKUP */
/* Throw stub routine.
@@ -3441,11 +3428,6 @@ __throw ()
abort ();
}
-/* This value identifies the place from which an exception is being
- thrown. */
-
-void *__eh_pc;
-
/* See expand_builtin_throw for details. */
void **__eh_pcnthrow () {
@@ -3456,11 +3438,6 @@ void **__eh_pcnthrow () {
return buf;
}
-void
-__empty ()
-{
-}
-
#if #machine(i386)
void
__unwind_function(void *ptr)
@@ -3539,6 +3516,267 @@ __unwind_function(void *ptr)
abort ();
}
#endif /* powerpc */
+
+#else /* DWARF2_UNWIND_INFO */
+/* Support code for exception handling using static unwind information. */
+
+#include "frame.h"
+
+/* This type is used in get_reg and put_reg to deal with ABIs where a void*
+ is smaller than a word, such as the Irix 6 n32 ABI. We cast twice to
+ avoid a warning about casting between int and pointer of different
+ sizes. */
+
+typedef int ptr_type __attribute__ ((mode (pointer)));
+
+/* Get the value of register REG as saved in UDATA, where SUB_UDATA is a
+ frame called by UDATA or 0. */
+
+static void*
+get_reg (unsigned reg, frame_state *udata, frame_state *sub_udata)
+{
+ if (udata->saved[reg] == REG_SAVED_OFFSET)
+ return (void *)(ptr_type)
+ *(word_type *)(udata->cfa + udata->reg_or_offset[reg]);
+ else if (udata->saved[reg] == REG_SAVED_REG && sub_udata)
+ return get_reg (udata->reg_or_offset[reg], sub_udata, 0);
+ else
+ abort ();
+}
+
+/* Overwrite the saved value for register REG in frame UDATA with VAL. */
+
+static void
+put_reg (unsigned reg, void *val, frame_state *udata)
+{
+ if (udata->saved[reg] == REG_SAVED_OFFSET)
+ *(word_type *)(udata->cfa + udata->reg_or_offset[reg])
+ = (word_type)(ptr_type) val;
+ else
+ abort ();
+}
+
+/* Retrieve the return address for frame UDATA, where SUB_UDATA is a
+ frame called by UDATA or 0. */
+
+static inline void *
+get_return_addr (frame_state *udata, frame_state *sub_udata)
+{
+ return __builtin_extract_return_addr
+ (get_reg (udata->retaddr_column, udata, sub_udata));
+}
+
+/* Overwrite the return address for frame UDATA with VAL. */
+
+static inline void
+put_return_addr (void *val, frame_state *udata)
+{
+ val = __builtin_frob_return_addr (val);
+ put_reg (udata->retaddr_column, val, udata);
+}
+
+/* Given the current frame UDATA and its return address PC, return the
+ information about the calling frame in CALLER_UDATA. */
+
+static void *
+next_stack_level (void *pc, frame_state *udata, frame_state *caller_udata)
+{
+ caller_udata = __frame_state_for (pc, caller_udata);
+ if (! caller_udata)
+ return 0;
+
+ /* Now go back to our caller's stack frame. If our caller's CFA register
+ was saved in our stack frame, restore it; otherwise, assume the CFA
+ register is SP and restore it to our CFA value. */
+ if (udata->saved[caller_udata->cfa_reg])
+ caller_udata->cfa = get_reg (caller_udata->cfa_reg, udata, 0);
+ else
+ caller_udata->cfa = udata->cfa;
+ caller_udata->cfa += caller_udata->cfa_offset;
+
+ return caller_udata;
+}
+
+#ifdef INCOMING_REGNO
+/* Is the saved value for register REG in frame UDATA stored in a register
+ window in the previous frame? */
+
+static int
+in_reg_window (int reg, frame_state *udata)
+{
+ if (udata->saved[reg] != REG_SAVED_OFFSET)
+ return 0;
+
+#ifdef STACK_GROWS_DOWNWARD
+ return udata->reg_or_offset[reg] > 0;
+#else
+ return udata->reg_or_offset[reg] < 0;
+#endif
+}
+#endif /* INCOMING_REGNO */
+
+/* We first search for an exception handler, and if we don't find
+ it, we call __terminate on the current stack frame so that we may
+ use the debugger to walk the stack and understand why no handler
+ was found.
+
+ If we find one, then we unwind the frames down to the one that
+ has the handler and transfer control into the handler. */
+
+void
+__throw ()
+{
+ void *pc, *handler, *retaddr;
+ frame_state ustruct, ustruct2;
+ frame_state *udata = &ustruct;
+ frame_state *sub_udata = &ustruct2;
+ frame_state my_ustruct, *my_udata = &my_ustruct;
+ long args_size;
+
+ /* This is required for C++ semantics. We must call terminate if we
+ try and rethrow an exception, when there is no exception currently
+ active. */
+ if (! __eh_type)
+ __terminate ();
+
+ /* Start at our stack frame. */
+label:
+ udata = __frame_state_for (&&label, udata);
+ if (! udata)
+ __terminate ();
+
+ /* We need to get the value from the CFA register. At this point in
+ compiling __throw we don't know whether or not we will use the frame
+ pointer register for the CFA, so we check our unwind info. */
+ if (udata->cfa_reg == __builtin_dwarf_fp_regnum ())
+ udata->cfa = __builtin_fp ();
+ else
+ udata->cfa = __builtin_sp ();
+ udata->cfa += udata->cfa_offset;
+
+ memcpy (my_udata, udata, sizeof (*udata));
+
+ /* Do any necessary initialization to access arbitrary stack frames.
+ On the SPARC, this means flushing the register windows. */
+ __builtin_unwind_init ();
+
+ /* Now reset pc to the right throw point. */
+ pc = __eh_pc;
+
+ for (;;)
+ {
+ frame_state *p = udata;
+ udata = next_stack_level (pc, udata, sub_udata);
+ sub_udata = p;
+
+ /* If we couldn't find the next frame, we lose. */
+ if (! udata)
+ break;
+
+ handler = find_exception_handler (pc, udata->eh_ptr);
+
+ /* If we found one, we can stop searching. */
+ if (handler)
+ {
+ args_size = udata->args_size;
+ break;
+ }
+
+ /* Otherwise, we continue searching. */
+ pc = get_return_addr (udata, sub_udata);
+ }
+
+ /* If we haven't found a handler by now, this is an unhandled
+ exception. */
+ if (! handler)
+ __terminate ();
+
+ if (pc == __eh_pc)
+ /* We found a handler in the throw context, no need to unwind. */
+ udata = my_udata;
+ else
+ {
+ int i;
+ void *val;
+
+ /* Unwind all the frames between this one and the handler by copying
+ their saved register values into our register save slots. */
+
+ /* Remember the PC where we found the handler. */
+ void *handler_pc = pc;
+
+ /* Start from the throw context again. */
+ pc = __eh_pc;
+ memcpy (udata, my_udata, sizeof (*udata));
+
+ while (pc != handler_pc)
+ {
+ frame_state *p = udata;
+ udata = next_stack_level (pc, udata, sub_udata);
+ sub_udata = p;
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; ++i)
+ if (udata->saved[i])
+ {
+#ifdef INCOMING_REGNO
+ /* If you modify the saved value of the return address
+ register on the SPARC, you modify the return address for
+ your caller's frame. Don't do that here, as it will
+ confuse get_return_addr. */
+ if (in_reg_window (i, udata)
+ && udata->saved[udata->retaddr_column] == REG_SAVED_REG
+ && udata->reg_or_offset[udata->retaddr_column] == i)
+ continue;
+#endif
+ val = get_reg (i, udata, sub_udata);
+ put_reg (i, val, my_udata);
+ }
+
+ pc = get_return_addr (udata, sub_udata);
+ }
+
+#ifdef INCOMING_REGNO
+ /* But we do need to update the saved return address register from
+ the last frame we unwind, or the handler frame will have the wrong
+ return address. */
+ if (udata->saved[udata->retaddr_column] == REG_SAVED_REG)
+ {
+ i = udata->reg_or_offset[udata->retaddr_column];
+ if (in_reg_window (i, udata))
+ {
+ val = get_reg (i, udata, sub_udata);
+ put_reg (i, val, my_udata);
+ }
+ }
+#endif
+ }
+ /* udata now refers to the frame called by the handler frame. */
+
+ /* Emit the stub to adjust sp and jump to the handler. */
+ retaddr = __builtin_eh_stub ();
+
+ /* And then set our return address to point to the stub. */
+ if (my_udata->saved[my_udata->retaddr_column] == REG_SAVED_OFFSET)
+ put_return_addr (retaddr, my_udata);
+ else
+ __builtin_set_return_addr_reg (retaddr);
+
+ /* Set up the registers we use to communicate with the stub.
+ We check STACK_GROWS_DOWNWARD so the stub can use adjust_stack. */
+ __builtin_set_eh_regs (handler,
+#ifdef STACK_GROWS_DOWNWARD
+ udata->cfa - my_udata->cfa
+#else
+ my_udata->cfa - udata->cfa
+#endif
+ + args_size
+ );
+
+ /* Epilogue: restore the handler frame's register values and return
+ to the stub. */
+}
+#endif /* !DWARF2_UNWIND_INFO */
+
#endif /* L_eh */
#ifdef L_pure