summaryrefslogtreecommitdiff
path: root/libgo/go/debug/gosym
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/debug/gosym')
-rw-r--r--libgo/go/debug/gosym/pclinetest.h7
-rw-r--r--libgo/go/debug/gosym/pclinetest.s89
-rw-r--r--libgo/go/debug/gosym/pclntab.go82
-rw-r--r--libgo/go/debug/gosym/pclntab_test.go207
-rw-r--r--libgo/go/debug/gosym/symtab.go548
5 files changed, 933 insertions, 0 deletions
diff --git a/libgo/go/debug/gosym/pclinetest.h b/libgo/go/debug/gosym/pclinetest.h
new file mode 100644
index 000000000..a6c40e76c
--- /dev/null
+++ b/libgo/go/debug/gosym/pclinetest.h
@@ -0,0 +1,7 @@
+// Empty include file to generate z symbols
+
+
+
+
+
+// EOF
diff --git a/libgo/go/debug/gosym/pclinetest.s b/libgo/go/debug/gosym/pclinetest.s
new file mode 100644
index 000000000..519656b63
--- /dev/null
+++ b/libgo/go/debug/gosym/pclinetest.s
@@ -0,0 +1,89 @@
+TEXT linefrompc(SB),7,$0 // Each byte stores its line delta
+BYTE $2;
+BYTE $1;
+BYTE $1; BYTE $0;
+BYTE $1; BYTE $0; BYTE $0;
+BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
+BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
+BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
+BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
+BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
+BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
+BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
+BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
+BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
+BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
+BYTE $1;
+BYTE $1;
+BYTE $1; BYTE $0;
+BYTE $1; BYTE $0; BYTE $0;
+BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
+BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
+BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
+BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
+BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
+#include "pclinetest.h"
+BYTE $2;
+#include "pclinetest.h"
+BYTE $2;
+
+TEXT pcfromline(SB),7,$0 // Each record stores its line delta, then n, then n more bytes
+BYTE $31; BYTE $0;
+BYTE $1; BYTE $1; BYTE $0;
+BYTE $1; BYTE $0;
+
+BYTE $2; BYTE $4; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
+
+
+#include "pclinetest.h"
+BYTE $4; BYTE $0;
+
+
+BYTE $3; BYTE $3; BYTE $0; BYTE $0; BYTE $0;
+#include "pclinetest.h"
+
+
+BYTE $4; BYTE $3; BYTE $0; BYTE $0; BYTE $0;
+
+TEXT main(SB),7,$0
+ // Prevent GC of our test symbols
+ CALL linefrompc(SB)
+ CALL pcfromline(SB)
+
+// Keep the linker happy
+TEXT runtime·morestack(SB),7,$0
+ RET
+
+TEXT runtime·morestack00(SB),7,$0
+ RET
+
+TEXT runtime·morestack10(SB),7,$0
+ RET
+
+TEXT runtime·morestack01(SB),7,$0
+ RET
+
+TEXT runtime·morestack11(SB),7,$0
+ RET
+
+TEXT runtime·morestack8(SB),7,$0
+ RET
+
+TEXT runtime·morestack16(SB),7,$0
+ RET
+
+TEXT runtime·morestack24(SB),7,$0
+ RET
+
+TEXT runtime·morestack32(SB),7,$0
+ RET
+
+TEXT runtime·morestack40(SB),7,$0
+ RET
+
+TEXT runtime·morestack48(SB),7,$0
+ RET
+
+TEXT runtime·morestack8(SB),7,$0
+ RET
+
diff --git a/libgo/go/debug/gosym/pclntab.go b/libgo/go/debug/gosym/pclntab.go
new file mode 100644
index 000000000..9d7b0d15f
--- /dev/null
+++ b/libgo/go/debug/gosym/pclntab.go
@@ -0,0 +1,82 @@
+// 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.
+
+/*
+ * Line tables
+ */
+
+package gosym
+
+import "encoding/binary"
+
+type LineTable struct {
+ Data []byte
+ PC uint64
+ Line int
+}
+
+// TODO(rsc): Need to pull in quantum from architecture definition.
+const quantum = 1
+
+func (t *LineTable) parse(targetPC uint64, targetLine int) (b []byte, pc uint64, line int) {
+ // The PC/line table can be thought of as a sequence of
+ // <pc update>* <line update>
+ // batches. Each update batch results in a (pc, line) pair,
+ // where line applies to every PC from pc up to but not
+ // including the pc of the next pair.
+ //
+ // Here we process each update individually, which simplifies
+ // the code, but makes the corner cases more confusing.
+ b, pc, line = t.Data, t.PC, t.Line
+ for pc <= targetPC && line != targetLine && len(b) > 0 {
+ code := b[0]
+ b = b[1:]
+ switch {
+ case code == 0:
+ if len(b) < 4 {
+ b = b[0:0]
+ break
+ }
+ val := binary.BigEndian.Uint32(b)
+ b = b[4:]
+ line += int(val)
+ case code <= 64:
+ line += int(code)
+ case code <= 128:
+ line -= int(code - 64)
+ default:
+ pc += quantum * uint64(code-128)
+ continue
+ }
+ pc += quantum
+ }
+ return b, pc, line
+}
+
+func (t *LineTable) slice(pc uint64) *LineTable {
+ data, pc, line := t.parse(pc, -1)
+ return &LineTable{data, pc, line}
+}
+
+func (t *LineTable) PCToLine(pc uint64) int {
+ _, _, line := t.parse(pc, -1)
+ return line
+}
+
+func (t *LineTable) LineToPC(line int, maxpc uint64) uint64 {
+ _, pc, line1 := t.parse(maxpc, line)
+ if line1 != line {
+ return 0
+ }
+ // Subtract quantum from PC to account for post-line increment
+ return pc - quantum
+}
+
+// NewLineTable returns a new PC/line table
+// corresponding to the encoded data.
+// Text must be the start address of the
+// corresponding text segment.
+func NewLineTable(data []byte, text uint64) *LineTable {
+ return &LineTable{data, text, 0}
+}
diff --git a/libgo/go/debug/gosym/pclntab_test.go b/libgo/go/debug/gosym/pclntab_test.go
new file mode 100644
index 000000000..908702173
--- /dev/null
+++ b/libgo/go/debug/gosym/pclntab_test.go
@@ -0,0 +1,207 @@
+// 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.
+
+package gosym
+
+import (
+ "debug/elf"
+ "os"
+ "testing"
+ "syscall"
+)
+
+func dotest() bool {
+ // For now, only works on ELF platforms.
+ return syscall.OS == "linux" && os.Getenv("GOARCH") == "amd64"
+}
+
+func getTable(t *testing.T) *Table {
+ f, tab := crack(os.Args[0], t)
+ f.Close()
+ return tab
+}
+
+func crack(file string, t *testing.T) (*elf.File, *Table) {
+ // Open self
+ f, err := elf.Open(file)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return parse(file, f, t)
+}
+
+func parse(file string, f *elf.File, t *testing.T) (*elf.File, *Table) {
+ symdat, err := f.Section(".gosymtab").Data()
+ if err != nil {
+ f.Close()
+ t.Fatalf("reading %s gosymtab: %v", file, err)
+ }
+ pclndat, err := f.Section(".gopclntab").Data()
+ if err != nil {
+ f.Close()
+ t.Fatalf("reading %s gopclntab: %v", file, err)
+ }
+
+ pcln := NewLineTable(pclndat, f.Section(".text").Addr)
+ tab, err := NewTable(symdat, pcln)
+ if err != nil {
+ f.Close()
+ t.Fatalf("parsing %s gosymtab: %v", file, err)
+ }
+
+ return f, tab
+}
+
+var goarch = os.Getenv("O")
+
+func TestLineFromAline(t *testing.T) {
+ if !dotest() {
+ return
+ }
+
+ tab := getTable(t)
+
+ // Find the sym package
+ pkg := tab.LookupFunc("debug/gosym.TestLineFromAline").Obj
+ if pkg == nil {
+ t.Fatalf("nil pkg")
+ }
+
+ // Walk every absolute line and ensure that we hit every
+ // source line monotonically
+ lastline := make(map[string]int)
+ final := -1
+ for i := 0; i < 10000; i++ {
+ path, line := pkg.lineFromAline(i)
+ // Check for end of object
+ if path == "" {
+ if final == -1 {
+ final = i - 1
+ }
+ continue
+ } else if final != -1 {
+ t.Fatalf("reached end of package at absolute line %d, but absolute line %d mapped to %s:%d", final, i, path, line)
+ }
+ // It's okay to see files multiple times (e.g., sys.a)
+ if line == 1 {
+ lastline[path] = 1
+ continue
+ }
+ // Check that the is the next line in path
+ ll, ok := lastline[path]
+ if !ok {
+ t.Errorf("file %s starts on line %d", path, line)
+ } else if line != ll+1 {
+ t.Errorf("expected next line of file %s to be %d, got %d", path, ll+1, line)
+ }
+ lastline[path] = line
+ }
+ if final == -1 {
+ t.Errorf("never reached end of object")
+ }
+}
+
+func TestLineAline(t *testing.T) {
+ if !dotest() {
+ return
+ }
+
+ tab := getTable(t)
+
+ for _, o := range tab.Files {
+ // A source file can appear multiple times in a
+ // object. alineFromLine will always return alines in
+ // the first file, so track which lines we've seen.
+ found := make(map[string]int)
+ for i := 0; i < 1000; i++ {
+ path, line := o.lineFromAline(i)
+ if path == "" {
+ break
+ }
+
+ // cgo files are full of 'Z' symbols, which we don't handle
+ if len(path) > 4 && path[len(path)-4:] == ".cgo" {
+ continue
+ }
+
+ if minline, ok := found[path]; path != "" && ok {
+ if minline >= line {
+ // We've already covered this file
+ continue
+ }
+ }
+ found[path] = line
+
+ a, err := o.alineFromLine(path, line)
+ if err != nil {
+ t.Errorf("absolute line %d in object %s maps to %s:%d, but mapping that back gives error %s", i, o.Paths[0].Name, path, line, err)
+ } else if a != i {
+ t.Errorf("absolute line %d in object %s maps to %s:%d, which maps back to absolute line %d\n", i, o.Paths[0].Name, path, line, a)
+ }
+ }
+ }
+}
+
+// gotest: if [ "$(uname)-$(uname -m)" = Linux-x86_64 -a "$GOARCH" = amd64 ]; then
+// gotest: mkdir -p _test && $AS pclinetest.s && $LD -E main -o _test/pclinetest pclinetest.$O
+// gotest: fi
+func TestPCLine(t *testing.T) {
+ if !dotest() {
+ return
+ }
+
+ f, tab := crack("_test/pclinetest", t)
+ text := f.Section(".text")
+ textdat, err := text.Data()
+ if err != nil {
+ t.Fatalf("reading .text: %v", err)
+ }
+
+ // Test PCToLine
+ sym := tab.LookupFunc("linefrompc")
+ wantLine := 0
+ for pc := sym.Entry; pc < sym.End; pc++ {
+ file, line, fn := tab.PCToLine(pc)
+ off := pc - text.Addr // TODO(rsc): should not need off; bug in 8g
+ wantLine += int(textdat[off])
+ if fn == nil {
+ t.Errorf("failed to get line of PC %#x", pc)
+ } else if len(file) < 12 || file[len(file)-12:] != "pclinetest.s" || line != wantLine || fn != sym {
+ t.Errorf("expected %s:%d (%s) at PC %#x, got %s:%d (%s)", "pclinetest.s", wantLine, sym.Name, pc, file, line, fn.Name)
+ }
+ }
+
+ // Test LineToPC
+ sym = tab.LookupFunc("pcfromline")
+ lookupline := -1
+ wantLine = 0
+ off := uint64(0) // TODO(rsc): should not need off; bug in 8g
+ for pc := sym.Value; pc < sym.End; pc += 2 + uint64(textdat[off]) {
+ file, line, fn := tab.PCToLine(pc)
+ off = pc - text.Addr
+ wantLine += int(textdat[off])
+ if line != wantLine {
+ t.Errorf("expected line %d at PC %#x in pcfromline, got %d", wantLine, pc, line)
+ off = pc + 1 - text.Addr
+ continue
+ }
+ if lookupline == -1 {
+ lookupline = line
+ }
+ for ; lookupline <= line; lookupline++ {
+ pc2, fn2, err := tab.LineToPC(file, lookupline)
+ if lookupline != line {
+ // Should be nothing on this line
+ if err == nil {
+ t.Errorf("expected no PC at line %d, got %#x (%s)", lookupline, pc2, fn2.Name)
+ }
+ } else if err != nil {
+ t.Errorf("failed to get PC of line %d: %s", lookupline, err)
+ } else if pc != pc2 {
+ t.Errorf("expected PC %#x (%s) at line %d, got PC %#x (%s)", pc, fn.Name, line, pc2, fn2.Name)
+ }
+ }
+ off = pc + 1 - text.Addr
+ }
+}
diff --git a/libgo/go/debug/gosym/symtab.go b/libgo/go/debug/gosym/symtab.go
new file mode 100644
index 000000000..dea460d71
--- /dev/null
+++ b/libgo/go/debug/gosym/symtab.go
@@ -0,0 +1,548 @@
+// 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.
+
+// Package gosym implements access to the Go symbol
+// and line number tables embedded in Go binaries generated
+// by the gc compilers.
+package gosym
+
+// The table format is a variant of the format used in Plan 9's a.out
+// format, documented at http://plan9.bell-labs.com/magic/man2html/6/a.out.
+// The best reference for the differences between the Plan 9 format
+// and the Go format is the runtime source, specifically ../../runtime/symtab.c.
+
+import (
+ "encoding/binary"
+ "fmt"
+ "os"
+ "strconv"
+ "strings"
+)
+
+/*
+ * Symbols
+ */
+
+// A Sym represents a single symbol table entry.
+type Sym struct {
+ Value uint64
+ Type byte
+ Name string
+ GoType uint64
+ // If this symbol if a function symbol, the corresponding Func
+ Func *Func
+}
+
+// Static returns whether this symbol is static (not visible outside its file).
+func (s *Sym) Static() bool { return s.Type >= 'a' }
+
+// PackageName returns the package part of the symbol name,
+// or the empty string if there is none.
+func (s *Sym) PackageName() string {
+ if i := strings.Index(s.Name, "."); i != -1 {
+ return s.Name[0:i]
+ }
+ return ""
+}
+
+// ReceiverName returns the receiver type name of this symbol,
+// or the empty string if there is none.
+func (s *Sym) ReceiverName() string {
+ l := strings.Index(s.Name, ".")
+ r := strings.LastIndex(s.Name, ".")
+ if l == -1 || r == -1 || l == r {
+ return ""
+ }
+ return s.Name[l+1 : r]
+}
+
+// BaseName returns the symbol name without the package or receiver name.
+func (s *Sym) BaseName() string {
+ if i := strings.LastIndex(s.Name, "."); i != -1 {
+ return s.Name[i+1:]
+ }
+ return s.Name
+}
+
+// A Func collects information about a single function.
+type Func struct {
+ Entry uint64
+ *Sym
+ End uint64
+ Params []*Sym
+ Locals []*Sym
+ FrameSize int
+ LineTable *LineTable
+ Obj *Obj
+}
+
+// An Obj represents a single object file.
+type Obj struct {
+ Funcs []Func
+ Paths []Sym
+}
+
+/*
+ * Symbol tables
+ */
+
+// Table represents a Go symbol table. It stores all of the
+// symbols decoded from the program and provides methods to translate
+// between symbols, names, and addresses.
+type Table struct {
+ Syms []Sym
+ Funcs []Func
+ Files map[string]*Obj
+ Objs []Obj
+ // textEnd uint64;
+}
+
+type sym struct {
+ value uint32
+ gotype uint32
+ typ byte
+ name []byte
+}
+
+func walksymtab(data []byte, fn func(sym) os.Error) os.Error {
+ var s sym
+ p := data
+ for len(p) >= 6 {
+ s.value = binary.BigEndian.Uint32(p[0:4])
+ typ := p[4]
+ if typ&0x80 == 0 {
+ return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ}
+ }
+ typ &^= 0x80
+ s.typ = typ
+ p = p[5:]
+ var i int
+ var nnul int
+ for i = 0; i < len(p); i++ {
+ if p[i] == 0 {
+ nnul = 1
+ break
+ }
+ }
+ switch typ {
+ case 'z', 'Z':
+ p = p[i+nnul:]
+ for i = 0; i+2 <= len(p); i += 2 {
+ if p[i] == 0 && p[i+1] == 0 {
+ nnul = 2
+ break
+ }
+ }
+ }
+ if i+nnul+4 > len(p) {
+ return &DecodingError{len(data), "unexpected EOF", nil}
+ }
+ s.name = p[0:i]
+ i += nnul
+ s.gotype = binary.BigEndian.Uint32(p[i : i+4])
+ p = p[i+4:]
+ fn(s)
+ }
+ return nil
+}
+
+// NewTable decodes the Go symbol table in data,
+// returning an in-memory representation.
+func NewTable(symtab []byte, pcln *LineTable) (*Table, os.Error) {
+ var n int
+ err := walksymtab(symtab, func(s sym) os.Error {
+ n++
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ var t Table
+ fname := make(map[uint16]string)
+ t.Syms = make([]Sym, 0, n)
+ nf := 0
+ nz := 0
+ lasttyp := uint8(0)
+ err = walksymtab(symtab, func(s sym) os.Error {
+ n := len(t.Syms)
+ t.Syms = t.Syms[0 : n+1]
+ ts := &t.Syms[n]
+ ts.Type = s.typ
+ ts.Value = uint64(s.value)
+ ts.GoType = uint64(s.gotype)
+ switch s.typ {
+ default:
+ // rewrite name to use . instead of · (c2 b7)
+ w := 0
+ b := s.name
+ for i := 0; i < len(b); i++ {
+ if b[i] == 0xc2 && i+1 < len(b) && b[i+1] == 0xb7 {
+ i++
+ b[i] = '.'
+ }
+ b[w] = b[i]
+ w++
+ }
+ ts.Name = string(s.name[0:w])
+ case 'z', 'Z':
+ if lasttyp != 'z' && lasttyp != 'Z' {
+ nz++
+ }
+ for i := 0; i < len(s.name); i += 2 {
+ eltIdx := binary.BigEndian.Uint16(s.name[i : i+2])
+ elt, ok := fname[eltIdx]
+ if !ok {
+ return &DecodingError{-1, "bad filename code", eltIdx}
+ }
+ if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' {
+ ts.Name += "/"
+ }
+ ts.Name += elt
+ }
+ }
+ switch s.typ {
+ case 'T', 't', 'L', 'l':
+ nf++
+ case 'f':
+ fname[uint16(s.value)] = ts.Name
+ }
+ lasttyp = s.typ
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ t.Funcs = make([]Func, 0, nf)
+ t.Objs = make([]Obj, 0, nz)
+ t.Files = make(map[string]*Obj)
+
+ // Count text symbols and attach frame sizes, parameters, and
+ // locals to them. Also, find object file boundaries.
+ var obj *Obj
+ lastf := 0
+ for i := 0; i < len(t.Syms); i++ {
+ sym := &t.Syms[i]
+ switch sym.Type {
+ case 'Z', 'z': // path symbol
+ // Finish the current object
+ if obj != nil {
+ obj.Funcs = t.Funcs[lastf:]
+ }
+ lastf = len(t.Funcs)
+
+ // Start new object
+ n := len(t.Objs)
+ t.Objs = t.Objs[0 : n+1]
+ obj = &t.Objs[n]
+
+ // Count & copy path symbols
+ var end int
+ for end = i + 1; end < len(t.Syms); end++ {
+ if c := t.Syms[end].Type; c != 'Z' && c != 'z' {
+ break
+ }
+ }
+ obj.Paths = t.Syms[i:end]
+ i = end - 1 // loop will i++
+
+ // Record file names
+ depth := 0
+ for j := range obj.Paths {
+ s := &obj.Paths[j]
+ if s.Name == "" {
+ depth--
+ } else {
+ if depth == 0 {
+ t.Files[s.Name] = obj
+ }
+ depth++
+ }
+ }
+
+ case 'T', 't', 'L', 'l': // text symbol
+ if n := len(t.Funcs); n > 0 {
+ t.Funcs[n-1].End = sym.Value
+ }
+ if sym.Name == "etext" {
+ continue
+ }
+
+ // Count parameter and local (auto) syms
+ var np, na int
+ var end int
+ countloop:
+ for end = i + 1; end < len(t.Syms); end++ {
+ switch t.Syms[end].Type {
+ case 'T', 't', 'L', 'l', 'Z', 'z':
+ break countloop
+ case 'p':
+ np++
+ case 'a':
+ na++
+ }
+ }
+
+ // Fill in the function symbol
+ n := len(t.Funcs)
+ t.Funcs = t.Funcs[0 : n+1]
+ fn := &t.Funcs[n]
+ sym.Func = fn
+ fn.Params = make([]*Sym, 0, np)
+ fn.Locals = make([]*Sym, 0, na)
+ fn.Sym = sym
+ fn.Entry = sym.Value
+ fn.Obj = obj
+ if pcln != nil {
+ fn.LineTable = pcln.slice(fn.Entry)
+ pcln = fn.LineTable
+ }
+ for j := i; j < end; j++ {
+ s := &t.Syms[j]
+ switch s.Type {
+ case 'm':
+ fn.FrameSize = int(s.Value)
+ case 'p':
+ n := len(fn.Params)
+ fn.Params = fn.Params[0 : n+1]
+ fn.Params[n] = s
+ case 'a':
+ n := len(fn.Locals)
+ fn.Locals = fn.Locals[0 : n+1]
+ fn.Locals[n] = s
+ }
+ }
+ i = end - 1 // loop will i++
+ }
+ }
+ if obj != nil {
+ obj.Funcs = t.Funcs[lastf:]
+ }
+ return &t, nil
+}
+
+// PCToFunc returns the function containing the program counter pc,
+// or nil if there is no such function.
+func (t *Table) PCToFunc(pc uint64) *Func {
+ funcs := t.Funcs
+ for len(funcs) > 0 {
+ m := len(funcs) / 2
+ fn := &funcs[m]
+ switch {
+ case pc < fn.Entry:
+ funcs = funcs[0:m]
+ case fn.Entry <= pc && pc < fn.End:
+ return fn
+ default:
+ funcs = funcs[m+1:]
+ }
+ }
+ return nil
+}
+
+// PCToLine looks up line number information for a program counter.
+// If there is no information, it returns fn == nil.
+func (t *Table) PCToLine(pc uint64) (file string, line int, fn *Func) {
+ if fn = t.PCToFunc(pc); fn == nil {
+ return
+ }
+ file, line = fn.Obj.lineFromAline(fn.LineTable.PCToLine(pc))
+ return
+}
+
+// LineToPC looks up the first program counter on the given line in
+// the named file. Returns UnknownPathError or UnknownLineError if
+// there is an error looking up this line.
+func (t *Table) LineToPC(file string, line int) (pc uint64, fn *Func, err os.Error) {
+ obj, ok := t.Files[file]
+ if !ok {
+ return 0, nil, UnknownFileError(file)
+ }
+ abs, err := obj.alineFromLine(file, line)
+ if err != nil {
+ return
+ }
+ for i := range obj.Funcs {
+ f := &obj.Funcs[i]
+ pc := f.LineTable.LineToPC(abs, f.End)
+ if pc != 0 {
+ return pc, f, nil
+ }
+ }
+ return 0, nil, &UnknownLineError{file, line}
+}
+
+// LookupSym returns the text, data, or bss symbol with the given name,
+// or nil if no such symbol is found.
+func (t *Table) LookupSym(name string) *Sym {
+ // TODO(austin) Maybe make a map
+ for i := range t.Syms {
+ s := &t.Syms[i]
+ switch s.Type {
+ case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
+ if s.Name == name {
+ return s
+ }
+ }
+ }
+ return nil
+}
+
+// LookupFunc returns the text, data, or bss symbol with the given name,
+// or nil if no such symbol is found.
+func (t *Table) LookupFunc(name string) *Func {
+ for i := range t.Funcs {
+ f := &t.Funcs[i]
+ if f.Sym.Name == name {
+ return f
+ }
+ }
+ return nil
+}
+
+// SymByAddr returns the text, data, or bss symbol starting at the given address.
+// TODO(rsc): Allow lookup by any address within the symbol.
+func (t *Table) SymByAddr(addr uint64) *Sym {
+ // TODO(austin) Maybe make a map
+ for i := range t.Syms {
+ s := &t.Syms[i]
+ switch s.Type {
+ case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
+ if s.Value == addr {
+ return s
+ }
+ }
+ }
+ return nil
+}
+
+/*
+ * Object files
+ */
+
+func (o *Obj) lineFromAline(aline int) (string, int) {
+ type stackEnt struct {
+ path string
+ start int
+ offset int
+ prev *stackEnt
+ }
+
+ noPath := &stackEnt{"", 0, 0, nil}
+ tos := noPath
+
+ // TODO(austin) I have no idea how 'Z' symbols work, except
+ // that they pop the stack.
+pathloop:
+ for _, s := range o.Paths {
+ val := int(s.Value)
+ switch {
+ case val > aline:
+ break pathloop
+
+ case val == 1:
+ // Start a new stack
+ tos = &stackEnt{s.Name, val, 0, noPath}
+
+ case s.Name == "":
+ // Pop
+ if tos == noPath {
+ return "<malformed symbol table>", 0
+ }
+ tos.prev.offset += val - tos.start
+ tos = tos.prev
+
+ default:
+ // Push
+ tos = &stackEnt{s.Name, val, 0, tos}
+ }
+ }
+
+ if tos == noPath {
+ return "", 0
+ }
+ return tos.path, aline - tos.start - tos.offset + 1
+}
+
+func (o *Obj) alineFromLine(path string, line int) (int, os.Error) {
+ if line < 1 {
+ return 0, &UnknownLineError{path, line}
+ }
+
+ for i, s := range o.Paths {
+ // Find this path
+ if s.Name != path {
+ continue
+ }
+
+ // Find this line at this stack level
+ depth := 0
+ var incstart int
+ line += int(s.Value)
+ pathloop:
+ for _, s := range o.Paths[i:] {
+ val := int(s.Value)
+ switch {
+ case depth == 1 && val >= line:
+ return line - 1, nil
+
+ case s.Name == "":
+ depth--
+ if depth == 0 {
+ break pathloop
+ } else if depth == 1 {
+ line += val - incstart
+ }
+
+ default:
+ if depth == 1 {
+ incstart = val
+ }
+ depth++
+ }
+ }
+ return 0, &UnknownLineError{path, line}
+ }
+ return 0, UnknownFileError(path)
+}
+
+/*
+ * Errors
+ */
+
+// UnknownFileError represents a failure to find the specific file in
+// the symbol table.
+type UnknownFileError string
+
+func (e UnknownFileError) String() string { return "unknown file: " + string(e) }
+
+// UnknownLineError represents a failure to map a line to a program
+// counter, either because the line is beyond the bounds of the file
+// or because there is no code on the given line.
+type UnknownLineError struct {
+ File string
+ Line int
+}
+
+func (e *UnknownLineError) String() string {
+ return "no code at " + e.File + ":" + strconv.Itoa(e.Line)
+}
+
+// DecodingError represents an error during the decoding of
+// the symbol table.
+type DecodingError struct {
+ off int
+ msg string
+ val interface{}
+}
+
+func (e *DecodingError) String() string {
+ msg := e.msg
+ if e.val != nil {
+ msg += fmt.Sprintf(" '%v'", e.val)
+ }
+ msg += fmt.Sprintf(" at byte %#x", e.off)
+ return msg
+}