ogle/program/server: provide PC->source evaluation For now, we must do this with the old Plan 9 symbol pclntab because the DWARF stuff seems corrupt. LGTM=nigeltao R=nigeltao https://golang.org/cl/86380045
diff --git a/program/server/server.go b/program/server/server.go index 315ce2d..2835bdc 100644 --- a/program/server/server.go +++ b/program/server/server.go
@@ -15,6 +15,8 @@ "sync" "syscall" + "code.google.com/p/ogle/gosym" + "code.google.com/p/ogle/debug/dwarf" "code.google.com/p/ogle/debug/elf" "code.google.com/p/ogle/debug/macho" @@ -33,6 +35,7 @@ arch arch.Architecture executable string // Name of executable. dwarfData *dwarf.Data + table *gosym.Table mu sync.Mutex @@ -55,7 +58,7 @@ return nil, err } defer fd.Close() - architecture, dwarfData, err := loadExecutable(fd) + architecture, dwarfData, table, err := loadExecutable(fd) if err != nil { return nil, err } @@ -63,6 +66,7 @@ arch: *architecture, executable: executable, dwarfData: dwarfData, + table: table, fc: make(chan func() error), ec: make(chan error), breakpoints: make(map[uint64]breakpoint), @@ -71,42 +75,76 @@ return srv, nil } -func loadExecutable(f *os.File) (*arch.Architecture, *dwarf.Data, error) { +func loadExecutable(f *os.File) (*arch.Architecture, *dwarf.Data, *gosym.Table, error) { // TODO: How do we detect NaCl? if obj, err := elf.NewFile(f); err == nil { dwarfData, err := obj.DWARF() if err != nil { - return nil, nil, err + return nil, nil, nil, err } + + table, err := parseElf(obj) + if err != nil { + return nil, nil, nil, fmt.Errorf("parsing go symbol table: %v", err) + } + switch obj.Machine { case elf.EM_ARM: - return &arch.ARM, dwarfData, nil + return &arch.ARM, dwarfData, table, nil case elf.EM_386: switch obj.Class { case elf.ELFCLASS32: - return &arch.X86, dwarfData, nil + return &arch.X86, dwarfData, table, nil case elf.ELFCLASS64: - return &arch.AMD64, dwarfData, nil + return &arch.AMD64, dwarfData, table, nil } case elf.EM_X86_64: - return &arch.AMD64, dwarfData, nil + return &arch.AMD64, dwarfData, table, nil } - return nil, nil, fmt.Errorf("unrecognized ELF architecture") + return nil, nil, nil, fmt.Errorf("unrecognized ELF architecture") } if obj, err := macho.NewFile(f); err == nil { dwarfData, err := obj.DWARF() if err != nil { - return nil, nil, err + return nil, nil, nil, err } + + /* TODO + table, err := parseMachO(obj) + if err != nil { + return nil, nil, nil, err + } + */ switch obj.Cpu { case macho.Cpu386: - return &arch.X86, dwarfData, nil + return &arch.X86, dwarfData, nil, nil case macho.CpuAmd64: - return &arch.AMD64, dwarfData, nil + return &arch.AMD64, dwarfData, nil, nil } - return nil, nil, fmt.Errorf("unrecognized Mach-O architecture") + return nil, nil, nil, fmt.Errorf("unrecognized Mach-O architecture") } - return nil, nil, fmt.Errorf("unrecognized binary format") + return nil, nil, nil, fmt.Errorf("unrecognized binary format") +} + +// parseElf returns the gosym.Table representation of the old symbol tables. +// TODO: Delete this once we know how to get PC/line data out of DWARF. +func parseElf(f *elf.File) (*gosym.Table, error) { + symdat, err := f.Section(".gosymtab").Data() // TODO unused. + if err != nil { + return nil, err + } + pclndat, err := f.Section(".gopclntab").Data() + if err != nil { + return nil, err + } + + pcln := gosym.NewLineTable(pclndat, f.Section(".text").Addr) + tab, err := gosym.NewTable(symdat, pcln) + if err != nil { + return nil, err + } + + return tab, nil } type file struct { @@ -369,6 +407,18 @@ } return []string{fmt.Sprintf("%#x", addr)}, nil + case strings.HasPrefix(expr, "src:"): + // Numerical address. Return file.go:123. + addr, err := strconv.ParseUint(expr[4:], 0, 0) + if err != nil { + return nil, err + } + file, line, ok := s.lookupSource(addr) + if !ok { + return nil, fmt.Errorf("no PC/line data for: %q", expr) + } + return []string{fmt.Sprintf("%s:%d", file, line)}, nil + case len(expr) > 0 && '0' <= expr[0] && expr[0] <= '9': // Numerical address. Return symbol. addr, err := strconv.ParseUint(expr, 0, 0) @@ -385,6 +435,14 @@ return nil, fmt.Errorf("bad expression syntax: %q", expr) } +func (s *Server) lookupSource(pc uint64) (file string, line int, ok bool) { + if s.table == nil { + return + } + file, line, fn := s.table.PCToLine(pc) + return file, line, fn != nil +} + // evalAddress takes a simple expression, either a symbol or hex value, // and evaluates it as an address. func (s *Server) evalAddress(expr string) (uint64, error) {