diff options
author | rth <rth@138bc75d-0d04-0410-961f-82ee72b054a4> | 2003-06-04 17:06:00 +0000 |
---|---|---|
committer | rth <rth@138bc75d-0d04-0410-961f-82ee72b054a4> | 2003-06-04 17:06:00 +0000 |
commit | 7acb29a3d9e072c65c26e582d4f4204dcf232d4a (patch) | |
tree | d3d591092d6a64dfc049785317e9f17e96cf7276 /gcc | |
parent | 3fd62642d6a042a192f405ab559a0fef3c49f588 (diff) | |
download | gcc-7acb29a3d9e072c65c26e582d4f4204dcf232d4a.tar.gz |
* c-common.c (handle_cleanup_attribute): New.
(c_common_attributes): Add it.
* c-decl.c (finish_decl): Honor the cleanup attribute.
* doc/extend.texi (Variable Attributes): Document it.
* unwind-c.c: New file.
* Makefile.in (LIB2ADDEH): Add it.
* config/t-darwin, config/t-linux, config/t-linux-gnulibc1,
config/ia64/t-ia64: Likewise.
* gcc.dg/cleanup-1.c: New.
* gcc.dg/cleanup-2.c: New.
* gcc.dg/cleanup-3.c: New.
* gcc.dg/cleanup-4.c: New.
* gcc.dg/cleanup-5.c: New.
* gcc.dg/cleanup-6.c: New.
* gcc.dg/cleanup-7.c: New.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@67449 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 12 | ||||
-rw-r--r-- | gcc/Makefile.in | 7 | ||||
-rw-r--r-- | gcc/c-common.c | 55 | ||||
-rw-r--r-- | gcc/c-decl.c | 37 | ||||
-rw-r--r-- | gcc/config/ia64/t-ia64 | 3 | ||||
-rw-r--r-- | gcc/config/t-darwin | 2 | ||||
-rw-r--r-- | gcc/config/t-linux | 2 | ||||
-rw-r--r-- | gcc/config/t-linux-gnulibc1 | 2 | ||||
-rw-r--r-- | gcc/doc/extend.texi | 16 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 10 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/cleanup-1.c | 36 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/cleanup-2.c | 22 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/cleanup-3.c | 45 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/cleanup-4.c | 39 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/cleanup-5.c | 50 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/cleanup-6.c | 14 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/cleanup-7.c | 22 | ||||
-rw-r--r-- | gcc/unwind-c.c | 186 |
18 files changed, 552 insertions, 8 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6bb5c6af19b..548ba28ea86 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,15 @@ +2003-06-04 Richard Henderson <rth@redhat.com> + + * c-common.c (handle_cleanup_attribute): New. + (c_common_attributes): Add it. + * c-decl.c (finish_decl): Honor the cleanup attribute. + * doc/extend.texi (Variable Attributes): Document it. + + * unwind-c.c: New file. + * Makefile.in (LIB2ADDEH): Add it. + * config/t-darwin, config/t-linux, config/t-linux-gnulibc1, + config/ia64/t-ia64: Likewise. + 2003-06-04 Jakub Jelinek <jakub@redhat.com> * function.c (trampolines_created): New variable. diff --git a/gcc/Makefile.in b/gcc/Makefile.in index f0ca525716e..6755643d742 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -472,7 +472,7 @@ CRTSTUFF_CFLAGS = -O2 $(GCC_CFLAGS) $(INCLUDES) $(MULTILIB_CFLAGS) -g0 \ # Additional sources to handle exceptions; overridden by targets as needed. LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde.c \ - $(srcdir)/unwind-sjlj.c $(srcdir)/gthr-gnat.c + $(srcdir)/unwind-sjlj.c $(srcdir)/gthr-gnat.c $(srcdir)/unwind-c.c LIB2ADDEHDEP = unwind.inc unwind-dw2-fde.h # nm flags to list global symbols in libgcc object files. @@ -1261,7 +1261,7 @@ c-incpath.o: c-incpath.c c-incpath.h $(CONFIG_H) $(SYSTEM_H) $(CPPLIB_H) \ c-decl.o : c-decl.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \ $(RTL_H) $(C_TREE_H) $(GGC_H) $(TARGET_H) flags.h function.h output.h \ $(EXPR_H) debug.h toplev.h intl.h $(TM_P_H) tree-inline.h $(TIMEVAR_H) \ - c-pragma.h gt-c-decl.h cgraph.h $(HASHTAB_H) + c-pragma.h gt-c-decl.h cgraph.h $(HASHTAB_H) libfuncs.h except.h c-typeck.o : c-typeck.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) $(C_TREE_H) \ $(TARGET_H) flags.h intl.h output.h $(EXPR_H) $(RTL_H) toplev.h $(TM_P_H) c-lang.o : c-lang.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) $(C_TREE_H) \ @@ -1310,7 +1310,8 @@ tlink.o: tlink.c $(DEMANGLE_H) $(HASHTAB_H) $(CONFIG_H) $(SYSTEM_H) coretypes.h c-common.o : c-common.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \ $(OBSTACK_H) $(C_COMMON_H) flags.h toplev.h output.h c-pragma.h intl.h \ $(GGC_H) $(EXPR_H) $(TM_P_H) builtin-types.def builtin-attrs.def \ - diagnostic.h gt-c-common.h langhooks.h varray.h $(RTL_H) $(TARGET_H) + diagnostic.h gt-c-common.h langhooks.h varray.h $(RTL_H) $(TARGET_H) \ + c-tree.h c-pretty-print.o : c-pretty-print.c c-pretty-print.h pretty-print.h \ $(C_COMMON_H) $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) real.h diff --git a/gcc/c-common.c b/gcc/c-common.c index fe196b124bf..d194012e956 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -41,7 +41,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "target.h" #include "langhooks.h" #include "tree-inline.h" - +#include "c-tree.h" cpp_reader *parse_in; /* Declared in c-pragma.h. */ @@ -792,6 +792,8 @@ static tree handle_nonnull_attribute PARAMS ((tree *, tree, tree, int, bool *)); static tree handle_nothrow_attribute PARAMS ((tree *, tree, tree, int, bool *)); +static tree handle_cleanup_attribute PARAMS ((tree *, tree, tree, int, + bool *)); static tree vector_size_helper PARAMS ((tree, tree)); static void check_function_nonnull PARAMS ((tree, tree)); @@ -868,6 +870,8 @@ const struct attribute_spec c_common_attribute_table[] = { "nothrow", 0, 0, true, false, false, handle_nothrow_attribute }, { "may_alias", 0, 0, false, true, false, NULL }, + { "cleanup", 1, 1, true, false, false, + handle_cleanup_attribute }, { NULL, 0, 0, false, false, false, NULL } }; @@ -6093,6 +6097,55 @@ handle_nothrow_attribute (node, name, args, flags, no_add_attrs) return NULL_TREE; } + +/* Handle a "cleanup" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_cleanup_attribute (node, name, args, flags, no_add_attrs) + tree *node; + tree name; + tree args; + int flags ATTRIBUTE_UNUSED; + bool *no_add_attrs; +{ + tree decl = *node; + tree cleanup_id, cleanup_decl; + + /* ??? Could perhaps support cleanups on TREE_STATIC, much like we do + for global destructors in C++. This requires infrastructure that + we don't have generically at the moment. It's also not a feature + we'd be missing too much, since we do have attribute constructor. */ + if (TREE_CODE (decl) != VAR_DECL || TREE_STATIC (decl)) + { + warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); + *no_add_attrs = true; + return NULL_TREE; + } + + /* Verify that the argument is a function in scope. */ + /* ??? We could support pointers to functions here as well, if + that was considered desirable. */ + cleanup_id = TREE_VALUE (args); + if (TREE_CODE (cleanup_id) != IDENTIFIER_NODE) + { + error ("cleanup arg not an identifier"); + *no_add_attrs = true; + return NULL_TREE; + } + cleanup_decl = lookup_name (cleanup_id); + if (!cleanup_decl || TREE_CODE (cleanup_decl) != FUNCTION_DECL) + { + error ("cleanup arg not a function"); + *no_add_attrs = true; + return NULL_TREE; + } + + /* That the function has proper type is checked with the + eventual call to build_function_call. */ + + return NULL_TREE; +} /* Check for valid arguments being passed to a function. */ void diff --git a/gcc/c-decl.c b/gcc/c-decl.c index 6eb0f55fd25..f9124e4a976 100644 --- a/gcc/c-decl.c +++ b/gcc/c-decl.c @@ -50,6 +50,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "c-pragma.h" #include "cgraph.h" #include "hashtab.h" +#include "libfuncs.h" +#include "except.h" /* In grokdeclarator, distinguish syntactic contexts of declarators. */ enum decl_context @@ -2985,6 +2987,41 @@ finish_decl (decl, init, asmspec_tree) computing them in the following function definition. */ if (current_binding_level == global_binding_level) get_pending_sizes (); + + /* Install a cleanup (aka destructor) if one was given. */ + if (TREE_CODE (decl) == VAR_DECL && !TREE_STATIC (decl)) + { + tree attr = lookup_attribute ("cleanup", DECL_ATTRIBUTES (decl)); + if (attr) + { + static bool eh_initialized_p; + + tree cleanup_id = TREE_VALUE (TREE_VALUE (attr)); + tree cleanup_decl = lookup_name (cleanup_id); + tree cleanup; + + /* Build "cleanup(&decl)" for the destructor. */ + cleanup = build_unary_op (ADDR_EXPR, decl, 0); + cleanup = build_tree_list (NULL_TREE, cleanup); + cleanup = build_function_call (cleanup_decl, cleanup); + + /* Don't warn about decl unused; the cleanup uses it. */ + TREE_USED (decl) = 1; + + /* Initialize EH, if we've been told to do so. */ + if (flag_exceptions && !eh_initialized_p) + { + eh_initialized_p = true; + eh_personality_libfunc + = init_one_libfunc (USING_SJLJ_EXCEPTIONS + ? "__gcc_personality_sj0" + : "__gcc_personality_v0"); + using_eh_for_cleanups (); + } + + add_stmt (build_stmt (CLEANUP_STMT, decl, cleanup)); + } + } } /* Given a parsed parameter declaration, diff --git a/gcc/config/ia64/t-ia64 b/gcc/config/ia64/t-ia64 index 4474b0a9294..0b8ab156b60 100644 --- a/gcc/config/ia64/t-ia64 +++ b/gcc/config/ia64/t-ia64 @@ -40,7 +40,8 @@ crtfastmath.o: $(srcdir)/config/ia64/crtfastmath.c $(GCC_PASSES) $(GCC_FOR_TARGET) $(LIBGCC2_CFLAGS) -c -o crtfastmath.o \ $(srcdir)/config/ia64/crtfastmath.c -LIB2ADDEH = $(srcdir)/config/ia64/unwind-ia64.c $(srcdir)/unwind-sjlj.c +LIB2ADDEH = $(srcdir)/config/ia64/unwind-ia64.c $(srcdir)/unwind-sjlj.c \ + $(srcdir)/unwind-c.c ia64-c.o: $(srcdir)/config/ia64/ia64-c.c $(CONFIG_H) $(SYSTEM_H) \ coretypes.h $(TM_H) $(TREE_H) $(CPPLIB_H) $(C_COMMON_H) c-pragma.h toplev.h diff --git a/gcc/config/t-darwin b/gcc/config/t-darwin index e8682e17db8..c5741e43234 100644 --- a/gcc/config/t-darwin +++ b/gcc/config/t-darwin @@ -18,5 +18,5 @@ $(T)crt2$(objext): $(srcdir)/config/darwin-crt2.c $(GCC_PASSES) \ # Use unwind-dw2-fde-darwin LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde-darwin.c \ - $(srcdir)/unwind-sjlj.c + $(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c LIB2ADDEHDEP = unwind.inc unwind-dw2-fde.h unwind-dw2-fde.c diff --git a/gcc/config/t-linux b/gcc/config/t-linux index b971c46ea21..752387c62ad 100644 --- a/gcc/config/t-linux +++ b/gcc/config/t-linux @@ -12,5 +12,5 @@ SHLIB_MAPFILES += $(srcdir)/config/libgcc-glibc.ver # Use unwind-dw2-fde-glibc LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde-glibc.c \ - $(srcdir)/unwind-sjlj.c $(srcdir)/gthr-gnat.c + $(srcdir)/unwind-sjlj.c $(srcdir)/gthr-gnat.c $(srcdir)/unwind-c.c LIB2ADDEHDEP = unwind.inc unwind-dw2-fde.h unwind-dw2-fde.c gthr-gnat.c diff --git a/gcc/config/t-linux-gnulibc1 b/gcc/config/t-linux-gnulibc1 index 56a471579ed..52effd5ca2f 100644 --- a/gcc/config/t-linux-gnulibc1 +++ b/gcc/config/t-linux-gnulibc1 @@ -3,5 +3,5 @@ T_CFLAGS = -DUSE_GNULIBC_1 # Use unwind-dw2-fde LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde.c \ - $(srcdir)/unwind-sjlj.c + $(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c LIB2ADDEHDEP = unwind.inc unwind-dw2-fde.h diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index c4152ef0092..0da4407b2dc 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -3034,6 +3034,22 @@ up to a maximum of 8 byte alignment, then specifying @code{aligned(16)} in an @code{__attribute__} will still only provide you with 8 byte alignment. See your linker documentation for further information. +@item cleanup (@var{cleanup_function}) +@cindex @code{cleanup} attribute +The @code{cleanup} attribute runs a function when the variable goes +out of scope. This attribute can only be applied to auto function +scope variables; it may not be applied to parameters or variables +with static storage duration. The function must take one parameter, +a pointer to a type compatible with the variable. The return value +of the function (if any) is ignored. + +If @option{-fexceptions} is enabled, then @var{cleanup_function} +will be run during the stack unwinding that happens during the +processing of the exception. Note that the @code{cleanup} attribute +does not allow the exception to be caught, only to perform an action. +It is undefined what happens if @var{cleanup_function} does not +return normally. + @item common @itemx nocommon @cindex @code{common} attribute diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index ec5e799b41c..1acdb65494c 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,13 @@ +2003-06-04 Richard Henderson <rth@redhat.com> + + * gcc.dg/cleanup-1.c: New. + * gcc.dg/cleanup-2.c: New. + * gcc.dg/cleanup-3.c: New. + * gcc.dg/cleanup-4.c: New. + * gcc.dg/cleanup-5.c: New. + * gcc.dg/cleanup-6.c: New. + * gcc.dg/cleanup-7.c: New. + 2003-06-04 Mark Mitchell <mark@codesourcery.com> * g++.dg/abi/vague1.C: Use xfail, rather than embedded Tcl code. diff --git a/gcc/testsuite/gcc.dg/cleanup-1.c b/gcc/testsuite/gcc.dg/cleanup-1.c new file mode 100644 index 00000000000..e5853c4baa8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/cleanup-1.c @@ -0,0 +1,36 @@ +/* { dg-do compile } */ +/* { dg-options "-Wall" } */ +/* Validate expected warnings and errors. */ + +#define U __attribute__((unused)) +#define C(x) __attribute__((cleanup(x))) + +static int f1(void *x U) { return 0; } +static void f2() { } +static void f3(void) { } +static void f4(void *x U) { } +static void f5(int *x U) { } +static void f6(double *x U) { } +static void f7(const int *x U) { } +static void f8(const int *x U, int y U) { } +static void f9(int x U) { } + +void test(void) +{ + int o1 C(f1); + int o2 C(f2); + int o3 C(f3); /* { dg-error "too many arguments" } */ + int o4 C(f4); + int o5 C(f5); + int o6 C(f6); /* { dg-warning "incompatible pointer type" } */ + int o7 C(f7); + int o8 C(f8); /* { dg-error "too few arguments" } */ + int o9 C(f9); /* { dg-warning "from pointer without a cast" } */ + int o10 U C(undef); /* { dg-error "not a function" } */ + int o11 U C(o1); /* { dg-error "not a function" } */ + int o12 U C("f1"); /* { dg-error "not an identifier" } */ + static int o13 U C(f1); /* { dg-warning "attribute ignored" } */ +} + +int o14 C(f1); /* { dg-warning "attribute ignored" } */ +void t15(int o U C(f1)) {} /* { dg-warning "attribute ignored" } */ diff --git a/gcc/testsuite/gcc.dg/cleanup-2.c b/gcc/testsuite/gcc.dg/cleanup-2.c new file mode 100644 index 00000000000..2c798023ccb --- /dev/null +++ b/gcc/testsuite/gcc.dg/cleanup-2.c @@ -0,0 +1,22 @@ +/* { dg-do run } */ +/* { dg-options "" } */ +/* Verify that cleanup works in the most basic of ways. */ + +extern void exit(int); +extern void abort(void); + +static void handler(void *p __attribute__((unused))) +{ + exit (0); +} + +static void doit(void) +{ + int x __attribute__((cleanup (handler))); +} + +int main() +{ + doit (); + abort (); +} diff --git a/gcc/testsuite/gcc.dg/cleanup-3.c b/gcc/testsuite/gcc.dg/cleanup-3.c new file mode 100644 index 00000000000..b5b01fd5905 --- /dev/null +++ b/gcc/testsuite/gcc.dg/cleanup-3.c @@ -0,0 +1,45 @@ +/* { dg-do run } */ +/* { dg-options "" } */ +/* Verify that the cleanup handler receives the proper contents + of the variable. */ + +extern void exit(int); +extern void abort(void); + +static int expected; + +static void +handler(int *p) +{ + if (*p != expected) + abort (); +} + +static void __attribute__((noinline)) +bar(void) +{ +} + +static void doit(int x, int y) +{ + int r __attribute__((cleanup (handler))); + if (x < y) + { + r = 0; + return; + } + + bar(); + r = x + y; +} + +int main() +{ + expected = 0; + doit (1, 2); + + expected = 3; + doit (2, 1); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/cleanup-4.c b/gcc/testsuite/gcc.dg/cleanup-4.c new file mode 100644 index 00000000000..a5487553d2b --- /dev/null +++ b/gcc/testsuite/gcc.dg/cleanup-4.c @@ -0,0 +1,39 @@ +/* { dg-do run } */ +/* { dg-options "" } */ +/* Verify cleanup execution on non-trivial exit from a block. */ + +extern void exit(int); +extern void abort(void); + +static int counter; + +static void +handler(int *p) +{ + counter += *p; +} + +static void __attribute__((noinline)) +bar(void) +{ +} + +static void doit(int n, int n2) +{ + int i; + for (i = 0; i < n; ++i) + { + int dummy __attribute__((cleanup (handler))) = i; + if (i == n2) + break; + bar(); + } +} + +int main() +{ + doit (10, 6); + if (counter != 0 + 1 + 2 + 3 + 4 + 5 + 6) + abort (); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/cleanup-5.c b/gcc/testsuite/gcc.dg/cleanup-5.c new file mode 100644 index 00000000000..f5306db4752 --- /dev/null +++ b/gcc/testsuite/gcc.dg/cleanup-5.c @@ -0,0 +1,50 @@ +/* { dg-do run } */ +/* { dg-options "-fexceptions" } */ +/* Verify that cleanups work with exception handling. */ + +#include <unwind.h> +#include <stdlib.h> + +static _Unwind_Reason_Code +force_unwind_stop (int version, _Unwind_Action actions, + _Unwind_Exception_Class exc_class, + struct _Unwind_Exception *exc_obj, + struct _Unwind_Context *context, + void *stop_parameter) +{ + if (actions & _UA_END_OF_STACK) + abort (); + return _URC_NO_REASON; +} + +static void force_unwind () +{ + struct _Unwind_Exception *exc = malloc (sizeof (*exc)); + exc->exception_class = 0; + exc->exception_cleanup = 0; + +#ifndef __USING_SJLJ_EXCEPTIONS__ + _Unwind_ForcedUnwind (exc, force_unwind_stop, 0); +#else + _Unwind_SjLj_ForcedUnwind (exc, force_unwind_stop, 0); +#endif + + abort (); +} + +static void handler (void *p __attribute__((unused))) +{ + exit (0); +} + +static void doit () +{ + char dummy __attribute__((cleanup (handler))); + force_unwind (); +} + +int main() +{ + doit (); + abort (); +} diff --git a/gcc/testsuite/gcc.dg/cleanup-6.c b/gcc/testsuite/gcc.dg/cleanup-6.c new file mode 100644 index 00000000000..4e3d53893af --- /dev/null +++ b/gcc/testsuite/gcc.dg/cleanup-6.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-O" } */ +/* Verify that a cleanup marked "inline" gets inlined. */ + +static inline void xyzzy(void *p __attribute__((unused))) +{ +} + +void doit(void) +{ + int x __attribute__((cleanup (xyzzy))); +} + +/* { dg-final { scan-assembler-not "xyzzy" } } */ diff --git a/gcc/testsuite/gcc.dg/cleanup-7.c b/gcc/testsuite/gcc.dg/cleanup-7.c new file mode 100644 index 00000000000..eae3d52980c --- /dev/null +++ b/gcc/testsuite/gcc.dg/cleanup-7.c @@ -0,0 +1,22 @@ +/* { dg-do run } */ +/* { dg-options "" } */ +/* Verify that the search for function happens in the proper scope. */ + +extern void exit(int); +extern void abort(void); + +int main() +{ + auto void xyzzy(void *p __attribute__((unused))) + { + exit (0); + } + + auto void doit () + { + int x __attribute__((cleanup (xyzzy))); + } + + doit (); + abort (); +} diff --git a/gcc/unwind-c.c b/gcc/unwind-c.c new file mode 100644 index 00000000000..9ce09568988 --- /dev/null +++ b/gcc/unwind-c.c @@ -0,0 +1,186 @@ +/* Supporting functions for C exception handling. + Copyright (C) 2002, 2003 Free Software Foundation, Inc. + Contributed by Aldy Hernandez <aldy@quesejoda.com>. + Shamelessly stolen from the Java front end. + +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 2, 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 COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA +02111-1307, USA. */ + +#include "tconfig.h" +#include "tsystem.h" +#include "unwind.h" +#include "unwind-pe.h" + +typedef struct +{ + _Unwind_Ptr Start; + _Unwind_Ptr LPStart; + _Unwind_Ptr ttype_base; + const unsigned char *TType; + const unsigned char *action_table; + unsigned char ttype_encoding; + unsigned char call_site_encoding; +} lsda_header_info; + +static const unsigned char * +parse_lsda_header (struct _Unwind_Context *context, const unsigned char *p, + lsda_header_info *info) +{ + _Unwind_Word tmp; + unsigned char lpstart_encoding; + + info->Start = (context ? _Unwind_GetRegionStart (context) : 0); + + /* Find @LPStart, the base to which landing pad offsets are relative. */ + lpstart_encoding = *p++; + if (lpstart_encoding != DW_EH_PE_omit) + p = read_encoded_value (context, lpstart_encoding, p, &info->LPStart); + else + info->LPStart = info->Start; + + /* Find @TType, the base of the handler and exception spec type data. */ + info->ttype_encoding = *p++; + if (info->ttype_encoding != DW_EH_PE_omit) + { + p = read_uleb128 (p, &tmp); + info->TType = p + tmp; + } + else + info->TType = 0; + + /* The encoding and length of the call-site table; the action table + immediately follows. */ + info->call_site_encoding = *p++; + p = read_uleb128 (p, &tmp); + info->action_table = p + tmp; + + return p; +} + +#ifdef __USING_SJLJ_EXCEPTIONS__ +#define PERSONALITY_FUNCTION __gcc_personality_sj0 +#define __builtin_eh_return_data_regno(x) x +#else +#define PERSONALITY_FUNCTION __gcc_personality_v0 +#endif +#define PERSONALITY_FUNCTION __gcc_personality_v0 + +_Unwind_Reason_Code +PERSONALITY_FUNCTION (int, _Unwind_Action, _Unwind_Exception_Class, + struct _Unwind_Exception *, struct _Unwind_Context *); + +_Unwind_Reason_Code +PERSONALITY_FUNCTION (int version, + _Unwind_Action actions, + _Unwind_Exception_Class exception_class ATTRIBUTE_UNUSED, + struct _Unwind_Exception *ue_header, + struct _Unwind_Context *context) +{ + lsda_header_info info; + const unsigned char *language_specific_data, *p, *action_record; + _Unwind_Ptr landing_pad, ip; + + if (version != 1) + return _URC_FATAL_PHASE1_ERROR; + + /* Currently we only support cleanups for C. */ + if ((actions & _UA_CLEANUP_PHASE) == 0) + return _URC_CONTINUE_UNWIND; + + language_specific_data = (const unsigned char *) + _Unwind_GetLanguageSpecificData (context); + + /* If no LSDA, then there are no handlers or cleanups. */ + if (! language_specific_data) + return _URC_CONTINUE_UNWIND; + + /* Parse the LSDA header. */ + p = parse_lsda_header (context, language_specific_data, &info); + ip = _Unwind_GetIP (context) - 1; + landing_pad = 0; + +#ifdef __USING_SJLJ_EXCEPTIONS__ + /* The given "IP" is an index into the call-site table, with two + exceptions -- -1 means no-action, and 0 means terminate. But + since we're using uleb128 values, we've not got random access + to the array. */ + if ((int) ip <= 0) + return _URC_CONTINUE_UNWIND; + else + { + _Unwind_Word cs_lp, cs_action; + do + { + p = read_uleb128 (p, &cs_lp); + p = read_uleb128 (p, &cs_action); + } + while (--ip); + + /* Can never have null landing pad for sjlj -- that would have + been indicated by a -1 call site index. */ + landing_pad = cs_lp + 1; + if (cs_action) + action_record = info.action_table + cs_action - 1; + goto found_something; + } +#else + /* Search the call-site table for the action associated with this IP. */ + while (p < info.action_table) + { + _Unwind_Ptr cs_start, cs_len, cs_lp; + _Unwind_Word cs_action; + + /* Note that all call-site encodings are "absolute" displacements. */ + p = read_encoded_value (0, info.call_site_encoding, p, &cs_start); + p = read_encoded_value (0, info.call_site_encoding, p, &cs_len); + p = read_encoded_value (0, info.call_site_encoding, p, &cs_lp); + p = read_uleb128 (p, &cs_action); + + /* The table is sorted, so if we've passed the ip, stop. */ + if (ip < info.Start + cs_start) + p = info.action_table; + else if (ip < info.Start + cs_start + cs_len) + { + if (cs_lp) + landing_pad = info.LPStart + cs_lp; + if (cs_action) + action_record = info.action_table + cs_action - 1; + goto found_something; + } + } + +#endif + + /* IP is not in table. No associated cleanups. */ + /* ??? This is where C++ calls std::terminate to catch throw + from a destructor. */ + return _URC_CONTINUE_UNWIND; + + found_something: + if (landing_pad == 0) + { + /* IP is present, but has a null landing pad. + No handler to be run. */ + return _URC_CONTINUE_UNWIND; + } + + _Unwind_SetGR (context, __builtin_eh_return_data_regno (0), + (_Unwind_Ptr) ue_header); + _Unwind_SetGR (context, __builtin_eh_return_data_regno (1), 0); + _Unwind_SetIP (context, landing_pad); + return _URC_INSTALL_CONTEXT; +} |