| // Copyright 2017 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package gocore |
| |
| import ( |
| "debug/dwarf" |
| "errors" |
| "fmt" |
| "reflect" |
| "strings" |
| |
| "golang.org/x/debug/dwtest" |
| "golang.org/x/debug/internal/core" |
| |
| "golang.org/x/debug/third_party/delve/dwarf/godwarf" |
| "golang.org/x/debug/third_party/delve/dwarf/loclist" |
| "golang.org/x/debug/third_party/delve/dwarf/op" |
| "golang.org/x/debug/third_party/delve/dwarf/regnum" |
| ) |
| |
| const ( |
| AttrGoKind dwarf.Attr = 0x2900 |
| AttrGoRuntimeType dwarf.Attr = 0x2904 |
| ) |
| |
| func readDWARFTypes(p *core.Process) (map[dwarf.Type]*Type, map[core.Address]*Type, error) { |
| d, err := p.DWARF() |
| if err != nil { |
| return nil, nil, fmt.Errorf("failed to read DWARF: %v", err) |
| } |
| dwarfMap := make(map[dwarf.Type]*Type) |
| addrMap := make(map[core.Address]*Type) |
| syms, _ := p.Symbols() // It's OK to ignore the error. If we don't have symbols, that's OK; it's a soft error. |
| // It's OK if typBase is 0 and not present. We won't be able to type the heap, probably, |
| // but it may still be useful (though painful) to someone to try and debug the core, so don't |
| // error out here. |
| typBase := syms["runtime.types"] |
| |
| // Make one of our own Types for each dwarf type. |
| r := d.Reader() |
| var types []*Type |
| for e, err := r.Next(); e != nil && err == nil; e, err = r.Next() { |
| if isNonGoCU(e) { |
| r.SkipChildren() |
| continue |
| } |
| switch e.Tag { |
| case dwarf.TagArrayType, dwarf.TagPointerType, dwarf.TagStructType, dwarf.TagBaseType, dwarf.TagSubroutineType, dwarf.TagTypedef: |
| dt, err := d.Type(e.Offset) |
| if err != nil { |
| continue |
| } |
| t := &Type{Name: gocoreName(dt), Size: dwarfSize(dt, p.PtrSize())} |
| if goKind, ok := e.Val(AttrGoKind).(int64); ok { |
| t.goKind = reflect.Kind(goKind) |
| } |
| // Guard against typBase being zero. In reality, it's very unlikely for the DWARF |
| // to be present but typBase to be zero, but let's just be defensive. addrMap is |
| // only really necessary for typing the heap, which is "optional." |
| if typBase != 0 { |
| if offset, ok := e.Val(AttrGoRuntimeType).(uint64); ok { |
| // N.B. AttrGoRuntimeType is not defined for typedefs, so addrMap will |
| // always refer to the base type. |
| t.goAddr = typBase.Add(int64(offset)) |
| addrMap[typBase.Add(int64(offset))] = t |
| } |
| } |
| dwarfMap[dt] = t |
| types = append(types, t) |
| } |
| } |
| |
| // Fill in fields of types. Postponed until now so we're sure |
| // we have all the Types allocated and available. |
| for dt, t := range dwarfMap { |
| switch x := dt.(type) { |
| case *dwarf.ArrayType: |
| t.Kind = KindArray |
| t.Elem = dwarfMap[x.Type] |
| t.Count = x.Count |
| case *dwarf.PtrType: |
| t.Kind = KindPtr |
| // unsafe.Pointer has a void base type. |
| if _, ok := x.Type.(*dwarf.VoidType); !ok { |
| t.Elem = dwarfMap[x.Type] |
| } |
| case *dwarf.StructType: |
| t.Kind = KindStruct |
| for _, f := range x.Field { |
| fType := dwarfMap[f.Type] |
| if fType == nil { |
| // Weird case: arrays of size 0 in structs, like |
| // Sysinfo_t.X_f. Synthesize a type so things later don't |
| // get sad. |
| if arr, ok := f.Type.(*dwarf.ArrayType); ok && arr.Count == 0 { |
| fType = &Type{ |
| Name: f.Type.String(), |
| Kind: KindArray, |
| Count: arr.Count, |
| Elem: dwarfMap[arr.Type], |
| } |
| } else { |
| return nil, nil, fmt.Errorf( |
| "found a nil ftype for field %s.%s, type %s (%s) on ", |
| x.StructName, f.Name, f.Type, reflect.TypeOf(f.Type)) |
| } |
| } |
| t.Fields = append(t.Fields, Field{Name: f.Name, Type: fType, Off: f.ByteOffset}) |
| } |
| case *dwarf.BoolType: |
| t.Kind = KindBool |
| case *dwarf.IntType: |
| t.Kind = KindInt |
| case *dwarf.UintType: |
| t.Kind = KindUint |
| case *dwarf.FloatType: |
| t.Kind = KindFloat |
| case *dwarf.ComplexType: |
| t.Kind = KindComplex |
| case *dwarf.FuncType: |
| t.Kind = KindFunc |
| case *dwarf.TypedefType: |
| // handle these types in the loop below |
| default: |
| return nil, nil, fmt.Errorf("unknown type %s %T", dt, dt) |
| } |
| } |
| |
| // Detect strings & slices |
| for _, t := range types { |
| if t.Kind != KindStruct { |
| continue |
| } |
| switch t.goKind { |
| case reflect.String: |
| t.Kind = KindString |
| t.Elem = t.Fields[0].Type.Elem // TODO: check that it is always uint8. |
| t.Fields = nil |
| case reflect.Slice: |
| t.Kind = KindSlice |
| t.Elem = t.Fields[0].Type.Elem |
| t.Fields = nil |
| } |
| } |
| |
| // Copy info from base types into typedefs. |
| for dt, t := range dwarfMap { |
| tt, ok := dt.(*dwarf.TypedefType) |
| if !ok { |
| continue |
| } |
| base := tt.Type |
| // Walk typedef chain until we reach a non-typedef type. |
| for { |
| if x, ok := base.(*dwarf.TypedefType); ok { |
| base = x.Type |
| continue |
| } |
| break |
| } |
| bt := dwarfMap[base] |
| |
| // Copy type info from base. Everything except the name. |
| name := t.Name |
| *t = *bt |
| t.Name = name |
| |
| // Detect some special types. If the base is some particular type, |
| // then the alias gets marked as special. |
| // We have aliases like: |
| // interface {} -> struct runtime.eface |
| // error -> struct runtime.iface |
| // Note: the base itself does not get marked as special. |
| // (Unlike strings and slices, where they do.) |
| if bt.Name == "runtime.eface" { |
| t.Kind = KindEface |
| t.Fields = nil |
| } |
| if bt.Name == "runtime.iface" { |
| t.Kind = KindIface |
| t.Fields = nil |
| } |
| } |
| return dwarfMap, addrMap, nil |
| } |
| |
| func isNonGoCU(e *dwarf.Entry) bool { |
| if e.Tag != dwarf.TagCompileUnit { |
| return false |
| } |
| prod, ok := e.Val(dwarf.AttrProducer).(string) |
| if !ok { |
| return true |
| } |
| return !strings.Contains(prod, "Go cmd/compile") |
| } |
| |
| // dwarfSize is used to compute the size of a DWARF type. |
| // dt.Size() is wrong when it returns a negative number. |
| // This function implements just enough to correct the bad behavior. |
| func dwarfSize(dt dwarf.Type, ptrSize int64) int64 { |
| s := dt.Size() |
| if s >= 0 { |
| return s |
| } |
| switch x := dt.(type) { |
| case *dwarf.FuncType: |
| return ptrSize // Fix for issue 21097. |
| case *dwarf.ArrayType: |
| return x.Count * dwarfSize(x.Type, ptrSize) |
| case *dwarf.TypedefType: |
| return dwarfSize(x.Type, ptrSize) |
| default: |
| panic(fmt.Sprintf("unhandled: %s, %T", x, x)) |
| } |
| } |
| |
| // gocoreName generates the name this package uses to refer to a dwarf type. |
| // This name differs from the dwarf name in that it stays closer to the Go name for the type. |
| // For instance (dwarf name -> gocoreName) |
| // |
| // struct runtime.siginfo -> runtime.siginfo |
| // *void -> unsafe.Pointer |
| // struct struct { runtime.signalLock uint32; runtime.hz int32 } -> struct { signalLock uint32; hz int32 } |
| func gocoreName(dt dwarf.Type) string { |
| switch x := dt.(type) { |
| case *dwarf.PtrType: |
| if _, ok := x.Type.(*dwarf.VoidType); ok { |
| return "unsafe.Pointer" |
| } |
| return "*" + gocoreName(x.Type) |
| case *dwarf.ArrayType: |
| return fmt.Sprintf("[%d]%s", x.Count, gocoreName(x.Type)) |
| case *dwarf.StructType: |
| if !strings.HasPrefix(x.StructName, "struct {") { |
| // This is a named type, return that name. |
| return x.StructName |
| } |
| // Build gocore name from the DWARF fields. |
| s := "struct {" |
| first := true |
| for _, f := range x.Field { |
| if !first { |
| s += ";" |
| } |
| name := f.Name |
| if i := strings.Index(name, "."); i >= 0 { |
| // Remove pkg path from field names. |
| name = name[i+1:] |
| } |
| s += fmt.Sprintf(" %s %s", name, gocoreName(f.Type)) |
| first = false |
| } |
| s += " }" |
| return s |
| default: |
| return dt.String() |
| } |
| } |
| |
| type constsMap map[string]int64 |
| |
| func (c constsMap) get(s string) int64 { |
| v, ok := c[s] |
| if !ok { |
| panic("failed to find constant " + s) |
| } |
| return v |
| } |
| |
| func (c constsMap) find(s string) (int64, bool) { |
| v, ok := c[s] |
| return v, ok |
| } |
| |
| func readDWARFConstants(p *core.Process) (constsMap, error) { |
| d, err := p.DWARF() |
| if err != nil { |
| return nil, fmt.Errorf("failed to read DWARF: %v", err) |
| } |
| consts := map[string]int64{} |
| |
| r := d.Reader() |
| for e, err := r.Next(); e != nil && err == nil; e, err = r.Next() { |
| if e.Tag != dwarf.TagConstant { |
| continue |
| } |
| f := e.AttrField(dwarf.AttrName) |
| if f == nil { |
| continue |
| } |
| name := f.Val.(string) |
| c := e.AttrField(dwarf.AttrConstValue) |
| if c == nil { |
| continue |
| } |
| consts[name] = c.Val.(int64) |
| } |
| return consts, nil |
| } |
| |
| func readDWARFGlobals(p *core.Process, nRoots *int, dwarfTypeMap map[dwarf.Type]*Type) ([]*Root, error) { |
| d, err := p.DWARF() |
| if err != nil { |
| return nil, fmt.Errorf("failed to read DWARF: %v", err) |
| } |
| var roots []*Root |
| r := d.Reader() |
| for e, err := r.Next(); e != nil && err == nil; e, err = r.Next() { |
| if isNonGoCU(e) { |
| r.SkipChildren() |
| continue |
| } |
| |
| if e.Tag != dwarf.TagVariable { |
| continue |
| } |
| f := e.AttrField(dwarf.AttrLocation) |
| if f == nil { |
| continue |
| } |
| if f.Class != dwarf.ClassExprLoc { |
| // Globals are all encoded with this class. |
| continue |
| } |
| loc := f.Val.([]byte) |
| if len(loc) == 0 || loc[0] != byte(op.DW_OP_addr) { |
| continue |
| } |
| var a core.Address |
| if p.PtrSize() == 8 { |
| a = core.Address(p.ByteOrder().Uint64(loc[1:])) |
| } else { |
| a = core.Address(p.ByteOrder().Uint32(loc[1:])) |
| } |
| a = a.Add(int64(p.StaticBase())) |
| if !p.Writeable(a) { |
| // Read-only globals can't have heap pointers. |
| // TODO: keep roots around anyway? |
| continue |
| } |
| f = e.AttrField(dwarf.AttrType) |
| if f == nil { |
| continue |
| } |
| dt, err := d.Type(f.Val.(dwarf.Offset)) |
| if err != nil { |
| return nil, err |
| } |
| if _, ok := dt.(*dwarf.UnspecifiedType); ok { |
| continue // Ignore markers like data/edata. |
| } |
| nf := e.AttrField(dwarf.AttrName) |
| if nf == nil { |
| continue |
| } |
| typ := dwarfTypeMap[dt] |
| roots = append(roots, makeMemRoot(nRoots, nf.Val.(string), typ, nil, a)) |
| } |
| return roots, nil |
| } |
| |
| type dwarfVarKind int |
| |
| const ( |
| dwarfVarUnknown dwarfVarKind = iota |
| dwarfParam |
| dwarfLocal |
| ) |
| |
| type dwarfVar struct { |
| lowPC, highPC core.Address |
| kind dwarfVarKind |
| name string |
| instr []byte |
| typ *Type |
| } |
| |
| func readDWARFVars(p *core.Process, fns *funcTab, dwarfTypeMap map[dwarf.Type]*Type) (map[*Func][]dwarfVar, error) { |
| d, err := p.DWARF() |
| if err != nil { |
| return nil, fmt.Errorf("failed to read DWARF: %v", err) |
| } |
| dLocSec, err := p.DWARFLoc() |
| if err != nil { |
| return nil, fmt.Errorf("failed to read DWARF: %v", err) |
| } |
| dLocListsSec, derr := p.DWARFLocLists() |
| if derr != nil { |
| return nil, fmt.Errorf("failed to read DWARF: %v", derr) |
| } |
| dAddrSec, daerr := p.DWARFAddr() |
| if daerr != nil { |
| return nil, fmt.Errorf("failed to read DWARF: %v", daerr) |
| } |
| debugAddrSec := godwarf.ParseAddr(dAddrSec) |
| vars := make(map[*Func][]dwarfVar) |
| var curfn *Func |
| r := d.Reader() |
| addrBase := uint64(0) |
| unitVersion := 4 |
| for e, err := r.Next(); e != nil && err == nil; e, err = r.Next() { |
| if isNonGoCU(e) { |
| r.SkipChildren() |
| continue |
| } |
| if e.Tag == dwarf.TagCompileUnit { |
| // Determine whether we're looking at DWARF version 5 or |
| // some version prior to 5. At the moment the DWARF |
| // reading machinery in debug/dwarf keeps track of DWARF |
| // version for each unit but doesn't expose this info to |
| // clients, so we need to do the detection indirectly, |
| // here by looking for an attribute that only gets generated |
| // if DWARF 5 is being used. |
| if f := e.AttrField(dwarf.AttrAddrBase); f != nil { |
| addrBase = uint64(f.Val.(int64)) |
| unitVersion = 5 |
| } else { |
| unitVersion = 4 |
| } |
| } |
| if e.Tag == dwarf.TagSubprogram { |
| if e.AttrField(dwarf.AttrLowpc) == nil || |
| e.AttrField(dwarf.AttrHighpc) == nil { |
| continue |
| } |
| |
| // Collect the start/end PC for the func. The format/class of |
| // the high PC attr may vary depending on which DWARF version |
| // we're generating; invoke a helper to handle the various |
| // possibilities. |
| lowpc, highpc, perr := dwtest.SubprogLoAndHighPc(e) |
| if perr != nil { |
| return nil, fmt.Errorf("subprog die malformed: %v", perr) |
| } |
| fmin := core.Address(lowpc + p.StaticBase()) |
| fmax := core.Address(highpc + p.StaticBase()) |
| f := fns.find(fmin) |
| if f == nil { |
| // some func Go doesn't know about. C? |
| curfn = nil |
| } else { |
| if f.entry != fmin { |
| return nil, errors.New("dwarf and runtime don't agree about start of " + f.name) |
| } |
| if fns.find(fmax-1) != f { |
| return nil, errors.New("function ranges don't match for " + f.name) |
| } |
| curfn = f |
| } |
| continue |
| } |
| if e.Tag != dwarf.TagVariable && e.Tag != dwarf.TagFormalParameter { |
| continue |
| } |
| var kind dwarfVarKind |
| switch e.Tag { |
| case dwarf.TagFormalParameter: |
| kind = dwarfParam |
| case dwarf.TagVariable: |
| kind = dwarfLocal |
| } |
| aloc := e.AttrField(dwarf.AttrLocation) |
| if aloc == nil { |
| continue |
| } |
| if aloc.Class != dwarf.ClassLocListPtr { |
| continue |
| } |
| |
| // Read attributes for some high-level information. |
| f := e.AttrField(dwarf.AttrType) |
| if f == nil { |
| continue |
| } |
| dt, err := d.Type(f.Val.(dwarf.Offset)) |
| if err != nil { |
| return nil, err |
| } |
| nf := e.AttrField(dwarf.AttrName) |
| if nf == nil { |
| continue |
| } |
| name := nf.Val.(string) |
| |
| // FIXME A note on the code above: we're screening out |
| // variables that don't have a specific name and type, which |
| // on the surface seems workable, however this also rejects |
| // vars corresponding to "concrete" instances of vars that |
| // have been inlined, which is almost certainly a mistake. For |
| // more on concrete/abstract variables see |
| // https://github.com/golang/proposal/blob/master/design/22080-dwarf-inlining.md#how-the-generated-dwarf-should-look, |
| // which has examples. Instead what the code above should be |
| // doing is caching any abstract variables it encounters in a |
| // map (indexed by DWARF offset), then when it encounters a |
| // concrete var, pick the name and type up from the abstract |
| // variable. |
| |
| switch unitVersion { |
| case 5: |
| { |
| // No .debug_loclists section, can't make progress. |
| if len(dLocListsSec) == 0 { |
| continue |
| } |
| |
| debugAddr := debugAddrSec.GetSubsection(addrBase) |
| // Read the location lists. |
| locListOff := aloc.Val.(int64) |
| dr := loclist.NewDwarf5Reader(dLocListsSec) |
| elist, err := dr.Enumerate(locListOff, p.StaticBase(), debugAddr) |
| if err != nil { |
| return nil, err |
| } |
| for _, e := range elist { |
| vars[curfn] = append(vars[curfn], dwarfVar{ |
| lowPC: core.Address(e.LowPC), |
| highPC: core.Address(e.HighPC), |
| kind: kind, |
| instr: e.Instr, |
| name: name, |
| typ: dwarfTypeMap[dt], |
| }) |
| } |
| } |
| default: |
| { |
| // No .debug_loc section, can't make progress. |
| if len(dLocSec) == 0 { |
| continue |
| } |
| |
| // Read the location list. |
| locListOff := aloc.Val.(int64) |
| dr := loclist.NewDwarf2Reader(dLocSec, int(p.PtrSize())) |
| dr.Seek(int(locListOff)) |
| var base uint64 |
| var e loclist.Entry |
| for dr.Next(&e) { |
| if e.BaseAddressSelection() { |
| base = e.HighPC + p.StaticBase() |
| continue |
| } |
| vars[curfn] = append(vars[curfn], dwarfVar{ |
| lowPC: core.Address(e.LowPC + base), |
| highPC: core.Address(e.HighPC + base), |
| kind: kind, |
| instr: e.Instr, |
| name: name, |
| typ: dwarfTypeMap[dt], |
| }) |
| } |
| } |
| } |
| } |
| return vars, nil |
| } |
| |
| func hardwareRegs2DWARF(hregs []core.Register) []*op.DwarfRegister { |
| n := regnum.AMD64MaxRegNum() |
| dregs := make([]*op.DwarfRegister, n) |
| for _, hreg := range hregs { |
| dwn, ok := regnum.AMD64NameToDwarf[hreg.Name] |
| if !ok { |
| continue |
| } |
| dreg := op.DwarfRegisterFromUint64(hreg.Value) |
| dreg.FillBytes() |
| dregs[dwn] = dreg |
| } |
| return dregs |
| } |
| |
| /* Dwarf encoding notes |
| |
| type XXX sss |
| |
| translates to a dwarf type pkg.XXX of the type of sss (uint, float, ...) |
| |
| exception: if sss is a struct or array, then we get two types, the "unnamed" and "named" type. |
| The unnamed type is a dwarf struct type with name "struct pkg.XXX" or a dwarf array type with |
| name [N]elem. |
| Then there is a typedef with pkg.XXX pointing to "struct pkg.XXX" or [N]elem. |
| |
| For structures, lowercase field names are prepended with the package name (pkg path?). |
| |
| type XXX interface{} |
| pkg.XXX is a typedef to "struct runtime.eface" |
| type XXX interface{f()} |
| pkg.XXX is a typedef to "struct runtime.iface" |
| |
| Sometimes there is even a chain of identically-named typedefs. I have no idea why. |
| main.XXX -> main.XXX -> struct runtime.iface |
| |
| */ |