This patch fixes bug 60406 in an architecture independent way. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60406
The key change in this patch is to use _Unwind_FindEnclosingFunction() to identify the defering function. However, this does not work on platforms that still use SJLJ exceptions. ChangeLog 2014-09-05 Dominik Vogt <v...@linux.vnet.ibm.com> * libgo/runtime/proc.c (runtime_main): Initialize new structure fields. * libgo/runtime/go-recover.c (__go_can_recover): Rewrite logic to detect recover by return address. * libgo/runtime/go-defer.c (__go_set_defer_retaddr): Removed. (__go_set_defering_fn): Replacement for __go_set_defer_retaddr. (__go_defer): Initialize new structure fields. * libgo/runtime/go-defer.h (struct __go_defer_stack): Replace __retaddr with __defering_fn. gcc/go/ChangeLog 2014-09-05 Dominik Vogt <v...@linux.vnet.ibm.com> * gofrontend/statements.cc (build_thunk): The thunk uses the new libgo runtime runctions __go_set_defering_fn and no longer depends on the fake label and fake return value. * gofrontend/runtime.def: Replace SET_DEFER_RETADDR with SET_DEFERING_FN. Ciao Dominik ^_^ ^_^ -- Dominik Vogt IBM Germany
>From e6bbac3a00095f86535eceb5bdc87bca2ec406c9 Mon Sep 17 00:00:00 2001 From: Dominik Vogt <v...@linux.vnet.ibm.com> Date: Fri, 5 Sep 2014 07:30:14 +0100 Subject: [PATCH 1/9] GO: Fix recover logic in libgo and gofrontend. 1) Fix recover logic in libgo. 2) Fix recover logic in gofrontend. --- gcc/go/gofrontend/runtime.def | 5 ++--- gcc/go/gofrontend/statements.cc | 25 +++++-------------------- libgo/runtime/go-defer.c | 17 ++++++++--------- libgo/runtime/go-defer.h | 11 ++++++----- libgo/runtime/go-recover.c | 40 +++++++++++++++++++++------------------- libgo/runtime/proc.c | 2 +- 6 files changed, 43 insertions(+), 57 deletions(-) diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def index 8c6e82b..0cd045a 100644 --- a/gcc/go/gofrontend/runtime.def +++ b/gcc/go/gofrontend/runtime.def @@ -190,9 +190,8 @@ DEF_GO_RUNTIME(CAN_RECOVER, "__go_can_recover", P1(POINTER), R1(BOOL)) // Get the return address of the function. DEF_GO_RUNTIME(RETURN_ADDRESS, "__go_return_address", P1(INT), R1(POINTER)) -// Set the return address for defer in a defer thunk. -DEF_GO_RUNTIME(SET_DEFER_RETADDR, "__go_set_defer_retaddr", P1(POINTER), - R1(BOOL)) +// Set the function address for defer in a defer thunk. +DEF_GO_RUNTIME(SET_DEFERING_FN, "__go_set_defering_fn", P1(POINTER), R0()) // Check for a deferred function in an exception handler. DEF_GO_RUNTIME(CHECK_DEFER, "__go_check_defer", P1(BOOLPTR), R0()) diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index 090c193..6f906fc 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -2348,25 +2348,14 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name) gogo->start_block(location); // For a defer statement, start with a call to - // __go_set_defer_retaddr. */ - Label* retaddr_label = NULL; + // __go_set_defering_fn. */ if (may_call_recover) { - retaddr_label = gogo->add_label_reference("retaddr", location, false); - Expression* arg = Expression::make_label_addr(retaddr_label, location); - Expression* call = Runtime::make_call(Runtime::SET_DEFER_RETADDR, + Expression* arg = + Expression::make_func_code_reference(function, location); + Expression* call = Runtime::make_call(Runtime::SET_DEFERING_FN, location, 1, arg); - - // This is a hack to prevent the middle-end from deleting the - // label. - gogo->start_block(location); - gogo->add_statement(Statement::make_goto_statement(retaddr_label, - location)); - Block* then_block = gogo->finish_block(location); - then_block->determine_types(); - - Statement* s = Statement::make_if_statement(call, then_block, NULL, - location); + Statement* s = Statement::make_statement(call, true); s->determine_types(); gogo->add_statement(s); } @@ -2455,12 +2444,8 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name) gogo->add_statement(call_statement); - // If this is a defer statement, the label comes immediately after - // the call. if (may_call_recover) { - gogo->add_label_definition("retaddr", location); - Expression_list* vals = new Expression_list(); vals->push_back(Expression::make_boolean(false, location)); gogo->add_statement(Statement::make_return_statement(vals, location)); diff --git a/libgo/runtime/go-defer.c b/libgo/runtime/go-defer.c index 5dd8c31..c0da2dc 100644 --- a/libgo/runtime/go-defer.c +++ b/libgo/runtime/go-defer.c @@ -26,7 +26,7 @@ __go_defer (_Bool *frame, void (*pfn) (void *), void *arg) n->__panic = g->panic; n->__pfn = pfn; n->__arg = arg; - n->__retaddr = NULL; + n->__defering_fn = NULL; n->__makefunc_can_recover = 0; n->__special = 0; g->defer = n; @@ -69,17 +69,16 @@ __go_undefer (_Bool *frame) } } -/* This function is called to record the address to which the deferred - function returns. This may in turn be checked by __go_can_recover. - The frontend relies on this function returning false. */ +/* This function is called to record the address of the function + to which the deferred function returns. This may in turn be + checked by __go_can_recover. */ -_Bool -__go_set_defer_retaddr (void *retaddr) +void +__go_set_defering_fn (void *defering_fn) { G *g; - g = runtime_g (); if (g->defer != NULL) - g->defer->__retaddr = retaddr; - return 0; + g->defer->__defering_fn = defering_fn; + return; } diff --git a/libgo/runtime/go-defer.h b/libgo/runtime/go-defer.h index acf2d40..c6d8b4a 100644 --- a/libgo/runtime/go-defer.h +++ b/libgo/runtime/go-defer.h @@ -30,14 +30,15 @@ struct __go_defer_stack /* The argument to pass to the function. */ void *__arg; - /* The return address that a recover thunk matches against. This is - set by __go_set_defer_retaddr which is called by the thunks - created by defer statements. */ - const void *__retaddr; + /* The address of the function enclosing the return address that + a recover thunk matches against. This is set by + __go_set_defering_fn which is called by the thunks created by + defer statements. */ + const void *__defering_fn; /* Set to true if a function created by reflect.MakeFunc is permitted to recover. The return address of such a function - function will be somewhere in libffi, so __retaddr is not + function will be somewhere in libffi, so __defering_fn is not useful. */ _Bool __makefunc_can_recover; diff --git a/libgo/runtime/go-recover.c b/libgo/runtime/go-recover.c index 2d3db55..85ebf52 100644 --- a/libgo/runtime/go-recover.c +++ b/libgo/runtime/go-recover.c @@ -8,6 +8,8 @@ #include "interface.h" #include "go-panic.h" #include "go-defer.h" +#include <stdint.h> +#include "unwind-generic.h" /* This is called by a thunk to see if the real function should be permitted to recover a panic value. Recovering a value is @@ -20,8 +22,6 @@ __go_can_recover (const void *retaddr) { G *g; struct __go_defer_stack *d; - const char* ret; - const char* dret; Location loc; const byte *name; @@ -38,24 +38,26 @@ __go_can_recover (const void *retaddr) if (d->__panic == g->panic) return 0; - /* D->__RETADDR is the address of a label immediately following the - call to the thunk. We can recover a panic if that is the same as - the return address of the thunk. We permit a bit of slack in - case there is any code between the function return and the label, - such as an instruction to adjust the stack pointer. */ + /* D->__DEFERING_FN is the address of the function that called + D->defer (or NULL if defer was not called). We can recover a + D->panic if that is the same as the caller of the thunk. */ - ret = (const char *) retaddr; - -#ifdef __sparc__ - /* On SPARC the address we get, from __builtin_return_address, is - the address of the call instruction. Adjust forward, also - skipping the delayed instruction following the call. */ - ret += 8; -#endif - - dret = (const char *) d->__retaddr; - if (ret <= dret && ret + 16 >= dret) - return 1; + if (d->__defering_fn != NULL) + { + void *real_retaddr; + + real_retaddr = __builtin_extract_return_addr ((void *)(uintptr_t)retaddr); + /* If the return address is before the defering function + start skip the more expensive calculations. */ + if ((uintptr_t)real_retaddr >= (uintptr_t)d->__defering_fn) + { + const void *caller; + + caller = _Unwind_FindEnclosingFunction (real_retaddr); + if (caller == (const void *)d->__defering_fn) + return 1; + } + } /* If the function calling recover was created by reflect.MakeFunc, then RETADDR will be somewhere in libffi. Our caller is diff --git a/libgo/runtime/proc.c b/libgo/runtime/proc.c index 4195aff..6f656d8 100644 --- a/libgo/runtime/proc.c +++ b/libgo/runtime/proc.c @@ -561,7 +561,7 @@ runtime_main(void* dummy __attribute__((unused))) d.__next = g->defer; d.__arg = (void*)-1; d.__panic = g->panic; - d.__retaddr = nil; + d.__defering_fn = nil; d.__makefunc_can_recover = 0; d.__frame = &frame; d.__special = true; -- 1.8.4.2