Commit d7b4a09c authored by Russ Cox's avatar Russ Cox

debug/gosym: update for Go 1.2 pcln table

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/11495043
parent 39100543
......@@ -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
......
This diff is collapsed.
......@@ -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)
......
......@@ -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)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment