123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- /* go-recover.c -- support for the go recover function.
- Copyright 2010 The Go Authors. All rights reserved.
- Use of this source code is governed by a BSD-style
- license that can be found in the LICENSE file. */
- #include "runtime.h"
- #include "interface.h"
- #include "go-panic.h"
- #include "go-defer.h"
- /* If the top of the defer stack can be recovered, then return it.
- Otherwise return NULL. */
- static struct __go_defer_stack *
- current_defer ()
- {
- G *g;
- struct __go_defer_stack *d;
- g = runtime_g ();
- d = g->defer;
- if (d == NULL)
- return NULL;
- /* The panic which would be recovered is the one on the top of the
- panic stack. We do not want to recover it if that panic was on
- the top of the panic stack when this function was deferred. */
- if (d->__panic == g->panic)
- return NULL;
- /* The deferred thunk will call _go_set_defer_retaddr. If this has
- not happened, then we have not been called via defer, and we can
- not recover. */
- if (d->__retaddr == NULL)
- return NULL;
- return d;
- }
- /* This is called by a thunk to see if the real function should be
- permitted to recover a panic value. Recovering a value is
- permitted if the thunk was called directly by defer. RETADDR is
- the return address of the function which is calling
- __go_can_recover--this is, the thunk. */
- _Bool
- __go_can_recover (void *retaddr)
- {
- struct __go_defer_stack *d;
- const char* ret;
- const char* dret;
- Location locs[16];
- const byte *name;
- intgo len;
- int n;
- int i;
- _Bool found_ffi_callback;
- d = current_defer ();
- if (d == NULL)
- return 0;
- ret = (const char *) __builtin_extract_return_addr (retaddr);
- dret = (const char *) d->__retaddr;
- if (ret <= dret && ret + 16 >= dret)
- return 1;
- /* On some systems, in some cases, the return address does not work
- reliably. See http://gcc.gnu.org/PR60406. If we are permitted
- to call recover, the call stack will look like this:
- __go_panic, __go_undefer, etc.
- thunk to call deferred function (calls __go_set_defer_retaddr)
- function that calls __go_can_recover (passing return address)
- __go_can_recover
- Calling runtime_callers will skip the thunks. So if our caller's
- caller starts with __go, then we are permitted to call
- recover. */
- if (runtime_callers (1, &locs[0], 2, false) < 2)
- return 0;
- name = locs[1].function.str;
- len = locs[1].function.len;
- /* Although locs[1].function is a Go string, we know it is
- NUL-terminated. */
- if (len > 4
- && __builtin_strchr ((const char *) name, '.') == NULL
- && __builtin_strncmp ((const char *) name, "__go_", 4) == 0)
- return 1;
- /* If we are called from __go_makefunc_can_recover, then we need to
- look one level higher. */
- if (locs[0].function.len > 0
- && __builtin_strcmp ((const char *) locs[0].function.str,
- "__go_makefunc_can_recover") == 0)
- {
- if (runtime_callers (3, &locs[0], 1, false) < 1)
- return 0;
- name = locs[0].function.str;
- len = locs[0].function.len;
- if (len > 4
- && __builtin_strchr ((const char *) name, '.') == NULL
- && __builtin_strncmp ((const char *) name, "__go_", 4) == 0)
- return 1;
- }
- /* If the function calling recover was created by reflect.MakeFunc,
- then __go_makefunc_can_recover or __go_makefunc_ffi_can_recover
- will have set the __makefunc_can_recover field. */
- if (!d->__makefunc_can_recover)
- return 0;
- /* We look up the stack, ignoring libffi functions and functions in
- the reflect package, until we find reflect.makeFuncStub or
- reflect.ffi_callback called by FFI functions. Then we check the
- caller of that function. */
- n = runtime_callers (2, &locs[0], sizeof locs / sizeof locs[0], false);
- found_ffi_callback = 0;
- for (i = 0; i < n; i++)
- {
- const byte *name;
- if (locs[i].function.len == 0)
- {
- /* No function name means this caller isn't Go code. Assume
- that this is libffi. */
- continue;
- }
- /* Ignore functions in libffi. */
- name = locs[i].function.str;
- if (__builtin_strncmp ((const char *) name, "ffi_", 4) == 0)
- continue;
- if (found_ffi_callback)
- break;
- if (__builtin_strcmp ((const char *) name, "reflect.ffi_callback") == 0)
- {
- found_ffi_callback = 1;
- continue;
- }
- if (__builtin_strcmp ((const char *) name, "reflect.makeFuncStub") == 0)
- {
- i++;
- break;
- }
- /* Ignore other functions in the reflect package. */
- if (__builtin_strncmp ((const char *) name, "reflect.", 8) == 0)
- continue;
- /* We should now be looking at the real caller. */
- break;
- }
- if (i < n && locs[i].function.len > 0)
- {
- name = locs[i].function.str;
- if (__builtin_strncmp ((const char *) name, "__go_", 4) == 0)
- return 1;
- }
- return 0;
- }
- /* This function is called when code is about to enter a function
- created by reflect.MakeFunc. It is called by the function stub
- used by MakeFunc. If the stub is permitted to call recover, then a
- real MakeFunc function is permitted to call recover. */
- void
- __go_makefunc_can_recover (void *retaddr)
- {
- struct __go_defer_stack *d;
- d = current_defer ();
- if (d == NULL)
- return;
- /* If we are already in a call stack of MakeFunc functions, there is
- nothing we can usefully check here. */
- if (d->__makefunc_can_recover)
- return;
- if (__go_can_recover (retaddr))
- d->__makefunc_can_recover = 1;
- }
- /* This function is called when code is about to enter a function
- created by the libffi version of reflect.MakeFunc. This function
- is passed the names of the callers of the libffi code that called
- the stub. It uses to decide whether it is permitted to call
- recover, and sets d->__makefunc_can_recover so that __go_recover
- can make the same decision. */
- void
- __go_makefunc_ffi_can_recover (struct Location *loc, int n)
- {
- struct __go_defer_stack *d;
- const byte *name;
- intgo len;
- d = current_defer ();
- if (d == NULL)
- return;
- /* If we are already in a call stack of MakeFunc functions, there is
- nothing we can usefully check here. */
- if (d->__makefunc_can_recover)
- return;
- /* LOC points to the caller of our caller. That will be a thunk.
- If its caller was a runtime function, then it was called directly
- by defer. */
- if (n < 2)
- return;
- name = (loc + 1)->function.str;
- len = (loc + 1)->function.len;
- if (len > 4
- && __builtin_strchr ((const char *) name, '.') == NULL
- && __builtin_strncmp ((const char *) name, "__go_", 4) == 0)
- d->__makefunc_can_recover = 1;
- }
- /* This function is called when code is about to exit a function
- created by reflect.MakeFunc. It is called by the function stub
- used by MakeFunc. It clears the __makefunc_can_recover field.
- It's OK to always clear this field, because __go_can_recover will
- only be called by a stub created for a function that calls recover.
- That stub will not call a function created by reflect.MakeFunc, so
- by the time we get here any caller higher up on the call stack no
- longer needs the information. */
- void
- __go_makefunc_returning (void)
- {
- struct __go_defer_stack *d;
- d = runtime_g ()->defer;
- if (d != NULL)
- d->__makefunc_can_recover = 0;
- }
- /* This is only called when it is valid for the caller to recover the
- value on top of the panic stack, if there is one. */
- struct __go_empty_interface
- __go_recover ()
- {
- G *g;
- struct __go_panic_stack *p;
- g = runtime_g ();
- if (g->panic == NULL || g->panic->__was_recovered)
- {
- struct __go_empty_interface ret;
- ret.__type_descriptor = NULL;
- ret.__object = NULL;
- return ret;
- }
- p = g->panic;
- p->__was_recovered = 1;
- return p->__arg;
- }
|