diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2013-11-06 19:49:01 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2013-11-06 19:49:01 +0000 |
commit | f038dae646bac2b31be98ab592c0e5206d2d96f5 (patch) | |
tree | 39530b071991b2326f881b2a30a2d82d6c133fd6 /libgo/go/debug | |
parent | f20f261304993444741e0f0a14d3147e591bc660 (diff) | |
download | gcc-f038dae646bac2b31be98ab592c0e5206d2d96f5.tar.gz |
libgo: Update to October 24 version of master library.
From-SVN: r204466
Diffstat (limited to 'libgo/go/debug')
-rw-r--r-- | libgo/go/debug/dwarf/entry.go | 7 | ||||
-rw-r--r-- | libgo/go/debug/dwarf/type.go | 43 | ||||
-rw-r--r-- | libgo/go/debug/elf/file_test.go | 1 | ||||
-rw-r--r-- | libgo/go/debug/gosym/pclntab.go | 360 | ||||
-rw-r--r-- | libgo/go/debug/gosym/pclntab_test.go | 46 | ||||
-rw-r--r-- | libgo/go/debug/gosym/symtab.go | 93 |
6 files changed, 496 insertions, 54 deletions
diff --git a/libgo/go/debug/dwarf/entry.go b/libgo/go/debug/dwarf/entry.go index ada723163a4..4775283ca3a 100644 --- a/libgo/go/debug/dwarf/entry.go +++ b/libgo/go/debug/dwarf/entry.go @@ -10,7 +10,10 @@ package dwarf -import "errors" +import ( + "errors" + "strconv" +) // a single entry's description: a sequence of attributes type abbrev struct { @@ -152,7 +155,7 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry { var val interface{} switch fmt { default: - b.error("unknown entry attr format") + b.error("unknown entry attr format 0x" + strconv.FormatInt(int64(fmt), 16)) // address case formAddr: diff --git a/libgo/go/debug/dwarf/type.go b/libgo/go/debug/dwarf/type.go index 54000fbd75e..1fbae6c144e 100644 --- a/libgo/go/debug/dwarf/type.go +++ b/libgo/go/debug/dwarf/type.go @@ -271,24 +271,43 @@ func (d *Data) Type(off Offset) (Type, error) { // d.Type recursively, to handle circular types correctly. var typ Type + nextDepth := 0 + // Get next child; set err if error happens. next := func() *Entry { if !e.Children { return nil } - kid, err1 := r.Next() - if err1 != nil { - err = err1 - return nil - } - if kid == nil { - err = DecodeError{"info", r.b.off, "unexpected end of DWARF entries"} - return nil - } - if kid.Tag == 0 { - return nil + // Only return direct children. + // Skip over composite entries that happen to be nested + // inside this one. Most DWARF generators wouldn't generate + // such a thing, but clang does. + // See golang.org/issue/6472. + for { + kid, err1 := r.Next() + if err1 != nil { + err = err1 + return nil + } + if kid == nil { + err = DecodeError{"info", r.b.off, "unexpected end of DWARF entries"} + return nil + } + if kid.Tag == 0 { + if nextDepth > 0 { + nextDepth-- + continue + } + return nil + } + if kid.Children { + nextDepth++ + } + if nextDepth > 0 { + continue + } + return kid } - return kid } // Get Type referred to by Entry's AttrType field. diff --git a/libgo/go/debug/elf/file_test.go b/libgo/go/debug/elf/file_test.go index f9aa7265af0..38b5f9e7075 100644 --- a/libgo/go/debug/elf/file_test.go +++ b/libgo/go/debug/elf/file_test.go @@ -166,6 +166,7 @@ func TestOpen(t *testing.T) { } else { f, err = Open(tt.file) } + defer f.Close() if err != nil { t.Errorf("cannot open file %s: %v", tt.file, err) continue diff --git a/libgo/go/debug/gosym/pclntab.go b/libgo/go/debug/gosym/pclntab.go index 9d7b0d15f36..3e6a8046b3e 100644 --- a/libgo/go/debug/gosym/pclntab.go +++ b/libgo/go/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+5*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+4*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+4*4:]) + linetab := t.binary.Uint32(f[t.ptrsize+5*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/libgo/go/debug/gosym/pclntab_test.go b/libgo/go/debug/gosym/pclntab_test.go index 20acba612f2..35502e8c395 100644 --- a/libgo/go/debug/gosym/pclntab_test.go +++ b/libgo/go/debug/gosym/pclntab_test.go @@ -21,9 +21,17 @@ 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 + } + // Command below expects "sh", so Unix. + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { return false } if pclinetestBinary != "" { @@ -41,7 +49,7 @@ 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", + 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 +108,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 +160,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 +206,7 @@ func TestLineAline(t *testing.T) { } func TestPCLine(t *testing.T) { - if !dotest() { + if !dotest(false) { return } defer endtest() @@ -206,16 +222,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 +244,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/libgo/go/debug/gosym/symtab.go b/libgo/go/debug/gosym/symtab.go index 81ed4fb27dc..9ab05bac2f7 100644 --- a/libgo/go/debug/gosym/symtab.go +++ b/libgo/go/debug/gosym/symtab.go @@ -34,7 +34,7 @@ type Sym struct { Func *Func } -// Static returns whether this symbol is static (not visible outside its file). +// Static reports 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, @@ -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) |