package proc import ( "bytes" "debug/dwarf" "encoding/binary" "errors" "fmt" "go/ast" "go/constant" "go/parser" "go/token" "path/filepath" "reflect" "sort" "strconv" "strings" "sync" "unsafe" "github.com/derekparker/delve/pkg/dwarf/godwarf" "github.com/derekparker/delve/pkg/dwarf/line" "github.com/derekparker/delve/pkg/dwarf/op" "github.com/derekparker/delve/pkg/dwarf/reader" "github.com/derekparker/delve/pkg/goversion" "github.com/derekparker/delve/pkg/logflags" "github.com/sirupsen/logrus" ) // The kind field in runtime._type is a reflect.Kind value plus // some extra flags defined here. // See equivalent declaration in $GOROOT/src/reflect/type.go const ( kindDirectIface = 1 << 5 kindGCProg = 1 << 6 // Type.gc points to GC program kindNoPointers = 1 << 7 kindMask = (1 << 5) - 1 ) // Value of tflag field in runtime._type. // See $GOROOT/reflect/type.go for a description of these flags. const ( tflagUncommon = 1 << 0 tflagExtraStar = 1 << 1 tflagNamed = 1 << 2 ) // These constants contain the names of the fields of runtime.interfacetype // and runtime.imethod. // runtime.interfacetype.mhdr is a slice of runtime.imethod describing the // methods of the interface. const ( imethodFieldName = "name" imethodFieldItyp = "ityp" interfacetypeFieldMhdr = "mhdr" ) // Do not call this function directly it isn't able to deal correctly with package paths func (bi *BinaryInfo) findType(name string) (godwarf.Type, error) { off, found := bi.types[name] if !found { return nil, reader.TypeNotFoundErr } return godwarf.ReadType(bi.dwarf, off, bi.typeCache) } func pointerTo(typ godwarf.Type, arch Arch) godwarf.Type { return &godwarf.PtrType{ CommonType: godwarf.CommonType{ ByteSize: int64(arch.PtrSize()), Name: "*" + typ.Common().Name, ReflectKind: reflect.Ptr, Offset: 0, }, Type: typ, } } func (bi *BinaryInfo) findTypeExpr(expr ast.Expr) (godwarf.Type, error) { bi.loadPackageMap() if lit, islit := expr.(*ast.BasicLit); islit && lit.Kind == token.STRING { // Allow users to specify type names verbatim as quoted // string. Useful as a catch-all workaround for cases where we don't // parse/serialize types correctly or can not resolve package paths. typn, _ := strconv.Unquote(lit.Value) return bi.findType(typn) } bi.expandPackagesInType(expr) if snode, ok := expr.(*ast.StarExpr); ok { // Pointer types only appear in the dwarf informations when // 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 // specify a pointer to any variable used in the target program ptyp, err := bi.findTypeExpr(snode.X) if err != nil { return nil, err } return pointerTo(ptyp, bi.Arch), nil } if anode, ok := expr.(*ast.ArrayType); ok { // Byte array types (i.e. [N]byte) are only present in DWARF if they are // used by the program, but it's convenient to make all of them available // to the user so that they can be used to read arbitrary memory, byte by // byte. alen, litlen := anode.Len.(*ast.BasicLit) if litlen && alen.Kind == token.INT { n, _ := strconv.Atoi(alen.Value) switch exprToString(anode.Elt) { case "byte", "uint8": btyp, err := bi.findType("uint8") if err != nil { return nil, err } return &godwarf.ArrayType{ CommonType: godwarf.CommonType{ ReflectKind: reflect.Array, ByteSize: int64(n), Name: fmt.Sprintf("[%d]uint8", n)}, Type: btyp, StrideBitSize: 8, Count: int64(n)}, nil } } } return bi.findType(exprToString(expr)) } func complexType(typename string) bool { for _, ch := range typename { switch ch { case '*', '[', '<', '{', '(', ' ': return true } } return false } func (bi *BinaryInfo) loadPackageMap() error { if bi.packageMap != nil { return nil } bi.packageMap = map[string]string{} reader := bi.DwarfReader() for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { if err != nil { return err } if entry.Tag != dwarf.TagTypedef && entry.Tag != dwarf.TagBaseType && entry.Tag != dwarf.TagClassType && entry.Tag != dwarf.TagStructType { continue } typename, ok := entry.Val(dwarf.AttrName).(string) if !ok || complexType(typename) { continue } dot := strings.LastIndex(typename, ".") if dot < 0 { continue } path := typename[:dot] slash := strings.LastIndex(path, "/") if slash < 0 || slash+1 >= len(path) { continue } name := path[slash+1:] bi.packageMap[name] = path } return nil } type functionsDebugInfoByEntry []Function func (v functionsDebugInfoByEntry) Len() int { return len(v) } func (v functionsDebugInfoByEntry) Less(i, j int) bool { return v[i].Entry < v[j].Entry } func (v functionsDebugInfoByEntry) Swap(i, j int) { v[i], v[j] = v[j], v[i] } type compileUnitsByLowpc []*compileUnit func (v compileUnitsByLowpc) Len() int { return len(v) } func (v compileUnitsByLowpc) Less(i int, j int) bool { return v[i].LowPC < v[j].LowPC } func (v compileUnitsByLowpc) Swap(i int, j int) { v[i], v[j] = v[j], v[i] } type packageVarsByAddr []packageVar func (v packageVarsByAddr) Len() int { return len(v) } func (v packageVarsByAddr) Less(i int, j int) bool { return v[i].addr < v[j].addr } func (v packageVarsByAddr) Swap(i int, j int) { v[i], v[j] = v[j], v[i] } func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGroup, cont func()) { if wg != nil { defer wg.Done() } var cu *compileUnit var pu *partialUnit var partialUnits = make(map[dwarf.Offset]*partialUnit) var lastOffset dwarf.Offset bi.types = make(map[string]dwarf.Offset) bi.packageVars = []packageVar{} bi.Functions = []Function{} bi.compileUnits = []*compileUnit{} bi.consts = make(map[dwarf.Offset]*constantType) bi.runtimeTypeToDIE = make(map[uint64]runtimeTypeDIE) reader := bi.DwarfReader() ardr := bi.DwarfReader() abstractOriginNameTable := make(map[dwarf.Offset]string) outer: for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { if err != nil { break } lastOffset = entry.Offset switch entry.Tag { case dwarf.TagCompileUnit: if pu != nil { partialUnits[pu.entry.Offset] = pu pu = nil } if cu != nil { cu.endOffset = entry.Offset } cu = &compileUnit{} cu.entry = entry cu.startOffset = entry.Offset if lang, _ := entry.Val(dwarf.AttrLanguage).(int64); lang == dwarfGoLanguage { cu.isgo = true } cu.Name, _ = entry.Val(dwarf.AttrName).(string) compdir, _ := entry.Val(dwarf.AttrCompDir).(string) if compdir != "" { cu.Name = filepath.Join(compdir, cu.Name) } cu.Ranges, _ = bi.dwarf.Ranges(entry) for i := range cu.Ranges { cu.Ranges[i][0] += bi.staticBase cu.Ranges[i][1] += bi.staticBase } if len(cu.Ranges) >= 1 { cu.LowPC = cu.Ranges[0][0] } lineInfoOffset, _ := entry.Val(dwarf.AttrStmtList).(int64) if lineInfoOffset >= 0 && lineInfoOffset < int64(len(debugLineBytes)) { var logfn func(string, ...interface{}) if logflags.DebugLineErrors() { logger := logrus.New().WithFields(logrus.Fields{"layer": "dwarf-line"}) logger.Logger.Level = logrus.DebugLevel logfn = func(fmt string, args ...interface{}) { logger.Printf(fmt, args) } } cu.lineInfo = line.Parse(compdir, bytes.NewBuffer(debugLineBytes[lineInfoOffset:]), logfn, bi.staticBase) } cu.producer, _ = entry.Val(dwarf.AttrProducer).(string) if cu.isgo && cu.producer != "" { semicolon := strings.Index(cu.producer, ";") if semicolon < 0 { cu.optimized = goversion.ProducerAfterOrEqual(cu.producer, 1, 10) } else { cu.optimized = !strings.Contains(cu.producer[semicolon:], "-N") || !strings.Contains(cu.producer[semicolon:], "-l") cu.producer = cu.producer[:semicolon] } } bi.compileUnits = append(bi.compileUnits, cu) case dwarf.TagPartialUnit: if pu != nil { partialUnits[pu.entry.Offset] = pu pu = nil } pu = &partialUnit{} pu.entry = entry pu.types = make(map[string]dwarf.Offset) case dwarf.TagImportedUnit: if unit, exists := partialUnits[entry.Val(dwarf.AttrImport).(dwarf.Offset)]; exists { for name, offset := range unit.types { if pu != nil { pu.types[name] = offset } else { if !cu.isgo { name = "C." + name } bi.types[name] = offset } } for _, pVar := range unit.variables { if pu != nil { pu.variables = append(pu.variables, pVar) } else { if !cu.isgo { pVar.name = "C." + pVar.name } bi.packageVars = append(bi.packageVars, pVar) } } for _, pCt := range unit.constants { if pu != nil { pu.constants = append(pu.constants, pCt) } else { if !cu.isgo { pCt.name = "C." + pCt.name } ct := bi.consts[pCt.typ] if ct == nil { ct = &constantType{} bi.consts[pCt.typ] = ct } ct.values = append(ct.values, constantValue{name: pCt.name, fullName: pCt.name, value: pCt.value}) } } for _, pFunc := range unit.functions { if pu != nil { pu.functions = append(pu.functions, pFunc) } else { if !cu.isgo { pFunc.Name = "C." + pFunc.Name } pFunc.cu = cu bi.Functions = append(bi.Functions, pFunc) } } } case dwarf.TagArrayType, dwarf.TagBaseType, dwarf.TagClassType, dwarf.TagStructType, dwarf.TagUnionType, dwarf.TagConstType, dwarf.TagVolatileType, dwarf.TagRestrictType, dwarf.TagEnumerationType, dwarf.TagPointerType, dwarf.TagSubroutineType, dwarf.TagTypedef, dwarf.TagUnspecifiedType: if name, ok := entry.Val(dwarf.AttrName).(string); ok { if pu != nil { if _, exists := pu.types[name]; !exists { pu.types[name] = entry.Offset } } else { if !cu.isgo { name = "C." + name } if _, exists := bi.types[name]; !exists { bi.types[name] = entry.Offset } } } bi.registerRuntimeTypeToDIE(entry, ardr) reader.SkipChildren() case dwarf.TagVariable: if n, ok := entry.Val(dwarf.AttrName).(string); ok { var addr uint64 if loc, ok := entry.Val(dwarf.AttrLocation).([]byte); ok { if len(loc) == bi.Arch.PtrSize()+1 && op.Opcode(loc[0]) == op.DW_OP_addr { addr = binary.LittleEndian.Uint64(loc[1:]) } } if pu != nil { pu.variables = append(pu.variables, packageVar{n, entry.Offset, addr + bi.staticBase}) } else { if !cu.isgo { n = "C." + n } bi.packageVars = append(bi.packageVars, packageVar{n, entry.Offset, addr + bi.staticBase}) } } case dwarf.TagConstant: name, okName := entry.Val(dwarf.AttrName).(string) typ, okType := entry.Val(dwarf.AttrType).(dwarf.Offset) val, okVal := entry.Val(dwarf.AttrConstValue).(int64) if okName && okType && okVal { if pu != nil { pu.constants = append(pu.constants, partialUnitConstant{name: name, typ: typ, value: val}) } else { if !cu.isgo { name = "C." + name } ct := bi.consts[typ] if ct == nil { ct = &constantType{} bi.consts[typ] = ct } ct.values = append(ct.values, constantValue{name: name, fullName: name, value: val}) } } case dwarf.TagSubprogram: ok1 := false inlined := false var lowpc, highpc uint64 if inval, ok := entry.Val(dwarf.AttrInline).(int64); ok { inlined = inval == 1 } if ranges, _ := bi.dwarf.Ranges(entry); len(ranges) == 1 { ok1 = true lowpc = ranges[0][0] + bi.staticBase highpc = ranges[0][1] + bi.staticBase } name, ok2 := entry.Val(dwarf.AttrName).(string) var fn Function if (ok1 == !inlined) && ok2 { if inlined { abstractOriginNameTable[entry.Offset] = name } if pu != nil { fn = Function{ Name: name, Entry: lowpc, End: highpc, offset: entry.Offset, cu: &compileUnit{}, } pu.functions = append(pu.functions, fn) } else { if !cu.isgo { name = "C." + name } fn = Function{ Name: name, Entry: lowpc, End: highpc, offset: entry.Offset, cu: cu, } bi.Functions = append(bi.Functions, fn) } } if entry.Children { for { entry, err = reader.Next() if err != nil { break outer } if entry.Tag == 0 { break } if entry.Tag == dwarf.TagInlinedSubroutine { originOffset := entry.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset) name := abstractOriginNameTable[originOffset] if ranges, _ := bi.dwarf.Ranges(entry); len(ranges) == 1 { ok1 = true lowpc = ranges[0][0] highpc = ranges[0][1] } callfileidx, ok1 := entry.Val(dwarf.AttrCallFile).(int64) callline, ok2 := entry.Val(dwarf.AttrCallLine).(int64) if ok1 && ok2 { callfile := cu.lineInfo.FileNames[callfileidx-1].Path cu.concreteInlinedFns = append(cu.concreteInlinedFns, inlinedFn{ Name: name, LowPC: lowpc + bi.staticBase, HighPC: highpc + bi.staticBase, CallFile: callfile, CallLine: callline, Parent: &fn, }) } } reader.SkipChildren() } } } } if cu != nil { cu.endOffset = lastOffset + 1 } sort.Sort(compileUnitsByLowpc(bi.compileUnits)) sort.Sort(functionsDebugInfoByEntry(bi.Functions)) sort.Sort(packageVarsByAddr(bi.packageVars)) bi.LookupFunc = make(map[string]*Function) for i := range bi.Functions { bi.LookupFunc[bi.Functions[i].Name] = &bi.Functions[i] } bi.Sources = []string{} for _, cu := range bi.compileUnits { if cu.lineInfo != nil { for _, fileEntry := range cu.lineInfo.FileNames { bi.Sources = append(bi.Sources, fileEntry.Path) } } } sort.Strings(bi.Sources) bi.Sources = uniq(bi.Sources) if cont != nil { cont() } } func uniq(s []string) []string { if len(s) <= 0 { return s } src, dst := 1, 1 for src < len(s) { if s[src] != s[dst-1] { s[dst] = s[src] dst++ } src++ } return s[:dst] } func (bi *BinaryInfo) expandPackagesInType(expr ast.Expr) { switch e := expr.(type) { case *ast.ArrayType: bi.expandPackagesInType(e.Elt) case *ast.ChanType: bi.expandPackagesInType(e.Value) case *ast.FuncType: for i := range e.Params.List { bi.expandPackagesInType(e.Params.List[i].Type) } if e.Results != nil { for i := range e.Results.List { bi.expandPackagesInType(e.Results.List[i].Type) } } case *ast.MapType: bi.expandPackagesInType(e.Key) bi.expandPackagesInType(e.Value) case *ast.ParenExpr: bi.expandPackagesInType(e.X) case *ast.SelectorExpr: switch x := e.X.(type) { case *ast.Ident: if path, ok := bi.packageMap[x.Name]; ok { x.Name = path } default: bi.expandPackagesInType(e.X) } case *ast.StarExpr: bi.expandPackagesInType(e.X) default: // nothing to do } } func (bi *BinaryInfo) registerRuntimeTypeToDIE(entry *dwarf.Entry, ardr *reader.Reader) { if off, ok := entry.Val(godwarf.AttrGoRuntimeType).(uint64); ok { if _, ok := bi.runtimeTypeToDIE[off]; !ok { bi.runtimeTypeToDIE[off+bi.staticBase] = runtimeTypeDIE{entry.Offset, -1} } } } // runtimeTypeToDIE returns the DIE corresponding to the runtime._type. // This is done in three different ways depending on the version of go. // * Before go1.7 the type name is retrieved directly from the runtime._type // and looked up in debug_info // * After go1.7 the runtime._type struct is read recursively to reconstruct // the name of the type, and then the type's name is used to look up // debug_info // * After go1.11 the runtimeTypeToDIE map is used to look up the address of // the type and map it drectly to a DIE. func runtimeTypeToDIE(_type *Variable, dataAddr uintptr) (typ godwarf.Type, kind int64, err error) { var go17 bool var typestring *Variable bi := _type.bi // Determine if we are in go1.7 or later typestring, err = _type.structMember("_string") if err == nil { typestring = typestring.maybeDereference() } else { err = nil go17 = true } if !go17 { // pre-go1.7 compatibility if typestring == nil || typestring.Addr == 0 || typestring.Kind != reflect.String { return nil, 0, fmt.Errorf("invalid interface type") } typestring.loadValue(LoadConfig{false, 0, 512, 0, 0}) if typestring.Unreadable != nil { return nil, 0, fmt.Errorf("invalid interface type: %v", typestring.Unreadable) } typename := constant.StringVal(typestring.Value) t, err := parser.ParseExpr(typename) if err != nil { return nil, 0, fmt.Errorf("invalid interface type, unparsable data type: %v", err) } typ, err := bi.findTypeExpr(t) if err != nil { return nil, 0, fmt.Errorf("interface type %q not found for %#x: %v", typename, dataAddr, err) } return typ, 0, nil } _type = _type.maybeDereference() // go 1.11 implementation: use extended attribute in debug_info md, err := findModuleDataForType(bi, _type.Addr, _type.mem) if err != nil { return nil, 0, fmt.Errorf("error loading module data: %v", err) } if md != nil { if rtdie, ok := bi.runtimeTypeToDIE[uint64(_type.Addr-md.types)]; ok { typ, err := godwarf.ReadType(bi.dwarf, rtdie.offset, bi.typeCache) if err != nil { return nil, 0, fmt.Errorf("invalid interface type: %v", err) } if rtdie.kind == -1 { if kindField := _type.loadFieldNamed("kind"); kindField != nil && kindField.Value != nil { rtdie.kind, _ = constant.Int64Val(kindField.Value) } } return typ, rtdie.kind, nil } } // go1.7 to go1.10 implementation: convert runtime._type structs to type names typename, kind, err := nameOfRuntimeType(_type) if err != nil { return nil, 0, fmt.Errorf("invalid interface type: %v", err) } typ, err = bi.findType(typename) if err != nil { return nil, 0, fmt.Errorf("interface type %q not found for %#x: %v", typename, dataAddr, err) } return typ, kind, nil } type nameOfRuntimeTypeEntry struct { typename string kind int64 } // Returns the type name of the type described in _type. // _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 func nameOfRuntimeType(_type *Variable) (typename string, kind int64, err error) { if e, ok := _type.bi.nameOfRuntimeType[_type.Addr]; ok { return e.typename, e.kind, nil } var tflag int64 if tflagField := _type.loadFieldNamed("tflag"); tflagField != nil && tflagField.Value != nil { tflag, _ = constant.Int64Val(tflagField.Value) } if kindField := _type.loadFieldNamed("kind"); kindField != nil && kindField.Value != nil { kind, _ = constant.Int64Val(kindField.Value) } // Named types are defined by a 'type' expression, everything else // (for example pointers to named types) are not considered named. if tflag&tflagNamed != 0 { typename, err = nameOfNamedRuntimeType(_type, kind, tflag) return typename, kind, err } typename, err = nameOfUnnamedRuntimeType(_type, kind, tflag) return typename, kind, err } // The layout of a runtime._type struct is as follows: // // // // with the 'uncommon type struct' being optional // // For named types first we extract the type name from the 'str' // field in the runtime._type struct. // Then we prepend the package path from the runtime.uncommontype // struct, when it exists. // // To find out the memory address of the runtime.uncommontype struct // we first cast the Variable pointing to the runtime._type struct // to a struct specific to the type's kind (for example, if the type // being described is a slice type the variable will be specialized // to a runtime.slicetype). func nameOfNamedRuntimeType(_type *Variable, kind, tflag int64) (typename string, err error) { var strOff int64 if strField := _type.loadFieldNamed("str"); strField != nil && strField.Value != nil { strOff, _ = constant.Int64Val(strField.Value) } else { return "", errors.New("could not find str field") } // The following code is adapted from reflect.(*rtype).Name. // For a description of how memory is organized for type names read // the comment to 'type name struct' in $GOROOT/src/reflect/type.go typename, _, _, err = resolveNameOff(_type.bi, _type.Addr, uintptr(strOff), _type.mem) if err != nil { return "", err } if tflag&tflagExtraStar != 0 { typename = typename[1:] } if i := strings.Index(typename, "."); i >= 0 { typename = typename[i+1:] } else { return typename, nil } // The following code is adapted from reflect.(*rtype).PkgPath in // $GOROOT/src/reflect/type.go _type, err = specificRuntimeType(_type, kind) if err != nil { return "", err } if ut := uncommon(_type, tflag); ut != nil { if pkgPathField := ut.loadFieldNamed("pkgpath"); pkgPathField != nil && pkgPathField.Value != nil { pkgPathOff, _ := constant.Int64Val(pkgPathField.Value) pkgPath, _, _, err := resolveNameOff(_type.bi, _type.Addr, uintptr(pkgPathOff), _type.mem) if err != nil { return "", err } if slash := strings.LastIndex(pkgPath, "/"); slash >= 0 { fixedName := strings.Replace(pkgPath[slash+1:], ".", "%2e", -1) if fixedName != pkgPath[slash+1:] { pkgPath = pkgPath[:slash+1] + fixedName } } typename = pkgPath + "." + typename } } return typename, nil } func nameOfUnnamedRuntimeType(_type *Variable, kind, tflag int64) (string, error) { _type, err := specificRuntimeType(_type, kind) if err != nil { return "", err } // The types referred to here are defined in $GOROOT/src/runtime/type.go switch reflect.Kind(kind & kindMask) { case reflect.Array: var len int64 if lenField := _type.loadFieldNamed("len"); lenField != nil && lenField.Value != nil { len, _ = constant.Int64Val(lenField.Value) } elemname, err := fieldToType(_type, "elem") if err != nil { return "", err } return fmt.Sprintf("[%d]%s", len, elemname), nil case reflect.Chan: elemname, err := fieldToType(_type, "elem") if err != nil { return "", err } return "chan " + elemname, nil case reflect.Func: return nameOfFuncRuntimeType(_type, tflag, true) case reflect.Interface: return nameOfInterfaceRuntimeType(_type, kind, tflag) case reflect.Map: keyname, err := fieldToType(_type, "key") if err != nil { return "", err } elemname, err := fieldToType(_type, "elem") if err != nil { return "", err } return "map[" + keyname + "]" + elemname, nil case reflect.Ptr: elemname, err := fieldToType(_type, "elem") if err != nil { return "", err } return "*" + elemname, nil case reflect.Slice: elemname, err := fieldToType(_type, "elem") if err != nil { return "", err } return "[]" + elemname, nil case reflect.Struct: return nameOfStructRuntimeType(_type, kind, tflag) default: return nameOfNamedRuntimeType(_type, kind, tflag) } } // Returns the expression describing an anonymous function type. // A runtime.functype is followed by a runtime.uncommontype // (optional) and then by an array of pointers to runtime._type, // one for each input and output argument. func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string, error) { rtyp, err := _type.bi.findType("runtime._type") if err != nil { return "", err } prtyp := pointerTo(rtyp, _type.bi.Arch) uadd := _type.RealType.Common().ByteSize if ut := uncommon(_type, tflag); ut != nil { uadd += ut.RealType.Common().ByteSize } var inCount, outCount int64 if inCountField := _type.loadFieldNamed("inCount"); inCountField != nil && inCountField.Value != nil { inCount, _ = constant.Int64Val(inCountField.Value) } if outCountField := _type.loadFieldNamed("outCount"); outCountField != nil && outCountField.Value != nil { outCount, _ = constant.Int64Val(outCountField.Value) // only the lowest 15 bits of outCount are used, rest are flags outCount = outCount & (1<<15 - 1) } cursortyp := _type.newVariable("", _type.Addr+uintptr(uadd), prtyp, _type.mem) var buf bytes.Buffer if anonymous { buf.WriteString("func(") } else { buf.WriteString("(") } for i := int64(0); i < inCount; i++ { argtype := cursortyp.maybeDereference() cursortyp.Addr += uintptr(_type.bi.Arch.PtrSize()) argtypename, _, err := nameOfRuntimeType(argtype) if err != nil { return "", err } buf.WriteString(argtypename) if i != inCount-1 { buf.WriteString(", ") } } buf.WriteString(")") switch outCount { case 0: // nothing to do case 1: buf.WriteString(" ") argtype := cursortyp.maybeDereference() argtypename, _, err := nameOfRuntimeType(argtype) if err != nil { return "", err } buf.WriteString(argtypename) default: buf.WriteString(" (") for i := int64(0); i < outCount; i++ { argtype := cursortyp.maybeDereference() cursortyp.Addr += uintptr(_type.bi.Arch.PtrSize()) argtypename, _, err := nameOfRuntimeType(argtype) if err != nil { return "", err } buf.WriteString(argtypename) if i != inCount-1 { buf.WriteString(", ") } } buf.WriteString(")") } return buf.String(), nil } func nameOfInterfaceRuntimeType(_type *Variable, kind, tflag int64) (string, error) { var buf bytes.Buffer buf.WriteString("interface {") methods, _ := _type.structMember(interfacetypeFieldMhdr) methods.loadArrayValues(0, LoadConfig{false, 1, 0, 4096, -1}) if methods.Unreadable != nil { return "", nil } if len(methods.Children) == 0 { buf.WriteString("}") return buf.String(), nil } buf.WriteString(" ") for i, im := range methods.Children { var methodname, methodtype string for i := range im.Children { switch im.Children[i].Name { case imethodFieldName: nameoff, _ := constant.Int64Val(im.Children[i].Value) var err error methodname, _, _, err = resolveNameOff(_type.bi, _type.Addr, uintptr(nameoff), _type.mem) if err != nil { return "", err } case imethodFieldItyp: typeoff, _ := constant.Int64Val(im.Children[i].Value) typ, err := resolveTypeOff(_type.bi, _type.Addr, uintptr(typeoff), _type.mem) if err != nil { return "", err } typ, err = specificRuntimeType(typ, int64(reflect.Func)) if err != nil { return "", err } var tflag int64 if tflagField := typ.loadFieldNamed("tflag"); tflagField != nil && tflagField.Value != nil { tflag, _ = constant.Int64Val(tflagField.Value) } methodtype, err = nameOfFuncRuntimeType(typ, tflag, false) if err != nil { return "", err } } } buf.WriteString(methodname) buf.WriteString(methodtype) if i != len(methods.Children)-1 { buf.WriteString("; ") } else { buf.WriteString(" }") } } return buf.String(), nil } func nameOfStructRuntimeType(_type *Variable, kind, tflag int64) (string, error) { var buf bytes.Buffer buf.WriteString("struct {") fields, _ := _type.structMember("fields") fields.loadArrayValues(0, LoadConfig{false, 2, 0, 4096, -1}) if fields.Unreadable != nil { return "", fields.Unreadable } if len(fields.Children) == 0 { buf.WriteString("}") return buf.String(), nil } buf.WriteString(" ") for i, field := range fields.Children { var fieldname, fieldtypename string var typeField *Variable isembed := false for i := range field.Children { switch field.Children[i].Name { case "name": var nameoff int64 switch field.Children[i].Kind { case reflect.Struct: nameoff = int64(field.Children[i].fieldVariable("bytes").Children[0].Addr) default: nameoff, _ = constant.Int64Val(field.Children[i].Value) } var err error fieldname, _, _, err = loadName(_type.bi, uintptr(nameoff), _type.mem) if err != nil { return "", err } case "typ": typeField = field.Children[i].maybeDereference() var err error fieldtypename, _, err = nameOfRuntimeType(typeField) if err != nil { return "", err } case "offsetAnon": // The offsetAnon field of runtime.structfield combines the offset of // the struct field from the base address of the struct with a flag // determining whether the field is anonymous (i.e. an embedded struct). // // offsetAnon = (offset<<1) | (anonFlag) // // Here we are only interested in the anonymous flag. offsetAnon, _ := constant.Int64Val(field.Children[i].Value) isembed = offsetAnon%2 != 0 } } // fieldname will be the empty string for anonymous fields if fieldname != "" && !isembed { buf.WriteString(fieldname) buf.WriteString(" ") } buf.WriteString(fieldtypename) if i != len(fields.Children)-1 { buf.WriteString("; ") } else { buf.WriteString(" }") } } return buf.String(), nil } func fieldToType(_type *Variable, fieldName string) (string, error) { typeField, err := _type.structMember(fieldName) if err != nil { return "", err } typeField = typeField.maybeDereference() typename, _, err := nameOfRuntimeType(typeField) return typename, err } func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) { typ, err := typeForKind(kind, _type.bi) if err != nil { return nil, err } if typ == nil { return _type, nil } return _type.newVariable(_type.Name, _type.Addr, typ, _type.mem), nil } // See reflect.(*rtype).uncommon in $GOROOT/src/reflect/type.go func uncommon(_type *Variable, tflag int64) *Variable { if tflag&tflagUncommon == 0 { return nil } typ, err := _type.bi.findType("runtime.uncommontype") if err != nil { return nil } return _type.newVariable(_type.Name, _type.Addr+uintptr(_type.RealType.Size()), typ, _type.mem) } // typeForKind returns a *dwarf.StructType describing the specialization of // runtime._type for the specified type kind. For example if kind is // reflect.ArrayType it will return runtime.arraytype func typeForKind(kind int64, bi *BinaryInfo) (*godwarf.StructType, error) { var typ godwarf.Type switch reflect.Kind(kind & kindMask) { case reflect.Array: typ, _ = bi.findType("runtime.arraytype") case reflect.Chan: // typ, _ = bi.findType("runtime.chantype") case reflect.Func: typ, _ = bi.findType("runtime.functype") case reflect.Interface: typ, _ = bi.findType("runtime.interfacetype") case reflect.Map: typ, _ = bi.findType("runtime.maptype") case reflect.Ptr: typ, _ = bi.findType("runtime.ptrtype") case reflect.Slice: typ, _ = bi.findType("runtime.slicetype") case reflect.Struct: typ, _ = bi.findType("runtime.structtype") default: return nil, nil } if typ != nil { typ = resolveTypedef(typ) return typ.(*godwarf.StructType), nil } return constructTypeForKind(kind, bi) } // constructTypeForKind synthesizes a *dwarf.StructType for the specified kind. // This is necessary because on go1.8 and previous the specialized types of // runtime._type were not exported. func constructTypeForKind(kind int64, bi *BinaryInfo) (*godwarf.StructType, error) { rtyp, err := bi.findType("runtime._type") if err != nil { return nil, err } prtyp := pointerTo(rtyp, bi.Arch) uintptrtyp, err := bi.findType("uintptr") if err != nil { return nil, err } uint32typ := &godwarf.UintType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 4, Name: "uint32"}}} uint16typ := &godwarf.UintType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 2, Name: "uint16"}}} newStructType := func(name string, sz uintptr) *godwarf.StructType { return &godwarf.StructType{ CommonType: godwarf.CommonType{Name: name, ByteSize: int64(sz)}, StructName: name, Kind: "struct", Field: nil, Incomplete: false, } } appendField := func(typ *godwarf.StructType, name string, fieldtype godwarf.Type, off uintptr) { typ.Field = append(typ.Field, &godwarf.StructField{Name: name, ByteOffset: int64(off), Type: fieldtype}) } newSliceType := func(elemtype godwarf.Type) *godwarf.SliceType { r := newStructType("[]"+elemtype.Common().Name, uintptr(3*uintptrtyp.Size())) appendField(r, "array", pointerTo(elemtype, bi.Arch), 0) appendField(r, "len", uintptrtyp, uintptr(uintptrtyp.Size())) appendField(r, "cap", uintptrtyp, uintptr(2*uintptrtyp.Size())) return &godwarf.SliceType{StructType: *r, ElemType: elemtype} } type rtype struct { size uintptr ptrdata uintptr hash uint32 // hash of type; avoids computation in hash tables tflag uint8 // extra type information flags align uint8 // alignment of variable with this type fieldAlign uint8 // alignment of struct field with this type kind uint8 // enumeration for C alg *byte // algorithm table gcdata *byte // garbage collection data str int32 // string form ptrToThis int32 // type for pointer to this type, may be zero } switch reflect.Kind(kind & kindMask) { case reflect.Array: // runtime.arraytype var a struct { rtype elem *rtype // array element type slice *rtype // slice type len uintptr } typ := newStructType("runtime.arraytype", unsafe.Sizeof(a)) appendField(typ, "elem", prtyp, unsafe.Offsetof(a.elem)) appendField(typ, "len", uintptrtyp, unsafe.Offsetof(a.len)) return typ, nil case reflect.Chan: // runtime.chantype var a struct { rtype elem *rtype // channel element type dir uintptr // channel direction (ChanDir) } typ := newStructType("runtime.chantype", unsafe.Sizeof(a)) appendField(typ, "elem", prtyp, unsafe.Offsetof(a.elem)) return typ, nil case reflect.Func: // runtime.functype var a struct { rtype `reflect:"func"` inCount uint16 outCount uint16 // top bit is set if last input parameter is ... } typ := newStructType("runtime.functype", unsafe.Sizeof(a)) appendField(typ, "inCount", uint16typ, unsafe.Offsetof(a.inCount)) appendField(typ, "outCount", uint16typ, unsafe.Offsetof(a.outCount)) return typ, nil case reflect.Interface: // runtime.imethod type imethod struct { name uint32 // name of method ityp uint32 // .(*FuncType) underneath } var im imethod // runtime.interfacetype var a struct { rtype `reflect:"interface"` pkgPath *byte // import path mhdr []imethod // sorted by hash } imethodtype := newStructType("runtime.imethod", unsafe.Sizeof(im)) appendField(imethodtype, imethodFieldName, uint32typ, unsafe.Offsetof(im.name)) appendField(imethodtype, imethodFieldItyp, uint32typ, unsafe.Offsetof(im.ityp)) typ := newStructType("runtime.interfacetype", unsafe.Sizeof(a)) appendField(typ, interfacetypeFieldMhdr, newSliceType(imethodtype), unsafe.Offsetof(a.mhdr)) return typ, nil case reflect.Map: // runtime.maptype var a struct { rtype `reflect:"map"` key *rtype // map key type elem *rtype // map element (value) type bucket *rtype // internal bucket structure hmap *rtype // internal map header keysize uint8 // size of key slot indirectkey uint8 // store ptr to key instead of key itself valuesize uint8 // size of value slot indirectvalue uint8 // store ptr to value instead of value itself bucketsize uint16 // size of bucket reflexivekey bool // true if k==k for all keys needkeyupdate bool // true if we need to update key on an overwrite } typ := newStructType("runtime.maptype", unsafe.Sizeof(a)) appendField(typ, "key", prtyp, unsafe.Offsetof(a.key)) appendField(typ, "elem", prtyp, unsafe.Offsetof(a.elem)) return typ, nil case reflect.Ptr: // runtime.ptrtype var a struct { rtype `reflect:"ptr"` elem *rtype // pointer element (pointed at) type } typ := newStructType("runtime.ptrtype", unsafe.Sizeof(a)) appendField(typ, "elem", prtyp, unsafe.Offsetof(a.elem)) return typ, nil case reflect.Slice: // runtime.slicetype var a struct { rtype `reflect:"slice"` elem *rtype // slice element type } typ := newStructType("runtime.slicetype", unsafe.Sizeof(a)) appendField(typ, "elem", prtyp, unsafe.Offsetof(a.elem)) return typ, nil case reflect.Struct: // runtime.structtype type structField struct { name *byte // name is empty for embedded fields typ *rtype // type of field offset uintptr // byte offset of field within struct } var sf structField var a struct { rtype `reflect:"struct"` pkgPath *byte fields []structField // sorted by offset } fieldtype := newStructType("runtime.structtype", unsafe.Sizeof(sf)) appendField(fieldtype, "name", uintptrtyp, unsafe.Offsetof(sf.name)) appendField(fieldtype, "typ", prtyp, unsafe.Offsetof(sf.typ)) typ := newStructType("runtime.structtype", unsafe.Sizeof(a)) appendField(typ, "fields", newSliceType(fieldtype), unsafe.Offsetof(a.fields)) return typ, nil default: return nil, nil } } func dwarfToRuntimeType(bi *BinaryInfo, mem MemoryReadWriter, typ godwarf.Type) (typeAddr uint64, typeKind uint64, found bool, err error) { rdr := bi.DwarfReader() rdr.Seek(typ.Common().Offset) e, err := rdr.Next() if err != nil { return 0, 0, false, err } off, ok := e.Val(godwarf.AttrGoRuntimeType).(uint64) if !ok { return 0, 0, false, nil } if err := loadModuleData(bi, mem); err != nil { return 0, 0, false, err } //TODO(aarzilli): when we support plugins this should be the plugin //corresponding to the shared object containing entry 'e'. typeAddr = uint64(bi.moduleData[0].types) + off rtyp, err := bi.findType("runtime._type") if err != nil { return 0, 0, false, err } _type := newVariable("", uintptr(typeAddr), rtyp, bi, mem) kindv := _type.loadFieldNamed("kind") if kindv.Unreadable != nil || kindv.Kind != reflect.Uint { return 0, 0, false, fmt.Errorf("unreadable interface type: %v", kindv.Unreadable) } typeKind, _ = constant.Uint64Val(kindv.Value) return typeAddr, typeKind, true, nil }