summaryrefslogtreecommitdiff
path: root/libgo/runtime/go-unwind.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/runtime/go-unwind.c')
-rw-r--r--libgo/runtime/go-unwind.c248
1 files changed, 248 insertions, 0 deletions
diff --git a/libgo/runtime/go-unwind.c b/libgo/runtime/go-unwind.c
index a059acbc22a..a1a9558597b 100644
--- a/libgo/runtime/go-unwind.c
+++ b/libgo/runtime/go-unwind.c
@@ -304,6 +304,26 @@ read_encoded_value (struct _Unwind_Context *context, uint8_t encoding,
return p;
}
+static inline int
+value_size (uint8_t encoding)
+{
+ switch (encoding & 0x0f)
+ {
+ case DW_EH_PE_sdata2:
+ case DW_EH_PE_udata2:
+ return 2;
+ case DW_EH_PE_sdata4:
+ case DW_EH_PE_udata4:
+ return 4;
+ case DW_EH_PE_sdata8:
+ case DW_EH_PE_udata8:
+ return 8;
+ default:
+ break;
+ }
+ abort ();
+}
+
/* The rest of this code is really similar to gcc/unwind-c.c and
libjava/exception.cc. */
@@ -563,3 +583,231 @@ PERSONALITY_FUNCTION (int version,
_Unwind_SetIP (context, landing_pad);
return _URC_INSTALL_CONTEXT;
}
+
+// A dummy personality function, which doesn't capture any exception
+// and simply passes by. This is used for functions that don't
+// capture exceptions but need LSDA for stack maps.
+_Unwind_Reason_Code
+__gccgo_personality_dummy (int, _Unwind_Action, _Unwind_Exception_Class,
+ struct _Unwind_Exception *, struct _Unwind_Context *)
+ __attribute__ ((no_split_stack));
+
+_Unwind_Reason_Code
+__gccgo_personality_dummy (int version __attribute__ ((unused)),
+ _Unwind_Action actions __attribute__ ((unused)),
+ _Unwind_Exception_Class exception_class __attribute__ ((unused)),
+ struct _Unwind_Exception *ue_header __attribute__ ((unused)),
+ struct _Unwind_Context *context __attribute__ ((unused)))
+{
+ CONTINUE_UNWINDING;
+}
+
+// A sentinel value for Go functions.
+// A function is a Go function if it has LSDA, which has type info,
+// and the first (dummy) landing pad's type info is a pointer to
+// this value.
+#define GO_FUNC_SENTINEL ((uint64)'G' | ((uint64)'O'<<8) | \
+ ((uint64)'.'<<16) | ((uint64)'.'<<24) | \
+ ((uint64)'F'<<32) | ((uint64)'U'<<40) | \
+ ((uint64)'N'<<48) | ((uint64)'C'<<56))
+
+struct _stackmap {
+ uint32 len;
+ uint8 data[1]; // variabe length
+};
+
+extern void
+ runtime_scanstackblockwithmap (uintptr ip, uintptr sp, uintptr size, uint8 *ptrmask, void* gcw)
+ __asm__ (GOSYM_PREFIX "runtime.scanstackblockwithmap");
+
+#define FOUND 0
+#define NOTFOUND_OK 1
+#define NOTFOUND_BAD 2
+
+// Helper function to search for stack maps in the unwinding records of a frame.
+// If found, populate ip, sp, and stackmap. Returns the #define'd values above.
+static int
+findstackmaps (struct _Unwind_Context *context, _Unwind_Ptr *ip, _Unwind_Ptr *sp, struct _stackmap **stackmap)
+{
+ lsda_header_info info;
+ const unsigned char *language_specific_data, *p, *action_record;
+ bool first;
+ struct _stackmap *stackmap1;
+ _Unwind_Ptr ip1;
+ int ip_before_insn = 0;
+ _sleb128_t index;
+ int size;
+
+ language_specific_data = (const unsigned char *)
+ _Unwind_GetLanguageSpecificData (context);
+
+ /* If no LSDA, then there is no stack maps. */
+ if (! language_specific_data)
+ return NOTFOUND_OK;
+
+ p = parse_lsda_header (context, language_specific_data, &info);
+
+ if (info.TType == NULL)
+ return NOTFOUND_OK;
+
+#ifdef HAVE_GETIPINFO
+ ip1 = _Unwind_GetIPInfo (context, &ip_before_insn);
+#else
+ ip1 = _Unwind_GetIP (context);
+#endif
+ if (! ip_before_insn)
+ --ip1;
+
+ size = value_size (info.ttype_encoding);
+
+ action_record = NULL;
+ first = true;
+
+ /* 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);
+
+ if (first)
+ {
+ // For a Go function, the first entry points to the sentinel value.
+ // Check this here.
+ const unsigned char *p1, *action1;
+ uint64 *x;
+
+ if (!cs_action)
+ return NOTFOUND_OK;
+
+ action1 = info.action_table + cs_action - 1;
+ read_sleb128 (action1, &index);
+ p1 = info.TType - index*size;
+ read_encoded_value (context, info.ttype_encoding, p1, (_Unwind_Ptr*)&x);
+ if (x == NULL || *x != GO_FUNC_SENTINEL)
+ return NOTFOUND_OK;
+
+ first = false;
+ continue;
+ }
+
+ /* The table is sorted, so if we've passed the ip, stop. */
+ if (ip1 < info.Start + cs_start)
+ return NOTFOUND_BAD;
+ else if (ip1 < info.Start + cs_start + cs_len)
+ {
+ if (cs_action)
+ action_record = info.action_table + cs_action - 1;
+ break;
+ }
+ }
+
+ if (action_record == NULL)
+ return NOTFOUND_BAD;
+
+ read_sleb128 (action_record, &index);
+ p = info.TType - index*size;
+ read_encoded_value (context, info.ttype_encoding, p, (_Unwind_Ptr*)&stackmap1);
+ if (stackmap1 == NULL)
+ return NOTFOUND_BAD;
+
+ if (ip != NULL)
+ *ip = ip1;
+ if (sp != NULL)
+ *sp = _Unwind_GetCFA (context);
+ if (stackmap != NULL)
+ *stackmap = stackmap1;
+ return FOUND;
+}
+
+// Callback function to scan a stack frame with stack maps.
+// It skips non-Go functions.
+static _Unwind_Reason_Code
+scanstackwithmap_callback (struct _Unwind_Context *context, void *arg)
+{
+ struct _stackmap *stackmap;
+ _Unwind_Ptr ip, sp;
+ G* gp;
+ void *gcw = arg;
+
+ switch (findstackmaps (context, &ip, &sp, &stackmap))
+ {
+ case NOTFOUND_OK:
+ // Not a Go function. Skip this frame.
+ return _URC_NO_REASON;
+ case NOTFOUND_BAD:
+ {
+ // No stack map found.
+ // If we're scanning from the signal stack, the goroutine
+ // may be not stopped at a safepoint. Allow this case.
+ gp = runtime_g ();
+ if (gp != gp->m->gsignal)
+ {
+ // TODO: print gp, pc, sp
+ runtime_throw ("no stack map");
+ }
+ return _URC_NORMAL_STOP;
+ }
+ case FOUND:
+ break;
+ default:
+ abort ();
+ }
+
+ runtime_scanstackblockwithmap (ip, sp, (uintptr)(stackmap->len) * sizeof(uintptr), stackmap->data, gcw);
+
+ return _URC_NO_REASON;
+}
+
+// Scan the stack with stack maps. Return whether the scan
+// succeeded.
+bool
+scanstackwithmap (void *gcw)
+{
+ _Unwind_Reason_Code code;
+ code = _Unwind_Backtrace (scanstackwithmap_callback, gcw);
+ return code == _URC_END_OF_STACK;
+}
+
+// Returns whether stack map is enabled.
+bool
+usestackmaps ()
+{
+ return runtime_usestackmaps;
+}
+
+// Callback function to probe if a stack frame has stack maps.
+static _Unwind_Reason_Code
+probestackmaps_callback (struct _Unwind_Context *context,
+ void *arg __attribute__ ((unused)))
+{
+ switch (findstackmaps (context, NULL, NULL, NULL))
+ {
+ case NOTFOUND_OK:
+ case NOTFOUND_BAD:
+ return _URC_NO_REASON;
+ case FOUND:
+ break;
+ default:
+ abort ();
+ }
+
+ // Found a stack map. No need to keep unwinding.
+ runtime_usestackmaps = true;
+ return _URC_NORMAL_STOP;
+}
+
+// Try to find a stack map, store the result in global variable runtime_usestackmaps.
+// Called in start-up time from Go code, so there is a Go frame on the stack.
+bool
+probestackmaps ()
+{
+ runtime_usestackmaps = false;
+ _Unwind_Backtrace (probestackmaps_callback, NULL);
+ return runtime_usestackmaps;
+}