summaryrefslogtreecommitdiff
path: root/src/pkg/debug
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2013-07-18 10:12:14 -0400
committerRuss Cox <rsc@golang.org>2013-07-18 10:12:14 -0400
commit1cc1462d94842d9e8dd6bfda7c5b5e4086445489 (patch)
tree9844b224aafd175e4d9d2c817cfeb2a70990fd68 /src/pkg/debug
parentc9f86c33cee5df2aab811376eae30c2080785347 (diff)
downloadgo-1cc1462d94842d9e8dd6bfda7c5b5e4086445489.tar.gz
debug/gosym: update for Go 1.2 pcln table
R=golang-dev, r CC=golang-dev https://codereview.appspot.com/11495043
Diffstat (limited to 'src/pkg/debug')
-rw-r--r--src/pkg/debug/gosym/pclinetest.asm4
-rw-r--r--src/pkg/debug/gosym/pclntab.go360
-rw-r--r--src/pkg/debug/gosym/pclntab_test.go43
-rw-r--r--src/pkg/debug/gosym/symtab.go91
4 files changed, 458 insertions, 40 deletions
diff --git a/src/pkg/debug/gosym/pclinetest.asm b/src/pkg/debug/gosym/pclinetest.asm
index 6305435b0..868afc660 100644
--- a/src/pkg/debug/gosym/pclinetest.asm
+++ b/src/pkg/debug/gosym/pclinetest.asm
@@ -26,9 +26,10 @@ BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
BYTE $2;
#include "pclinetest.h"
BYTE $2;
+BYTE $255;
TEXT pcfromline(SB),7,$0 // Each record stores its line delta, then n, then n more bytes
-BYTE $31; BYTE $0;
+BYTE $32; BYTE $0;
BYTE $1; BYTE $1; BYTE $0;
BYTE $1; BYTE $0;
@@ -44,6 +45,7 @@ BYTE $3; BYTE $3; BYTE $0; BYTE $0; BYTE $0;
BYTE $4; BYTE $3; BYTE $0; BYTE $0; BYTE $0;
+BYTE $255;
TEXT main(SB),7,$0
// Prevent GC of our test symbols
diff --git a/src/pkg/debug/gosym/pclntab.go b/src/pkg/debug/gosym/pclntab.go
index 9d7b0d15f..2b9bedb45 100644
--- a/src/pkg/debug/gosym/pclntab.go
+++ b/src/pkg/debug/gosym/pclntab.go
@@ -8,16 +8,47 @@
package gosym
-import "encoding/binary"
+import (
+ "encoding/binary"
+ "sync"
+)
+// A LineTable is a data structure mapping program counters to line numbers.
+//
+// In Go 1.1 and earlier, each function (represented by a Func) had its own LineTable,
+// and the line number corresponded to a numbering of all source lines in the
+// program, across all files. That absolute line number would then have to be
+// converted separately to a file name and line number within the file.
+//
+// In Go 1.2, the format of the data changed so that there is a single LineTable
+// for the entire program, shared by all Funcs, and there are no absolute line
+// numbers, just line numbers within specific files.
+//
+// For the most part, LineTable's methods should be treated as an internal
+// detail of the package; callers should use the methods on Table instead.
type LineTable struct {
Data []byte
PC uint64
Line int
+
+ // Go 1.2 state
+ mu sync.Mutex
+ go12 int // is this in Go 1.2 format? -1 no, 0 unknown, 1 yes
+ binary binary.ByteOrder
+ quantum uint32
+ ptrsize uint32
+ functab []byte
+ nfunctab uint32
+ filetab []byte
+ nfiletab uint32
+ fileMap map[string]uint32
}
-// TODO(rsc): Need to pull in quantum from architecture definition.
-const quantum = 1
+// NOTE(rsc): This is wrong for GOARCH=arm, which uses a quantum of 4,
+// but we have no idea whether we're using arm or not. This only
+// matters in the old (pre-Go 1.2) symbol table format, so it's not worth
+// fixing.
+const oldQuantum = 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
@@ -46,31 +77,42 @@ func (t *LineTable) parse(targetPC uint64, targetLine int) (b []byte, pc uint64,
case code <= 128:
line -= int(code - 64)
default:
- pc += quantum * uint64(code-128)
+ pc += oldQuantum * uint64(code-128)
continue
}
- pc += quantum
+ pc += oldQuantum
}
return b, pc, line
}
func (t *LineTable) slice(pc uint64) *LineTable {
data, pc, line := t.parse(pc, -1)
- return &LineTable{data, pc, line}
+ return &LineTable{Data: data, PC: pc, Line: line}
}
+// PCToLine returns the line number for the given program counter.
+// Callers should use Table's PCToLine method instead.
func (t *LineTable) PCToLine(pc uint64) int {
+ if t.isGo12() {
+ return t.go12PCToLine(pc)
+ }
_, _, line := t.parse(pc, -1)
return line
}
+// LineToPC returns the program counter for the given line number,
+// considering only program counters before maxpc.
+// Callers should use Table's LineToPC method instead.
func (t *LineTable) LineToPC(line int, maxpc uint64) uint64 {
+ if t.isGo12() {
+ return 0
+ }
_, pc, line1 := t.parse(maxpc, line)
if line1 != line {
return 0
}
// Subtract quantum from PC to account for post-line increment
- return pc - quantum
+ return pc - oldQuantum
}
// NewLineTable returns a new PC/line table
@@ -78,5 +120,307 @@ func (t *LineTable) LineToPC(line int, maxpc uint64) uint64 {
// Text must be the start address of the
// corresponding text segment.
func NewLineTable(data []byte, text uint64) *LineTable {
- return &LineTable{data, text, 0}
+ return &LineTable{Data: data, PC: text, Line: 0}
+}
+
+// Go 1.2 symbol table format.
+// See golang.org/s/go12symtab.
+//
+// A general note about the methods here: rather than try to avoid
+// index out of bounds errors, we trust Go to detect them, and then
+// we recover from the panics and treat them as indicative of a malformed
+// or incomplete table.
+//
+// The methods called by symtab.go, which begin with "go12" prefixes,
+// are expected to have that recovery logic.
+
+// isGo12 reports whether this is a Go 1.2 (or later) symbol table.
+func (t *LineTable) isGo12() bool {
+ t.go12Init()
+ return t.go12 == 1
+}
+
+const go12magic = 0xfffffffb
+
+// uintptr returns the pointer-sized value encoded at b.
+// The pointer size is dictated by the table being read.
+func (t *LineTable) uintptr(b []byte) uint64 {
+ if t.ptrsize == 4 {
+ return uint64(t.binary.Uint32(b))
+ }
+ return t.binary.Uint64(b)
+}
+
+// go12init initializes the Go 1.2 metadata if t is a Go 1.2 symbol table.
+func (t *LineTable) go12Init() {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ if t.go12 != 0 {
+ return
+ }
+
+ defer func() {
+ // If we panic parsing, assume it's not a Go 1.2 symbol table.
+ recover()
+ }()
+
+ // Check header: 4-byte magic, two zeros, pc quantum, pointer size.
+ t.go12 = -1 // not Go 1.2 until proven otherwise
+ if len(t.Data) < 16 || t.Data[4] != 0 || t.Data[5] != 0 ||
+ (t.Data[6] != 1 && t.Data[6] != 4) || // pc quantum
+ (t.Data[7] != 4 && t.Data[7] != 8) { // pointer size
+ return
+ }
+
+ switch uint32(go12magic) {
+ case binary.LittleEndian.Uint32(t.Data):
+ t.binary = binary.LittleEndian
+ case binary.BigEndian.Uint32(t.Data):
+ t.binary = binary.BigEndian
+ default:
+ return
+ }
+
+ t.quantum = uint32(t.Data[6])
+ t.ptrsize = uint32(t.Data[7])
+
+ t.nfunctab = uint32(t.uintptr(t.Data[8:]))
+ t.functab = t.Data[8+t.ptrsize:]
+ functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize
+ fileoff := t.binary.Uint32(t.functab[functabsize:])
+ t.functab = t.functab[:functabsize]
+ t.filetab = t.Data[fileoff:]
+ t.nfiletab = t.binary.Uint32(t.filetab)
+ t.filetab = t.filetab[:t.nfiletab*4]
+
+ t.go12 = 1 // so far so good
+}
+
+// findFunc returns the func corresponding to the given program counter.
+func (t *LineTable) findFunc(pc uint64) []byte {
+ if pc < t.uintptr(t.functab) || pc >= t.uintptr(t.functab[len(t.functab)-int(t.ptrsize):]) {
+ return nil
+ }
+
+ // The function table is a list of 2*nfunctab+1 uintptrs,
+ // alternating program counters and offsets to func structures.
+ f := t.functab
+ nf := t.nfunctab
+ for nf > 0 {
+ m := nf / 2
+ fm := f[2*t.ptrsize*m:]
+ if t.uintptr(fm) <= pc && pc < t.uintptr(fm[2*t.ptrsize:]) {
+ return t.Data[t.uintptr(fm[t.ptrsize:]):]
+ } else if pc < t.uintptr(fm) {
+ nf = m
+ } else {
+ f = f[(m+1)*2*t.ptrsize:]
+ nf -= m + 1
+ }
+ }
+ return nil
+}
+
+// readvarint reads, removes, and returns a varint from *pp.
+func (t *LineTable) readvarint(pp *[]byte) uint32 {
+ var v, shift uint32
+ p := *pp
+ for shift = 0; ; shift += 7 {
+ b := p[0]
+ p = p[1:]
+ v |= (uint32(b) & 0x7F) << shift
+ if b&0x80 == 0 {
+ break
+ }
+ }
+ *pp = p
+ return v
+}
+
+// string returns a Go string found at off.
+func (t *LineTable) string(off uint32) string {
+ for i := off; ; i++ {
+ if t.Data[i] == 0 {
+ return string(t.Data[off:i])
+ }
+ }
+}
+
+// step advances to the next pc, value pair in the encoded table.
+func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool {
+ uvdelta := t.readvarint(p)
+ if uvdelta == 0 && !first {
+ return false
+ }
+ if uvdelta&1 != 0 {
+ uvdelta = ^(uvdelta >> 1)
+ } else {
+ uvdelta >>= 1
+ }
+ vdelta := int32(uvdelta)
+ pcdelta := t.readvarint(p) * t.quantum
+ *pc += uint64(pcdelta)
+ *val += vdelta
+ return true
+}
+
+// pcvalue reports the value associated with the target pc.
+// off is the offset to the beginning of the pc-value table,
+// and entry is the start PC for the corresponding function.
+func (t *LineTable) pcvalue(off uint32, entry, targetpc uint64) int32 {
+ if off == 0 {
+ return -1
+ }
+ p := t.Data[off:]
+
+ val := int32(-1)
+ pc := entry
+ for t.step(&p, &pc, &val, pc == entry) {
+ if targetpc < pc {
+ return val
+ }
+ }
+ return -1
+}
+
+// findFileLine scans one function in the binary looking for a
+// program counter in the given file on the given line.
+// It does so by running the pc-value tables mapping program counter
+// to file number. Since most functions come from a single file, these
+// are usually short and quick to scan. If a file match is found, then the
+// code goes to the expense of looking for a simultaneous line number match.
+func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum, line int32) uint64 {
+ if filetab == 0 || linetab == 0 {
+ return 0
+ }
+
+ fp := t.Data[filetab:]
+ fl := t.Data[linetab:]
+ fileVal := int32(-1)
+ filePC := entry
+ lineVal := int32(-1)
+ linePC := entry
+ fileStartPC := filePC
+ for t.step(&fp, &filePC, &fileVal, filePC == entry) {
+ if fileVal == filenum && fileStartPC < filePC {
+ // fileVal is in effect starting at fileStartPC up to
+ // but not including filePC, and it's the file we want.
+ // Run the PC table looking for a matching line number
+ // or until we reach filePC.
+ lineStartPC := linePC
+ for linePC < filePC && t.step(&fl, &linePC, &lineVal, linePC == entry) {
+ // lineVal is in effect until linePC, and lineStartPC < filePC.
+ if lineVal == line {
+ if fileStartPC <= lineStartPC {
+ return lineStartPC
+ }
+ if fileStartPC < linePC {
+ return fileStartPC
+ }
+ }
+ lineStartPC = linePC
+ }
+ }
+ fileStartPC = filePC
+ }
+ return 0
+}
+
+// go12PCToLine maps program counter to line number for the Go 1.2 pcln table.
+func (t *LineTable) go12PCToLine(pc uint64) (line int) {
+ defer func() {
+ if recover() != nil {
+ line = -1
+ }
+ }()
+
+ f := t.findFunc(pc)
+ if f == nil {
+ return -1
+ }
+ entry := t.uintptr(f)
+ linetab := t.binary.Uint32(f[t.ptrsize+8*4:])
+ return int(t.pcvalue(linetab, entry, pc))
+}
+
+// go12PCToFile maps program counter to file name for the Go 1.2 pcln table.
+func (t *LineTable) go12PCToFile(pc uint64) (file string) {
+ defer func() {
+ if recover() != nil {
+ file = ""
+ }
+ }()
+
+ f := t.findFunc(pc)
+ if f == nil {
+ return ""
+ }
+ entry := t.uintptr(f)
+ filetab := t.binary.Uint32(f[t.ptrsize+7*4:])
+ fno := t.pcvalue(filetab, entry, pc)
+ if fno <= 0 {
+ return ""
+ }
+ return t.string(t.binary.Uint32(t.filetab[4*fno:]))
+}
+
+// go12LineToPC maps a (file, line) pair to a program counter for the Go 1.2 pcln table.
+func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) {
+ defer func() {
+ if recover() != nil {
+ pc = 0
+ }
+ }()
+
+ t.initFileMap()
+ filenum := t.fileMap[file]
+ if filenum == 0 {
+ return 0
+ }
+
+ // Scan all functions.
+ // If this turns out to be a bottleneck, we could build a map[int32][]int32
+ // mapping file number to a list of functions with code from that file.
+ for i := uint32(0); i < t.nfunctab; i++ {
+ f := t.Data[t.uintptr(t.functab[2*t.ptrsize*i+t.ptrsize:]):]
+ entry := t.uintptr(f)
+ filetab := t.binary.Uint32(f[t.ptrsize+7*4:])
+ linetab := t.binary.Uint32(f[t.ptrsize+8*4:])
+ pc := t.findFileLine(entry, filetab, linetab, int32(filenum), int32(line))
+ if pc != 0 {
+ return pc
+ }
+ }
+ return 0
+}
+
+// initFileMap initializes the map from file name to file number.
+func (t *LineTable) initFileMap() {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+
+ if t.fileMap != nil {
+ return
+ }
+ m := make(map[string]uint32)
+
+ for i := uint32(1); i < t.nfiletab; i++ {
+ s := t.string(t.binary.Uint32(t.filetab[4*i:]))
+ m[s] = i
+ }
+ t.fileMap = m
+}
+
+// go12MapFiles adds to m a key for every file in the Go 1.2 LineTable.
+// Every key maps to obj. That's not a very interesting map, but it provides
+// a way for callers to obtain the list of files in the program.
+func (t *LineTable) go12MapFiles(m map[string]*Obj, obj *Obj) {
+ defer func() {
+ recover()
+ }()
+
+ t.initFileMap()
+ for file := range t.fileMap {
+ m[file] = obj
+ }
}
diff --git a/src/pkg/debug/gosym/pclntab_test.go b/src/pkg/debug/gosym/pclntab_test.go
index 20acba612..5616cdbd5 100644
--- a/src/pkg/debug/gosym/pclntab_test.go
+++ b/src/pkg/debug/gosym/pclntab_test.go
@@ -21,9 +21,13 @@ var (
pclinetestBinary string
)
-func dotest() bool {
- // For now, only works on ELF platforms.
- if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
+func dotest(self bool) bool {
+ // For now, only works on amd64 platforms.
+ if runtime.GOARCH != "amd64" {
+ return false
+ }
+ // Self test reads test binary; only works on Linux.
+ if self && runtime.GOOS != "linux" {
return false
}
if pclinetestBinary != "" {
@@ -41,7 +45,8 @@ func dotest() bool {
// the resulting binary looks like it was built from pclinetest.s,
// but we have renamed it to keep it away from the go tool.
pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest")
- command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -E main -o %s %s.6",
+ pclinetestBinary = "pclinetest"
+ command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -H linux -E main -o %s %s.6",
pclinetestBinary, pclinetestBinary, pclinetestBinary)
cmd := exec.Command("sh", "-c", command)
cmd.Stdout = os.Stdout
@@ -100,12 +105,16 @@ func parse(file string, f *elf.File, t *testing.T) (*elf.File, *Table) {
var goarch = os.Getenv("O")
func TestLineFromAline(t *testing.T) {
- if !dotest() {
+ if !dotest(true) {
return
}
defer endtest()
tab := getTable(t)
+ if tab.go12line != nil {
+ // aline's don't exist in the Go 1.2 table.
+ t.Skip("not relevant to Go 1.2 symbol table")
+ }
// Find the sym package
pkg := tab.LookupFunc("debug/gosym.TestLineFromAline").Obj
@@ -148,12 +157,16 @@ func TestLineFromAline(t *testing.T) {
}
func TestLineAline(t *testing.T) {
- if !dotest() {
+ if !dotest(true) {
return
}
defer endtest()
tab := getTable(t)
+ if tab.go12line != nil {
+ // aline's don't exist in the Go 1.2 table.
+ t.Skip("not relevant to Go 1.2 symbol table")
+ }
for _, o := range tab.Files {
// A source file can appear multiple times in a
@@ -190,7 +203,7 @@ func TestLineAline(t *testing.T) {
}
func TestPCLine(t *testing.T) {
- if !dotest() {
+ if !dotest(false) {
return
}
defer endtest()
@@ -206,16 +219,17 @@ func TestPCLine(t *testing.T) {
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
+ if textdat[off] == 255 {
+ break
+ }
wantLine += int(textdat[off])
- t.Logf("off is %d", off)
+ t.Logf("off is %d %#x (max %d)", off, textdat[off], sym.End-pc)
+ file, line, fn := tab.PCToLine(pc)
if fn == nil {
t.Errorf("failed to get line of PC %#x", pc)
- } else if !strings.HasSuffix(file, "pclinetest.asm") {
- t.Errorf("expected %s (%s) at PC %#x, got %s (%s)", "pclinetest.asm", sym.Name, pc, file, fn.Name)
- } else if line != wantLine || fn != sym {
- t.Errorf("expected :%d (%s) at PC %#x, got :%d (%s)", wantLine, sym.Name, pc, line, fn.Name)
+ } else if !strings.HasSuffix(file, "pclinetest.asm") || line != wantLine || fn != sym {
+ t.Errorf("PCToLine(%#x) = %s:%d (%s), want %s:%d (%s)", pc, file, line, fn.Name, "pclinetest.asm", wantLine, sym.Name)
}
}
@@ -227,6 +241,9 @@ func TestPCLine(t *testing.T) {
for pc := sym.Value; pc < sym.End; pc += 2 + uint64(textdat[off]) {
file, line, fn := tab.PCToLine(pc)
off = pc - text.Addr
+ if textdat[off] == 255 {
+ break
+ }
wantLine += int(textdat[off])
if line != wantLine {
t.Errorf("expected line %d at PC %#x in pcfromline, got %d", wantLine, pc, line)
diff --git a/src/pkg/debug/gosym/symtab.go b/src/pkg/debug/gosym/symtab.go
index 81ed4fb27..6a60b51e3 100644
--- a/src/pkg/debug/gosym/symtab.go
+++ b/src/pkg/debug/gosym/symtab.go
@@ -77,10 +77,26 @@ type Func struct {
Obj *Obj
}
-// An Obj represents a single object file.
+// An Obj represents a collection of functions in a symbol table.
+//
+// The exact method of division of a binary into separate Objs is an internal detail
+// of the symbol table format.
+//
+// In early versions of Go each source file became a different Obj.
+//
+// In Go 1 and Go 1.1, each package produced one Obj for all Go sources
+// and one Obj per C source file.
+//
+// In Go 1.2, there is a single Obj for the entire program.
type Obj struct {
+ // Funcs is a list of functions in the Obj.
Funcs []Func
- Paths []Sym
+
+ // In Go 1.1 and earlier, Paths is a list of symbols corresponding
+ // to the source file names that produced the Obj.
+ // In Go 1.2, Paths is nil.
+ // Use the keys of Table.Files to obtain a list of source files.
+ Paths []Sym // meta
}
/*
@@ -93,9 +109,10 @@ type Obj struct {
type Table struct {
Syms []Sym
Funcs []Func
- Files map[string]*Obj
- Objs []Obj
- // textEnd uint64;
+ Files map[string]*Obj // nil for Go 1.2 and later binaries
+ Objs []Obj // nil for Go 1.2 and later binaries
+
+ go12line *LineTable // Go 1.2 line number table
}
type sym struct {
@@ -105,10 +122,11 @@ type sym struct {
name []byte
}
-var littleEndianSymtab = []byte{0xFD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00}
-var bigEndianSymtab = []byte{0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x00, 0x00}
-
-var oldLittleEndianSymtab = []byte{0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00}
+var (
+ littleEndianSymtab = []byte{0xFD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00}
+ bigEndianSymtab = []byte{0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x00, 0x00}
+ oldLittleEndianSymtab = []byte{0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00}
+)
func walksymtab(data []byte, fn func(sym) error) error {
var order binary.ByteOrder = binary.BigEndian
@@ -260,6 +278,9 @@ func NewTable(symtab []byte, pcln *LineTable) (*Table, error) {
}
var t Table
+ if pcln.isGo12() {
+ t.go12line = pcln
+ }
fname := make(map[uint16]string)
t.Syms = make([]Sym, 0, n)
nf := 0
@@ -316,17 +337,29 @@ func NewTable(symtab []byte, pcln *LineTable) (*Table, error) {
}
t.Funcs = make([]Func, 0, nf)
- t.Objs = make([]Obj, 0, nz)
t.Files = make(map[string]*Obj)
+ var obj *Obj
+ if t.go12line != nil {
+ // Put all functions into one Obj.
+ t.Objs = make([]Obj, 1)
+ obj = &t.Objs[0]
+ t.go12line.go12MapFiles(t.Files, obj)
+ } else {
+ t.Objs = make([]Obj, 0, nz)
+ }
+
// 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
+ if t.go12line != nil {
+ // Go 1.2 binaries have the file information elsewhere. Ignore.
+ break
+ }
// Finish the current object
if obj != nil {
obj.Funcs = t.Funcs[lastf:]
@@ -395,7 +428,12 @@ func NewTable(symtab []byte, pcln *LineTable) (*Table, error) {
fn.Sym = sym
fn.Entry = sym.Value
fn.Obj = obj
- if pcln != nil {
+ if t.go12line != nil {
+ // All functions share the same line table.
+ // It knows how to narrow down to a specific
+ // function quickly.
+ fn.LineTable = t.go12line
+ } else if pcln != nil {
fn.LineTable = pcln.slice(fn.Entry)
pcln = fn.LineTable
}
@@ -448,18 +486,32 @@ 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))
+ if t.go12line != nil {
+ file = t.go12line.go12PCToFile(pc)
+ line = t.go12line.go12PCToLine(pc)
+ } else {
+ 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
+// the named file. It 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 error) {
obj, ok := t.Files[file]
if !ok {
return 0, nil, UnknownFileError(file)
}
+
+ if t.go12line != nil {
+ pc := t.go12line.go12LineToPC(file, line)
+ if pc == 0 {
+ return 0, nil, &UnknownLineError{file, line}
+ }
+ return pc, t.PCToFunc(pc), nil
+ }
+
abs, err := obj.alineFromLine(file, line)
if err != nil {
return
@@ -503,9 +555,7 @@ func (t *Table) LookupFunc(name string) *Func {
}
// 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 {
@@ -522,6 +572,13 @@ func (t *Table) SymByAddr(addr uint64) *Sym {
* Object files
*/
+// This is legacy code for Go 1.1 and earlier, which used the
+// Plan 9 format for pc-line tables. This code was never quite
+// correct. It's probably very close, and it's usually correct, but
+// we never quite found all the corner cases.
+//
+// Go 1.2 and later use a simpler format, documented at golang.org/s/go12symtab.
+
func (o *Obj) lineFromAline(aline int) (string, int) {
type stackEnt struct {
path string
@@ -533,8 +590,6 @@ func (o *Obj) lineFromAline(aline int) (string, int) {
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)