diff mbox

[2/2,libgo] Backport recover fix for gccgo to gcc 4.9

Message ID 54AC0365.1030402@linux.vnet.ibm.com
State New
Headers show

Commit Message

Lynn A. Boger Jan. 6, 2015, 3:46 p.m. UTC
This is a backport to gcc 4.9 of the following change: 
https://gcc.gnu.org/ml/gcc-patches/2014-10/msg00660.html.  The original 
patch had changes required for use with the FFI reflection support.  
Since FFI is not used for reflection in gcc 4.9 those changes were omitted.

2015-01-06    Lynn Boger <laboger@linux.vnet.ibm.com>

     * libgo/runtime/go-defer.c
     * libgo/runtime/go-panic.h
     * libgo/go-recover.c

Thanks
diff mbox

Patch

Index: libgo/runtime/go-defer.c
===================================================================
--- libgo/runtime/go-defer.c	(revision 218817)
+++ libgo/runtime/go-defer.c	(working copy)
@@ -82,6 +82,6 @@  __go_set_defer_retaddr (void *retaddr)
 
   g = runtime_g ();
   if (g->defer != NULL)
-    g->defer->__retaddr = retaddr;
+    g->defer->__retaddr = __builtin_extract_return_addr (retaddr);
   return 0;
 }
Index: libgo/runtime/go-panic.h
===================================================================
--- libgo/runtime/go-panic.h	(revision 218817)
+++ libgo/runtime/go-panic.h	(working copy)
@@ -38,6 +38,12 @@  extern void __go_print_string (struct String);
 
 extern struct __go_empty_interface __go_recover (void);
 
+extern _Bool __go_can_recover (void *);
+
+extern void __go_makefunc_can_recover (void *retaddr);
+
+extern void __go_makefunc_returning (void);
+
 extern void __go_unwind_stack (void);
 
 #endif /* !defined(LIBGO_GO_PANIC_H) */
Index: libgo/runtime/go-recover.c
===================================================================
--- libgo/runtime/go-recover.c	(revision 218817)
+++ libgo/runtime/go-recover.c	(working copy)
@@ -9,6 +9,36 @@ 
 #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
@@ -16,79 +46,127 @@ 
    __go_can_recover--this is, the thunk.  */
 
 _Bool
-__go_can_recover (const void *retaddr)
+__go_can_recover (void *retaddr)
 {
-  G *g;
   struct __go_defer_stack *d;
   const char* ret;
   const char* dret;
-  Location loc;
+  Location locs[16];
   const byte *name;
+  intgo len;
+  int n;
+  int i;
+  _Bool found_ffi_callback;
 
-  g = runtime_g ();
-
-  d = g->defer;
+  d = current_defer ();
   if (d == NULL)
     return 0;
 
-  /* The panic which this function would recover 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 0;
+  ret = (const char *) __builtin_extract_return_addr (retaddr);
 
-  /* 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.  */
-
-  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 the function calling recover was created by reflect.MakeFunc,
-     then RETADDR will be somewhere in libffi.  Our caller is
-     permitted to recover if it was called from libffi.  */
-  if (!d->__makefunc_can_recover)
-    return 0;
+  /* 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 (2, &loc, 1) < 1)
+  if (runtime_callers (1, &locs[0], 2) < 2)
     return 0;
 
-  /* If we have no function name, then we weren't called by Go code.
-     Guess that we were called by libffi.  */
-  if (loc.function.len == 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 (loc.function.len < 4)
-    return 0;
-  name = loc.function.str;
-  if (*name == '_')
+  /* 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 (loc.function.len < 5)
+      if (runtime_callers (3, &locs[0], 1) < 1)
 	return 0;
-      ++name;
+      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 (name[0] == 'f' && name[1] == 'f' && name[2] == 'i' && name[3] == '_')
-    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 may also be called by reflect.makeFuncImpl.call, for a
-     function created by reflect.MakeFunc.  */
-  if (__builtin_strstr ((const char *) name, "makeFuncImpl") != NULL)
-    return 1;
+  /* 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]);
+  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;
 }
 
@@ -98,14 +176,20 @@  _Bool
    real MakeFunc function is permitted to call recover.  */
 
 void
-__go_makefunc_can_recover (const void *retaddr)
+__go_makefunc_can_recover (void *retaddr)
 {
   struct __go_defer_stack *d;
 
-  d = runtime_g ()->defer;
-  if (d != NULL
-      && !d->__makefunc_can_recover
-      && __go_can_recover (retaddr))
+  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;
 }