proc refactor: split out BinaryInfo implementation (#745)

* proc: refactor BinaryInfo part of proc.Process to own type

The data structures and associated code used by proc.Process
to implement target.BinaryInfo will also be useful to support a
backend for examining core dumps, split this part of proc.Process
to a different type.

* proc: compile support for all executable formats unconditionally

So far we only compiled in support for loading the executable format
supported by the host operating system.
Once support for core files is introduced it is however useful to
support loading in all executable formats, there is no reason why it
shouldn't be possible to examine a linux coredump on windows, or
viceversa.

* proc: bugfix: do not resume threads on detach if killing

* Replace BinaryInfo interface with BinInfo() method returning proc.BinaryInfo
This commit is contained in:
Alessandro Arzilli 2017-04-06 20:14:01 +02:00 committed by Derek Parker
parent 7b19fe9e69
commit 436a3c2149
22 changed files with 748 additions and 677 deletions

@ -1,7 +1,5 @@
package proc package proc
import "runtime"
// Arch defines an interface for representing a // Arch defines an interface for representing a
// CPU architecture. // CPU architecture.
type Arch interface { type Arch interface {
@ -19,11 +17,12 @@ type AMD64 struct {
breakInstructionLen int breakInstructionLen int
gStructOffset uint64 gStructOffset uint64
hardwareBreakpointUsage []bool hardwareBreakpointUsage []bool
goos string
} }
// AMD64Arch returns an initialized AMD64 // AMD64Arch returns an initialized AMD64
// struct. // struct.
func AMD64Arch() *AMD64 { func AMD64Arch(goos string) *AMD64 {
var breakInstr = []byte{0xCC} var breakInstr = []byte{0xCC}
return &AMD64{ return &AMD64{
@ -31,6 +30,7 @@ func AMD64Arch() *AMD64 {
breakInstruction: breakInstr, breakInstruction: breakInstr,
breakInstructionLen: len(breakInstr), breakInstructionLen: len(breakInstr),
hardwareBreakpointUsage: make([]bool, 4), hardwareBreakpointUsage: make([]bool, 4),
goos: goos,
} }
} }
@ -38,7 +38,7 @@ func AMD64Arch() *AMD64 {
// arch struct. The offset is dependent on the Go compiler Version // arch struct. The offset is dependent on the Go compiler Version
// and whether or not the target program was externally linked. // and whether or not the target program was externally linked.
func (a *AMD64) SetGStructOffset(ver GoVersion, isextld bool) { func (a *AMD64) SetGStructOffset(ver GoVersion, isextld bool) {
switch runtime.GOOS { switch a.goos {
case "darwin": case "darwin":
a.gStructOffset = 0x8a0 a.gStructOffset = 0x8a0
case "linux": case "linux":

513
pkg/proc/bininfo.go Normal file

@ -0,0 +1,513 @@
package proc
import (
"debug/gosym"
"debug/pe"
"errors"
"fmt"
"io"
"os"
"sync"
"time"
"github.com/derekparker/delve/pkg/dwarf/frame"
"github.com/derekparker/delve/pkg/dwarf/line"
"github.com/derekparker/delve/pkg/dwarf/reader"
"golang.org/x/debug/dwarf"
"golang.org/x/debug/elf"
"golang.org/x/debug/macho"
)
type BinaryInfo struct {
lastModified time.Time // Time the executable of this process was last modified
goos string
closer io.Closer
// Maps package names to package paths, needed to lookup types inside DWARF info
packageMap map[string]string
arch Arch
dwarf *dwarf.Data
frameEntries frame.FrameDescriptionEntries
lineInfo line.DebugLines
goSymTable *gosym.Table
types map[string]dwarf.Offset
functions []functionDebugInfo
loadModuleDataOnce sync.Once
moduleData []moduleData
nameOfRuntimeType map[uintptr]nameOfRuntimeTypeEntry
}
var UnsupportedLinuxArchErr = errors.New("unsupported architecture - only linux/amd64 is supported")
var UnsupportedWindowsArchErr = errors.New("unsupported architecture of windows/386 - only windows/amd64 is supported")
var UnsupportedDarwinArchErr = errors.New("unsupported architecture - only darwin/amd64 is supported")
func NewBinaryInfo(goos, goarch string) BinaryInfo {
r := BinaryInfo{goos: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry)}
// TODO: find better way to determine proc arch (perhaps use executable file info)
switch goarch {
case "amd64":
r.arch = AMD64Arch(goos)
}
return r
}
func (bininfo *BinaryInfo) LoadBinaryInfo(path string, wg *sync.WaitGroup) error {
fi, err := os.Stat(path)
if err == nil {
bininfo.lastModified = fi.ModTime()
}
switch bininfo.goos {
case "linux":
return bininfo.LoadBinaryInfoElf(path, wg)
case "windows":
return bininfo.LoadBinaryInfoPE(path, wg)
case "darwin":
return bininfo.LoadBinaryInfoMacho(path, wg)
}
return errors.New("unsupported operating system")
}
func (bi *BinaryInfo) LastModified() time.Time {
return bi.lastModified
}
// DwarfReader returns a reader for the dwarf data
func (bi *BinaryInfo) DwarfReader() *reader.Reader {
return reader.New(bi.dwarf)
}
// Sources returns list of source files that comprise the debugged binary.
func (bi *BinaryInfo) Sources() map[string]*gosym.Obj {
return bi.goSymTable.Files
}
// Funcs returns list of functions present in the debugged program.
func (bi *BinaryInfo) Funcs() []gosym.Func {
return bi.goSymTable.Funcs
}
// Types returns list of types present in the debugged program.
func (bi *BinaryInfo) Types() ([]string, error) {
types := make([]string, 0, len(bi.types))
for k := range bi.types {
types = append(types, k)
}
return types, nil
}
// PCToLine converts an instruction address to a file/line/function.
func (bi *BinaryInfo) PCToLine(pc uint64) (string, int, *gosym.Func) {
return bi.goSymTable.PCToLine(pc)
}
func (bi *BinaryInfo) Close() error {
return bi.closer.Close()
}
// ELF ///////////////////////////////////////////////////////////////
func (bi *BinaryInfo) LoadBinaryInfoElf(path string, wg *sync.WaitGroup) error {
exe, err := os.OpenFile(path, 0, os.ModePerm)
if err != nil {
return err
}
bi.closer = exe
elfFile, err := elf.NewFile(exe)
if err != nil {
return err
}
if elfFile.Machine != elf.EM_X86_64 {
return UnsupportedLinuxArchErr
}
bi.dwarf, err = elfFile.DWARF()
if err != nil {
return err
}
wg.Add(4)
go bi.parseDebugFrameElf(elfFile, wg)
go bi.obtainGoSymbolsElf(elfFile, wg)
go bi.parseDebugLineInfoElf(elfFile, wg)
go bi.loadDebugInfoMaps(wg)
return nil
}
func (bi *BinaryInfo) parseDebugFrameElf(exe *elf.File, wg *sync.WaitGroup) {
defer wg.Done()
debugFrameSec := exe.Section(".debug_frame")
debugInfoSec := exe.Section(".debug_info")
if debugFrameSec != nil && debugInfoSec != nil {
debugFrame, err := exe.Section(".debug_frame").Data()
if err != nil {
fmt.Println("could not get .debug_frame section", err)
os.Exit(1)
}
dat, err := debugInfoSec.Data()
if err != nil {
fmt.Println("could not get .debug_info section", err)
os.Exit(1)
}
bi.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
} else {
fmt.Println("could not find .debug_frame section in binary")
os.Exit(1)
}
}
func (bi *BinaryInfo) obtainGoSymbolsElf(exe *elf.File, wg *sync.WaitGroup) {
defer wg.Done()
var (
symdat []byte
pclndat []byte
err error
)
if sec := exe.Section(".gosymtab"); sec != nil {
symdat, err = sec.Data()
if err != nil {
fmt.Println("could not get .gosymtab section", err)
os.Exit(1)
}
}
if sec := exe.Section(".gopclntab"); sec != nil {
pclndat, err = sec.Data()
if err != nil {
fmt.Println("could not get .gopclntab section", err)
os.Exit(1)
}
}
pcln := gosym.NewLineTable(pclndat, exe.Section(".text").Addr)
tab, err := gosym.NewTable(symdat, pcln)
if err != nil {
fmt.Println("could not get initialize line table", err)
os.Exit(1)
}
bi.goSymTable = tab
}
func (bi *BinaryInfo) parseDebugLineInfoElf(exe *elf.File, wg *sync.WaitGroup) {
defer wg.Done()
if sec := exe.Section(".debug_line"); sec != nil {
debugLine, err := exe.Section(".debug_line").Data()
if err != nil {
fmt.Println("could not get .debug_line section", err)
os.Exit(1)
}
bi.lineInfo = line.Parse(debugLine)
} else {
fmt.Println("could not find .debug_line section in binary")
os.Exit(1)
}
}
// PE ////////////////////////////////////////////////////////////////
func (bi *BinaryInfo) LoadBinaryInfoPE(path string, wg *sync.WaitGroup) error {
peFile, closer, err := openExecutablePathPE(path)
if err != nil {
return err
}
bi.closer = closer
if peFile.Machine != pe.IMAGE_FILE_MACHINE_AMD64 {
return UnsupportedWindowsArchErr
}
bi.dwarf, err = dwarfFromPE(peFile)
if err != nil {
return err
}
wg.Add(4)
go bi.parseDebugFramePE(peFile, wg)
go bi.obtainGoSymbolsPE(peFile, wg)
go bi.parseDebugLineInfoPE(peFile, wg)
go bi.loadDebugInfoMaps(wg)
return nil
}
func openExecutablePathPE(path string) (*pe.File, io.Closer, error) {
f, err := os.OpenFile(path, 0, os.ModePerm)
if err != nil {
return nil, nil, err
}
peFile, err := pe.NewFile(f)
if err != nil {
f.Close()
return nil, nil, err
}
return peFile, f, nil
}
// Adapted from src/debug/pe/file.go: pe.(*File).DWARF()
func dwarfFromPE(f *pe.File) (*dwarf.Data, error) {
// There are many other DWARF sections, but these
// are the ones the debug/dwarf package uses.
// Don't bother loading others.
var names = [...]string{"abbrev", "info", "line", "str"}
var dat [len(names)][]byte
for i, name := range names {
name = ".debug_" + name
s := f.Section(name)
if s == nil {
continue
}
b, err := s.Data()
if err != nil && uint32(len(b)) < s.Size {
return nil, err
}
if 0 < s.VirtualSize && s.VirtualSize < s.Size {
b = b[:s.VirtualSize]
}
dat[i] = b
}
abbrev, info, line, str := dat[0], dat[1], dat[2], dat[3]
return dwarf.New(abbrev, nil, nil, info, line, nil, nil, str)
}
func (bi *BinaryInfo) parseDebugFramePE(exe *pe.File, wg *sync.WaitGroup) {
defer wg.Done()
debugFrameSec := exe.Section(".debug_frame")
debugInfoSec := exe.Section(".debug_info")
if debugFrameSec != nil && debugInfoSec != nil {
debugFrame, err := debugFrameSec.Data()
if err != nil && uint32(len(debugFrame)) < debugFrameSec.Size {
fmt.Println("could not get .debug_frame section", err)
os.Exit(1)
}
if 0 < debugFrameSec.VirtualSize && debugFrameSec.VirtualSize < debugFrameSec.Size {
debugFrame = debugFrame[:debugFrameSec.VirtualSize]
}
dat, err := debugInfoSec.Data()
if err != nil {
fmt.Println("could not get .debug_info section", err)
os.Exit(1)
}
bi.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
} else {
fmt.Println("could not find .debug_frame section in binary")
os.Exit(1)
}
}
func (dbp *BinaryInfo) obtainGoSymbolsPE(exe *pe.File, wg *sync.WaitGroup) {
defer wg.Done()
_, symdat, pclndat, err := pclnPE(exe)
if err != nil {
fmt.Println("could not get Go symbols", err)
os.Exit(1)
}
pcln := gosym.NewLineTable(pclndat, uint64(exe.Section(".text").Offset))
tab, err := gosym.NewTable(symdat, pcln)
if err != nil {
fmt.Println("could not get initialize line table", err)
os.Exit(1)
}
dbp.goSymTable = tab
}
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
for _, s := range f.Symbols {
if s.Name != name {
continue
}
if s.SectionNumber <= 0 {
return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
}
if len(f.Sections) < int(s.SectionNumber) {
return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
}
return s, nil
}
return nil, fmt.Errorf("no %s symbol found", name)
}
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
func loadPETable(f *pe.File, sname, ename string) ([]byte, error) {
ssym, err := findPESymbol(f, sname)
if err != nil {
return nil, err
}
esym, err := findPESymbol(f, ename)
if err != nil {
return nil, err
}
if ssym.SectionNumber != esym.SectionNumber {
return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename)
}
sect := f.Sections[ssym.SectionNumber-1]
data, err := sect.Data()
if err != nil {
return nil, err
}
return data[ssym.Value:esym.Value], nil
}
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
func pclnPE(exe *pe.File) (textStart uint64, symtab, pclntab []byte, err error) {
var imageBase uint64
switch oh := exe.OptionalHeader.(type) {
case *pe.OptionalHeader32:
imageBase = uint64(oh.ImageBase)
case *pe.OptionalHeader64:
imageBase = oh.ImageBase
default:
return 0, nil, nil, fmt.Errorf("pe file format not recognized")
}
if sect := exe.Section(".text"); sect != nil {
textStart = imageBase + uint64(sect.VirtualAddress)
}
if pclntab, err = loadPETable(exe, "runtime.pclntab", "runtime.epclntab"); err != nil {
// We didn't find the symbols, so look for the names used in 1.3 and earlier.
// TODO: Remove code looking for the old symbols when we no longer care about 1.3.
var err2 error
if pclntab, err2 = loadPETable(exe, "pclntab", "epclntab"); err2 != nil {
return 0, nil, nil, err
}
}
if symtab, err = loadPETable(exe, "runtime.symtab", "runtime.esymtab"); err != nil {
// Same as above.
var err2 error
if symtab, err2 = loadPETable(exe, "symtab", "esymtab"); err2 != nil {
return 0, nil, nil, err
}
}
return textStart, symtab, pclntab, nil
}
func (bi *BinaryInfo) parseDebugLineInfoPE(exe *pe.File, wg *sync.WaitGroup) {
defer wg.Done()
if sec := exe.Section(".debug_line"); sec != nil {
debugLine, err := sec.Data()
if err != nil && uint32(len(debugLine)) < sec.Size {
fmt.Println("could not get .debug_line section", err)
os.Exit(1)
}
if 0 < sec.VirtualSize && sec.VirtualSize < sec.Size {
debugLine = debugLine[:sec.VirtualSize]
}
bi.lineInfo = line.Parse(debugLine)
} else {
fmt.Println("could not find .debug_line section in binary")
os.Exit(1)
}
}
// MACH-O ////////////////////////////////////////////////////////////
func (bi *BinaryInfo) LoadBinaryInfoMacho(path string, wg *sync.WaitGroup) error {
exe, err := macho.Open(path)
if err != nil {
return err
}
bi.closer = exe
if exe.Cpu != macho.CpuAmd64 {
return UnsupportedDarwinArchErr
}
bi.dwarf, err = exe.DWARF()
if err != nil {
return err
}
wg.Add(4)
go bi.parseDebugFrameMacho(exe, wg)
go bi.obtainGoSymbolsMacho(exe, wg)
go bi.parseDebugLineInfoMacho(exe, wg)
go bi.loadDebugInfoMaps(wg)
return nil
}
func (bi *BinaryInfo) parseDebugFrameMacho(exe *macho.File, wg *sync.WaitGroup) {
defer wg.Done()
debugFrameSec := exe.Section("__debug_frame")
debugInfoSec := exe.Section("__debug_info")
if debugFrameSec != nil && debugInfoSec != nil {
debugFrame, err := exe.Section("__debug_frame").Data()
if err != nil {
fmt.Println("could not get __debug_frame section", err)
os.Exit(1)
}
dat, err := debugInfoSec.Data()
if err != nil {
fmt.Println("could not get .debug_info section", err)
os.Exit(1)
}
bi.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
} else {
fmt.Println("could not find __debug_frame section in binary")
os.Exit(1)
}
}
func (bi *BinaryInfo) obtainGoSymbolsMacho(exe *macho.File, wg *sync.WaitGroup) {
defer wg.Done()
var (
symdat []byte
pclndat []byte
err error
)
if sec := exe.Section("__gosymtab"); sec != nil {
symdat, err = sec.Data()
if err != nil {
fmt.Println("could not get .gosymtab section", err)
os.Exit(1)
}
}
if sec := exe.Section("__gopclntab"); sec != nil {
pclndat, err = sec.Data()
if err != nil {
fmt.Println("could not get .gopclntab section", err)
os.Exit(1)
}
}
pcln := gosym.NewLineTable(pclndat, exe.Section("__text").Addr)
tab, err := gosym.NewTable(symdat, pcln)
if err != nil {
fmt.Println("could not get initialize line table", err)
os.Exit(1)
}
bi.goSymTable = tab
}
func (bi *BinaryInfo) parseDebugLineInfoMacho(exe *macho.File, wg *sync.WaitGroup) {
defer wg.Done()
if sec := exe.Section("__debug_line"); sec != nil {
debugLine, err := exe.Section("__debug_line").Data()
if err != nil {
fmt.Println("could not get __debug_line section", err)
os.Exit(1)
}
bi.lineInfo = line.Parse(debugLine)
} else {
fmt.Println("could not find __debug_line section in binary")
os.Exit(1)
}
}

@ -104,7 +104,7 @@ func (iae InvalidAddressError) Error() string {
} }
func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error { func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error {
_, err := thread.writeMemory(uintptr(addr), dbp.arch.BreakpointInstruction()) _, err := thread.writeMemory(uintptr(addr), dbp.bi.arch.BreakpointInstruction())
return err return err
} }

@ -47,7 +47,7 @@ func (thread *Thread) Disassemble(startPC, endPC uint64, currentGoroutine bool)
mem[i] = bp.OriginalData[i] mem[i] = bp.OriginalData[i]
} }
} }
file, line, fn := thread.dbp.PCToLine(pc) file, line, fn := thread.dbp.bi.PCToLine(pc)
loc := Location{PC: pc, File: file, Line: line, Fn: fn} loc := Location{PC: pc, File: file, Line: line, Fn: fn}
inst, err := asmDecode(mem, pc) inst, err := asmDecode(mem, pc)
if err == nil { if err == nil {

@ -109,7 +109,7 @@ func (thread *Thread) resolveCallArg(inst *ArchInst, currentGoroutine bool, regs
return nil return nil
} }
file, line, fn := thread.dbp.PCToLine(pc) file, line, fn := thread.dbp.bi.PCToLine(pc)
if fn == nil { if fn == nil {
return nil return nil
} }

@ -141,7 +141,7 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
fnnode = p.X fnnode = p.X
} }
styp, err := scope.Thread.dbp.findTypeExpr(fnnode) styp, err := scope.Thread.dbp.bi.findTypeExpr(fnnode)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -454,7 +454,7 @@ func (scope *EvalScope) evalIdent(node *ast.Ident) (*Variable, error) {
return v, nil return v, nil
} }
// if it's not a local variable then it could be a package variable w/o explicit package name // if it's not a local variable then it could be a package variable w/o explicit package name
_, _, fn := scope.Thread.dbp.PCToLine(scope.PC) _, _, fn := scope.Thread.dbp.bi.PCToLine(scope.PC)
if fn != nil { if fn != nil {
if v, err = scope.packageVarAddr(fn.PackageName() + "." + node.Name); err == nil { if v, err = scope.packageVarAddr(fn.PackageName() + "." + node.Name); err == nil {
v.Name = node.Name v.Name = node.Name
@ -492,7 +492,7 @@ func (scope *EvalScope) evalTypeAssert(node *ast.TypeAssertExpr) (*Variable, err
if xv.Children[0].Addr == 0 { if xv.Children[0].Addr == 0 {
return nil, fmt.Errorf("interface conversion: %s is nil, not %s", xv.DwarfType.String(), exprToString(node.Type)) return nil, fmt.Errorf("interface conversion: %s is nil, not %s", xv.DwarfType.String(), exprToString(node.Type))
} }
typ, err := scope.Thread.dbp.findTypeExpr(node.Type) typ, err := scope.Thread.dbp.bi.findTypeExpr(node.Type)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -637,7 +637,7 @@ func (scope *EvalScope) evalAddrOf(node *ast.UnaryExpr) (*Variable, error) {
xev.OnlyAddr = true xev.OnlyAddr = true
typename := "*" + xev.DwarfType.Common().Name typename := "*" + xev.DwarfType.Common().Name
rv := scope.newVariable("", 0, &dwarf.PtrType{CommonType: dwarf.CommonType{ByteSize: int64(scope.Thread.dbp.arch.PtrSize()), Name: typename}, Type: xev.DwarfType}) rv := scope.newVariable("", 0, &dwarf.PtrType{CommonType: dwarf.CommonType{ByteSize: int64(scope.Thread.dbp.bi.arch.PtrSize()), Name: typename}, Type: xev.DwarfType})
rv.Children = []Variable{*xev} rv.Children = []Variable{*xev}
rv.loaded = true rv.loaded = true

@ -11,9 +11,9 @@ type moduleData struct {
typemapVar *Variable typemapVar *Variable
} }
func (dbp *Process) loadModuleData() (err error) { func (bi *BinaryInfo) loadModuleData(thread *Thread) (err error) {
dbp.loadModuleDataOnce.Do(func() { bi.loadModuleDataOnce.Do(func() {
scope := &EvalScope{Thread: dbp.currentThread, PC: 0, CFA: 0} scope, _ := thread.Scope()
var md *Variable var md *Variable
md, err = scope.packageVarAddr("runtime.firstmoduledata") md, err = scope.packageVarAddr("runtime.firstmoduledata")
if err != nil { if err != nil {
@ -43,7 +43,7 @@ func (dbp *Process) loadModuleData() (err error) {
return return
} }
dbp.moduleData = append(dbp.moduleData, moduleData{uintptr(types), uintptr(etypes), typemapVar}) bi.moduleData = append(bi.moduleData, moduleData{uintptr(types), uintptr(etypes), typemapVar})
md = nextVar.maybeDereference() md = nextVar.maybeDereference()
if md.Unreadable != nil { if md.Unreadable != nil {
@ -56,26 +56,27 @@ func (dbp *Process) loadModuleData() (err error) {
return return
} }
func (dbp *Process) resolveTypeOff(typeAddr uintptr, off uintptr) (*Variable, error) { func (bi *BinaryInfo) resolveTypeOff(typeAddr uintptr, off uintptr, thread *Thread) (*Variable, error) {
var mem memoryReadWriter = thread
// See runtime.(*_type).typeOff in $GOROOT/src/runtime/type.go // See runtime.(*_type).typeOff in $GOROOT/src/runtime/type.go
if err := dbp.loadModuleData(); err != nil { if err := bi.loadModuleData(thread); err != nil {
return nil, err return nil, err
} }
var md *moduleData var md *moduleData
for i := range dbp.moduleData { for i := range bi.moduleData {
if typeAddr >= dbp.moduleData[i].types && typeAddr < dbp.moduleData[i].etypes { if typeAddr >= bi.moduleData[i].types && typeAddr < bi.moduleData[i].etypes {
md = &dbp.moduleData[i] md = &bi.moduleData[i]
} }
} }
rtyp, err := dbp.findType("runtime._type") rtyp, err := bi.findType("runtime._type")
if err != nil { if err != nil {
return nil, err return nil, err
} }
if md == nil { if md == nil {
v, err := dbp.reflectOffsMapAccess(off) v, err := bi.reflectOffsMapAccess(off, thread)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -84,28 +85,29 @@ func (dbp *Process) resolveTypeOff(typeAddr uintptr, off uintptr) (*Variable, er
return v.newVariable(v.Name, uintptr(addr), rtyp), nil return v.newVariable(v.Name, uintptr(addr), rtyp), nil
} }
if t, _ := md.typemapVar.mapAccess(newConstant(constant.MakeUint64(uint64(off)), dbp.currentThread)); t != nil { if t, _ := md.typemapVar.mapAccess(newConstant(constant.MakeUint64(uint64(off)), mem)); t != nil {
return t, nil return t, nil
} }
res := md.types + uintptr(off) res := md.types + uintptr(off)
return dbp.currentThread.newVariable("", res, rtyp), nil return newVariable("", res, rtyp, thread.dbp, thread), nil
} }
func (dbp *Process) resolveNameOff(typeAddr uintptr, off uintptr) (name, tag string, pkgpathoff int32, err error) { func (bi *BinaryInfo) resolveNameOff(typeAddr uintptr, off uintptr, thread *Thread) (name, tag string, pkgpathoff int32, err error) {
var mem memoryReadWriter = thread
// See runtime.resolveNameOff in $GOROOT/src/runtime/type.go // See runtime.resolveNameOff in $GOROOT/src/runtime/type.go
if err = dbp.loadModuleData(); err != nil { if err = bi.loadModuleData(thread); err != nil {
return "", "", 0, err return "", "", 0, err
} }
for _, md := range dbp.moduleData { for _, md := range bi.moduleData {
if typeAddr >= md.types && typeAddr < md.etypes { if typeAddr >= md.types && typeAddr < md.etypes {
return dbp.loadName(md.types + off) return bi.loadName(md.types+off, mem)
} }
} }
v, err := dbp.reflectOffsMapAccess(off) v, err := bi.reflectOffsMapAccess(off, thread)
if err != nil { if err != nil {
return "", "", 0, err return "", "", 0, err
} }
@ -115,11 +117,11 @@ func (dbp *Process) resolveNameOff(typeAddr uintptr, off uintptr) (name, tag str
return "", "", 0, resv.Unreadable return "", "", 0, resv.Unreadable
} }
return dbp.loadName(resv.Addr) return bi.loadName(resv.Addr, mem)
} }
func (dbp *Process) reflectOffsMapAccess(off uintptr) (*Variable, error) { func (bi *BinaryInfo) reflectOffsMapAccess(off uintptr, thread *Thread) (*Variable, error) {
scope := &EvalScope{Thread: dbp.currentThread, PC: 0, CFA: 0} scope, _ := thread.Scope()
reflectOffs, err := scope.packageVarAddr("runtime.reflectOffs") reflectOffs, err := scope.packageVarAddr("runtime.reflectOffs")
if err != nil { if err != nil {
return nil, err return nil, err
@ -130,7 +132,7 @@ func (dbp *Process) reflectOffsMapAccess(off uintptr) (*Variable, error) {
return nil, err return nil, err
} }
return reflectOffsm.mapAccess(newConstant(constant.MakeUint64(uint64(off)), dbp.currentThread)) return reflectOffsm.mapAccess(newConstant(constant.MakeUint64(uint64(off)), thread))
} }
const ( const (
@ -140,9 +142,9 @@ const (
nameflagHasPkg = 1 << 2 nameflagHasPkg = 1 << 2
) )
func (dbp *Process) loadName(addr uintptr) (name, tag string, pkgpathoff int32, err error) { func (bi *BinaryInfo) loadName(addr uintptr, mem memoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
off := addr off := addr
namedata, err := dbp.currentThread.readMemory(off, 3) namedata, err := mem.readMemory(off, 3)
off += 3 off += 3
if err != nil { if err != nil {
return "", "", 0, err return "", "", 0, err
@ -150,7 +152,7 @@ func (dbp *Process) loadName(addr uintptr) (name, tag string, pkgpathoff int32,
namelen := uint16(namedata[1]<<8) | uint16(namedata[2]) namelen := uint16(namedata[1]<<8) | uint16(namedata[2])
rawstr, err := dbp.currentThread.readMemory(off, int(namelen)) rawstr, err := mem.readMemory(off, int(namelen))
off += uintptr(namelen) off += uintptr(namelen)
if err != nil { if err != nil {
return "", "", 0, err return "", "", 0, err
@ -159,14 +161,14 @@ func (dbp *Process) loadName(addr uintptr) (name, tag string, pkgpathoff int32,
name = string(rawstr) name = string(rawstr)
if namedata[0]&nameflagHasTag != 0 { if namedata[0]&nameflagHasTag != 0 {
taglendata, err := dbp.currentThread.readMemory(off, 2) taglendata, err := mem.readMemory(off, 2)
off += 2 off += 2
if err != nil { if err != nil {
return "", "", 0, err return "", "", 0, err
} }
taglen := uint16(taglendata[0]<<8) | uint16(taglendata[1]) taglen := uint16(taglendata[0]<<8) | uint16(taglendata[1])
rawstr, err := dbp.currentThread.readMemory(off, int(taglen)) rawstr, err := mem.readMemory(off, int(taglen))
off += uintptr(taglen) off += uintptr(taglen)
if err != nil { if err != nil {
return "", "", 0, err return "", "", 0, err
@ -176,7 +178,7 @@ func (dbp *Process) loadName(addr uintptr) (name, tag string, pkgpathoff int32,
} }
if namedata[0]&nameflagHasPkg != 0 { if namedata[0]&nameflagHasPkg != 0 {
pkgdata, err := dbp.currentThread.readMemory(off, 4) pkgdata, err := mem.readMemory(off, 4)
if err != nil { if err != nil {
return "", "", 0, err return "", "", 0, err
} }

@ -1,7 +1,6 @@
package proc package proc
import ( import (
"debug/gosym"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
@ -14,20 +13,16 @@ import (
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"time"
"github.com/derekparker/delve/pkg/dwarf/frame"
"github.com/derekparker/delve/pkg/dwarf/line"
"github.com/derekparker/delve/pkg/dwarf/reader"
"golang.org/x/debug/dwarf" "golang.org/x/debug/dwarf"
) )
// Process represents all of the information the debugger // Process represents all of the information the debugger
// is holding onto regarding the process we are debugging. // is holding onto regarding the process we are debugging.
type Process struct { type Process struct {
pid int // Process Pid bi BinaryInfo
Process *os.Process // Pointer to process struct for the actual process we are debugging pid int // Process Pid
lastModified time.Time // Time the executable of this process was last modified Process *os.Process // Pointer to process struct for the actual process we are debugging
// Breakpoint table, holds information on breakpoints. // Breakpoint table, holds information on breakpoints.
// Maps instruction address to Breakpoint struct. // Maps instruction address to Breakpoint struct.
@ -43,16 +38,8 @@ type Process struct {
// Normally selectedGoroutine is currentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread // Normally selectedGoroutine is currentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread
selectedGoroutine *G selectedGoroutine *G
// Maps package names to package paths, needed to lookup types inside DWARF info
packageMap map[string]string
allGCache []*G allGCache []*G
dwarf *dwarf.Data
goSymTable *gosym.Table
frameEntries frame.FrameDescriptionEntries
lineInfo line.DebugLines
os *OSProcessDetails os *OSProcessDetails
arch Arch
breakpointIDCounter int breakpointIDCounter int
internalBreakpointIDCounter int internalBreakpointIDCounter int
firstStart bool firstStart bool
@ -60,12 +47,6 @@ type Process struct {
exited bool exited bool
ptraceChan chan func() ptraceChan chan func()
ptraceDoneChan chan interface{} ptraceDoneChan chan interface{}
types map[string]dwarf.Offset
functions []functionDebugInfo
loadModuleDataOnce sync.Once
moduleData []moduleData
nameOfRuntimeType map[uintptr]nameOfRuntimeTypeEntry
} }
type functionDebugInfo struct { type functionDebugInfo struct {
@ -81,19 +62,14 @@ var NotExecutableErr = errors.New("not an executable file")
// `handlePtraceFuncs`. // `handlePtraceFuncs`.
func New(pid int) *Process { func New(pid int) *Process {
dbp := &Process{ dbp := &Process{
pid: pid, pid: pid,
threads: make(map[int]*Thread), threads: make(map[int]*Thread),
breakpoints: make(map[uint64]*Breakpoint), breakpoints: make(map[uint64]*Breakpoint),
firstStart: true, firstStart: true,
os: new(OSProcessDetails), os: new(OSProcessDetails),
ptraceChan: make(chan func()), ptraceChan: make(chan func()),
ptraceDoneChan: make(chan interface{}), ptraceDoneChan: make(chan interface{}),
nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry), bi: NewBinaryInfo(runtime.GOOS, runtime.GOARCH),
}
// TODO: find better way to determine proc arch (perhaps use executable file info)
switch runtime.GOARCH {
case "amd64":
dbp.arch = AMD64Arch()
} }
go dbp.handlePtraceFuncs() go dbp.handlePtraceFuncs()
return dbp return dbp
@ -110,6 +86,10 @@ func (pe ProcessExitedError) Error() string {
return fmt.Sprintf("Process %d has exited with status %d", pe.Pid, pe.Status) return fmt.Sprintf("Process %d has exited with status %d", pe.Pid, pe.Status)
} }
func (dbp *Process) BinInfo() *BinaryInfo {
return &dbp.bi
}
// Detach from the process being debugged, optionally killing it. // Detach from the process being debugged, optionally killing it.
func (dbp *Process) Detach(kill bool) (err error) { func (dbp *Process) Detach(kill bool) (err error) {
if dbp.exited { if dbp.exited {
@ -132,7 +112,7 @@ func (dbp *Process) Detach(kill bool) (err error) {
} }
} }
dbp.execPtraceFunc(func() { dbp.execPtraceFunc(func() {
err = dbp.detach() err = dbp.detach(kill)
if err != nil { if err != nil {
return return
} }
@ -140,6 +120,7 @@ func (dbp *Process) Detach(kill bool) (err error) {
err = killProcess(dbp.pid) err = killProcess(dbp.pid)
} }
}) })
dbp.bi.Close()
return return
} }
@ -160,10 +141,6 @@ func (dbp *Process) Running() bool {
return false return false
} }
func (dbp *Process) LastModified() time.Time {
return dbp.lastModified
}
func (dbp *Process) Pid() int { func (dbp *Process) Pid() int {
return dbp.pid return dbp.pid
} }
@ -192,21 +169,11 @@ func (dbp *Process) Breakpoints() map[uint64]*Breakpoint {
func (dbp *Process) LoadInformation(path string) error { func (dbp *Process) LoadInformation(path string) error {
var wg sync.WaitGroup var wg sync.WaitGroup
exe, path, err := dbp.findExecutable(path) path = findExecutable(path, dbp.pid)
if err != nil {
return err
}
fi, err := os.Stat(path)
if err == nil {
dbp.lastModified = fi.ModTime()
}
wg.Add(5) wg.Add(1)
go dbp.loadProcessInformation(&wg) go dbp.loadProcessInformation(&wg)
go dbp.parseDebugFrame(exe, &wg) dbp.bi.LoadBinaryInfo(path, &wg)
go dbp.obtainGoSymbols(exe, &wg)
go dbp.parseDebugLineInfo(exe, &wg)
go dbp.loadDebugInfoMaps(&wg)
wg.Wait() wg.Wait()
return nil return nil
@ -215,7 +182,7 @@ func (dbp *Process) LoadInformation(path string) error {
// FindFileLocation returns the PC for a given file:line. // FindFileLocation returns the PC for a given file:line.
// Assumes that `file` is normailzed to lower case and '/' on Windows. // Assumes that `file` is normailzed to lower case and '/' on Windows.
func (dbp *Process) FindFileLocation(fileName string, lineno int) (uint64, error) { func (dbp *Process) FindFileLocation(fileName string, lineno int) (uint64, error) {
pc, fn, err := dbp.goSymTable.LineToPC(fileName, lineno) pc, fn, err := dbp.bi.goSymTable.LineToPC(fileName, lineno)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -232,7 +199,7 @@ func (dbp *Process) FindFileLocation(fileName string, lineno int) (uint64, error
// Note that setting breakpoints at that address will cause surprising behavior: // Note that setting breakpoints at that address will cause surprising behavior:
// https://github.com/derekparker/delve/issues/170 // https://github.com/derekparker/delve/issues/170
func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) { func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) {
origfn := dbp.goSymTable.LookupFunc(funcName) origfn := dbp.bi.goSymTable.LookupFunc(funcName)
if origfn == nil { if origfn == nil {
return 0, fmt.Errorf("Could not find function %s\n", funcName) return 0, fmt.Errorf("Could not find function %s\n", funcName)
} }
@ -240,8 +207,8 @@ func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOf
if firstLine { if firstLine {
return dbp.FirstPCAfterPrologue(origfn, false) return dbp.FirstPCAfterPrologue(origfn, false)
} else if lineOffset > 0 { } else if lineOffset > 0 {
filename, lineno, _ := dbp.goSymTable.PCToLine(origfn.Entry) filename, lineno, _ := dbp.bi.goSymTable.PCToLine(origfn.Entry)
breakAddr, _, err := dbp.goSymTable.LineToPC(filename, lineno+lineOffset) breakAddr, _, err := dbp.bi.goSymTable.LineToPC(filename, lineno+lineOffset)
return breakAddr, err return breakAddr, err
} }
@ -273,7 +240,7 @@ func (dbp *Process) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Exp
return nil, BreakpointExistsError{bp.File, bp.Line, bp.Addr} return nil, BreakpointExistsError{bp.File, bp.Line, bp.Addr}
} }
f, l, fn := dbp.goSymTable.PCToLine(uint64(addr)) f, l, fn := dbp.bi.goSymTable.PCToLine(uint64(addr))
if fn == nil { if fn == nil {
return nil, InvalidAddressError{address: addr} return nil, InvalidAddressError{address: addr}
} }
@ -297,7 +264,7 @@ func (dbp *Process) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Exp
} }
thread := dbp.threads[tid] thread := dbp.threads[tid]
originalData, err := thread.readMemory(uintptr(addr), dbp.arch.BreakpointSize()) originalData, err := thread.readMemory(uintptr(addr), dbp.bi.arch.BreakpointSize())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -572,7 +539,7 @@ func (dbp *Process) StepOut() error {
if dbp.selectedGoroutine != nil { if dbp.selectedGoroutine != nil {
deferPCEntry := dbp.selectedGoroutine.DeferPC() deferPCEntry := dbp.selectedGoroutine.DeferPC()
if deferPCEntry != 0 { if deferPCEntry != 0 {
_, _, deferfn := dbp.goSymTable.PCToLine(deferPCEntry) _, _, deferfn := dbp.bi.goSymTable.PCToLine(deferPCEntry)
deferpc, err = dbp.FirstPCAfterPrologue(deferfn, false) deferpc, err = dbp.FirstPCAfterPrologue(deferfn, false)
if err != nil { if err != nil {
return err return err
@ -658,7 +625,7 @@ func (dbp *Process) GoroutinesInfo() ([]*G, error) {
var ( var (
threadg = map[int]*Thread{} threadg = map[int]*Thread{}
allg []*G allg []*G
rdr = dbp.DwarfReader() rdr = dbp.bi.DwarfReader()
) )
for i := range dbp.threads { for i := range dbp.threads {
@ -690,11 +657,11 @@ func (dbp *Process) GoroutinesInfo() ([]*G, error) {
return nil, err return nil, err
} }
} }
faddr, err := dbp.currentThread.readMemory(uintptr(allgentryaddr), dbp.arch.PtrSize()) faddr, err := dbp.currentThread.readMemory(uintptr(allgentryaddr), dbp.bi.arch.PtrSize())
allgptr := binary.LittleEndian.Uint64(faddr) allgptr := binary.LittleEndian.Uint64(faddr)
for i := uint64(0); i < allglen; i++ { for i := uint64(0); i < allglen; i++ {
gvar, err := dbp.currentThread.newGVariable(uintptr(allgptr+(i*uint64(dbp.arch.PtrSize()))), true) gvar, err := dbp.currentThread.newGVariable(uintptr(allgptr+(i*uint64(dbp.bi.arch.PtrSize()))), true)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -753,35 +720,6 @@ func (dbp *Process) CurrentBreakpoint() *Breakpoint {
return dbp.currentThread.CurrentBreakpoint return dbp.currentThread.CurrentBreakpoint
} }
// DwarfReader returns a reader for the dwarf data
func (dbp *Process) DwarfReader() *reader.Reader {
return reader.New(dbp.dwarf)
}
// Sources returns list of source files that comprise the debugged binary.
func (dbp *Process) Sources() map[string]*gosym.Obj {
return dbp.goSymTable.Files
}
// Funcs returns list of functions present in the debugged program.
func (dbp *Process) Funcs() []gosym.Func {
return dbp.goSymTable.Funcs
}
// Types returns list of types present in the debugged program.
func (dbp *Process) Types() ([]string, error) {
types := make([]string, 0, len(dbp.types))
for k := range dbp.types {
types = append(types, k)
}
return types, nil
}
// PCToLine converts an instruction address to a file/line/function.
func (dbp *Process) PCToLine(pc uint64) (string, int, *gosym.Func) {
return dbp.goSymTable.PCToLine(pc)
}
// FindBreakpointByID finds the breakpoint for the given ID. // FindBreakpointByID finds the breakpoint for the given ID.
func (dbp *Process) FindBreakpointByID(id int) (*Breakpoint, bool) { func (dbp *Process) FindBreakpointByID(id int) (*Breakpoint, bool) {
for _, bp := range dbp.breakpoints { for _, bp := range dbp.breakpoints {
@ -795,7 +733,7 @@ func (dbp *Process) FindBreakpointByID(id int) (*Breakpoint, bool) {
// FindBreakpoint finds the breakpoint for the given pc. // FindBreakpoint finds the breakpoint for the given pc.
func (dbp *Process) FindBreakpoint(pc uint64) (*Breakpoint, bool) { func (dbp *Process) FindBreakpoint(pc uint64) (*Breakpoint, bool) {
// Check to see if address is past the breakpoint, (i.e. breakpoint was hit). // Check to see if address is past the breakpoint, (i.e. breakpoint was hit).
if bp, ok := dbp.breakpoints[pc-uint64(dbp.arch.BreakpointSize())]; ok { if bp, ok := dbp.breakpoints[pc-uint64(dbp.bi.arch.BreakpointSize())]; ok {
return bp, true return bp, true
} }
// Directly use addr to lookup breakpoint. // Directly use addr to lookup breakpoint.
@ -839,7 +777,7 @@ func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, e
return nil, err return nil, err
} }
dbp.arch.SetGStructOffset(ver, isextld) dbp.bi.arch.SetGStructOffset(ver, isextld)
// selectedGoroutine can not be set correctly by the call to updateThreadList // selectedGoroutine can not be set correctly by the call to updateThreadList
// because without calling SetGStructOffset we can not read the G struct of currentThread // because without calling SetGStructOffset we can not read the G struct of currentThread
// but without calling updateThreadList we can not examine memory to determine // but without calling updateThreadList we can not examine memory to determine
@ -910,7 +848,7 @@ func (dbp *Process) getGoInformation() (ver GoVersion, isextld bool, err error)
return return
} }
rdr := dbp.DwarfReader() rdr := dbp.bi.DwarfReader()
rdr.Seek(0) rdr.Seek(0)
for entry, err := rdr.NextCompileUnit(); entry != nil; entry, err = rdr.NextCompileUnit() { for entry, err := rdr.NextCompileUnit(); entry != nil; entry, err = rdr.NextCompileUnit() {
if err != nil { if err != nil {

@ -6,7 +6,6 @@ package proc
// #include <stdlib.h> // #include <stdlib.h>
import "C" import "C"
import ( import (
"debug/gosym"
"errors" "errors"
"fmt" "fmt"
"os" "os"
@ -15,10 +14,6 @@ import (
"sync" "sync"
"unsafe" "unsafe"
"golang.org/x/debug/macho"
"github.com/derekparker/delve/pkg/dwarf/frame"
"github.com/derekparker/delve/pkg/dwarf/line"
sys "golang.org/x/sys/unix" sys "golang.org/x/sys/unix"
) )
@ -259,99 +254,11 @@ func (dbp *Process) addThread(port int, attach bool) (*Thread, error) {
return thread, nil return thread, nil
} }
func (dbp *Process) parseDebugFrame(exe *macho.File, wg *sync.WaitGroup) { func findExecutable(path string, pid int) string {
defer wg.Done()
debugFrameSec := exe.Section("__debug_frame")
debugInfoSec := exe.Section("__debug_info")
if debugFrameSec != nil && debugInfoSec != nil {
debugFrame, err := exe.Section("__debug_frame").Data()
if err != nil {
fmt.Println("could not get __debug_frame section", err)
os.Exit(1)
}
dat, err := debugInfoSec.Data()
if err != nil {
fmt.Println("could not get .debug_info section", err)
os.Exit(1)
}
dbp.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
} else {
fmt.Println("could not find __debug_frame section in binary")
os.Exit(1)
}
}
func (dbp *Process) obtainGoSymbols(exe *macho.File, wg *sync.WaitGroup) {
defer wg.Done()
var (
symdat []byte
pclndat []byte
err error
)
if sec := exe.Section("__gosymtab"); sec != nil {
symdat, err = sec.Data()
if err != nil {
fmt.Println("could not get .gosymtab section", err)
os.Exit(1)
}
}
if sec := exe.Section("__gopclntab"); sec != nil {
pclndat, err = sec.Data()
if err != nil {
fmt.Println("could not get .gopclntab section", err)
os.Exit(1)
}
}
pcln := gosym.NewLineTable(pclndat, exe.Section("__text").Addr)
tab, err := gosym.NewTable(symdat, pcln)
if err != nil {
fmt.Println("could not get initialize line table", err)
os.Exit(1)
}
dbp.goSymTable = tab
}
func (dbp *Process) parseDebugLineInfo(exe *macho.File, wg *sync.WaitGroup) {
defer wg.Done()
if sec := exe.Section("__debug_line"); sec != nil {
debugLine, err := exe.Section("__debug_line").Data()
if err != nil {
fmt.Println("could not get __debug_line section", err)
os.Exit(1)
}
dbp.lineInfo = line.Parse(debugLine)
} else {
fmt.Println("could not find __debug_line section in binary")
os.Exit(1)
}
}
var UnsupportedArchErr = errors.New("unsupported architecture - only darwin/amd64 is supported")
func (dbp *Process) findExecutable(path string) (*macho.File, string, error) {
if path == "" { if path == "" {
path = C.GoString(C.find_executable(C.int(dbp.pid))) path = C.GoString(C.find_executable(C.int(pid)))
} }
exe, err := macho.Open(path) return path
if err != nil {
return nil, path, err
}
if exe.Cpu != macho.CpuAmd64 {
return nil, path, UnsupportedArchErr
}
dbp.dwarf, err = exe.DWARF()
if err != nil {
return nil, path, err
}
return exe, path, nil
} }
func (dbp *Process) trapWait(pid int) (*Thread, error) { func (dbp *Process) trapWait(pid int) (*Thread, error) {
@ -510,6 +417,6 @@ func (dbp *Process) resume() error {
return nil return nil
} }
func (dbp *Process) detach() error { func (dbp *Process) detach(kill bool) error {
return PtraceDetach(dbp.pid, 0) return PtraceDetach(dbp.pid, 0)
} }

@ -2,7 +2,6 @@ package proc
import ( import (
"bytes" "bytes"
"debug/gosym"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -17,10 +16,6 @@ import (
"time" "time"
sys "golang.org/x/sys/unix" sys "golang.org/x/sys/unix"
"github.com/derekparker/delve/pkg/dwarf/frame"
"github.com/derekparker/delve/pkg/dwarf/line"
"golang.org/x/debug/elf"
) )
// Process statuses // Process statuses
@ -171,103 +166,11 @@ func (dbp *Process) updateThreadList() error {
return nil return nil
} }
var UnsupportedArchErr = errors.New("unsupported architecture - only linux/amd64 is supported") func findExecutable(path string, pid int) string {
func (dbp *Process) findExecutable(path string) (*elf.File, string, error) {
if path == "" { if path == "" {
path = fmt.Sprintf("/proc/%d/exe", dbp.pid) path = fmt.Sprintf("/proc/%d/exe", pid)
}
f, err := os.OpenFile(path, 0, os.ModePerm)
if err != nil {
return nil, path, err
}
elfFile, err := elf.NewFile(f)
if err != nil {
return nil, path, err
}
if elfFile.Machine != elf.EM_X86_64 {
return nil, path, UnsupportedArchErr
}
dbp.dwarf, err = elfFile.DWARF()
if err != nil {
return nil, path, err
}
return elfFile, path, nil
}
func (dbp *Process) parseDebugFrame(exe *elf.File, wg *sync.WaitGroup) {
defer wg.Done()
debugFrameSec := exe.Section(".debug_frame")
debugInfoSec := exe.Section(".debug_info")
if debugFrameSec != nil && debugInfoSec != nil {
debugFrame, err := exe.Section(".debug_frame").Data()
if err != nil {
fmt.Println("could not get .debug_frame section", err)
os.Exit(1)
}
dat, err := debugInfoSec.Data()
if err != nil {
fmt.Println("could not get .debug_info section", err)
os.Exit(1)
}
dbp.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
} else {
fmt.Println("could not find .debug_frame section in binary")
os.Exit(1)
}
}
func (dbp *Process) obtainGoSymbols(exe *elf.File, wg *sync.WaitGroup) {
defer wg.Done()
var (
symdat []byte
pclndat []byte
err error
)
if sec := exe.Section(".gosymtab"); sec != nil {
symdat, err = sec.Data()
if err != nil {
fmt.Println("could not get .gosymtab section", err)
os.Exit(1)
}
}
if sec := exe.Section(".gopclntab"); sec != nil {
pclndat, err = sec.Data()
if err != nil {
fmt.Println("could not get .gopclntab section", err)
os.Exit(1)
}
}
pcln := gosym.NewLineTable(pclndat, exe.Section(".text").Addr)
tab, err := gosym.NewTable(symdat, pcln)
if err != nil {
fmt.Println("could not get initialize line table", err)
os.Exit(1)
}
dbp.goSymTable = tab
}
func (dbp *Process) parseDebugLineInfo(exe *elf.File, wg *sync.WaitGroup) {
defer wg.Done()
if sec := exe.Section(".debug_line"); sec != nil {
debugLine, err := exe.Section(".debug_line").Data()
if err != nil {
fmt.Println("could not get .debug_line section", err)
os.Exit(1)
}
dbp.lineInfo = line.Parse(debugLine)
} else {
fmt.Println("could not find .debug_line section in binary")
os.Exit(1)
} }
return path
} }
func (dbp *Process) trapWait(pid int) (*Thread, error) { func (dbp *Process) trapWait(pid int) (*Thread, error) {
@ -484,13 +387,16 @@ func (dbp *Process) resume() error {
return nil return nil
} }
func (dbp *Process) detach() error { func (dbp *Process) detach(kill bool) error {
for threadID := range dbp.threads { for threadID := range dbp.threads {
err := PtraceDetach(threadID, 0) err := PtraceDetach(threadID, 0)
if err != nil { if err != nil {
return err return err
} }
} }
if kill {
return nil
}
// For some reason the process will sometimes enter stopped state after a // For some reason the process will sometimes enter stopped state after a
// detach, this doesn't happen immediately either. // detach, this doesn't happen immediately either.
// We have to wait a bit here, then check if the main thread is stopped and // We have to wait a bit here, then check if the main thread is stopped and

@ -94,7 +94,7 @@ func currentPC(p *Process, t *testing.T) uint64 {
func currentLineNumber(p *Process, t *testing.T) (string, int) { func currentLineNumber(p *Process, t *testing.T) (string, int) {
pc := currentPC(p, t) pc := currentPC(p, t)
f, l, _ := p.goSymTable.PCToLine(pc) f, l, _ := p.BinInfo().goSymTable.PCToLine(pc)
return f, l return f, l
} }
@ -198,7 +198,7 @@ func TestHalt(t *testing.T) {
func TestStep(t *testing.T) { func TestStep(t *testing.T) {
withTestProcess("testprog", t, func(p *Process, fixture protest.Fixture) { withTestProcess("testprog", t, func(p *Process, fixture protest.Fixture) {
helloworldfunc := p.goSymTable.LookupFunc("main.helloworld") helloworldfunc := p.BinInfo().goSymTable.LookupFunc("main.helloworld")
helloworldaddr := helloworldfunc.Entry helloworldaddr := helloworldfunc.Entry
_, err := p.SetBreakpoint(helloworldaddr, UserBreakpoint, nil) _, err := p.SetBreakpoint(helloworldaddr, UserBreakpoint, nil)
@ -220,7 +220,7 @@ func TestStep(t *testing.T) {
func TestBreakpoint(t *testing.T) { func TestBreakpoint(t *testing.T) {
withTestProcess("testprog", t, func(p *Process, fixture protest.Fixture) { withTestProcess("testprog", t, func(p *Process, fixture protest.Fixture) {
helloworldfunc := p.goSymTable.LookupFunc("main.helloworld") helloworldfunc := p.BinInfo().goSymTable.LookupFunc("main.helloworld")
helloworldaddr := helloworldfunc.Entry helloworldaddr := helloworldfunc.Entry
bp, err := p.SetBreakpoint(helloworldaddr, UserBreakpoint, nil) bp, err := p.SetBreakpoint(helloworldaddr, UserBreakpoint, nil)
@ -237,7 +237,7 @@ func TestBreakpoint(t *testing.T) {
} }
if pc-1 != bp.Addr && pc != bp.Addr { if pc-1 != bp.Addr && pc != bp.Addr {
f, l, _ := p.goSymTable.PCToLine(pc) f, l, _ := p.BinInfo().goSymTable.PCToLine(pc)
t.Fatalf("Break not respected:\nPC:%#v %s:%d\nFN:%#v \n", pc, f, l, bp.Addr) t.Fatalf("Break not respected:\nPC:%#v %s:%d\nFN:%#v \n", pc, f, l, bp.Addr)
} }
}) })
@ -245,7 +245,7 @@ func TestBreakpoint(t *testing.T) {
func TestBreakpointInSeperateGoRoutine(t *testing.T) { func TestBreakpointInSeperateGoRoutine(t *testing.T) {
withTestProcess("testthreads", t, func(p *Process, fixture protest.Fixture) { withTestProcess("testthreads", t, func(p *Process, fixture protest.Fixture) {
fn := p.goSymTable.LookupFunc("main.anotherthread") fn := p.BinInfo().goSymTable.LookupFunc("main.anotherthread")
if fn == nil { if fn == nil {
t.Fatal("No fn exists") t.Fatal("No fn exists")
} }
@ -265,7 +265,7 @@ func TestBreakpointInSeperateGoRoutine(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
f, l, _ := p.goSymTable.PCToLine(pc) f, l, _ := p.BinInfo().goSymTable.PCToLine(pc)
if f != "testthreads.go" && l != 8 { if f != "testthreads.go" && l != 8 {
t.Fatal("Program did not hit breakpoint") t.Fatal("Program did not hit breakpoint")
} }
@ -283,7 +283,7 @@ func TestBreakpointWithNonExistantFunction(t *testing.T) {
func TestClearBreakpointBreakpoint(t *testing.T) { func TestClearBreakpointBreakpoint(t *testing.T) {
withTestProcess("testprog", t, func(p *Process, fixture protest.Fixture) { withTestProcess("testprog", t, func(p *Process, fixture protest.Fixture) {
fn := p.goSymTable.LookupFunc("main.sleepytime") fn := p.BinInfo().goSymTable.LookupFunc("main.sleepytime")
bp, err := p.SetBreakpoint(fn.Entry, UserBreakpoint, nil) bp, err := p.SetBreakpoint(fn.Entry, UserBreakpoint, nil)
assertNoError(err, t, "SetBreakpoint()") assertNoError(err, t, "SetBreakpoint()")
@ -579,7 +579,7 @@ func TestRuntimeBreakpoint(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, l, _ := p.PCToLine(pc) _, l, _ := p.BinInfo().PCToLine(pc)
if l != 10 { if l != 10 {
t.Fatal("did not respect breakpoint") t.Fatal("did not respect breakpoint")
} }
@ -588,7 +588,7 @@ func TestRuntimeBreakpoint(t *testing.T) {
func TestFindReturnAddress(t *testing.T) { func TestFindReturnAddress(t *testing.T) {
withTestProcess("testnextprog", t, func(p *Process, fixture protest.Fixture) { withTestProcess("testnextprog", t, func(p *Process, fixture protest.Fixture) {
start, _, err := p.goSymTable.LineToPC(fixture.Source, 24) start, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 24)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -604,7 +604,7 @@ func TestFindReturnAddress(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, l, _ := p.goSymTable.PCToLine(addr) _, l, _ := p.BinInfo().goSymTable.PCToLine(addr)
if l != 40 { if l != 40 {
t.Fatalf("return address not found correctly, expected line 40") t.Fatalf("return address not found correctly, expected line 40")
} }
@ -614,7 +614,7 @@ func TestFindReturnAddress(t *testing.T) {
func TestFindReturnAddressTopOfStackFn(t *testing.T) { func TestFindReturnAddressTopOfStackFn(t *testing.T) {
withTestProcess("testreturnaddress", t, func(p *Process, fixture protest.Fixture) { withTestProcess("testreturnaddress", t, func(p *Process, fixture protest.Fixture) {
fnName := "runtime.rt0_go" fnName := "runtime.rt0_go"
fn := p.goSymTable.LookupFunc(fnName) fn := p.BinInfo().goSymTable.LookupFunc(fnName)
if fn == nil { if fn == nil {
t.Fatalf("could not find function %s", fnName) t.Fatalf("could not find function %s", fnName)
} }
@ -1002,7 +1002,7 @@ func TestProcessReceivesSIGCHLD(t *testing.T) {
func TestIssue239(t *testing.T) { func TestIssue239(t *testing.T) {
withTestProcess("is sue239", t, func(p *Process, fixture protest.Fixture) { withTestProcess("is sue239", t, func(p *Process, fixture protest.Fixture) {
pos, _, err := p.goSymTable.LineToPC(fixture.Source, 17) pos, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 17)
assertNoError(err, t, "LineToPC()") assertNoError(err, t, "LineToPC()")
_, err = p.SetBreakpoint(pos, UserBreakpoint, nil) _, err = p.SetBreakpoint(pos, UserBreakpoint, nil)
assertNoError(err, t, fmt.Sprintf("SetBreakpoint(%d)", pos)) assertNoError(err, t, fmt.Sprintf("SetBreakpoint(%d)", pos))
@ -1266,7 +1266,7 @@ func TestIssue325(t *testing.T) {
func TestBreakpointCounts(t *testing.T) { func TestBreakpointCounts(t *testing.T) {
withTestProcess("bpcountstest", t, func(p *Process, fixture protest.Fixture) { withTestProcess("bpcountstest", t, func(p *Process, fixture protest.Fixture) {
addr, _, err := p.goSymTable.LineToPC(fixture.Source, 12) addr, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 12)
assertNoError(err, t, "LineToPC") assertNoError(err, t, "LineToPC")
bp, err := p.SetBreakpoint(addr, UserBreakpoint, nil) bp, err := p.SetBreakpoint(addr, UserBreakpoint, nil)
assertNoError(err, t, "SetBreakpoint()") assertNoError(err, t, "SetBreakpoint()")
@ -1317,7 +1317,7 @@ func TestBreakpointCountsWithDetection(t *testing.T) {
} }
m := map[int64]int64{} m := map[int64]int64{}
withTestProcess("bpcountstest", t, func(p *Process, fixture protest.Fixture) { withTestProcess("bpcountstest", t, func(p *Process, fixture protest.Fixture) {
addr, _, err := p.goSymTable.LineToPC(fixture.Source, 12) addr, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 12)
assertNoError(err, t, "LineToPC") assertNoError(err, t, "LineToPC")
bp, err := p.SetBreakpoint(addr, UserBreakpoint, nil) bp, err := p.SetBreakpoint(addr, UserBreakpoint, nil)
assertNoError(err, t, "SetBreakpoint()") assertNoError(err, t, "SetBreakpoint()")
@ -1412,7 +1412,7 @@ func BenchmarkGoroutinesInfo(b *testing.B) {
func TestIssue262(t *testing.T) { func TestIssue262(t *testing.T) {
// Continue does not work when the current breakpoint is set on a NOP instruction // Continue does not work when the current breakpoint is set on a NOP instruction
withTestProcess("issue262", t, func(p *Process, fixture protest.Fixture) { withTestProcess("issue262", t, func(p *Process, fixture protest.Fixture) {
addr, _, err := p.goSymTable.LineToPC(fixture.Source, 11) addr, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 11)
assertNoError(err, t, "LineToPC") assertNoError(err, t, "LineToPC")
_, err = p.SetBreakpoint(addr, UserBreakpoint, nil) _, err = p.SetBreakpoint(addr, UserBreakpoint, nil)
assertNoError(err, t, "SetBreakpoint()") assertNoError(err, t, "SetBreakpoint()")
@ -1434,7 +1434,7 @@ func TestIssue305(t *testing.T) {
// the internal breakpoints aren't cleared preventing further use of // the internal breakpoints aren't cleared preventing further use of
// 'next' command // 'next' command
withTestProcess("issue305", t, func(p *Process, fixture protest.Fixture) { withTestProcess("issue305", t, func(p *Process, fixture protest.Fixture) {
addr, _, err := p.goSymTable.LineToPC(fixture.Source, 5) addr, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 5)
assertNoError(err, t, "LineToPC()") assertNoError(err, t, "LineToPC()")
_, err = p.SetBreakpoint(addr, UserBreakpoint, nil) _, err = p.SetBreakpoint(addr, UserBreakpoint, nil)
assertNoError(err, t, "SetBreakpoint()") assertNoError(err, t, "SetBreakpoint()")
@ -1477,7 +1477,7 @@ func BenchmarkLocalVariables(b *testing.B) {
func TestCondBreakpoint(t *testing.T) { func TestCondBreakpoint(t *testing.T) {
withTestProcess("parallel_next", t, func(p *Process, fixture protest.Fixture) { withTestProcess("parallel_next", t, func(p *Process, fixture protest.Fixture) {
addr, _, err := p.goSymTable.LineToPC(fixture.Source, 9) addr, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 9)
assertNoError(err, t, "LineToPC") assertNoError(err, t, "LineToPC")
bp, err := p.SetBreakpoint(addr, UserBreakpoint, nil) bp, err := p.SetBreakpoint(addr, UserBreakpoint, nil)
assertNoError(err, t, "SetBreakpoint()") assertNoError(err, t, "SetBreakpoint()")
@ -1501,7 +1501,7 @@ func TestCondBreakpoint(t *testing.T) {
func TestCondBreakpointError(t *testing.T) { func TestCondBreakpointError(t *testing.T) {
withTestProcess("parallel_next", t, func(p *Process, fixture protest.Fixture) { withTestProcess("parallel_next", t, func(p *Process, fixture protest.Fixture) {
addr, _, err := p.goSymTable.LineToPC(fixture.Source, 9) addr, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 9)
assertNoError(err, t, "LineToPC") assertNoError(err, t, "LineToPC")
bp, err := p.SetBreakpoint(addr, UserBreakpoint, nil) bp, err := p.SetBreakpoint(addr, UserBreakpoint, nil)
assertNoError(err, t, "SetBreakpoint()") assertNoError(err, t, "SetBreakpoint()")
@ -1581,7 +1581,7 @@ func TestStepIntoFunction(t *testing.T) {
func TestIssue384(t *testing.T) { func TestIssue384(t *testing.T) {
// Crash related to reading uninitialized memory, introduced by the memory prefetching optimization // Crash related to reading uninitialized memory, introduced by the memory prefetching optimization
withTestProcess("issue384", t, func(p *Process, fixture protest.Fixture) { withTestProcess("issue384", t, func(p *Process, fixture protest.Fixture) {
start, _, err := p.goSymTable.LineToPC(fixture.Source, 13) start, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 13)
assertNoError(err, t, "LineToPC()") assertNoError(err, t, "LineToPC()")
_, err = p.SetBreakpoint(start, UserBreakpoint, nil) _, err = p.SetBreakpoint(start, UserBreakpoint, nil)
assertNoError(err, t, "SetBreakpoint()") assertNoError(err, t, "SetBreakpoint()")
@ -1594,7 +1594,7 @@ func TestIssue384(t *testing.T) {
func TestIssue332_Part1(t *testing.T) { func TestIssue332_Part1(t *testing.T) {
// Next shouldn't step inside a function call // Next shouldn't step inside a function call
withTestProcess("issue332", t, func(p *Process, fixture protest.Fixture) { withTestProcess("issue332", t, func(p *Process, fixture protest.Fixture) {
start, _, err := p.goSymTable.LineToPC(fixture.Source, 8) start, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 8)
assertNoError(err, t, "LineToPC()") assertNoError(err, t, "LineToPC()")
_, err = p.SetBreakpoint(start, UserBreakpoint, nil) _, err = p.SetBreakpoint(start, UserBreakpoint, nil)
assertNoError(err, t, "SetBreakpoint()") assertNoError(err, t, "SetBreakpoint()")
@ -1620,7 +1620,7 @@ func TestIssue332_Part2(t *testing.T) {
// which leads to 'next' and 'stack' failing with error "could not find FDE for PC: <garbage>" // which leads to 'next' and 'stack' failing with error "could not find FDE for PC: <garbage>"
// because the incorrect FDE data leads to reading the wrong stack address as the return address // because the incorrect FDE data leads to reading the wrong stack address as the return address
withTestProcess("issue332", t, func(p *Process, fixture protest.Fixture) { withTestProcess("issue332", t, func(p *Process, fixture protest.Fixture) {
start, _, err := p.goSymTable.LineToPC(fixture.Source, 8) start, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 8)
assertNoError(err, t, "LineToPC()") assertNoError(err, t, "LineToPC()")
_, err = p.SetBreakpoint(start, UserBreakpoint, nil) _, err = p.SetBreakpoint(start, UserBreakpoint, nil)
assertNoError(err, t, "SetBreakpoint()") assertNoError(err, t, "SetBreakpoint()")
@ -1671,7 +1671,7 @@ func TestIssue396(t *testing.T) {
func TestIssue414(t *testing.T) { func TestIssue414(t *testing.T) {
// Stepping until the program exits // Stepping until the program exits
withTestProcess("math", t, func(p *Process, fixture protest.Fixture) { withTestProcess("math", t, func(p *Process, fixture protest.Fixture) {
start, _, err := p.goSymTable.LineToPC(fixture.Source, 9) start, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 9)
assertNoError(err, t, "LineToPC()") assertNoError(err, t, "LineToPC()")
_, err = p.SetBreakpoint(start, UserBreakpoint, nil) _, err = p.SetBreakpoint(start, UserBreakpoint, nil)
assertNoError(err, t, "SetBreakpoint()") assertNoError(err, t, "SetBreakpoint()")
@ -1936,7 +1936,7 @@ func TestUnsupportedArch(t *testing.T) {
p, err := Launch([]string{outfile}, ".") p, err := Launch([]string{outfile}, ".")
switch err { switch err {
case UnsupportedArchErr: case UnsupportedLinuxArchErr, UnsupportedWindowsArchErr, UnsupportedDarwinArchErr:
// all good // all good
case nil: case nil:
p.Halt() p.Halt()
@ -1951,7 +1951,7 @@ func TestIssue573(t *testing.T) {
// calls to runtime.duffzero and runtime.duffcopy jump directly into the middle // calls to runtime.duffzero and runtime.duffcopy jump directly into the middle
// of the function and the internal breakpoint set by StepInto may be missed. // of the function and the internal breakpoint set by StepInto may be missed.
withTestProcess("issue573", t, func(p *Process, fixture protest.Fixture) { withTestProcess("issue573", t, func(p *Process, fixture protest.Fixture) {
f := p.goSymTable.LookupFunc("main.foo") f := p.BinInfo().goSymTable.LookupFunc("main.foo")
_, err := p.SetBreakpoint(f.Entry, UserBreakpoint, nil) _, err := p.SetBreakpoint(f.Entry, UserBreakpoint, nil)
assertNoError(err, t, "SetBreakpoint()") assertNoError(err, t, "SetBreakpoint()")
assertNoError(p.Continue(), t, "Continue()") assertNoError(p.Continue(), t, "Continue()")
@ -2271,7 +2271,7 @@ func TestStepOutDefer(t *testing.T) {
assertNoError(p.StepOut(), t, "StepOut()") assertNoError(p.StepOut(), t, "StepOut()")
f, l, _ := p.goSymTable.PCToLine(currentPC(p, t)) f, l, _ := p.BinInfo().goSymTable.PCToLine(currentPC(p, t))
if f == fixture.Source || l == 6 { if f == fixture.Source || l == 6 {
t.Fatalf("wrong location %s:%d, expected to end somewhere in runtime", f, l) t.Fatalf("wrong location %s:%d, expected to end somewhere in runtime", f, l)
} }
@ -2375,7 +2375,7 @@ func TestWorkDir(t *testing.T) {
wd = "/private/tmp" wd = "/private/tmp"
} }
withTestProcessArgs("workdir", t, wd, func(p *Process, fixture protest.Fixture) { withTestProcessArgs("workdir", t, wd, func(p *Process, fixture protest.Fixture) {
addr, _, err := p.goSymTable.LineToPC(fixture.Source, 14) addr, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 14)
assertNoError(err, t, "LineToPC") assertNoError(err, t, "LineToPC")
p.SetBreakpoint(addr, UserBreakpoint, nil) p.SetBreakpoint(addr, UserBreakpoint, nil)
p.Continue() p.Continue()

@ -1,8 +1,6 @@
package proc package proc
import ( import (
"debug/gosym"
"debug/pe"
"errors" "errors"
"fmt" "fmt"
"os" "os"
@ -13,10 +11,6 @@ import (
"unsafe" "unsafe"
sys "golang.org/x/sys/windows" sys "golang.org/x/sys/windows"
"github.com/derekparker/delve/pkg/dwarf/frame"
"github.com/derekparker/delve/pkg/dwarf/line"
"golang.org/x/debug/dwarf"
) )
// OSProcessDetails holds Windows specific information. // OSProcessDetails holds Windows specific information.
@ -39,11 +33,11 @@ func Launch(cmd []string, wd string) (*Process, error) {
} }
} }
peFile, err := openExecutablePath(argv0Go) _, closer, err := openExecutablePathPE(argv0Go)
if err != nil { if err != nil {
return nil, NotExecutableErr return nil, NotExecutableErr
} }
peFile.Close() closer.Close()
// Duplicate the stdin/stdout/stderr handles // Duplicate the stdin/stdout/stderr handles
files := []uintptr{uintptr(syscall.Stdin), uintptr(syscall.Stdout), uintptr(syscall.Stderr)} files := []uintptr{uintptr(syscall.Stdin), uintptr(syscall.Stdout), uintptr(syscall.Stderr)}
@ -253,191 +247,8 @@ func (dbp *Process) addThread(hThread syscall.Handle, threadID int, attach, susp
return thread, nil return thread, nil
} }
func (dbp *Process) parseDebugFrame(exe *pe.File, wg *sync.WaitGroup) { func findExecutable(path string, pid int) string {
defer wg.Done() return path
debugFrameSec := exe.Section(".debug_frame")
debugInfoSec := exe.Section(".debug_info")
if debugFrameSec != nil && debugInfoSec != nil {
debugFrame, err := debugFrameSec.Data()
if err != nil && uint32(len(debugFrame)) < debugFrameSec.Size {
fmt.Println("could not get .debug_frame section", err)
os.Exit(1)
}
if 0 < debugFrameSec.VirtualSize && debugFrameSec.VirtualSize < debugFrameSec.Size {
debugFrame = debugFrame[:debugFrameSec.VirtualSize]
}
dat, err := debugInfoSec.Data()
if err != nil {
fmt.Println("could not get .debug_info section", err)
os.Exit(1)
}
dbp.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
} else {
fmt.Println("could not find .debug_frame section in binary")
os.Exit(1)
}
}
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
for _, s := range f.Symbols {
if s.Name != name {
continue
}
if s.SectionNumber <= 0 {
return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
}
if len(f.Sections) < int(s.SectionNumber) {
return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
}
return s, nil
}
return nil, fmt.Errorf("no %s symbol found", name)
}
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
func loadPETable(f *pe.File, sname, ename string) ([]byte, error) {
ssym, err := findPESymbol(f, sname)
if err != nil {
return nil, err
}
esym, err := findPESymbol(f, ename)
if err != nil {
return nil, err
}
if ssym.SectionNumber != esym.SectionNumber {
return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename)
}
sect := f.Sections[ssym.SectionNumber-1]
data, err := sect.Data()
if err != nil {
return nil, err
}
return data[ssym.Value:esym.Value], nil
}
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
func pcln(exe *pe.File) (textStart uint64, symtab, pclntab []byte, err error) {
var imageBase uint64
switch oh := exe.OptionalHeader.(type) {
case *pe.OptionalHeader32:
imageBase = uint64(oh.ImageBase)
case *pe.OptionalHeader64:
imageBase = oh.ImageBase
default:
return 0, nil, nil, fmt.Errorf("pe file format not recognized")
}
if sect := exe.Section(".text"); sect != nil {
textStart = imageBase + uint64(sect.VirtualAddress)
}
if pclntab, err = loadPETable(exe, "runtime.pclntab", "runtime.epclntab"); err != nil {
// We didn't find the symbols, so look for the names used in 1.3 and earlier.
// TODO: Remove code looking for the old symbols when we no longer care about 1.3.
var err2 error
if pclntab, err2 = loadPETable(exe, "pclntab", "epclntab"); err2 != nil {
return 0, nil, nil, err
}
}
if symtab, err = loadPETable(exe, "runtime.symtab", "runtime.esymtab"); err != nil {
// Same as above.
var err2 error
if symtab, err2 = loadPETable(exe, "symtab", "esymtab"); err2 != nil {
return 0, nil, nil, err
}
}
return textStart, symtab, pclntab, nil
}
func (dbp *Process) obtainGoSymbols(exe *pe.File, wg *sync.WaitGroup) {
defer wg.Done()
_, symdat, pclndat, err := pcln(exe)
if err != nil {
fmt.Println("could not get Go symbols", err)
os.Exit(1)
}
pcln := gosym.NewLineTable(pclndat, uint64(exe.Section(".text").Offset))
tab, err := gosym.NewTable(symdat, pcln)
if err != nil {
fmt.Println("could not get initialize line table", err)
os.Exit(1)
}
dbp.goSymTable = tab
}
func (dbp *Process) parseDebugLineInfo(exe *pe.File, wg *sync.WaitGroup) {
defer wg.Done()
if sec := exe.Section(".debug_line"); sec != nil {
debugLine, err := sec.Data()
if err != nil && uint32(len(debugLine)) < sec.Size {
fmt.Println("could not get .debug_line section", err)
os.Exit(1)
}
if 0 < sec.VirtualSize && sec.VirtualSize < sec.Size {
debugLine = debugLine[:sec.VirtualSize]
}
dbp.lineInfo = line.Parse(debugLine)
} else {
fmt.Println("could not find .debug_line section in binary")
os.Exit(1)
}
}
var UnsupportedArchErr = errors.New("unsupported architecture of windows/386 - only windows/amd64 is supported")
func (dbp *Process) findExecutable(path string) (*pe.File, string, error) {
peFile, err := openExecutablePath(path)
if err != nil {
return nil, path, err
}
if peFile.Machine != pe.IMAGE_FILE_MACHINE_AMD64 {
return nil, path, UnsupportedArchErr
}
dbp.dwarf, err = dwarfFromPE(peFile)
if err != nil {
return nil, path, err
}
return peFile, path, nil
}
func openExecutablePath(path string) (*pe.File, error) {
f, err := os.OpenFile(path, 0, os.ModePerm)
if err != nil {
return nil, err
}
return pe.NewFile(f)
}
// Adapted from src/debug/pe/file.go: pe.(*File).DWARF()
func dwarfFromPE(f *pe.File) (*dwarf.Data, error) {
// There are many other DWARF sections, but these
// are the ones the debug/dwarf package uses.
// Don't bother loading others.
var names = [...]string{"abbrev", "info", "line", "str"}
var dat [len(names)][]byte
for i, name := range names {
name = ".debug_" + name
s := f.Section(name)
if s == nil {
continue
}
b, err := s.Data()
if err != nil && uint32(len(b)) < s.Size {
return nil, err
}
if 0 < s.VirtualSize && s.VirtualSize < s.Size {
b = b[:s.VirtualSize]
}
dat[i] = b
}
abbrev, info, line, str := dat[0], dat[1], dat[2], dat[3]
return dwarf.New(abbrev, nil, nil, info, line, nil, nil, str)
} }
type waitForDebugEventFlags int type waitForDebugEventFlags int
@ -519,8 +330,8 @@ func (dbp *Process) waitForDebugEvent(flags waitForDebugEventFlags) (threadID, e
// this exception anymore. // this exception anymore.
atbp := true atbp := true
if thread, found := dbp.threads[tid]; found { if thread, found := dbp.threads[tid]; found {
if data, err := thread.readMemory(exception.ExceptionRecord.ExceptionAddress, dbp.arch.BreakpointSize()); err == nil { if data, err := thread.readMemory(exception.ExceptionRecord.ExceptionAddress, dbp.bi.arch.BreakpointSize()); err == nil {
instr := dbp.arch.BreakpointInstruction() instr := dbp.bi.arch.BreakpointInstruction()
for i := range instr { for i := range instr {
if data[i] != instr[i] { if data[i] != instr[i] {
atbp = false atbp = false
@ -663,11 +474,13 @@ func (dbp *Process) resume() error {
return nil return nil
} }
func (dbp *Process) detach() error { func (dbp *Process) detach(kill bool) error {
for _, thread := range dbp.threads { if !kill {
_, err := _ResumeThread(thread.os.hThread) for _, thread := range dbp.threads {
if err != nil { _, err := _ResumeThread(thread.os.hThread)
return err if err != nil {
return err
}
} }
} }
return PtraceDetach(dbp.pid, 0) return PtraceDetach(dbp.pid, 0)

@ -98,7 +98,7 @@ func (g *G) Stacktrace(depth int) ([]Stackframe, error) {
// GoroutineLocation returns the location of the given // GoroutineLocation returns the location of the given
// goroutine. // goroutine.
func (dbp *Process) GoroutineLocation(g *G) *Location { func (dbp *Process) GoroutineLocation(g *G) *Location {
f, l, fn := dbp.PCToLine(g.PC) f, l, fn := dbp.bi.PCToLine(g.PC)
return &Location{PC: g.PC, File: f, Line: l, Fn: fn} return &Location{PC: g.PC, File: f, Line: l, Fn: fn}
} }
@ -130,11 +130,11 @@ type savedLR struct {
} }
func newStackIterator(dbp *Process, pc, sp, bp uint64, stkbar []savedLR, stkbarPos int) *stackIterator { func newStackIterator(dbp *Process, pc, sp, bp uint64, stkbar []savedLR, stkbarPos int) *stackIterator {
stackBarrierFunc := dbp.goSymTable.LookupFunc(runtimeStackBarrier) // stack barriers were removed in Go 1.9 stackBarrierFunc := dbp.bi.goSymTable.LookupFunc(runtimeStackBarrier) // stack barriers were removed in Go 1.9
var stackBarrierPC uint64 var stackBarrierPC uint64
if stackBarrierFunc != nil && stkbar != nil { if stackBarrierFunc != nil && stkbar != nil {
stackBarrierPC = stackBarrierFunc.Entry stackBarrierPC = stackBarrierFunc.Entry
fn := dbp.goSymTable.PCToFunc(pc) fn := dbp.bi.goSymTable.PCToFunc(pc)
if fn != nil && fn.Name == runtimeStackBarrier { if fn != nil && fn.Name == runtimeStackBarrier {
// We caught the goroutine as it's executing the stack barrier, we must // We caught the goroutine as it's executing the stack barrier, we must
// determine whether or not g.stackPos has already been incremented or not. // determine whether or not g.stackPos has already been incremented or not.
@ -188,7 +188,7 @@ func (it *stackIterator) Next() bool {
it.top = false it.top = false
it.pc = it.frame.Ret it.pc = it.frame.Ret
it.sp = uint64(it.frame.CFA) it.sp = uint64(it.frame.CFA)
it.bp, _ = readUintRaw(it.dbp.currentThread, uintptr(it.bp), int64(it.dbp.arch.PtrSize())) it.bp, _ = readUintRaw(it.dbp.currentThread, uintptr(it.bp), int64(it.dbp.bi.arch.PtrSize()))
return true return true
} }
@ -206,14 +206,14 @@ func (it *stackIterator) Err() error {
} }
func (dbp *Process) frameInfo(pc, sp, bp uint64, top bool) (Stackframe, error) { func (dbp *Process) frameInfo(pc, sp, bp uint64, top bool) (Stackframe, error) {
fde, err := dbp.frameEntries.FDEForPC(pc) fde, err := dbp.bi.frameEntries.FDEForPC(pc)
if _, nofde := err.(*frame.NoFDEForPCError); nofde { if _, nofde := err.(*frame.NoFDEForPCError); nofde {
if bp == 0 { if bp == 0 {
return Stackframe{}, err return Stackframe{}, err
} }
// When no FDE is available attempt to use BP instead // When no FDE is available attempt to use BP instead
retaddr := uintptr(int(bp) + dbp.arch.PtrSize()) retaddr := uintptr(int(bp) + dbp.bi.arch.PtrSize())
cfa := int64(retaddr) + int64(dbp.arch.PtrSize()) cfa := int64(retaddr) + int64(dbp.bi.arch.PtrSize())
return dbp.newStackframe(pc, cfa, retaddr, nil, top) return dbp.newStackframe(pc, cfa, retaddr, nil, top)
} }
@ -228,14 +228,14 @@ func (dbp *Process) newStackframe(pc uint64, cfa int64, retaddr uintptr, fde *fr
if retaddr == 0 { if retaddr == 0 {
return Stackframe{}, NullAddrError{} return Stackframe{}, NullAddrError{}
} }
f, l, fn := dbp.PCToLine(pc) f, l, fn := dbp.bi.PCToLine(pc)
ret, err := readUintRaw(dbp.currentThread, retaddr, int64(dbp.arch.PtrSize())) ret, err := readUintRaw(dbp.currentThread, retaddr, int64(dbp.bi.arch.PtrSize()))
if err != nil { if err != nil {
return Stackframe{}, err return Stackframe{}, err
} }
r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, FDE: fde, Ret: ret, addrret: uint64(retaddr)} r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, FDE: fde, Ret: ret, addrret: uint64(retaddr)}
if !top { if !top {
r.Call.File, r.Call.Line, r.Call.Fn = dbp.PCToLine(pc - 1) r.Call.File, r.Call.Line, r.Call.Fn = dbp.bi.PCToLine(pc - 1)
r.Call.PC = r.Current.PC r.Call.PC = r.Current.PC
} else { } else {
r.Call = r.Current r.Call = r.Current

@ -112,7 +112,7 @@ func (thread *Thread) Location() (*Location, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
f, l, fn := thread.dbp.PCToLine(pc) f, l, fn := thread.dbp.bi.PCToLine(pc)
return &Location{PC: pc, File: f, Line: l, Fn: fn}, nil return &Location{PC: pc, File: f, Line: l, Fn: fn}, nil
} }
@ -218,7 +218,7 @@ func (dbp *Process) next(stepInto bool) error {
if dbp.selectedGoroutine != nil { if dbp.selectedGoroutine != nil {
deferPCEntry := dbp.selectedGoroutine.DeferPC() deferPCEntry := dbp.selectedGoroutine.DeferPC()
if deferPCEntry != 0 { if deferPCEntry != 0 {
_, _, deferfn := dbp.goSymTable.PCToLine(deferPCEntry) _, _, deferfn := dbp.bi.goSymTable.PCToLine(deferPCEntry)
var err error var err error
deferpc, err = dbp.FirstPCAfterPrologue(deferfn, false) deferpc, err = dbp.FirstPCAfterPrologue(deferfn, false)
if err != nil { if err != nil {
@ -240,7 +240,7 @@ func (dbp *Process) next(stepInto bool) error {
} }
// Add breakpoints on all the lines in the current function // Add breakpoints on all the lines in the current function
pcs, err := dbp.lineInfo.AllPCsBetween(topframe.FDE.Begin(), topframe.FDE.End()-1, topframe.Current.File) pcs, err := dbp.bi.lineInfo.AllPCsBetween(topframe.FDE.Begin(), topframe.FDE.End()-1, topframe.Current.File)
if err != nil { if err != nil {
return err return err
} }
@ -255,7 +255,7 @@ func (dbp *Process) next(stepInto bool) error {
} }
if !covered { if !covered {
fn := dbp.goSymTable.PCToFunc(topframe.Ret) fn := dbp.bi.goSymTable.PCToFunc(topframe.Ret)
if dbp.selectedGoroutine != nil && fn != nil && fn.Name == "runtime.goexit" { if dbp.selectedGoroutine != nil && fn != nil && fn.Name == "runtime.goexit" {
return nil return nil
} }
@ -340,13 +340,13 @@ func (thread *Thread) getGVariable() (*Variable, error) {
return nil, err return nil, err
} }
if thread.dbp.arch.GStructOffset() == 0 { if thread.dbp.bi.arch.GStructOffset() == 0 {
// GetG was called through SwitchThread / updateThreadList during initialization // GetG was called through SwitchThread / updateThreadList during initialization
// thread.dbp.arch isn't setup yet (it needs a current thread to read global variables from) // thread.dbp.arch isn't setup yet (it needs a current thread to read global variables from)
return nil, fmt.Errorf("g struct offset not initialized") return nil, fmt.Errorf("g struct offset not initialized")
} }
gaddrbs, err := thread.readMemory(uintptr(regs.TLS()+thread.dbp.arch.GStructOffset()), thread.dbp.arch.PtrSize()) gaddrbs, err := thread.readMemory(uintptr(regs.TLS()+thread.dbp.bi.arch.GStructOffset()), thread.dbp.bi.arch.PtrSize())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -360,7 +360,7 @@ func (thread *Thread) getGVariable() (*Variable, error) {
} }
func (thread *Thread) newGVariable(gaddr uintptr, deref bool) (*Variable, error) { func (thread *Thread) newGVariable(gaddr uintptr, deref bool) (*Variable, error) {
typ, err := thread.dbp.findType("runtime.g") typ, err := thread.dbp.bi.findType("runtime.g")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -368,7 +368,7 @@ func (thread *Thread) newGVariable(gaddr uintptr, deref bool) (*Variable, error)
name := "" name := ""
if deref { if deref {
typ = &dwarf.PtrType{dwarf.CommonType{int64(thread.dbp.arch.PtrSize()), "", reflect.Ptr, 0}, typ} typ = &dwarf.PtrType{dwarf.CommonType{int64(thread.dbp.bi.arch.PtrSize()), "", reflect.Ptr, 0}, typ}
} else { } else {
name = "runtime.curg" name = "runtime.curg"
} }

@ -87,7 +87,7 @@ func (t *Thread) blocked() bool {
if err != nil { if err != nil {
return false return false
} }
fn := t.dbp.goSymTable.PCToFunc(pc) fn := t.dbp.bi.goSymTable.PCToFunc(pc)
if fn == nil { if fn == nil {
return false return false
} }

@ -69,7 +69,7 @@ func (t *Thread) singleStep() (err error) {
func (t *Thread) blocked() bool { func (t *Thread) blocked() bool {
pc, _ := t.PC() pc, _ := t.PC()
fn := t.dbp.goSymTable.PCToFunc(pc) fn := t.dbp.bi.goSymTable.PCToFunc(pc)
if fn != nil && ((fn.Name == "runtime.futex") || (fn.Name == "runtime.usleep") || (fn.Name == "runtime.clone")) { if fn != nil && ((fn.Name == "runtime.futex") || (fn.Name == "runtime.usleep") || (fn.Name == "runtime.clone")) {
return true return true
} }

@ -110,7 +110,7 @@ func (t *Thread) blocked() bool {
if err != nil { if err != nil {
return false return false
} }
fn := t.dbp.goSymTable.PCToFunc(pc) fn := t.dbp.bi.goSymTable.PCToFunc(pc)
if fn == nil { if fn == nil {
return false return false
} }

@ -38,40 +38,40 @@ const (
) )
// Do not call this function directly it isn't able to deal correctly with package paths // Do not call this function directly it isn't able to deal correctly with package paths
func (dbp *Process) findType(name string) (dwarf.Type, error) { func (bi *BinaryInfo) findType(name string) (dwarf.Type, error) {
off, found := dbp.types[name] off, found := bi.types[name]
if !found { if !found {
return nil, reader.TypeNotFoundErr return nil, reader.TypeNotFoundErr
} }
return dbp.dwarf.Type(off) return bi.dwarf.Type(off)
} }
func (dbp *Process) pointerTo(typ dwarf.Type) dwarf.Type { func pointerTo(typ dwarf.Type, arch Arch) dwarf.Type {
return &dwarf.PtrType{dwarf.CommonType{int64(dbp.arch.PtrSize()), "*" + typ.Common().Name, reflect.Ptr, 0}, typ} return &dwarf.PtrType{dwarf.CommonType{int64(arch.PtrSize()), "*" + typ.Common().Name, reflect.Ptr, 0}, typ}
} }
func (dbp *Process) findTypeExpr(expr ast.Expr) (dwarf.Type, error) { func (bi *BinaryInfo) findTypeExpr(expr ast.Expr) (dwarf.Type, error) {
dbp.loadPackageMap() bi.loadPackageMap()
if lit, islit := expr.(*ast.BasicLit); islit && lit.Kind == token.STRING { if lit, islit := expr.(*ast.BasicLit); islit && lit.Kind == token.STRING {
// Allow users to specify type names verbatim as quoted // Allow users to specify type names verbatim as quoted
// string. Useful as a catch-all workaround for cases where we don't // string. Useful as a catch-all workaround for cases where we don't
// parse/serialize types correctly or can not resolve package paths. // parse/serialize types correctly or can not resolve package paths.
typn, _ := strconv.Unquote(lit.Value) typn, _ := strconv.Unquote(lit.Value)
return dbp.findType(typn) return bi.findType(typn)
} }
dbp.expandPackagesInType(expr) bi.expandPackagesInType(expr)
if snode, ok := expr.(*ast.StarExpr); ok { if snode, ok := expr.(*ast.StarExpr); ok {
// Pointer types only appear in the dwarf informations when // Pointer types only appear in the dwarf informations when
// a pointer to the type is used in the target program, here // a pointer to the type is used in the target program, here
// we create a pointer type on the fly so that the user can // we create a pointer type on the fly so that the user can
// specify a pointer to any variable used in the target program // specify a pointer to any variable used in the target program
ptyp, err := dbp.findTypeExpr(snode.X) ptyp, err := bi.findTypeExpr(snode.X)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return dbp.pointerTo(ptyp), nil return pointerTo(ptyp, bi.arch), nil
} }
return dbp.findType(exprToString(expr)) return bi.findType(exprToString(expr))
} }
func complexType(typename string) bool { func complexType(typename string) bool {
@ -84,12 +84,12 @@ func complexType(typename string) bool {
return false return false
} }
func (dbp *Process) loadPackageMap() error { func (bi *BinaryInfo) loadPackageMap() error {
if dbp.packageMap != nil { if bi.packageMap != nil {
return nil return nil
} }
dbp.packageMap = map[string]string{} bi.packageMap = map[string]string{}
reader := dbp.DwarfReader() reader := bi.DwarfReader()
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
if err != nil { if err != nil {
return err return err
@ -114,7 +114,7 @@ func (dbp *Process) loadPackageMap() error {
continue continue
} }
name := path[slash+1:] name := path[slash+1:]
dbp.packageMap[name] = path bi.packageMap[name] = path
} }
return nil return nil
} }
@ -129,11 +129,11 @@ func (v sortFunctionsDebugInfoByLowpc) Swap(i, j int) {
v[j] = temp v[j] = temp
} }
func (dbp *Process) loadDebugInfoMaps(wg *sync.WaitGroup) { func (bi *BinaryInfo) loadDebugInfoMaps(wg *sync.WaitGroup) {
defer wg.Done() defer wg.Done()
dbp.types = make(map[string]dwarf.Offset) bi.types = make(map[string]dwarf.Offset)
dbp.functions = []functionDebugInfo{} bi.functions = []functionDebugInfo{}
reader := dbp.DwarfReader() reader := bi.DwarfReader()
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
if err != nil { if err != nil {
break break
@ -144,8 +144,8 @@ func (dbp *Process) loadDebugInfoMaps(wg *sync.WaitGroup) {
if !ok { if !ok {
continue continue
} }
if _, exists := dbp.types[name]; !exists { if _, exists := bi.types[name]; !exists {
dbp.types[name] = entry.Offset bi.types[name] = entry.Offset
} }
case dwarf.TagSubprogram: case dwarf.TagSubprogram:
lowpc, ok := entry.Val(dwarf.AttrLowpc).(uint64) lowpc, ok := entry.Val(dwarf.AttrLowpc).(uint64)
@ -156,19 +156,19 @@ func (dbp *Process) loadDebugInfoMaps(wg *sync.WaitGroup) {
if !ok { if !ok {
continue continue
} }
dbp.functions = append(dbp.functions, functionDebugInfo{lowpc, highpc, entry.Offset}) bi.functions = append(bi.functions, functionDebugInfo{lowpc, highpc, entry.Offset})
} }
} }
sort.Sort(sortFunctionsDebugInfoByLowpc(dbp.functions)) sort.Sort(sortFunctionsDebugInfoByLowpc(bi.functions))
} }
func (dbp *Process) findFunctionDebugInfo(pc uint64) (dwarf.Offset, error) { func (bi *BinaryInfo) findFunctionDebugInfo(pc uint64) (dwarf.Offset, error) {
i := sort.Search(len(dbp.functions), func(i int) bool { i := sort.Search(len(bi.functions), func(i int) bool {
fn := dbp.functions[i] fn := bi.functions[i]
return pc <= fn.lowpc || (fn.lowpc <= pc && pc < fn.highpc) return pc <= fn.lowpc || (fn.lowpc <= pc && pc < fn.highpc)
}) })
if i != len(dbp.functions) { if i != len(bi.functions) {
fn := dbp.functions[i] fn := bi.functions[i]
if fn.lowpc <= pc && pc < fn.highpc { if fn.lowpc <= pc && pc < fn.highpc {
return fn.offset, nil return fn.offset, nil
} }
@ -176,37 +176,37 @@ func (dbp *Process) findFunctionDebugInfo(pc uint64) (dwarf.Offset, error) {
return 0, errors.New("unable to find function context") return 0, errors.New("unable to find function context")
} }
func (dbp *Process) expandPackagesInType(expr ast.Expr) { func (bi *BinaryInfo) expandPackagesInType(expr ast.Expr) {
switch e := expr.(type) { switch e := expr.(type) {
case *ast.ArrayType: case *ast.ArrayType:
dbp.expandPackagesInType(e.Elt) bi.expandPackagesInType(e.Elt)
case *ast.ChanType: case *ast.ChanType:
dbp.expandPackagesInType(e.Value) bi.expandPackagesInType(e.Value)
case *ast.FuncType: case *ast.FuncType:
for i := range e.Params.List { for i := range e.Params.List {
dbp.expandPackagesInType(e.Params.List[i].Type) bi.expandPackagesInType(e.Params.List[i].Type)
} }
if e.Results != nil { if e.Results != nil {
for i := range e.Results.List { for i := range e.Results.List {
dbp.expandPackagesInType(e.Results.List[i].Type) bi.expandPackagesInType(e.Results.List[i].Type)
} }
} }
case *ast.MapType: case *ast.MapType:
dbp.expandPackagesInType(e.Key) bi.expandPackagesInType(e.Key)
dbp.expandPackagesInType(e.Value) bi.expandPackagesInType(e.Value)
case *ast.ParenExpr: case *ast.ParenExpr:
dbp.expandPackagesInType(e.X) bi.expandPackagesInType(e.X)
case *ast.SelectorExpr: case *ast.SelectorExpr:
switch x := e.X.(type) { switch x := e.X.(type) {
case *ast.Ident: case *ast.Ident:
if path, ok := dbp.packageMap[x.Name]; ok { if path, ok := bi.packageMap[x.Name]; ok {
x.Name = path x.Name = path
} }
default: default:
dbp.expandPackagesInType(e.X) bi.expandPackagesInType(e.X)
} }
case *ast.StarExpr: case *ast.StarExpr:
dbp.expandPackagesInType(e.X) bi.expandPackagesInType(e.X)
default: default:
// nothing to do // nothing to do
} }
@ -221,7 +221,7 @@ type nameOfRuntimeTypeEntry struct {
// _type is a non-loaded Variable pointing to runtime._type struct in the target. // _type is a non-loaded Variable pointing to runtime._type struct in the target.
// The returned string is in the format that's used in DWARF data // The returned string is in the format that's used in DWARF data
func nameOfRuntimeType(_type *Variable) (typename string, kind int64, err error) { func nameOfRuntimeType(_type *Variable) (typename string, kind int64, err error) {
if e, ok := _type.dbp.nameOfRuntimeType[_type.Addr]; ok { if e, ok := _type.dbp.bi.nameOfRuntimeType[_type.Addr]; ok {
return e.typename, e.kind, nil return e.typename, e.kind, nil
} }
@ -244,7 +244,7 @@ func nameOfRuntimeType(_type *Variable) (typename string, kind int64, err error)
return typename, kind, err return typename, kind, err
} }
_type.dbp.nameOfRuntimeType[_type.Addr] = nameOfRuntimeTypeEntry{typename, kind} _type.dbp.bi.nameOfRuntimeType[_type.Addr] = nameOfRuntimeTypeEntry{typename, kind}
return typename, kind, nil return typename, kind, nil
} }
@ -277,7 +277,7 @@ func nameOfNamedRuntimeType(_type *Variable, kind, tflag int64) (typename string
// For a description of how memory is organized for type names read // For a description of how memory is organized for type names read
// the comment to 'type name struct' in $GOROOT/src/reflect/type.go // the comment to 'type name struct' in $GOROOT/src/reflect/type.go
typename, _, _, err = _type.dbp.resolveNameOff(_type.Addr, uintptr(strOff)) typename, _, _, err = _type.dbp.bi.resolveNameOff(_type.Addr, uintptr(strOff), _type.dbp.currentThread)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -303,7 +303,7 @@ func nameOfNamedRuntimeType(_type *Variable, kind, tflag int64) (typename string
if ut := uncommon(_type, tflag); ut != nil { if ut := uncommon(_type, tflag); ut != nil {
if pkgPathField := ut.loadFieldNamed("pkgpath"); pkgPathField != nil && pkgPathField.Value != nil { if pkgPathField := ut.loadFieldNamed("pkgpath"); pkgPathField != nil && pkgPathField.Value != nil {
pkgPathOff, _ := constant.Int64Val(pkgPathField.Value) pkgPathOff, _ := constant.Int64Val(pkgPathField.Value)
pkgPath, _, _, err := _type.dbp.resolveNameOff(_type.Addr, uintptr(pkgPathOff)) pkgPath, _, _, err := _type.dbp.bi.resolveNameOff(_type.Addr, uintptr(pkgPathOff), _type.dbp.currentThread)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -376,11 +376,11 @@ func nameOfUnnamedRuntimeType(_type *Variable, kind, tflag int64) (string, error
// (optional) and then by an array of pointers to runtime._type, // (optional) and then by an array of pointers to runtime._type,
// one for each input and output argument. // one for each input and output argument.
func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string, error) { func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string, error) {
rtyp, err := _type.dbp.findType("runtime._type") rtyp, err := _type.dbp.bi.findType("runtime._type")
if err != nil { if err != nil {
return "", err return "", err
} }
prtyp := _type.dbp.pointerTo(rtyp) prtyp := pointerTo(rtyp, _type.dbp.bi.arch)
uadd := _type.RealType.Common().ByteSize uadd := _type.RealType.Common().ByteSize
if ut := uncommon(_type, tflag); ut != nil { if ut := uncommon(_type, tflag); ut != nil {
@ -407,7 +407,7 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string
for i := int64(0); i < inCount; i++ { for i := int64(0); i < inCount; i++ {
argtype := cursortyp.maybeDereference() argtype := cursortyp.maybeDereference()
cursortyp.Addr += uintptr(_type.dbp.arch.PtrSize()) cursortyp.Addr += uintptr(_type.dbp.bi.arch.PtrSize())
argtypename, _, err := nameOfRuntimeType(argtype) argtypename, _, err := nameOfRuntimeType(argtype)
if err != nil { if err != nil {
return "", err return "", err
@ -434,7 +434,7 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string
buf.WriteString(" (") buf.WriteString(" (")
for i := int64(0); i < outCount; i++ { for i := int64(0); i < outCount; i++ {
argtype := cursortyp.maybeDereference() argtype := cursortyp.maybeDereference()
cursortyp.Addr += uintptr(_type.dbp.arch.PtrSize()) cursortyp.Addr += uintptr(_type.dbp.bi.arch.PtrSize())
argtypename, _, err := nameOfRuntimeType(argtype) argtypename, _, err := nameOfRuntimeType(argtype)
if err != nil { if err != nil {
return "", err return "", err
@ -473,14 +473,14 @@ func nameOfInterfaceRuntimeType(_type *Variable, kind, tflag int64) (string, err
case "name": case "name":
nameoff, _ := constant.Int64Val(im.Children[i].Value) nameoff, _ := constant.Int64Val(im.Children[i].Value)
var err error var err error
methodname, _, _, err = _type.dbp.resolveNameOff(_type.Addr, uintptr(nameoff)) methodname, _, _, err = _type.dbp.bi.resolveNameOff(_type.Addr, uintptr(nameoff), _type.dbp.currentThread)
if err != nil { if err != nil {
return "", err return "", err
} }
case "typ": case "typ":
typeoff, _ := constant.Int64Val(im.Children[i].Value) typeoff, _ := constant.Int64Val(im.Children[i].Value)
typ, err := _type.dbp.resolveTypeOff(_type.Addr, uintptr(typeoff)) typ, err := _type.dbp.bi.resolveTypeOff(_type.Addr, uintptr(typeoff), _type.dbp.currentThread)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -536,7 +536,7 @@ func nameOfStructRuntimeType(_type *Variable, kind, tflag int64) (string, error)
case "name": case "name":
nameoff, _ := constant.Int64Val(field.Children[i].Value) nameoff, _ := constant.Int64Val(field.Children[i].Value)
var err error var err error
fieldname, _, _, err = _type.dbp.loadName(uintptr(nameoff)) fieldname, _, _, err = _type.dbp.bi.loadName(uintptr(nameoff), _type.mem)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -578,13 +578,13 @@ func fieldToType(_type *Variable, fieldName string) (string, error) {
} }
func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) { func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) {
rtyp, err := _type.dbp.findType("runtime._type") rtyp, err := _type.dbp.bi.findType("runtime._type")
if err != nil { if err != nil {
return nil, err return nil, err
} }
prtyp := _type.dbp.pointerTo(rtyp) prtyp := pointerTo(rtyp, _type.dbp.bi.arch)
uintptrtyp, err := _type.dbp.findType("uintptr") uintptrtyp, err := _type.dbp.bi.findType("uintptr")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -602,7 +602,7 @@ func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) {
newSliceType := func(elemtype dwarf.Type) *dwarf.SliceType { newSliceType := func(elemtype dwarf.Type) *dwarf.SliceType {
r := newStructType("[]"+elemtype.Common().Name, uintptr(3*uintptrtyp.Size())) r := newStructType("[]"+elemtype.Common().Name, uintptr(3*uintptrtyp.Size()))
appendField(r, "array", _type.dbp.pointerTo(elemtype), 0) appendField(r, "array", pointerTo(elemtype, _type.dbp.bi.arch), 0)
appendField(r, "len", uintptrtyp, uintptr(uintptrtyp.Size())) appendField(r, "len", uintptrtyp, uintptr(uintptrtyp.Size()))
appendField(r, "cap", uintptrtyp, uintptr(2*uintptrtyp.Size())) appendField(r, "cap", uintptrtyp, uintptr(2*uintptrtyp.Size()))
return &dwarf.SliceType{StructType: *r, ElemType: elemtype} return &dwarf.SliceType{StructType: *r, ElemType: elemtype}
@ -746,7 +746,7 @@ func uncommon(_type *Variable, tflag int64) *Variable {
return nil return nil
} }
typ, err := _type.dbp.findType("runtime.uncommontype") typ, err := _type.dbp.bi.findType("runtime.uncommontype")
if err != nil { if err != nil {
return nil return nil
} }

@ -190,7 +190,7 @@ func newVariable(name string, addr uintptr, dwarfType dwarf.Type, dbp *Process,
v.stride = 1 v.stride = 1
v.fieldType = &dwarf.UintType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 1, Name: "byte"}, BitSize: 8, BitOffset: 0}} v.fieldType = &dwarf.UintType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 1, Name: "byte"}, BitSize: 8, BitOffset: 0}}
if v.Addr != 0 { if v.Addr != 0 {
v.Base, v.Len, v.Unreadable = readStringInfo(v.mem, v.dbp.arch, v.Addr) v.Base, v.Len, v.Unreadable = readStringInfo(v.mem, v.dbp.bi.arch, v.Addr)
} }
case *dwarf.SliceType: case *dwarf.SliceType:
v.Kind = reflect.Slice v.Kind = reflect.Slice
@ -321,17 +321,17 @@ func (v *Variable) toField(field *dwarf.StructField) (*Variable, error) {
// DwarfReader returns the DwarfReader containing the // DwarfReader returns the DwarfReader containing the
// Dwarf information for the target process. // Dwarf information for the target process.
func (scope *EvalScope) DwarfReader() *reader.Reader { func (scope *EvalScope) DwarfReader() *reader.Reader {
return scope.Thread.dbp.DwarfReader() return scope.Thread.dbp.bi.DwarfReader()
} }
// Type returns the Dwarf type entry at `offset`. // Type returns the Dwarf type entry at `offset`.
func (scope *EvalScope) Type(offset dwarf.Offset) (dwarf.Type, error) { func (scope *EvalScope) Type(offset dwarf.Offset) (dwarf.Type, error) {
return scope.Thread.dbp.dwarf.Type(offset) return scope.Thread.dbp.bi.dwarf.Type(offset)
} }
// PtrSize returns the size of a pointer. // PtrSize returns the size of a pointer.
func (scope *EvalScope) PtrSize() int { func (scope *EvalScope) PtrSize() int {
return scope.Thread.dbp.arch.PtrSize() return scope.Thread.dbp.bi.arch.PtrSize()
} }
// ChanRecvBlocked returns whether the goroutine is blocked on // ChanRecvBlocked returns whether the goroutine is blocked on
@ -367,7 +367,7 @@ func (gvar *Variable) parseG() (*G, error) {
_, deref := gvar.RealType.(*dwarf.PtrType) _, deref := gvar.RealType.(*dwarf.PtrType)
if deref { if deref {
gaddrbytes, err := mem.readMemory(uintptr(gaddr), dbp.arch.PtrSize()) gaddrbytes, err := mem.readMemory(uintptr(gaddr), dbp.bi.arch.PtrSize())
if err != nil { if err != nil {
return nil, fmt.Errorf("error derefing *G %s", err) return nil, fmt.Errorf("error derefing *G %s", err)
} }
@ -405,7 +405,7 @@ func (gvar *Variable) parseG() (*G, error) {
} }
status, _ := constant.Int64Val(gvar.fieldVariable("atomicstatus").Value) status, _ := constant.Int64Val(gvar.fieldVariable("atomicstatus").Value)
f, l, fn := gvar.dbp.goSymTable.PCToLine(uint64(pc)) f, l, fn := gvar.dbp.bi.goSymTable.PCToLine(uint64(pc))
g := &G{ g := &G{
ID: int(id), ID: int(id),
GoPC: uint64(gopc), GoPC: uint64(gopc),
@ -498,7 +498,7 @@ func (g *G) UserCurrent() Location {
// Go returns the location of the 'go' statement // Go returns the location of the 'go' statement
// that spawned this goroutine. // that spawned this goroutine.
func (g *G) Go() Location { func (g *G) Go() Location {
f, l, fn := g.dbp.goSymTable.PCToLine(g.GoPC) f, l, fn := g.dbp.bi.goSymTable.PCToLine(g.GoPC)
return Location{PC: g.GoPC, File: f, Line: l, Fn: fn} return Location{PC: g.GoPC, File: f, Line: l, Fn: fn}
} }
@ -586,7 +586,7 @@ func (scope *EvalScope) extractVariableFromEntry(entry *dwarf.Entry, cfg LoadCon
func (scope *EvalScope) extractVarInfo(varName string) (*Variable, error) { func (scope *EvalScope) extractVarInfo(varName string) (*Variable, error) {
reader := scope.DwarfReader() reader := scope.DwarfReader()
off, err := scope.Thread.dbp.findFunctionDebugInfo(scope.PC) off, err := scope.Thread.dbp.bi.findFunctionDebugInfo(scope.PC)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1207,7 +1207,7 @@ func (v *Variable) writeBool(value bool) error {
} }
func (v *Variable) readFunctionPtr() { func (v *Variable) readFunctionPtr() {
val, err := v.mem.readMemory(v.Addr, v.dbp.arch.PtrSize()) val, err := v.mem.readMemory(v.Addr, v.dbp.bi.arch.PtrSize())
if err != nil { if err != nil {
v.Unreadable = err v.Unreadable = err
return return
@ -1221,14 +1221,14 @@ func (v *Variable) readFunctionPtr() {
return return
} }
val, err = v.mem.readMemory(fnaddr, v.dbp.arch.PtrSize()) val, err = v.mem.readMemory(fnaddr, v.dbp.bi.arch.PtrSize())
if err != nil { if err != nil {
v.Unreadable = err v.Unreadable = err
return return
} }
v.Base = uintptr(binary.LittleEndian.Uint64(val)) v.Base = uintptr(binary.LittleEndian.Uint64(val))
fn := v.dbp.goSymTable.PCToFunc(uint64(v.Base)) fn := v.dbp.bi.goSymTable.PCToFunc(uint64(v.Base))
if fn == nil { if fn == nil {
v.Unreadable = fmt.Errorf("could not find function for %#v", v.Base) v.Unreadable = fmt.Errorf("could not find function for %#v", v.Base)
return return
@ -1603,7 +1603,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
return return
} }
typ, err = v.dbp.findType(typename) typ, err = v.dbp.bi.findType(typename)
if err != nil { if err != nil {
v.Unreadable = fmt.Errorf("interface type %q not found for %#x: %v", typename, data.Addr, err) v.Unreadable = fmt.Errorf("interface type %q not found for %#x: %v", typename, data.Addr, err)
return return
@ -1627,7 +1627,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
return return
} }
typ, err = v.dbp.findTypeExpr(t) typ, err = v.dbp.bi.findTypeExpr(t)
if err != nil { if err != nil {
v.Unreadable = fmt.Errorf("interface type %q not found for %#x: %v", typename, data.Addr, err) v.Unreadable = fmt.Errorf("interface type %q not found for %#x: %v", typename, data.Addr, err)
return return
@ -1637,7 +1637,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
if kind&kindDirectIface == 0 { if kind&kindDirectIface == 0 {
realtyp := resolveTypedef(typ) realtyp := resolveTypedef(typ)
if _, isptr := realtyp.(*dwarf.PtrType); !isptr { if _, isptr := realtyp.(*dwarf.PtrType); !isptr {
typ = v.dbp.pointerTo(typ) typ = pointerTo(typ, v.dbp.bi.arch)
} }
} }
@ -1655,7 +1655,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
// Fetches all variables of a specific type in the current function scope // Fetches all variables of a specific type in the current function scope
func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg LoadConfig) ([]*Variable, error) { func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg LoadConfig) ([]*Variable, error) {
reader := scope.DwarfReader() reader := scope.DwarfReader()
off, err := scope.Thread.dbp.findFunctionDebugInfo(scope.PC) off, err := scope.Thread.dbp.bi.findFunctionDebugInfo(scope.PC)
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -3,7 +3,6 @@ package target
import ( import (
"debug/gosym" "debug/gosym"
"go/ast" "go/ast"
"time"
"github.com/derekparker/delve/pkg/proc" "github.com/derekparker/delve/pkg/proc"
) )
@ -22,23 +21,14 @@ type Info interface {
Pid() int Pid() int
Exited() bool Exited() bool
Running() bool Running() bool
BinInfo() *proc.BinaryInfo
BinaryInfo
ThreadInfo ThreadInfo
GoroutineInfo GoroutineInfo
}
// BinaryInfo is an interface for accessing information on the binary file
// and the contents of binary sections.
type BinaryInfo interface {
LastModified() time.Time
Sources() map[string]*gosym.Obj
FindFileLocation(fileName string, lineNumber int) (uint64, error) FindFileLocation(fileName string, lineNumber int) (uint64, error)
FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error)
Funcs() []gosym.Func
Types() ([]string, error)
PCToLine(uint64) (string, int, *gosym.Func)
FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error)
FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error)
} }
// ThreadInfo is an interface for getting information on active threads // ThreadInfo is an interface for getting information on active threads
@ -82,3 +72,5 @@ type BreakpointManipulation interface {
type VariableEval interface { type VariableEval interface {
ConvertEvalScope(gid, frame int) (*proc.EvalScope, error) ConvertEvalScope(gid, frame int) (*proc.EvalScope, error)
} }
var _ Interface = &proc.Process{}

@ -68,7 +68,7 @@ func New(config *Config) (*Debugger, error) {
log.Printf("launching process with args: %v", d.config.ProcessArgs) log.Printf("launching process with args: %v", d.config.ProcessArgs)
p, err := proc.Launch(d.config.ProcessArgs, d.config.WorkingDir) p, err := proc.Launch(d.config.ProcessArgs, d.config.WorkingDir)
if err != nil { if err != nil {
if err != proc.NotExecutableErr && err != proc.UnsupportedArchErr { if err != proc.NotExecutableErr && err != proc.UnsupportedLinuxArchErr && err != proc.UnsupportedWindowsArchErr && err != proc.UnsupportedDarwinArchErr {
err = fmt.Errorf("could not launch process: %s", err) err = fmt.Errorf("could not launch process: %s", err)
} }
return nil, err return nil, err
@ -87,7 +87,7 @@ func (d *Debugger) ProcessPid() int {
// LastModified returns the time that the process' executable was last // LastModified returns the time that the process' executable was last
// modified. // modified.
func (d *Debugger) LastModified() time.Time { func (d *Debugger) LastModified() time.Time {
return d.target.LastModified() return d.target.BinInfo().LastModified()
} }
// Detach detaches from the target process. // Detach detaches from the target process.
@ -223,7 +223,7 @@ func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoin
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
// Accept fileName which is case-insensitive and slash-insensitive match // Accept fileName which is case-insensitive and slash-insensitive match
fileNameNormalized := strings.ToLower(filepath.ToSlash(fileName)) fileNameNormalized := strings.ToLower(filepath.ToSlash(fileName))
for symFile := range d.target.Sources() { for symFile := range d.target.BinInfo().Sources() {
if fileNameNormalized == strings.ToLower(filepath.ToSlash(symFile)) { if fileNameNormalized == strings.ToLower(filepath.ToSlash(symFile)) {
fileName = symFile fileName = symFile
break break
@ -531,7 +531,7 @@ func (d *Debugger) Sources(filter string) ([]string, error) {
} }
files := []string{} files := []string{}
for f := range d.target.Sources() { for f := range d.target.BinInfo().Sources() {
if regex.Match([]byte(f)) { if regex.Match([]byte(f)) {
files = append(files, f) files = append(files, f)
} }
@ -544,7 +544,7 @@ func (d *Debugger) Functions(filter string) ([]string, error) {
d.processMutex.Lock() d.processMutex.Lock()
defer d.processMutex.Unlock() defer d.processMutex.Unlock()
return regexFilterFuncs(filter, d.target.Funcs()) return regexFilterFuncs(filter, d.target.BinInfo().Funcs())
} }
func (d *Debugger) Types(filter string) ([]string, error) { func (d *Debugger) Types(filter string) ([]string, error) {
@ -556,7 +556,7 @@ func (d *Debugger) Types(filter string) ([]string, error) {
return nil, fmt.Errorf("invalid filter argument: %s", err.Error()) return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
} }
types, err := d.target.Types() types, err := d.target.BinInfo().Types()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -785,7 +785,7 @@ func (d *Debugger) FindLocation(scope api.EvalScope, locStr string) ([]api.Locat
locs, err := loc.Find(d, s, locStr) locs, err := loc.Find(d, s, locStr)
for i := range locs { for i := range locs {
file, line, fn := d.target.PCToLine(locs[i].PC) file, line, fn := d.target.BinInfo().PCToLine(locs[i].PC)
locs[i].File = file locs[i].File = file
locs[i].Line = line locs[i].Line = line
locs[i].Function = api.ConvertFunction(fn) locs[i].Function = api.ConvertFunction(fn)
@ -800,7 +800,7 @@ func (d *Debugger) Disassemble(scope api.EvalScope, startPC, endPC uint64, flavo
defer d.processMutex.Unlock() defer d.processMutex.Unlock()
if endPC == 0 { if endPC == 0 {
_, _, fn := d.target.PCToLine(startPC) _, _, fn := d.target.BinInfo().PCToLine(startPC)
if fn == nil { if fn == nil {
return nil, fmt.Errorf("Address 0x%x does not belong to any function", startPC) return nil, fmt.Errorf("Address 0x%x does not belong to any function", startPC)
} }

@ -243,7 +243,7 @@ func (spec *FuncLocationSpec) Match(sym *gosym.Sym) bool {
} }
func (loc *RegexLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) { func (loc *RegexLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
funcs := d.target.Funcs() funcs := d.target.BinInfo().Funcs()
matches, err := regexFilterFuncs(loc.FuncRegex, funcs) matches, err := regexFilterFuncs(loc.FuncRegex, funcs)
if err != nil { if err != nil {
return nil, err return nil, err
@ -278,7 +278,7 @@ func (loc *AddrLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr str
addr, _ := constant.Uint64Val(v.Value) addr, _ := constant.Uint64Val(v.Value)
return []api.Location{{PC: addr}}, nil return []api.Location{{PC: addr}}, nil
case reflect.Func: case reflect.Func:
_, _, fn := d.target.PCToLine(uint64(v.Base)) _, _, fn := d.target.BinInfo().PCToLine(uint64(v.Base))
pc, err := d.target.FirstPCAfterPrologue(fn, false) pc, err := d.target.FirstPCAfterPrologue(fn, false)
if err != nil { if err != nil {
return nil, err return nil, err
@ -327,8 +327,8 @@ func (ale AmbiguousLocationError) Error() string {
} }
func (loc *NormalLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) { func (loc *NormalLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
funcs := d.target.Funcs() funcs := d.target.BinInfo().Funcs()
files := d.target.Sources() files := d.target.BinInfo().Sources()
candidates := []string{} candidates := []string{}
for file := range files { for file := range files {
@ -390,7 +390,7 @@ func (loc *OffsetLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr s
if scope == nil { if scope == nil {
return nil, fmt.Errorf("could not determine current location (scope is nil)") return nil, fmt.Errorf("could not determine current location (scope is nil)")
} }
file, line, fn := d.target.PCToLine(scope.PC) file, line, fn := d.target.BinInfo().PCToLine(scope.PC)
if fn == nil { if fn == nil {
return nil, fmt.Errorf("could not determine current location") return nil, fmt.Errorf("could not determine current location")
} }
@ -402,7 +402,7 @@ func (loc *LineLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr str
if scope == nil { if scope == nil {
return nil, fmt.Errorf("could not determine current location (scope is nil)") return nil, fmt.Errorf("could not determine current location (scope is nil)")
} }
file, _, fn := d.target.PCToLine(scope.PC) file, _, fn := d.target.BinInfo().PCToLine(scope.PC)
if fn == nil { if fn == nil {
return nil, fmt.Errorf("could not determine current location") return nil, fmt.Errorf("could not determine current location")
} }