@@ -28,7 +28,8 @@
ACLOCAL_AMFLAGS = -I . -I .. -I ../config
-AM_CFLAGS = -fplan9-extensions $(SPLIT_STACK) $(WARN_CFLAGS)
+AM_CFLAGS = -fexceptions -fplan9-extensions $(SPLIT_STACK) $(WARN_CFLAGS) \
+ -I $(srcdir)/../gcc -I $(MULTIBUILDTOP)../../gcc/include
# Multilib support.
MAKEOVERRIDES=
@@ -248,6 +249,8 @@
runtime/go-construct-map.c \
runtime/go-convert-interface.c \
runtime/go-defer.c \
+ runtime/go-defer-stack.c \
+ runtime/go-deferred-recover.c \
runtime/go-go.c \
runtime/go-goexit.c \
runtime/go-gomaxprocs.c \
@@ -268,11 +271,13 @@
runtime/go-new.c \
runtime/go-note.c \
runtime/go-panic.c \
+ runtime/go-panic-defer.c \
runtime/go-print.c \
runtime/go-rec-big.c \
runtime/go-rec-nb-big.c \
runtime/go-rec-nb-small.c \
runtime/go-rec-small.c \
+ runtime/go-recover.c \
runtime/go-refcount-decrement.c \
runtime/go-refcount-flush.c \
runtime/go-refcount-increment.c \
@@ -305,9 +310,10 @@
runtime/go-unsafe-new.c \
runtime/go-unsafe-newarray.c \
runtime/go-unsafe-pointer.c \
+ runtime/go-unwind.c \
runtime/mcache.c \
runtime/mcentral.c \
-\ runtime/mem.c \
+ runtime/mem.c \
runtime/mfinal.c \
runtime/mfixalloc.c \
runtime/mgc0.c \
@@ -531,6 +537,7 @@
go/rpc/server.go
go_runtime_files = \
+ go/runtime/error.go \
go/runtime/extern.go \
go/runtime/type.go
@@ -182,6 +182,8 @@
AC_C_BIGENDIAN
+GCC_CHECK_UNWIND_GETIPINFO
+
AC_CHECK_HEADERS(sys/mman.h syscall.h sys/epoll.h sys/ptrace.h sys/user.h)
AC_CHECK_FUNCS(srandom random strsignal)
@@ -11,5 +11,5 @@
void
__go_bad_index ()
{
- __go_panic_msg ("index out of range");
+ __go_panic_msg ("index or slice out of range");
}
@@ -86,29 +86,27 @@
--rhs_method_count;
}
- if (rhs_method_count == 0)
+ if (rhs_method_count == 0
+ || !__go_type_descriptors_equal (p_lhs_method->__type,
+ p_rhs_method->__mtype))
{
+ struct __go_interface *panic_arg;
+
if (success != NULL)
{
*success = 0;
return NULL;
}
- __go_print_msg (1, "interface conversion failed: no '");
- __go_print_string (1, *p_lhs_method->__name);
- __go_panic_msg ("' method");
- }
- if (!__go_type_descriptors_equal (p_lhs_method->__type,
- p_rhs_method->__mtype))
- {
- if (success != NULL)
- {
- *success = 0;
- return NULL;
- }
- __go_print_msg (1, "interface conversion failed: '");
- __go_print_string (1, *p_lhs_method->__name);
- __go_panic_msg ("' method has wrong type");
+ newTypeAssertionError(NULL,
+ rhs->__type_descriptor,
+ lhs_descriptor,
+ NULL,
+ rhs->__type_descriptor->__reflection,
+ lhs_descriptor->__reflection,
+ p_lhs_method->__name,
+ &panic_arg);
+ __go_panic (panic_arg);
}
methods[i] = p_rhs_method->__function;
@@ -0,0 +1,9 @@
+/* go-defer-stack.c -- The defer stack.
+
+ 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 "go-defer.h"
+
+__thread struct __defer_stack *__go_defer_stack;
@@ -7,50 +7,63 @@
#include <stddef.h>
#include "go-alloc.h"
-
-/* The defer stack is a list of these structures. */
-
-struct __defer_stack
-{
- /* The next entry in the stack. */
- struct __defer_stack* __next;
-
- /* The function to call. */
- void (*__pfn) (void *);
-
- /* The argument to pass to the function. */
- void *__arg;
-};
+#include "go-panic.h"
+#include "go-defer.h"
/* This function is called each time we need to defer a call. */
-void *
-__go_defer (void *stack, void (*pfn) (void *), void *arg)
+void
+__go_defer (void *frame, void (*pfn) (void *), void *arg)
{
- struct __defer_stack *n;
+ struct __go_defer_stack *n;
- n = (struct __defer_stack *) __go_alloc (sizeof (struct __defer_stack));
- n->__next = (struct __defer_stack *) stack;
+ if (__go_panic_defer == NULL)
+ __go_panic_defer = ((struct __go_panic_defer_struct *)
+ __go_alloc (sizeof (struct __go_panic_defer_struct)));
+
+ n = (struct __go_defer_stack *) __go_alloc (sizeof (struct __go_defer_stack));
+ n->__next = __go_panic_defer->__defer;
+ n->__frame = frame;
+ n->__panic = __go_panic_defer->__panic;
n->__pfn = pfn;
n->__arg = arg;
- return (void *) n;
+ n->__retaddr = NULL;
+ __go_panic_defer->__defer = n;
}
/* This function is called when we want to undefer the stack. */
void
-__go_undefer (void *arg)
+__go_undefer (void *frame)
{
- struct __defer_stack *p;
+ if (__go_panic_defer == NULL)
+ return;
+ while (__go_panic_defer->__defer != NULL
+ && __go_panic_defer->__defer->__frame == frame)
+ {
+ struct __go_defer_stack *d;
+ void (*pfn) (void *);
- p = (struct __defer_stack *) arg;
- while (p != NULL)
- {
- struct __defer_stack *n;
+ d = __go_panic_defer->__defer;
+ pfn = d->__pfn;
+ d->__pfn = NULL;
- n = p->__next;
- (*p->__pfn) (p->__arg);
- __go_free (p);
- p = n;
+ if (pfn != NULL)
+ (*pfn) (d->__arg);
+
+ __go_panic_defer->__defer = d->__next;
+ __go_free (d);
}
}
+
+/* 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. */
+
+_Bool
+__go_set_defer_retaddr (void *retaddr)
+{
+ if (__go_panic_defer != NULL && __go_panic_defer->__defer != NULL)
+ __go_panic_defer->__defer->__retaddr = retaddr;
+ return 0;
+}
@@ -0,0 +1,36 @@
+/* go-defer.h -- the defer stack.
+
+ 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. */
+
+struct __go_panic_stack;
+
+/* The defer stack is a list of these structures. */
+
+struct __go_defer_stack
+{
+ /* The next entry in the stack. */
+ struct __go_defer_stack *__next;
+
+ /* The frame pointer for the function which called this defer
+ statement. */
+ void *__frame;
+
+ /* The value of the panic stack when this function is deferred.
+ This function can not recover this value from the panic stack.
+ This can happen if a deferred function uses its own defer
+ statement. */
+ struct __go_panic_stack *__panic;
+
+ /* The function to call. */
+ void (*__pfn) (void *);
+
+ /* 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;
+};
@@ -0,0 +1,91 @@
+/* go-deferred-recover.c -- support for a deferred 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 <stddef.h>
+
+#include "go-panic.h"
+#include "go-defer.h"
+
+/* This is called when a call to recover is deferred. That is,
+ something like
+ defer recover()
+
+ We need to handle this specially. In 6g/8g, the recover function
+ looks up the stack frame. In particular, that means that a
+ deferred recover will not recover a panic thrown in the same
+ function that defers the recover. It will only recover a panic
+ thrown in a function that defers the deferred call to recover.
+
+ In other words:
+
+ func f1() {
+ defer recover() // does not stop panic
+ panic(0)
+ }
+
+ func f2() {
+ defer func() {
+ defer recover() // stops panic(0)
+ }()
+ panic(0)
+ }
+
+ func f3() {
+ defer func() {
+ defer recover() // does not stop panic
+ panic(0)
+ }()
+ panic(1)
+ }
+
+ func f4() {
+ defer func() {
+ defer func() {
+ defer recover() // stops panic(0)
+ }()
+ panic(0)
+ }()
+ panic(1)
+ }
+
+ The interesting case here is f3. As can be seen from f2, the
+ deferred recover could pick up panic(1). However, this does not
+ happen because it is blocked by the panic(0).
+
+ When a function calls recover, then when we invoke it we pass a
+ hidden parameter indicating whether it should recover something.
+ This parameter is set based on whether the function is being
+ invoked directly from defer. The parameter winds up determining
+ whether __go_recover or __go_deferred_recover is called at all.
+
+ In the case of a deferred recover, the hidden parameter which
+ controls the call is actually the one set up for the function which
+ runs the defer recover() statement. That is the right thing in all
+ the cases above except for f3. In f3 the function is permitted to
+ call recover, but the deferred recover call is not. We address
+ that here by checking for that specific case before calling
+ recover. If this function was deferred when there is already a
+ panic on the panic stack, then we can only recover that panic, not
+ any other.
+
+ Note that we can get away with using a special function here
+ because you are not permitted to take the address of a predeclared
+ function like recover. */
+
+struct __go_interface *
+__go_deferred_recover ()
+{
+ struct __go_defer_stack *d;
+
+ if (__go_panic_defer == NULL)
+ return NULL;
+ d = __go_panic_defer->__defer;
+ if (d == NULL)
+ return 0;
+ if (d->__panic != __go_panic_defer->__panic)
+ return 0;
+ return __go_recover();
+}
@@ -5,9 +5,9 @@
license that can be found in the LICENSE file. */
#include <assert.h>
-#include <stdlib.h>
#include "interface.h"
+#include "go-panic.h"
/* Get an object from an interface. This checks that the types match,
and crashes if they don't. */
@@ -20,10 +20,24 @@
const struct __go_interface *rhs = (const struct __go_interface *) rhs_arg;
assert (!__go_is_pointer_type (lhs_descriptor));
+
+ if (rhs == NULL)
+ {
+ __builtin_memset (result, 0, object_size);
+ return;
+ }
+
if ((rhs->__type_descriptor != lhs_descriptor
&& !__go_type_descriptors_equal (rhs->__type_descriptor,
lhs_descriptor))
|| rhs->__type_descriptor->__size != object_size)
- abort ();
+ {
+ struct __go_interface *panic_arg;
+
+ newTypeAssertionError(NULL, rhs->__type_descriptor, lhs_descriptor,
+ NULL, rhs->__type_descriptor->__reflection,
+ lhs_descriptor->__reflection, NULL, &panic_arg);
+ __go_panic(panic_arg);
+ }
__builtin_memcpy (result, rhs->__object, object_size);
}
@@ -5,8 +5,8 @@
license that can be found in the LICENSE file. */
#include <assert.h>
-#include <stdlib.h>
+#include "go-panic.h"
#include "interface.h"
/* Get a pointer from an interface. This checks that the types match,
@@ -19,10 +19,21 @@
const struct __go_interface *rhs = (const struct __go_interface *) rhs_arg;
assert (__go_is_pointer_type (lhs_descriptor));
+
+ if (rhs == NULL)
+ return NULL;
+
if (rhs->__type_descriptor != lhs_descriptor
&& !__go_type_descriptors_equal (rhs->__type_descriptor, lhs_descriptor)
&& lhs_descriptor->__code != GO_UNSAFE_POINTER
&& rhs->__type_descriptor->__code != GO_UNSAFE_POINTER)
- abort ();
+ {
+ struct __go_interface *panic_arg;
+
+ newTypeAssertionError(NULL, rhs->__type_descriptor, lhs_descriptor,
+ NULL, rhs->__type_descriptor->__reflection,
+ lhs_descriptor->__reflection, NULL, &panic_arg);
+ __go_panic(panic_arg);
+ }
return rhs->__object;
}
@@ -4,10 +4,15 @@
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file. */
+#include "config.h"
+
#include <stdlib.h>
#include <time.h>
-#include "config.h"
+#ifdef HAVE_FPU_CONTROL_H
+#include <fpu_control.h>
+#endif
+
#include "go-alloc.h"
#include "array.h"
#include "go-signal.h"
@@ -0,0 +1,9 @@
+/* go-panic-stack.c -- The panic/defer stack.
+
+ 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 "go-panic.h"
+
+__thread struct __go_panic_defer_struct *__go_panic_defer;
@@ -1,4 +1,4 @@
-/* go-panic.c -- support for the go panic statement.
+/* go-panic.c -- support for the go panic function.
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
@@ -7,29 +7,115 @@
#include <stdio.h>
#include <stdlib.h>
+#include "runtime.h"
+#include "malloc.h"
+#include "go-alloc.h"
+#include "go-defer.h"
#include "go-panic.h"
+#include "go-string.h"
+#include "interface.h"
+
+/* Print the panic stack. This is used when there is no recover. */
+
+static void
+__printpanics (struct __go_panic_stack *p)
+{
+ if (p->__next != NULL)
+ {
+ __printpanics (p->__next);
+ printf ("\t");
+ }
+ printf ("panic: ");
+ printany (p->__arg);
+ if (p->__was_recovered)
+ printf (" [recovered]");
+ putchar ('\n');
+}
/* This implements __go_panic which is used for the panic
- statement. */
+ function. */
void
-__go_panic ()
+__go_panic (struct __go_interface *arg)
{
- fputs ("\npanic\n", stderr);
+ struct __go_panic_stack *n;
+
+ if (__go_panic_defer == NULL)
+ __go_panic_defer = ((struct __go_panic_defer_struct *)
+ __go_alloc (sizeof (struct __go_panic_defer_struct)));
+
+ n = (struct __go_panic_stack *) __go_alloc (sizeof (struct __go_panic_stack));
+ n->__arg = arg;
+ n->__next = __go_panic_defer->__panic;
+ __go_panic_defer->__panic = n;
+
+ /* Run all the defer functions. */
+
+ while (1)
+ {
+ struct __go_defer_stack *d;
+ void (*pfn) (void *);
+
+ d = __go_panic_defer->__defer;
+ if (d == NULL)
+ break;
+
+ pfn = d->__pfn;
+ d->__pfn = NULL;
+
+ if (pfn != NULL)
+ {
+ (*pfn) (d->__arg);
+
+ if (n->__was_recovered)
+ {
+ /* Some defer function called recover. That means that
+ we should stop running this panic. */
+
+ __go_panic_defer->__panic = n->__next;
+ __go_free (n);
+
+ /* Now unwind the stack by throwing an exception. The
+ compiler has arranged to create exception handlers in
+ each function which uses a defer statement. These
+ exception handlers will check whether the entry on
+ the top of the defer stack is from the current
+ function. If it is, we have unwound the stack far
+ enough. */
+ __go_unwind_stack ();
+
+ /* __go_unwind_stack should not return. */
+ abort ();
+ }
+ }
+
+ __go_panic_defer->__defer = d->__next;
+ __go_free (d);
+ }
+
+ /* The panic was not recovered. */
+
+ __printpanics (__go_panic_defer->__panic);
+
+ /* FIXME: We should dump a call stack here. */
abort ();
}
-/* These are used by the runtime library. */
+/* This is used by the runtime library. */
void
__go_panic_msg (const char* msg)
{
- __go_print_msg (1, msg);
- __go_panic ();
+ size_t len;
+ unsigned char *sdata;
+ struct __go_string s;
+ struct __go_interface *arg;
+
+ len = __builtin_strlen (msg);
+ sdata = mallocgc (len, RefNoPointers, 0, 0);
+ __builtin_memcpy (sdata, msg, len);
+ s.__data = sdata;
+ s.__length = len;
+ newErrorString(s, &arg);
+ __go_panic (arg);
}
-
-void
-__go_print_msg (_Bool is_panic, const char* msg)
-{
- fputs (msg, is_panic ? stderr : stdout);
-}
@@ -1,16 +1,85 @@
-/* go-panic.h -- declare the go panic statement.
+/* go-panic.h -- declare the go panic functions.
Copyright 2009 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. */
-extern void __go_panic (void)
+#ifndef LIBGO_GO_PANIC_H
+#define LIBGO_GO_PANIC_H
+
+struct __go_interface;
+struct __go_string;
+struct __go_type_descriptor;
+struct __go_defer_stack;
+
+/* The stack of panic calls. */
+
+struct __go_panic_stack
+{
+ /* The next entry in the stack. */
+ struct __go_panic_stack *__next;
+
+ /* The value associated with this panic. */
+ struct __go_interface *__arg;
+
+ /* Whether this panic has been recovered. */
+ _Bool __was_recovered;
+
+ /* Whether this panic was pushed on the stack because of an
+ exception thrown in some other language. */
+ _Bool __is_foreign;
+};
+
+/* The panic and defer stacks, grouped together into a single thread
+ local variable for convenience for systems without TLS. */
+
+struct __go_panic_defer_struct
+{
+ /* The list of defers to execute. */
+ struct __go_defer_stack *__defer;
+
+ /* The list of currently active panics. There will be more than one
+ if a deferred function calls panic. */
+ struct __go_panic_stack *__panic;
+
+ /* The current exception being thrown when unwinding after a call to
+ panic . This is really struct _UnwindException *. */
+ void *__exception;
+
+ /* Whether the current exception is from some other language. */
+ _Bool __is_foreign;
+};
+
+extern __thread struct __go_panic_defer_struct *__go_panic_defer;
+
+extern void __go_panic (struct __go_interface *)
__attribute__ ((noreturn));
extern void __go_panic_msg (const char* msg)
__attribute__ ((noreturn));
-extern void __go_print_msg (_Bool is_panic, const char* msg);
+extern void __go_print_string (struct __go_string);
-struct __go_string;
-extern void __go_print_string (_Bool is_panic, struct __go_string);
+extern struct __go_interface * __go_recover (void);
+
+extern void __go_unwind_stack (void);
+
+/* Functions defined in libgo/go/runtime/error.go. */
+
+extern void newTypeAssertionError(const struct __go_type_descriptor *pt1,
+ const struct __go_type_descriptor *pt2,
+ const struct __go_type_descriptor *pt3,
+ const struct __go_string *ps1,
+ const struct __go_string *ps2,
+ const struct __go_string *ps3,
+ const struct __go_string *pmeth,
+ struct __go_interface **ret)
+ __asm__ ("libgo_runtime.runtime.NewTypeAssertionError");
+
+extern void newErrorString(struct __go_string, struct __go_interface **)
+ __asm__ ("libgo_runtime.runtime.NewErrorString");
+
+extern void printany(struct __go_interface *)
+ __asm__ ("libgo_runtime.runtime.Printany");
+
+#endif /* !defined(LIBGO_GO_PANIC_H) */
@@ -15,66 +15,66 @@
the predeclared functions print/println/panic/panicln. */
void
-__go_print_space (_Bool is_panic)
+__go_print_space ()
{
- putc (' ', is_panic ? stderr : stdout);
+ putchar (' ');
}
void
-__go_print_nl (_Bool is_panic)
+__go_print_nl ()
{
- putc ('\n', is_panic ? stderr : stdout);
+ putchar ('\n');
}
void
-__go_print_string (_Bool is_panic, struct __go_string val)
+__go_print_string (struct __go_string val)
{
- fprintf (is_panic ? stderr : stdout, "%.*s", (int) val.__length,
- (const char *) val.__data);
+ printf ("%.*s", (int) val.__length, (const char *) val.__data);
}
void
-__go_print_uint64 (_Bool is_panic, uint64_t val)
+__go_print_uint64 (uint64_t val)
{
- fprintf (is_panic ? stderr : stdout, "%llu", (unsigned long long) val);
+ printf ("%llu", (unsigned long long) val);
}
void
-__go_print_int64 (_Bool is_panic, int64_t val)
+__go_print_int64 (int64_t val)
{
- fprintf (is_panic ? stderr : stdout, "%lld", (long long) val);
+ printf ("%lld", (long long) val);
}
void
-__go_print_double (_Bool is_panic, double val)
+__go_print_double (double val)
{
- fprintf (is_panic ? stderr : stdout, "%.24g", val);
+ printf ("%.24g", val);
}
void
-__go_print_complex (_Bool is_panic, __complex double val)
+__go_print_complex (__complex double val)
{
- fprintf (is_panic ? stderr : stdout, "(%.24g%s%.24gi)",
- __builtin_creal (val),
- __builtin_cimag (val) >= 0 ? "+" : "",
- __builtin_cimag (val));
+ printf ("(%.24g%s%.24gi)",
+ __builtin_creal (val),
+ (__builtin_cimag (val) >= 0 || __builtin_isnan (__builtin_cimag(val))
+ ? "+"
+ : ""),
+ __builtin_cimag (val));
}
void
-__go_print_bool (_Bool is_panic, _Bool val)
+__go_print_bool (_Bool val)
{
- fputs (val ? "true" : "false", is_panic ? stderr : stdout);
+ fputs (val ? "true" : "false", stdout);
}
void
-__go_print_pointer (_Bool is_panic, void *val)
+__go_print_pointer (void *val)
{
- fprintf (is_panic ? stderr : stdout, "%p", val);
+ printf ("%p", val);
}
void
-__go_print_slice (_Bool is_panic, struct __go_open_array val)
+__go_print_slice (struct __go_open_array val)
{
- fprintf (is_panic ? stderr : stdout, "[%d/%d]%p",
- val.__count, val.__capacity, val.__values);
+ printf ("[%d/%d]%p", val.__count, val.__capacity, val.__values);
}
@@ -36,7 +36,11 @@
{
++channel->closed_op_count;
if (channel->closed_op_count >= MAX_CLOSED_OPERATIONS)
- __go_panic_msg ("too many operations on closed channel");
+ {
+ i = pthread_mutex_unlock (&channel->lock);
+ assert (i == 0);
+ __go_panic_msg ("too many operations on closed channel");
+ }
}
channel->saw_close = 1;
__go_unlock_and_notify_selects (channel);
@@ -0,0 +1,64 @@
+/* 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 "interface.h"
+#include "go-panic.h"
+#include "go-defer.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
+ 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 (const void* retaddr)
+{
+ struct __go_defer_stack *d;
+ const char* ret;
+ const char* dret;
+
+ if (__go_panic_defer == NULL)
+ return 0;
+ d = __go_panic_defer->__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 == __go_panic_defer->__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. */
+
+ ret = (const char *) retaddr;
+ dret = (const char *) d->__retaddr;
+ return ret <= dret && ret + 16 >= dret;
+}
+
+/* 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_interface *
+__go_recover ()
+{
+ struct __go_panic_stack *p;
+
+ if (__go_panic_defer == NULL)
+ return NULL;
+ p = __go_panic_defer->__panic;
+ if (p == NULL || p->__was_recovered)
+ return NULL;
+
+ p->__was_recovered = 1;
+ return p->__arg;
+}
@@ -31,7 +31,11 @@
{
++channel->closed_op_count;
if (channel->closed_op_count >= MAX_CLOSED_OPERATIONS)
- __go_panic_msg ("too many operations on closed channel");
+ {
+ i = pthread_mutex_unlock (&channel->lock);
+ assert (i == 0);
+ __go_panic_msg ("too many operations on closed channel");
+ }
i = pthread_mutex_unlock (&channel->lock);
assert (i == 0);
return SEND_NONBLOCKING_ACQUIRE_CLOSED;
@@ -30,7 +30,11 @@
{
++channel->closed_op_count;
if (channel->closed_op_count >= MAX_CLOSED_OPERATIONS)
- __go_panic_msg ("too many operations on closed channel");
+ {
+ i = pthread_mutex_unlock (&channel->lock);
+ assert (i == 0);
+ __go_panic_msg ("too many operations on closed channel");
+ }
channel->selected_for_send = 0;
__go_unlock_and_notify_selects (channel);
return 0;
@@ -8,6 +8,7 @@
#include <signal.h>
#include <stdlib.h>
+#include "go-panic.h"
#include "go-signal.h"
#include "runtime.h"
@@ -36,12 +37,27 @@
{ SIGINT, 0 },
{ SIGALRM, 1 },
{ SIGTERM, 0 },
+#ifdef SIGBUS
+ { SIGBUS, 0 },
+#endif
+#ifdef SIGFPE
+ { SIGFPE, 0 },
+#endif
#ifdef SIGUSR1
{ SIGUSR1, 1 },
#endif
+#ifdef SIGSEGV
+ { SIGSEGV, 0 },
+#endif
#ifdef SIGUSR2
{ SIGUSR2, 1 },
#endif
+#ifdef SIGPIPE
+ { SIGPIPE, 1 },
+#endif
+#ifdef SIGCHLD
+ { SIGCHLD, 1 },
+#endif
#ifdef SIGTSTP
{ SIGTSTP, 1 },
#endif
@@ -83,8 +99,49 @@
static void
sighandler (int sig)
{
+ const char *msg;
int i;
+ /* FIXME: Should check siginfo for more information when
+ available. */
+ msg = NULL;
+ switch (sig)
+ {
+#ifdef SIGBUS
+ case SIGBUS:
+ msg = "invalid memory address or nil pointer dereference";
+ break;
+#endif
+
+#ifdef SIGFPE
+ case SIGFPE:
+ msg = "division by zero or floating point error";
+ break;
+#endif
+
+#ifdef SIGSEGV
+ case SIGSEGV:
+ msg = "invalid memory address or nil pointer dereference";
+ break;
+#endif
+
+ default:
+ break;
+ }
+
+ if (msg != NULL)
+ {
+ sigset_t clear;
+
+ /* The signal handler blocked signals; unblock them. */
+ i = sigfillset (&clear);
+ assert (i == 0);
+ i = sigprocmask (SIG_UNBLOCK, &clear, NULL);
+ assert (i == 0);
+
+ __go_panic_msg (msg);
+ }
+
if (sigsend (sig))
return;
for (i = 0; signals[i].sig != -1; ++i)
@@ -14,7 +14,7 @@
__go_type_hash_error (const void *val __attribute__ ((unused)),
size_t key_size __attribute__ ((unused)))
{
- __go_panic_msg ("hash of type which does not support hash computations");
+ __go_panic_msg ("hash of unhashable type");
}
/* An equality function for an interface. */
@@ -24,5 +24,5 @@
const void *v2 __attribute__ ((unused)),
size_t key_size __attribute__ ((unused)))
{
- __go_panic_msg ("comparison of type which may not be compared");
+ __go_panic_msg ("comparing uncomparable types");
}
@@ -0,0 +1,401 @@
+/* go-unwind.c -- unwind the stack for panic/recover.
+
+ 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 "config.h"
+
+#include <stdlib.h>
+
+#include "unwind.h"
+#define NO_SIZE_OF_ENCODED_VALUE
+#include "unwind-pe.h"
+
+#include "go-alloc.h"
+#include "go-defer.h"
+#include "go-panic.h"
+
+/* The code for a Go exception. */
+
+#ifdef __ARM_EABI_UNWINDER__
+static const _Unwind_Exception_Class __go_exception_class =
+ { 'G', 'N', 'U', 'C', 'G', 'O', '\0', '\0' };
+#else
+static const _Unwind_Exception_Class __go_exception_class =
+ ((((((((_Unwind_Exception_Class) 'G'
+ << 8 | (_Unwind_Exception_Class) 'N')
+ << 8 | (_Unwind_Exception_Class) 'U')
+ << 8 | (_Unwind_Exception_Class) 'C')
+ << 8 | (_Unwind_Exception_Class) 'G')
+ << 8 | (_Unwind_Exception_Class) 'O')
+ << 8 | (_Unwind_Exception_Class) '\0')
+ << 8 | (_Unwind_Exception_Class) '\0');
+#endif
+
+
+/* This function is called by exception handlers used when unwinding
+ the stack after a recovered panic. The exception handler looks
+ like this:
+ __go_check_defer (frame);
+ return;
+ If we have not yet reached the frame we are looking for, we
+ continue unwinding. */
+
+void
+__go_check_defer (void *frame)
+{
+ struct _Unwind_Exception *hdr;
+
+ if (__go_panic_defer->__is_foreign)
+ {
+ struct __go_panic_stack *n;
+ _Bool was_recovered;
+
+ /* Some other language has thrown an exception. We need to run
+ the local defer handlers. If they call recover, we stop
+ unwinding the stack here. */
+
+ n = ((struct __go_panic_stack *)
+ __go_alloc (sizeof (struct __go_panic_stack)));
+
+ n->__arg = NULL;
+ n->__was_recovered = 0;
+ n->__is_foreign = 1;
+ n->__next = __go_panic_defer->__panic;
+ __go_panic_defer->__panic = n;
+
+ while (1)
+ {
+ struct __go_defer_stack *d;
+ void (*pfn) (void *);
+
+ d = __go_panic_defer->__defer;
+ if (d == NULL || d->__frame != frame || d->__pfn == NULL)
+ break;
+
+ pfn = d->__pfn;
+ __go_panic_defer->__defer = d->__next;
+
+ (*pfn) (d->__arg);
+
+ __go_free (d);
+
+ if (n->__was_recovered)
+ {
+ /* The recover function caught the panic thrown by some
+ other language. */
+ break;
+ }
+ }
+
+ was_recovered = n->__was_recovered;
+ __go_panic_defer->__panic = n->__next;
+ __go_free (n);
+
+ if (was_recovered)
+ {
+ /* Just return and continue executing Go code. */
+ return;
+ }
+ }
+ else if (__go_panic_defer->__defer != NULL
+ && __go_panic_defer->__defer->__pfn == NULL
+ && __go_panic_defer->__defer->__frame == frame)
+ {
+ struct __go_defer_stack *d;
+
+ /* This is the defer function which called recover. Simply
+ return to stop the stack unwind, and let the Go code continue
+ to execute. */
+ d = __go_panic_defer->__defer;
+ __go_panic_defer->__defer = d->__next;
+ __go_free (d);
+ return;
+ }
+
+ /* This is some other defer function. It was already run by the
+ call to panic, or just above. Rethrow the exception. */
+
+ hdr = (struct _Unwind_Exception *) __go_panic_defer->__exception;
+
+#ifdef _GLIBCXX_SJLJ_EXCEPTIONS
+ _Unwind_SjLj_Resume_or_Rethrow (hdr);
+#else
+#if defined(_LIBUNWIND_STD_ABI)
+ _Unwind_RaiseException (hdr);
+#else
+ _Unwind_Resume_or_Rethrow (hdr);
+#endif
+#endif
+
+ /* Rethrowing the exception should not return. */
+ abort();
+}
+
+/* Unwind function calls until we reach the one which used a defer
+ function which called recover. Each function which uses a defer
+ statement will have an exception handler, as shown above. */
+
+void
+__go_unwind_stack ()
+{
+ struct _Unwind_Exception *hdr;
+
+ hdr = ((struct _Unwind_Exception *)
+ __go_alloc (sizeof (struct _Unwind_Exception)));
+ __builtin_memcpy (&hdr->exception_class, &__go_exception_class,
+ sizeof hdr->exception_class);
+ hdr->exception_cleanup = NULL;
+
+ __go_panic_defer->__exception = hdr;
+
+ _Unwind_Reason_Code code;
+#ifdef __USING_SJLJ_EXCEPTIONS__
+ code = _Unwind_SjLj_RaiseException (hdr);
+#else
+ code = _Unwind_RaiseException (hdr);
+#endif
+
+ /* Raising an exception should not return. */
+ abort ();
+}
+
+/* The rest of this code is really similar to gcc/unwind-c.c and
+ libjava/exception.cc. */
+
+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)
+{
+ _uleb128_t 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 __ARM_EABI_UNWINDER__
+/* ARM EABI personality routines must also unwind the stack. */
+#define CONTINUE_UNWINDING \
+ do \
+ { \
+ if (__gnu_unwind_frame (ue_header, context) != _URC_OK) \
+ return _URC_FAILURE; \
+ return _URC_CONTINUE_UNWIND; \
+ } \
+ while (0)
+#else
+#define CONTINUE_UNWINDING return _URC_CONTINUE_UNWIND
+#endif
+
+#ifdef __USING_SJLJ_EXCEPTIONS__
+#define PERSONALITY_FUNCTION __gccgo_personality_sj0
+#define __builtin_eh_return_data_regno(x) x
+#else
+#define PERSONALITY_FUNCTION __gccgo_personality_v0
+#endif
+
+#ifdef __ARM_EABI_UNWINDER__
+_Unwind_Reason_Code
+PERSONALITY_FUNCTION (_Unwind_State, struct _Unwind_Exception *,
+ struct _Unwind_Context *);
+
+_Unwind_Reason_Code
+PERSONALITY_FUNCTION (_Unwind_State state,
+ struct _Unwind_Exception * ue_header,
+ struct _Unwind_Context * context)
+#else
+_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,
+ struct _Unwind_Exception *ue_header,
+ struct _Unwind_Context *context)
+#endif
+{
+ lsda_header_info info;
+ const unsigned char *language_specific_data, *p, *action_record;
+ _Unwind_Ptr landing_pad, ip;
+ int ip_before_insn = 0;
+ _Bool is_foreign;
+
+#ifdef __ARM_EABI_UNWINDER__
+ _Unwind_Action actions;
+
+ switch (state & _US_ACTION_MASK)
+ {
+ case _US_VIRTUAL_UNWIND_FRAME:
+ actions = _UA_SEARCH_PHASE;
+ break;
+
+ case _US_UNWIND_FRAME_STARTING:
+ actions = _UA_CLEANUP_PHASE;
+ if (!(state & _US_FORCE_UNWIND)
+ && ue_header->barrier_cache.sp == _Unwind_GetGR(context, 13))
+ actions |= _UA_HANDLER_FRAME;
+ break;
+
+ case _US_UNWIND_FRAME_RESUME:
+ CONTINUE_UNWINDING;
+ break;
+
+ default:
+ std::abort();
+ }
+ actions |= state & _US_FORCE_UNWIND;
+
+ is_foreign = 0;
+
+ /* The dwarf unwinder assumes the context structure holds things like the
+ function and LSDA pointers. The ARM implementation caches these in
+ the exception header (UCB). To avoid rewriting everything we make the
+ virtual IP register point at the UCB. */
+ ip = (_Unwind_Ptr) ue_header;
+ _Unwind_SetGR (context, 12, ip);
+#else
+ if (version != 1)
+ return _URC_FATAL_PHASE1_ERROR;
+
+ is_foreign = exception_class != __go_exception_class;
+#endif
+
+ language_specific_data = (const unsigned char *)
+ _Unwind_GetLanguageSpecificData (context);
+
+ /* If no LSDA, then there are no handlers or cleanups. */
+ if (! language_specific_data)
+ CONTINUE_UNWINDING;
+
+ /* Parse the LSDA header. */
+ p = parse_lsda_header (context, language_specific_data, &info);
+#ifdef HAVE_GETIPINFO
+ ip = _Unwind_GetIPInfo (context, &ip_before_insn);
+#else
+ ip = _Unwind_GetIP (context);
+#endif
+ if (! ip_before_insn)
+ --ip;
+ 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
+ {
+ _uleb128_t 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 = (_Unwind_Ptr)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;
+ _uleb128_t 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. */
+ CONTINUE_UNWINDING;
+
+ found_something:
+ if (landing_pad == 0)
+ {
+ /* IP is present, but has a null landing pad.
+ No handler to be run. */
+ CONTINUE_UNWINDING;
+ }
+
+ if (actions & _UA_SEARCH_PHASE)
+ return _URC_HANDLER_FOUND;
+
+ if (__go_panic_defer == NULL)
+ {
+ if (!is_foreign)
+ abort();
+ __go_panic_defer = ((struct __go_panic_defer_struct *)
+ __go_alloc (sizeof (struct __go_panic_defer_struct)));
+ }
+
+ __go_panic_defer->__exception = ue_header;
+ __go_panic_defer->__is_foreign = is_foreign;
+
+ _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;
+}
@@ -7,6 +7,7 @@
#include "config.h"
#define _GNU_SOURCE
+#include <assert.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
@@ -89,8 +90,8 @@
#define nil ((void*)0)
#define USED(v) ((void) v)
-/* We map throw to panic. */
-#define throw(s) __go_panic_msg (s)
+/* We map throw to assert. */
+#define throw(s) assert(s == 0)
void mallocinit(void);
void siginit(void);