diff options
author | jason <jason@138bc75d-0d04-0410-961f-82ee72b054a4> | 1997-09-16 02:07:50 +0000 |
---|---|---|
committer | jason <jason@138bc75d-0d04-0410-961f-82ee72b054a4> | 1997-09-16 02:07:50 +0000 |
commit | 447a9eb9a6224483e22ea816ace798558a37c74d (patch) | |
tree | d3f3fa9225d4040922a030638ba72ca3349ef34e /gcc/libgcc2.c | |
parent | 241e4f8babf0f4f354d81bc32e0f156fec5346af (diff) | |
download | gcc-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.c | 460 |
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 |