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

R=iant
CC=gofrontend-dev
https://golang.org/cl/5758060
diff --git a/libgo/Makefile.am b/libgo/Makefile.am
index d43f054..99294f1 100644
--- a/libgo/Makefile.am
+++ b/libgo/Makefile.am
@@ -1024,12 +1024,14 @@
 	go/debug/dwarf/buf.go \
 	go/debug/dwarf/const.go \
 	go/debug/dwarf/entry.go \
+	go/debug/dwarf/line.go \
 	go/debug/dwarf/open.go \
 	go/debug/dwarf/type.go \
 	go/debug/dwarf/unit.go
 go_debug_elf_files = \
 	go/debug/elf/elf.go \
-	go/debug/elf/file.go
+	go/debug/elf/file.go \
+	go/debug/elf/runtime.go
 go_debug_gosym_files = \
 	go/debug/gosym/pclntab.go \
 	go/debug/gosym/symtab.go
diff --git a/libgo/Makefile.in b/libgo/Makefile.in
index 418c787..b57d929 100644
--- a/libgo/Makefile.in
+++ b/libgo/Makefile.in
@@ -1340,13 +1340,15 @@
 	go/debug/dwarf/buf.go \
 	go/debug/dwarf/const.go \
 	go/debug/dwarf/entry.go \
+	go/debug/dwarf/line.go \
 	go/debug/dwarf/open.go \
 	go/debug/dwarf/type.go \
 	go/debug/dwarf/unit.go
 
 go_debug_elf_files = \
 	go/debug/elf/elf.go \
-	go/debug/elf/file.go
+	go/debug/elf/file.go \
+	go/debug/elf/runtime.go
 
 go_debug_gosym_files = \
 	go/debug/gosym/pclntab.go \
diff --git a/libgo/go/debug/dwarf/const.go b/libgo/go/debug/dwarf/const.go
index 918b153..5301edc 100644
--- a/libgo/go/debug/dwarf/const.go
+++ b/libgo/go/debug/dwarf/const.go
@@ -431,3 +431,30 @@
 	encUnsignedChar   = 0x08
 	encImaginaryFloat = 0x09
 )
+
+// Line number opcodes.
+const (
+	LineExtendedOp     = 0
+	LineCopy           = 1
+	LineAdvancePC      = 2
+	LineAdvanceLine    = 3
+	LineSetFile        = 4
+	LineSetColumn      = 5
+	LineNegateStmt     = 6
+	LineSetBasicBlock  = 7
+	LineConstAddPC     = 8
+	LineFixedAdvancePC = 9
+	// next 3 are DWARF 3
+	LineSetPrologueEnd   = 10
+	LineSetEpilogueBegin = 11
+	LineSetISA           = 12
+)
+
+// Line number extended opcodes.
+const (
+	LineExtEndSequence = 1
+	LineExtSetAddress  = 2
+	LineExtDefineFile  = 3
+	// next 1 is DWARF 4
+	LineExtSetDiscriminator = 4
+)
diff --git a/libgo/go/debug/dwarf/entry.go b/libgo/go/debug/dwarf/entry.go
index 2885d8f..f9a4c1b 100644
--- a/libgo/go/debug/dwarf/entry.go
+++ b/libgo/go/debug/dwarf/entry.go
@@ -246,6 +246,15 @@
 	return r
 }
 
+// unitReader returns a new reader starting at a specific unit.
+func (d *Data) unitReader(i int) *Reader {
+	r := &Reader{d: d}
+	r.unit = i
+	u := &d.unit[i]
+	r.b = makeBuf(d, "info", u.off, u.data, u.addrsize)
+	return r
+}
+
 // Seek positions the Reader at offset off in the encoded entry stream.
 // Offset 0 can be used to denote the first entry.
 func (r *Reader) Seek(off Offset) {
diff --git a/libgo/go/debug/dwarf/line.go b/libgo/go/debug/dwarf/line.go
new file mode 100644
index 0000000..091ebe0
--- /dev/null
+++ b/libgo/go/debug/dwarf/line.go
@@ -0,0 +1,416 @@
+// Copyright 2012 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.
+
+// DWARF line number information.
+
+package dwarf
+
+import (
+	"errors"
+	"path/filepath"
+	"sort"
+	"strconv"
+)
+
+// A Line holds all the available information about the source code
+// corresponding to a specific program counter address.
+type Line struct {
+	Filename      string // source file name
+	OpIndex       int    // index of operation in VLIW instruction
+	Line          int    // line number
+	Column        int    // column number
+	ISA           int    // instruction set code
+	Discriminator int    // block discriminator
+	Stmt          bool   // instruction starts statement
+	Block         bool   // instruction starts basic block
+	EndPrologue   bool   // instruction ends function prologue
+	BeginEpilogue bool   // instruction begins function epilogue
+}
+
+// LineForPc returns the line number information for a program counter
+// address, if any.  When this returns multiple Line structures in a
+// context where only one can be used, the last one is the best.
+func (d *Data) LineForPC(pc uint64) ([]*Line, error) {
+	for i := range d.unit {
+		u := &d.unit[i]
+		if u.pc == nil {
+			if err := d.readUnitLine(i, u); err != nil {
+				return nil, err
+			}
+		}
+		for _, ar := range u.pc {
+			if pc >= ar.low && pc < ar.high {
+				return d.findLine(u, pc)
+			}
+		}
+	}
+	return nil, nil
+}
+
+// readUnitLine reads in the line number information for a compilation
+// unit.
+func (d *Data) readUnitLine(i int, u *unit) error {
+	r := d.unitReader(i)
+	setLineOff := false
+	for {
+		e, err := r.Next()
+		if err != nil {
+			return err
+		}
+		if e == nil {
+			break
+		}
+		if r.unit != i {
+			break
+		}
+		switch e.Tag {
+		case TagCompileUnit, TagSubprogram, TagEntryPoint, TagInlinedSubroutine:
+			low, lowok := e.Val(AttrLowpc).(uint64)
+			high, highok := e.Val(AttrHighpc).(uint64)
+			if lowok && highok {
+				u.pc = append(u.pc, addrRange{low, high})
+			} else if f, ok := e.Val(AttrRanges).(Offset); ok {
+				// TODO: Handle AttrRanges and .debug_ranges.
+				_ = f
+			}
+			if off, ok := e.Val(AttrStmtList).(int64); ok {
+				u.lineoff = Offset(off)
+				setLineOff = true
+			}
+			if dir, ok := e.Val(AttrCompDir).(string); ok {
+				u.dir = dir
+			}
+		}
+	}
+	if !setLineOff {
+		u.lineoff = Offset(0)
+		u.lineoff--
+	}
+	return nil
+}
+
+// findLine finds the line information for a PC value, given the unit
+// containing the information.
+func (d *Data) findLine(u *unit, pc uint64) ([]*Line, error) {
+	if u.lines == nil {
+		if err := d.parseLine(u); err != nil {
+			return nil, err
+		}
+	}
+
+	for _, ln := range u.lines {
+		if pc < ln.addrs[0].pc || pc > ln.addrs[len(ln.addrs)-1].pc {
+			continue
+		}
+		i := sort.Search(len(ln.addrs),
+			func(i int) bool { return ln.addrs[i].pc > pc })
+		i--
+		p := new(Line)
+		*p = ln.line
+		p.Line = ln.addrs[i].line
+		ret := []*Line{p}
+		for i++; i < len(ln.addrs) && ln.addrs[i].pc == pc; i++ {
+			p = new(Line)
+			*p = ln.line
+			p.Line = ln.addrs[i].line
+			ret = append(ret, p)
+		}
+		return ret, nil
+	}
+
+	return nil, nil
+}
+
+// FileLine returns the file name and line number for a program
+// counter address, or "", 0 if unknown.
+func (d *Data) FileLine(pc uint64) (string, int, error) {
+	r, err := d.LineForPC(pc)
+	if err != nil {
+		return "", 0, err
+	}
+	if r == nil {
+		return "", 0, nil
+	}
+	ln := r[len(r)-1]
+	return ln.Filename, ln.Line, nil
+}
+
+// A mapLineInfo holds the PC values and line numbers associated with
+// a single Line structure.  This representation is chosen to reduce
+// memory usage based on typical debug info.
+type mapLineInfo struct {
+	line  Line      // line.Line will be zero
+	addrs lineAddrs // sorted by PC
+}
+
+// A list of lines.  This will be sorted by PC.
+type lineAddrs []oneLineInfo
+
+func (p lineAddrs) Len() int           { return len(p) }
+func (p lineAddrs) Less(i, j int) bool { return p[i].pc < p[j].pc }
+func (p lineAddrs) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+
+// A oneLineInfo is a single PC and line number.
+type oneLineInfo struct {
+	pc   uint64
+	line int
+}
+
+// A lineHdr holds the relevant information from a line number
+// program header.
+type lineHdr struct {
+	version       uint16   // version of line number encoding
+	minInsnLen    uint8    // minimum instruction length
+	maxOpsPerInsn uint8    // maximum number of ops per instruction
+	defStmt       bool     // initial value of stmt register
+	lineBase      int8     // line adjustment base
+	lineRange     uint8    // line adjustment step
+	opBase        uint8    // base of special opcode values
+	opLen         []uint8  // lengths of standard opcodes
+	dirs          []string // directories
+	files         []string // file names
+}
+
+// parseLine parses the line number information for a compilation unit
+func (d *Data) parseLine(u *unit) error {
+	if u.lineoff+1 == 0 {
+		return errors.New("unknown line offset")
+	}
+	b := makeBuf(d, "line", u.lineoff, d.line, u.addrsize)
+	len := uint64(b.uint32())
+	offSize := 4
+	if len == 0xffffffff {
+		len = b.uint64()
+		offSize = 8
+	}
+	end := b.off + Offset(len)
+	hdr := d.parseLineHdr(u, &b, offSize)
+	if b.err == nil {
+		d.parseLineProgram(u, &b, hdr, end)
+	}
+	return b.err
+}
+
+// parseLineHdr parses a line number program header.
+func (d *Data) parseLineHdr(u *unit, b *buf, offSize int) (hdr lineHdr) {
+	hdr.version = b.uint16()
+	if hdr.version < 2 || hdr.version > 4 {
+		b.error("unsupported DWARF version " + strconv.Itoa(int(hdr.version)))
+		return
+	}
+
+	b.bytes(offSize) // header length
+
+	hdr.minInsnLen = b.uint8()
+	if hdr.version < 4 {
+		hdr.maxOpsPerInsn = 1
+	} else {
+		hdr.maxOpsPerInsn = b.uint8()
+	}
+
+	if b.uint8() == 0 {
+		hdr.defStmt = false
+	} else {
+		hdr.defStmt = true
+	}
+	hdr.lineBase = int8(b.uint8())
+	hdr.lineRange = b.uint8()
+	hdr.opBase = b.uint8()
+	hdr.opLen = b.bytes(int(hdr.opBase - 1))
+
+	for d := b.string(); len(d) > 0; d = b.string() {
+		hdr.dirs = append(hdr.dirs, d)
+	}
+
+	for f := b.string(); len(f) > 0; f = b.string() {
+		d := b.uint()
+		if !filepath.IsAbs(f) {
+			if d > 0 {
+				if d > uint64(len(hdr.dirs)) {
+					b.error("DWARF directory index out of range")
+					return
+				}
+				f = filepath.Join(hdr.dirs[d-1], f)
+			} else if u.dir != "" {
+				f = filepath.Join(u.dir, f)
+			}
+		}
+		b.uint() // file's last mtime
+		b.uint() // file length
+		hdr.files = append(hdr.files, f)
+	}
+
+	return
+}
+
+// parseLineProgram parses a line program, adding information to
+// d.lineInfo as it goes.
+func (d *Data) parseLineProgram(u *unit, b *buf, hdr lineHdr, end Offset) {
+	address := uint64(0)
+	line := 1
+	resetLineInfo := Line{
+		Filename:      "",
+		OpIndex:       0,
+		Line:          0,
+		Column:        0,
+		ISA:           0,
+		Discriminator: 0,
+		Stmt:          hdr.defStmt,
+		Block:         false,
+		EndPrologue:   false,
+		BeginEpilogue: false,
+	}
+	if len(hdr.files) > 0 {
+		resetLineInfo.Filename = hdr.files[0]
+	}
+	lineInfo := resetLineInfo
+
+	var lines []mapLineInfo
+
+	minInsnLen := uint64(hdr.minInsnLen)
+	maxOpsPerInsn := uint64(hdr.maxOpsPerInsn)
+	lineBase := int(hdr.lineBase)
+	lineRange := hdr.lineRange
+	newLineInfo := true
+	for b.off < end && b.err == nil {
+		op := b.uint8()
+		if op >= hdr.opBase {
+			// This is a special opcode.
+			op -= hdr.opBase
+			advance := uint64(op / hdr.lineRange)
+			opIndex := uint64(lineInfo.OpIndex)
+			address += minInsnLen * ((opIndex + advance) / maxOpsPerInsn)
+			newOpIndex := int((opIndex + advance) % maxOpsPerInsn)
+			line += lineBase + int(op%lineRange)
+			if newOpIndex != lineInfo.OpIndex {
+				lineInfo.OpIndex = newOpIndex
+				newLineInfo = true
+			}
+			lines, lineInfo, newLineInfo = d.addLine(lines, lineInfo, address, line, newLineInfo)
+		} else if op == LineExtendedOp {
+			c := b.uint()
+			op = b.uint8()
+			switch op {
+			case LineExtEndSequence:
+				u.lines = append(u.lines, lines...)
+				lineInfo = resetLineInfo
+				lines = nil
+			case LineExtSetAddress:
+				address = b.addr()
+			case LineExtDefineFile:
+				f := b.string()
+				d := b.uint()
+				b.uint() // mtime
+				b.uint() // length
+				if d > 0 && !filepath.IsAbs(f) {
+					if d >= uint64(len(hdr.dirs)) {
+						b.error("DWARF directory index out of range")
+						return
+					}
+					f = filepath.Join(hdr.dirs[d-1], f)
+				}
+				hdr.files = append(hdr.files, f)
+			case LineExtSetDiscriminator:
+				lineInfo.Discriminator = int(b.uint())
+				newLineInfo = true
+			default:
+				if c > 0 {
+					b.bytes(int(c) - 1)
+				}
+			}
+		} else {
+			switch op {
+			case LineCopy:
+				lines, lineInfo, newLineInfo = d.addLine(lines, lineInfo, address, line, newLineInfo)
+			case LineAdvancePC:
+				advance := b.uint()
+				opIndex := uint64(lineInfo.OpIndex)
+				address += minInsnLen * ((opIndex + advance) / maxOpsPerInsn)
+				newOpIndex := int((opIndex + advance) % maxOpsPerInsn)
+				if newOpIndex != lineInfo.OpIndex {
+					lineInfo.OpIndex = newOpIndex
+					newLineInfo = true
+				}
+			case LineAdvanceLine:
+				line += int(b.int())
+			case LineSetFile:
+				i := b.uint()
+				if i > uint64(len(hdr.files)) {
+					b.error("DWARF file number out of range")
+					return
+				}
+				lineInfo.Filename = hdr.files[i]
+				newLineInfo = true
+			case LineSetColumn:
+				lineInfo.Column = int(b.uint())
+				newLineInfo = true
+			case LineNegateStmt:
+				lineInfo.Stmt = !lineInfo.Stmt
+				newLineInfo = true
+			case LineSetBasicBlock:
+				lineInfo.Block = true
+				newLineInfo = true
+			case LineConstAddPC:
+				op = 255 - hdr.opBase
+				advance := uint64(op / hdr.lineRange)
+				opIndex := uint64(lineInfo.OpIndex)
+				address += minInsnLen * ((opIndex + advance) / maxOpsPerInsn)
+				newOpIndex := int((opIndex + advance) % maxOpsPerInsn)
+				if newOpIndex != lineInfo.OpIndex {
+					lineInfo.OpIndex = newOpIndex
+					newLineInfo = true
+				}
+			case LineFixedAdvancePC:
+				address += uint64(b.uint16())
+				if lineInfo.OpIndex != 0 {
+					lineInfo.OpIndex = 0
+					newLineInfo = true
+				}
+			case LineSetPrologueEnd:
+				lineInfo.EndPrologue = true
+				newLineInfo = true
+			case LineSetEpilogueBegin:
+				lineInfo.BeginEpilogue = true
+				newLineInfo = true
+			case LineSetISA:
+				lineInfo.ISA = int(b.uint())
+				newLineInfo = true
+			default:
+				if int(op) >= len(hdr.opLen) {
+					b.error("DWARF line opcode has unknown length")
+					return
+				}
+				for i := hdr.opLen[op-1]; i > 0; i-- {
+					b.int()
+				}
+			}
+		}
+	}
+}
+
+// addLine adds the current address and line to lines using lineInfo.
+// If newLineInfo is true this is a new lineInfo.  This returns the
+// updated lines, lineInfo, and newLineInfo.
+func (d *Data) addLine(lines []mapLineInfo, lineInfo Line, address uint64, line int, newLineInfo bool) ([]mapLineInfo, Line, bool) {
+	if newLineInfo {
+		if len(lines) > 0 {
+			sort.Sort(lines[len(lines)-1].addrs)
+		}
+		lines = append(lines, mapLineInfo{line: lineInfo})
+	}
+	p := &lines[len(lines)-1]
+	p.addrs = append(p.addrs, oneLineInfo{address, line})
+
+	if lineInfo.Block || lineInfo.EndPrologue || lineInfo.BeginEpilogue || lineInfo.Discriminator != 0 {
+		lineInfo.Block = false
+		lineInfo.EndPrologue = false
+		lineInfo.BeginEpilogue = false
+		lineInfo.Discriminator = 0
+		newLineInfo = true
+	} else {
+		newLineInfo = false
+	}
+
+	return lines, lineInfo, newLineInfo
+}
diff --git a/libgo/go/debug/dwarf/line_test.go b/libgo/go/debug/dwarf/line_test.go
new file mode 100644
index 0000000..2476a6f
--- /dev/null
+++ b/libgo/go/debug/dwarf/line_test.go
@@ -0,0 +1,53 @@
+// Copyright 2012 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.
+
+package dwarf_test
+
+import (
+	. "debug/dwarf"
+	"path/filepath"
+	"testing"
+)
+
+type lineTest struct {
+	pc   uint64
+	file string
+	line int
+}
+
+var elfLineTests = [...]lineTest{
+	{0x4004c4, "typedef.c", 83},
+	{0x4004c8, "typedef.c", 84},
+	{0x4004ca, "typedef.c", 84},
+	{0x4003e0, "", 0},
+}
+
+var machoLineTests = [...]lineTest{
+	{0x0, "typedef.c", 83},
+}
+
+func TestLineElf(t *testing.T) {
+	testLine(t, elfData(t, "testdata/typedef.elf"), elfLineTests[:], "elf")
+}
+
+func TestLineMachO(t *testing.T) {
+	testLine(t, machoData(t, "testdata/typedef.macho"), machoLineTests[:], "macho")
+}
+
+func testLine(t *testing.T, d *Data, tests []lineTest, kind string) {
+	for _, v := range tests {
+		file, line, err := d.FileLine(v.pc)
+		if err != nil {
+			t.Errorf("%s: %v", kind, err)
+			continue
+		}
+		if file != "" {
+			file = filepath.Base(file)
+		}
+		if file != v.file || line != v.line {
+			t.Errorf("%s: for %d have %q:%d want %q:%d",
+				kind, v.pc, file, line, v.file, v.line)
+		}
+	}
+}
diff --git a/libgo/go/debug/dwarf/unit.go b/libgo/go/debug/dwarf/unit.go
index c10d75d..931468a 100644
--- a/libgo/go/debug/dwarf/unit.go
+++ b/libgo/go/debug/dwarf/unit.go
@@ -12,9 +12,19 @@
 type unit struct {
 	base     Offset // byte offset of header within the aggregate info
 	off      Offset // byte offset of data within the aggregate info
+	lineoff  Offset // byte offset of data within the line info
 	data     []byte
 	atable   abbrevTable
 	addrsize int
+	dir      string
+	pc       []addrRange   // PC ranges in this compilation unit
+	lines    []mapLineInfo // PC -> line mapping
+}
+
+// A range is an address range.
+type addrRange struct {
+	low  uint64
+	high uint64
 }
 
 func (d *Data) parseUnits() ([]unit, error) {
diff --git a/libgo/go/debug/elf/elf_test.go b/libgo/go/debug/elf/elf_test.go
index 67b961b..b8cdbcc 100644
--- a/libgo/go/debug/elf/elf_test.go
+++ b/libgo/go/debug/elf/elf_test.go
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package elf
+package elf_test
 
 import (
+	. "debug/elf"
 	"fmt"
 	"testing"
 )
diff --git a/libgo/go/debug/elf/file.go b/libgo/go/debug/elf/file.go
index 184ca83..c2c03d2 100644
--- a/libgo/go/debug/elf/file.go
+++ b/libgo/go/debug/elf/file.go
@@ -563,7 +563,7 @@
 	// There are many other DWARF sections, but these
 	// are the required ones, and the debug/dwarf package
 	// does not use the others, so don't bother loading them.
-	var names = [...]string{"abbrev", "info", "str"}
+	var names = [...]string{"abbrev", "info", "line", "str"}
 	var dat [len(names)][]byte
 	for i, name := range names {
 		name = ".debug_" + name
@@ -592,8 +592,8 @@
 		}
 	}
 
-	abbrev, info, str := dat[0], dat[1], dat[2]
-	return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)
+	abbrev, info, line, str := dat[0], dat[1], dat[2], dat[3]
+	return dwarf.New(abbrev, nil, nil, info, line, nil, nil, str)
 }
 
 // Symbols returns the symbol table for f.
diff --git a/libgo/go/debug/elf/file_test.go b/libgo/go/debug/elf/file_test.go
index 98f2723..105b697 100644
--- a/libgo/go/debug/elf/file_test.go
+++ b/libgo/go/debug/elf/file_test.go
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package elf
+package elf_test
 
 import (
 	"debug/dwarf"
+	. "debug/elf"
 	"encoding/binary"
 	"net"
 	"os"
diff --git a/libgo/go/debug/elf/runtime.go b/libgo/go/debug/elf/runtime.go
new file mode 100644
index 0000000..23e79bf
--- /dev/null
+++ b/libgo/go/debug/elf/runtime.go
@@ -0,0 +1,161 @@
+// Copyright 2012 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.
+
+// This is gccgo-specific code that uses DWARF information to fetch
+// file/line information for PC values.  This package registers itself
+// with the runtime package.
+
+package elf
+
+import (
+	"debug/dwarf"
+	"debug/macho"
+	"os"
+	"runtime"
+	"sort"
+	"sync"
+)
+
+func init() {
+	// Register our lookup functions with the runtime package.
+	runtime.RegisterDebugLookup(funcFileLine, symbolValue)
+}
+
+// The file struct holds information for a specific file that is part
+// of the execution.
+type file struct {
+	elf   *File       // If ELF
+	macho *macho.File // If Mach-O
+	dwarf *dwarf.Data // DWARF information
+
+	symsByName []sym // Sorted by name
+	symsByAddr []sym // Sorted by address
+}
+
+// Sort symbols by name.
+type symsByName []sym
+
+func (s symsByName) Len() int           { return len(s) }
+func (s symsByName) Less(i, j int) bool { return s[i].name < s[j].name }
+func (s symsByName) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
+
+// Sort symbols by address.
+type symsByAddr []sym
+
+func (s symsByAddr) Len() int           { return len(s) }
+func (s symsByAddr) Less(i, j int) bool { return s[i].addr < s[j].addr }
+func (s symsByAddr) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
+
+// The sym structure holds the information we care about for a symbol,
+// namely name and address.
+type sym struct {
+	name string
+	addr uintptr
+}
+
+// Open an input file.
+func open(name string) (*file, error) {
+	efile, err := Open(name)
+	var mfile *macho.File
+	if err != nil {
+		var merr error
+		mfile, merr = macho.Open(name)
+		if merr != nil {
+			return nil, err
+		}
+	}
+
+	r := &file{elf: efile, macho: mfile}
+
+	if efile != nil {
+		r.dwarf, err = efile.DWARF()
+	} else {
+		r.dwarf, err = mfile.DWARF()
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	var syms []sym
+	if efile != nil {
+		esyms, err := efile.Symbols()
+		if err != nil {
+			return nil, err
+		}
+		syms = make([]sym, 0, len(esyms))
+		for _, s := range esyms {
+			if ST_TYPE(s.Info) == STT_FUNC {
+				syms = append(syms, sym{s.Name, uintptr(s.Value)})
+			}
+		}
+	} else {
+		syms = make([]sym, 0, len(mfile.Symtab.Syms))
+		for _, s := range mfile.Symtab.Syms {
+			syms = append(syms, sym{s.Name, uintptr(s.Value)})
+		}
+	}
+
+	r.symsByName = make([]sym, len(syms))
+	copy(r.symsByName, syms)
+	sort.Sort(symsByName(r.symsByName))
+
+	r.symsByAddr = syms
+	sort.Sort(symsByAddr(r.symsByAddr))
+
+	return r, nil
+}
+
+// The main executable
+var executable *file
+
+// Only open the executable once.
+var executableOnce sync.Once
+
+func openExecutable() {
+	executableOnce.Do(func() {
+		f, err := open("/proc/self/exe")
+		if err != nil {
+			f, err = open(os.Args[0])
+			if err != nil {
+				return
+			}
+		}
+		executable = f
+	})
+}
+
+// The funcFileLine function looks up the function name, file name,
+// and line number for a PC value.
+func funcFileLine(pc uintptr, function *string, file *string, line *int) bool {
+	openExecutable()
+	if executable.dwarf == nil {
+		return false
+	}
+	f, ln, err := executable.dwarf.FileLine(uint64(pc))
+	if err != nil {
+		return false
+	}
+	*file = f
+	*line = ln
+
+	*function = ""
+	if len(executable.symsByAddr) > 0 && pc >= executable.symsByAddr[0].addr {
+		i := sort.Search(len(executable.symsByAddr),
+			func(i int) bool { return executable.symsByAddr[i].addr > pc })
+		*function = executable.symsByAddr[i-1].name
+	}
+
+	return true
+}
+
+// The symbolValue function fetches the value of a symbol.
+func symbolValue(name string, val *uintptr) bool {
+	i := sort.Search(len(executable.symsByName),
+		func(i int) bool { return executable.symsByName[i].name >= name })
+	if i >= len(executable.symsByName) || executable.symsByName[i].name != name {
+		return false
+	}
+	*val = executable.symsByName[i].addr
+	return true
+}
diff --git a/libgo/go/debug/macho/file.go b/libgo/go/debug/macho/file.go
index fa73a31..6577803 100644
--- a/libgo/go/debug/macho/file.go
+++ b/libgo/go/debug/macho/file.go
@@ -467,7 +467,7 @@
 	// There are many other DWARF sections, but these
 	// are the required ones, and the debug/dwarf package
 	// does not use the others, so don't bother loading them.
-	var names = [...]string{"abbrev", "info", "str"}
+	var names = [...]string{"abbrev", "info", "line", "str"}
 	var dat [len(names)][]byte
 	for i, name := range names {
 		name = "__debug_" + name
@@ -482,8 +482,8 @@
 		dat[i] = b
 	}
 
-	abbrev, info, str := dat[0], dat[1], dat[2]
-	return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)
+	abbrev, info, line, str := dat[0], dat[1], dat[2], dat[3]
+	return dwarf.New(abbrev, nil, nil, info, line, nil, nil, str)
 }
 
 // ImportedSymbols returns the names of all symbols
diff --git a/libgo/go/debug/macho/file_test.go b/libgo/go/debug/macho/file_test.go
index 640225b..ecc6f68 100644
--- a/libgo/go/debug/macho/file_test.go
+++ b/libgo/go/debug/macho/file_test.go
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package macho
+package macho_test
 
 import (
+	. "debug/macho"
 	"reflect"
 	"testing"
 )
diff --git a/libgo/go/encoding/binary/binary_test.go b/libgo/go/encoding/binary/binary_test.go
index ff361b7..dec47a1 100644
--- a/libgo/go/encoding/binary/binary_test.go
+++ b/libgo/go/encoding/binary/binary_test.go
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package binary
+package binary_test
 
 import (
 	"bytes"
+	. "encoding/binary"
 	"io"
 	"math"
 	"reflect"
@@ -187,7 +188,7 @@
 	bsr := &byteSliceReader{}
 	var buf bytes.Buffer
 	Write(&buf, BigEndian, &s)
-	n := dataSize(reflect.ValueOf(s))
+	n := DataSize(reflect.ValueOf(s))
 	b.SetBytes(int64(n))
 	t := s
 	b.ResetTimer()
diff --git a/libgo/go/encoding/binary/export_test.go b/libgo/go/encoding/binary/export_test.go
new file mode 100644
index 0000000..9eae2a9
--- /dev/null
+++ b/libgo/go/encoding/binary/export_test.go
@@ -0,0 +1,15 @@
+// Copyright 2012 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.
+
+package binary
+
+import "reflect"
+
+// Export for testing.
+
+func DataSize(v reflect.Value) int {
+	return dataSize(v)
+}
+
+var Overflow = overflow
diff --git a/libgo/go/encoding/binary/varint_test.go b/libgo/go/encoding/binary/varint_test.go
index 9476bd5..f67ca63 100644
--- a/libgo/go/encoding/binary/varint_test.go
+++ b/libgo/go/encoding/binary/varint_test.go
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package binary
+package binary_test
 
 import (
 	"bytes"
+	. "encoding/binary"
 	"io"
 	"testing"
 )
@@ -134,8 +135,8 @@
 }
 
 func TestOverflow(t *testing.T) {
-	testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x2}, -10, overflow)
-	testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x1, 0, 0}, -13, overflow)
+	testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x2}, -10, Overflow)
+	testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x1, 0, 0}, -13, Overflow)
 }
 
 func TestNonCanonicalZero(t *testing.T) {
diff --git a/libgo/go/log/log.go b/libgo/go/log/log.go
index a5d88fd..02a407e 100644
--- a/libgo/go/log/log.go
+++ b/libgo/go/log/log.go
@@ -14,6 +14,7 @@
 
 import (
 	"bytes"
+	_ "debug/elf"
 	"fmt"
 	"io"
 	"os"
diff --git a/libgo/go/log/log_test.go b/libgo/go/log/log_test.go
index 72ebf39..158c3d9 100644
--- a/libgo/go/log/log_test.go
+++ b/libgo/go/log/log_test.go
@@ -17,9 +17,9 @@
 	Rdate         = `[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]`
 	Rtime         = `[0-9][0-9]:[0-9][0-9]:[0-9][0-9]`
 	Rmicroseconds = `\.[0-9][0-9][0-9][0-9][0-9][0-9]`
-	Rline         = `[0-9]+:` // must update if the calls to l.Printf / l.Print below move
-	Rlongfile     = `.*/[A-Za-z0-9_\-]+\.go:|\?\?\?:` + Rline
-	Rshortfile    = `[A-Za-z0-9_\-]+\.go:|\?\?\?:` + Rline
+	Rline         = `(54|56):` // must update if the calls to l.Printf / l.Print below move
+	Rlongfile     = `.*/[A-Za-z0-9_\-]+\.go:` + Rline
+	Rshortfile    = `[A-Za-z0-9_\-]+\.go:` + Rline
 )
 
 type tester struct {
diff --git a/libgo/go/net/http/pprof/pprof.go b/libgo/go/net/http/pprof/pprof.go
index 06fcde1..b8874f3 100644
--- a/libgo/go/net/http/pprof/pprof.go
+++ b/libgo/go/net/http/pprof/pprof.go
@@ -35,6 +35,7 @@
 import (
 	"bufio"
 	"bytes"
+	_ "debug/elf"
 	"fmt"
 	"html/template"
 	"io"
diff --git a/libgo/go/net/ip_test.go b/libgo/go/net/ip_test.go
index df647ef..d0e987b 100644
--- a/libgo/go/net/ip_test.go
+++ b/libgo/go/net/ip_test.go
@@ -6,6 +6,7 @@
 
 import (
 	"bytes"
+	_ "debug/elf"
 	"reflect"
 	"runtime"
 	"testing"
diff --git a/libgo/go/runtime/debug/stack.go b/libgo/go/runtime/debug/stack.go
index a533a5c..fc74e53 100644
--- a/libgo/go/runtime/debug/stack.go
+++ b/libgo/go/runtime/debug/stack.go
@@ -8,6 +8,7 @@
 
 import (
 	"bytes"
+	_ "debug/elf"
 	"fmt"
 	"io/ioutil"
 	"os"
diff --git a/libgo/go/runtime/extern.go b/libgo/go/runtime/extern.go
index 5fbfe54..2c097b0 100644
--- a/libgo/go/runtime/extern.go
+++ b/libgo/go/runtime/extern.go
@@ -32,16 +32,8 @@
 func Callers(skip int, pc []uintptr) int
 
 type Func struct { // Keep in sync with runtime.h:struct Func
-	name   string
-	typ    string  // go type string
-	src    string  // src file name
-	pcln   []byte  // pc/ln tab for this func
-	entry  uintptr // entry pc
-	pc0    uintptr // starting pc, ln for table
-	ln0    int32
-	frame  int32 // stack frame size
-	args   int32 // number of 32-bit in/out args
-	locals int32 // number of 32-bit locals
+	name  string
+	entry uintptr // entry pc
 }
 
 // FuncForPC returns a *Func describing the function that contains the
@@ -65,6 +57,10 @@
 // implemented in symtab.c
 func funcline_go(*Func, uintptr) (string, int)
 
+// A gccgo specific hook to use debug info to get file/line info.
+func RegisterDebugLookup(func(pc uintptr, function *string, file *string, line *int) bool,
+	func(sym string, val *uintptr) bool)
+
 // mid returns the current os thread (m) id.
 func mid() uint32
 
diff --git a/libgo/go/runtime/pprof/pprof.go b/libgo/go/runtime/pprof/pprof.go
index f67e8a8..87f17d2 100644
--- a/libgo/go/runtime/pprof/pprof.go
+++ b/libgo/go/runtime/pprof/pprof.go
@@ -11,6 +11,7 @@
 import (
 	"bufio"
 	"bytes"
+	_ "debug/elf"
 	"fmt"
 	"io"
 	"runtime"
diff --git a/libgo/go/testing/testing.go b/libgo/go/testing/testing.go
index 477d2ac..7072262 100644
--- a/libgo/go/testing/testing.go
+++ b/libgo/go/testing/testing.go
@@ -79,6 +79,7 @@
 package testing
 
 import (
+	_ "debug/elf"
 	"flag"
 	"fmt"
 	"os"
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;
 }
diff --git a/libgo/runtime/go-callers.c b/libgo/runtime/go-callers.c
index 0089c67..09556c3 100644
--- a/libgo/runtime/go-callers.c
+++ b/libgo/runtime/go-callers.c
@@ -25,8 +25,13 @@
 {
   struct callers_data *arg = (struct callers_data *) varg;
   uintptr pc;
+  int ip_before_insn = 0;
 
+#ifdef HAVE_GETIPINFO
+  pc = _Unwind_GetIPInfo (context, &ip_before_insn);
+#else
   pc = _Unwind_GetIP (context);
+#endif
 
   /* FIXME: If PC is in the __morestack routine, we should ignore
      it.  */
@@ -37,6 +42,11 @@
     return _URC_END_OF_STACK;
   else
     {
+      /* Here PC will be the return address.  We actually want the
+	 address of the call instruction, so back up one byte and
+	 count on the lookup routines handling that correctly.  */
+      if (!ip_before_insn)
+	--pc;
       arg->pcbuf[arg->index] = pc;
       ++arg->index;
     }
@@ -48,7 +58,7 @@
 {
   struct callers_data arg;
 
-  arg.skip = skip;
+  arg.skip = skip + 1;
   arg.pcbuf = pcbuf;
   arg.index = 0;
   arg.max = m;
diff --git a/libgo/runtime/mprof.goc b/libgo/runtime/mprof.goc
index e40dd61..c61c65c 100644
--- a/libgo/runtime/mprof.goc
+++ b/libgo/runtime/mprof.goc
@@ -225,11 +225,7 @@
 		return;
 
 	m->nomemprof++;
-#if 0
 	nstk = runtime_callers(1, stk, 32);
-#else
-	nstk = 0;
-#endif
 	runtime_lock(&proflock);
 	b = stkbucket(stk, nstk, true);
 	b->recent_allocs++;
diff --git a/libgo/runtime/runtime.c b/libgo/runtime/runtime.c
index 78c865b..d1ce26d 100644
--- a/libgo/runtime/runtime.c
+++ b/libgo/runtime/runtime.c
@@ -211,22 +211,3 @@
   return 0;
 #endif
 }
-
-struct funcline_go_return
-{
-  String retfile;
-  int32 retline;
-};
-
-struct funcline_go_return
-runtime_funcline_go(void *f, uintptr targetpc)
-  __asm__("libgo_runtime.runtime.funcline_go");
-
-struct funcline_go_return
-runtime_funcline_go(void *f __attribute__((unused)),
-		    uintptr targetpc __attribute__((unused)))
-{
-  struct funcline_go_return ret;
-  runtime_memclr(&ret, sizeof ret);
-  return ret;
-}
diff --git a/libgo/runtime/runtime.h b/libgo/runtime/runtime.h
index 40c59a8..e012e18 100644
--- a/libgo/runtime/runtime.h
+++ b/libgo/runtime/runtime.h
@@ -48,6 +48,7 @@
 
 typedef	uint8			bool;
 typedef	uint8			byte;
+typedef	struct	Func		Func;
 typedef	struct	G		G;
 typedef	union	Lock		Lock;
 typedef	struct	M		M;
@@ -201,6 +202,14 @@
 #define NSIG 32
 #endif
 
+// NOTE(rsc): keep in sync with extern.go:/type.Func.
+// Eventually, the loaded symbol table should be closer to this form.
+struct	Func
+{
+	String	name;
+	uintptr	entry;	// entry pc
+};
+
 /* Macros.  */
 
 #ifdef GOOS_windows