libgo: Implement and use runtime.Caller, runtime.Func.FileLine.

R=iant
CC=gofrontend-dev
https://golang.org/cl/5758060
diff --git a/libgo/runtime/go-caller.c b/libgo/runtime/go-caller.c
index b18759f..f2bebeb 100644
--- a/libgo/runtime/go-caller.c
+++ b/libgo/runtime/go-caller.c
@@ -8,8 +8,64 @@
 
 #include <stdint.h>
 
+#include "runtime.h"
 #include "go-string.h"
 
+/* Get the function name, file name, and line number for a PC value.
+   We use the DWARF debug information to get this.  Rather than write
+   a whole new library in C, we use the existing Go library.
+   Unfortunately, the Go library is only available if the debug/elf
+   package is imported (we use debug/elf for both ELF and Mach-O, in
+   this case).  We arrange for the debug/elf package to register
+   itself, and tweak the various packages that need this information
+   to import debug/elf where possible.  */
+
+/* The function that returns function/file/line information.  */
+
+typedef _Bool (*infofn_type) (uintptr_t, struct __go_string *,
+			      struct __go_string *, int *);
+static infofn_type infofn;
+
+/* The function that returns the value of a symbol, used to get the
+   entry address of a function.  */
+
+typedef _Bool (*symvalfn_type) (struct __go_string, uintptr_t *);
+static symvalfn_type symvalfn;
+
+/* This is called by debug/elf to register the function that returns
+   function/file/line information.  */
+
+void RegisterDebugLookup (infofn_type, symvalfn_type)
+  __asm__ ("libgo_runtime.runtime.RegisterDebugLookup");
+
+void
+RegisterDebugLookup (infofn_type pi, symvalfn_type ps)
+{
+  infofn = pi;
+  symvalfn = ps;
+}
+
+/* Return function/file/line information for PC.  */
+
+_Bool
+__go_file_line (uintptr_t pc, struct __go_string *fn, struct __go_string *file,
+		int *line)
+{
+  if (infofn == NULL)
+    return 0;
+  return infofn (pc, fn, file, line);
+}
+
+/* Return the value of a symbol.  */
+
+_Bool
+__go_symbol_value (struct __go_string sym, uintptr_t *val)
+{
+  if (symvalfn == NULL)
+    return 0;
+  return symvalfn (sym, val);
+}
+
 /* The values returned by runtime.Caller.  */
 
 struct caller_ret
@@ -20,32 +76,71 @@
   _Bool ok;
 };
 
-/* Implement runtime.Caller.  */
-
 struct caller_ret Caller (int n) asm ("libgo_runtime.runtime.Caller");
 
+Func *FuncForPC (uintptr_t) asm ("libgo_runtime.runtime.FuncForPC");
+
+/* Implement runtime.Caller.  */
+
 struct caller_ret
-Caller (int n __attribute__ ((unused)))
+Caller (int skip)
 {
   struct caller_ret ret;
+  uintptr pc;
+  int32 n;
+  struct __go_string fn;
 
-  /* A proper implementation needs to dig through the debugging
-     information.  */
-  ret.pc = (uint64_t) (uintptr_t) __builtin_return_address (0);
-  ret.file.__data = NULL;
-  ret.file.__length = 0;
-  ret.line = 0;
-  ret.ok = 0;
-
+  runtime_memclr (&ret, sizeof ret);
+  n = runtime_callers (skip + 1, &pc, 1);
+  if (n < 1)
+    return ret;
+  ret.pc = pc;
+  ret.ok = __go_file_line (pc, &fn, &ret.file, &ret.line);
   return ret;
 }
 
 /* Implement runtime.FuncForPC.  */
 
-void *FuncForPC (uintptr_t) asm ("libgo_runtime.runtime.FuncForPC");
-
-void *
-FuncForPC(uintptr_t pc __attribute__ ((unused)))
+Func *
+FuncForPC (uintptr_t pc)
 {
-  return NULL;
+  Func *ret;
+  struct __go_string fn;
+  struct __go_string file;
+  int line;
+  uintptr_t val;
+
+  if (!__go_file_line (pc, &fn, &file, &line))
+    return NULL;
+  if (!__go_symbol_value (fn, &val))
+    return NULL;
+
+  ret = (Func *) runtime_malloc (sizeof (*ret));
+  ret->name = fn;
+  ret->entry = val;
+  return ret;
+}
+
+/* Look up the file and line information for a PC within a
+   function.  */
+
+struct funcline_go_return
+{
+  struct __go_string retfile;
+  int retline;
+};
+
+struct funcline_go_return
+runtime_funcline_go (Func *f, uintptr targetpc)
+  __asm__ ("libgo_runtime.runtime.funcline_go");
+
+struct funcline_go_return
+runtime_funcline_go (Func *f __attribute__((unused)), uintptr targetpc)
+{
+  struct funcline_go_return ret;
+  struct __go_string fn;
+
+  if (!__go_file_line (targetpc, &fn, &ret.retfile,  &ret.retline))
+    runtime_memclr (&ret, sizeof ret);
+  return ret;
 }