Skip to content

Commit

Permalink
proc: support debugging plugins
Browse files Browse the repository at this point in the history
This change splits the BinaryInfo object into a slice of Image objects
containing information about the base executable and each loaded shared
library (note: go plugins are shared libraries).

Delve backens are supposed to call BinaryInfo.AddImage whenever they
detect that a new shared library has been loaded.

Member fields of BinaryInfo that are used to speed up access to dwarf
(Functions, packageVars, consts, etc...) remain part of BinaryInfo and
are updated to reference the correct image object. This simplifies this
change.

This approach has a few shortcomings:

1. Multiple shared libraries can define functions or globals with the
   same name and we have no way to disambiguate between them.

2. We don't have a way to handle library unloading.

Both of those affect C shared libraries much more than they affect go
plugins. Go plugins can't be unloaded at all and a lot of name
collisions are prevented by import paths.

There's only one problem that is concerning: if two plugins both import
the same package they will end up with multiple definition for the same
function.
For example if two plugins use fmt.Printf the final in-memory image
(and therefore our BinaryInfo object) will end up with two copies of
fmt.Printf at different memory addresses. If a user types
  break fmt.Printf
a breakpoint should be created at *both* locations.
Allowing this is a relatively complex change that should be done in a
different PR than this.

For this reason I consider this approach an acceptable and sustainable
stopgap.

Updates go-delve#865
  • Loading branch information
aarzilli committed Nov 22, 2018
1 parent 0992b89 commit ef99a98
Show file tree
Hide file tree
Showing 18 changed files with 696 additions and 390 deletions.
9 changes: 9 additions & 0 deletions pkg/dwarf/frame/entries.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,12 @@ func (fdes FrameDescriptionEntries) FDEForPC(pc uint64) (*FrameDescriptionEntry,
}
return fdes[idx], nil
}

// Append appends otherFDEs to fdes and returns the result.
func (fdes FrameDescriptionEntries) Append(otherFDEs FrameDescriptionEntries) FrameDescriptionEntries {
r := append(fdes, otherFDEs...)
sort.Slice(r, func(i, j int) bool {
return r[i].Begin() < r[j].Begin()
})
return r
}
9 changes: 7 additions & 2 deletions pkg/dwarf/godwarf/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type Type interface {
// If a field is not known or not applicable for a given type,
// the zero value is used.
type CommonType struct {
Index int // index supplied by caller of ReadType
ByteSize int64 // size of value of this type, in bytes
Name string // name that can be used to refer to type
ReflectKind reflect.Kind // the reflect kind of the type.
Expand Down Expand Up @@ -359,8 +360,12 @@ func (t *ChanType) String() string {
}

// Type reads the type at off in the DWARF ``info'' section.
func ReadType(d *dwarf.Data, off dwarf.Offset, typeCache map[dwarf.Offset]Type) (Type, error) {
return readType(d, "info", d.Reader(), off, typeCache)
func ReadType(d *dwarf.Data, index int, off dwarf.Offset, typeCache map[dwarf.Offset]Type) (Type, error) {
typ, err := readType(d, "info", d.Reader(), off, typeCache)
if typ != nil {
typ.Common().Index = index
}
return typ, err
}

func getKind(e *dwarf.Entry) reflect.Kind {
Expand Down
15 changes: 11 additions & 4 deletions pkg/proc/arch.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type Arch interface {
DerefTLS() bool
FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext
RegSize(uint64) int
RegistersToDwarfRegisters(regs Registers, staticBase uint64) op.DwarfRegisters
RegistersToDwarfRegisters(bi *BinaryInfo, regs Registers) op.DwarfRegisters
GoroutineToDwarfRegisters(*G) op.DwarfRegisters
}

Expand Down Expand Up @@ -270,7 +270,7 @@ func maxAmd64DwarfRegister() int {

// RegistersToDwarfRegisters converts hardware registers to the format used
// by the DWARF expression interpreter.
func (a *AMD64) RegistersToDwarfRegisters(regs Registers, staticBase uint64) op.DwarfRegisters {
func (a *AMD64) RegistersToDwarfRegisters(bi *BinaryInfo, regs Registers) op.DwarfRegisters {
dregs := make([]*op.DwarfRegister, maxAmd64DwarfRegister()+1)

dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(regs.PC())
Expand All @@ -292,7 +292,9 @@ func (a *AMD64) RegistersToDwarfRegisters(regs Registers, staticBase uint64) op.
}
}

return op.DwarfRegisters{StaticBase: staticBase, Regs: dregs, ByteOrder: binary.LittleEndian, PCRegNum: amd64DwarfIPRegNum, SPRegNum: amd64DwarfSPRegNum, BPRegNum: amd64DwarfBPRegNum}
so := bi.funcToImage(bi.PCToFunc(regs.PC()))

return op.DwarfRegisters{StaticBase: so.StaticBase, Regs: dregs, ByteOrder: binary.LittleEndian, PCRegNum: amd64DwarfIPRegNum, SPRegNum: amd64DwarfSPRegNum, BPRegNum: amd64DwarfBPRegNum}
}

// GoroutineToDwarfRegisters extract the saved DWARF registers from a parked
Expand All @@ -302,5 +304,10 @@ func (a *AMD64) GoroutineToDwarfRegisters(g *G) op.DwarfRegisters {
dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(g.PC)
dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(g.SP)
dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(g.BP)
return op.DwarfRegisters{StaticBase: g.variable.bi.staticBase, Regs: dregs, ByteOrder: binary.LittleEndian, PCRegNum: amd64DwarfIPRegNum, SPRegNum: amd64DwarfSPRegNum, BPRegNum: amd64DwarfBPRegNum}

bi := g.variable.bi
fn := bi.PCToFunc(g.PC)
so := bi.funcToImage(fn)

return op.DwarfRegisters{StaticBase: so.StaticBase, Regs: dregs, ByteOrder: binary.LittleEndian, PCRegNum: amd64DwarfIPRegNum, SPRegNum: amd64DwarfSPRegNum, BPRegNum: amd64DwarfBPRegNum}
}

0 comments on commit ef99a98

Please sign in to comment.