mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-31 10:47:27 +08:00 
			
		
		
		
	 c379296cc8
			
		
	
	c379296cc8
	
	
	
		
			
			Adds a script that check automatically that the the assumptions that pkg/proc makes about runtime are met by the actual runtime, using a combination of parsing and magic comments. Also emits a file describing all the struct fields, constants and variables of the runtime that we use in pkg/proc.
		
			
				
	
	
		
			210 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			210 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package proc
 | |
| 
 | |
| import (
 | |
| 	"go/constant"
 | |
| 	"unsafe"
 | |
| )
 | |
| 
 | |
| // delve counterpart to runtime.moduledata
 | |
| type moduleData struct {
 | |
| 	text, etext   uint64
 | |
| 	types, etypes uint64
 | |
| 	typemapVar    *Variable
 | |
| }
 | |
| 
 | |
| func loadModuleData(bi *BinaryInfo, mem MemoryReadWriter) ([]moduleData, error) {
 | |
| 	// +rtype -var firstmoduledata moduledata
 | |
| 	// +rtype -field moduledata.text uintptr
 | |
| 	// +rtype -field moduledata.types uintptr
 | |
| 
 | |
| 	scope := globalScope(nil, bi, bi.Images[0], mem)
 | |
| 	var md *Variable
 | |
| 	md, err := scope.findGlobal("runtime", "firstmoduledata")
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	r := []moduleData{}
 | |
| 
 | |
| 	for md.Addr != 0 {
 | |
| 		const (
 | |
| 			typesField   = "types"
 | |
| 			etypesField  = "etypes"
 | |
| 			textField    = "text"
 | |
| 			etextField   = "etext"
 | |
| 			nextField    = "next"
 | |
| 			typemapField = "typemap"
 | |
| 		)
 | |
| 		vars := map[string]*Variable{}
 | |
| 
 | |
| 		for _, fieldName := range []string{typesField, etypesField, textField, etextField, nextField, typemapField} {
 | |
| 			var err error
 | |
| 			vars[fieldName], err = md.structMember(fieldName)
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		var err error
 | |
| 
 | |
| 		touint := func(name string) (ret uint64) {
 | |
| 			if err == nil {
 | |
| 				var n uint64
 | |
| 				n, err = vars[name].asUint()
 | |
| 				ret = n
 | |
| 			}
 | |
| 			return ret
 | |
| 		}
 | |
| 
 | |
| 		r = append(r, moduleData{
 | |
| 			types: touint(typesField), etypes: touint(etypesField),
 | |
| 			text: touint(textField), etext: touint(etextField),
 | |
| 			typemapVar: vars[typemapField],
 | |
| 		})
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		md = vars[nextField].maybeDereference()
 | |
| 		if md.Unreadable != nil {
 | |
| 			return nil, md.Unreadable
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return r, nil
 | |
| }
 | |
| 
 | |
| func findModuleDataForType(bi *BinaryInfo, mds []moduleData, typeAddr uint64, mem MemoryReadWriter) *moduleData {
 | |
| 	for i := range mds {
 | |
| 		if typeAddr >= mds[i].types && typeAddr < mds[i].etypes {
 | |
| 			return &mds[i]
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func resolveTypeOff(bi *BinaryInfo, mds []moduleData, typeAddr, off uint64, mem MemoryReadWriter) (*Variable, error) {
 | |
| 	// See runtime.(*_type).typeOff in $GOROOT/src/runtime/type.go
 | |
| 	md := findModuleDataForType(bi, mds, typeAddr, mem)
 | |
| 
 | |
| 	rtyp, err := bi.findType("runtime._type")
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if md == nil {
 | |
| 		v, err := reflectOffsMapAccess(bi, off, mem)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		v.loadValue(LoadConfig{false, 1, 0, 0, -1, 0})
 | |
| 		addr, _ := constant.Int64Val(v.Value)
 | |
| 		return v.newVariable(v.Name, uint64(addr), rtyp, mem), nil
 | |
| 	}
 | |
| 
 | |
| 	if t, _ := md.typemapVar.mapAccess(newConstant(constant.MakeUint64(uint64(off)), mem)); t != nil {
 | |
| 		return t, nil
 | |
| 	}
 | |
| 
 | |
| 	res := md.types + off
 | |
| 
 | |
| 	return newVariable("", uint64(res), rtyp, bi, mem), nil
 | |
| }
 | |
| 
 | |
| func resolveNameOff(bi *BinaryInfo, mds []moduleData, typeAddr, off uint64, mem MemoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
 | |
| 	// See runtime.resolveNameOff in $GOROOT/src/runtime/type.go
 | |
| 	for _, md := range mds {
 | |
| 		if typeAddr >= md.types && typeAddr < md.etypes {
 | |
| 			return loadName(bi, md.types+off, mem)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	v, err := reflectOffsMapAccess(bi, off, mem)
 | |
| 	if err != nil {
 | |
| 		return "", "", 0, err
 | |
| 	}
 | |
| 
 | |
| 	resv := v.maybeDereference()
 | |
| 	if resv.Unreadable != nil {
 | |
| 		return "", "", 0, resv.Unreadable
 | |
| 	}
 | |
| 
 | |
| 	return loadName(bi, resv.Addr, mem)
 | |
| }
 | |
| 
 | |
| func reflectOffsMapAccess(bi *BinaryInfo, off uint64, mem MemoryReadWriter) (*Variable, error) {
 | |
| 	scope := globalScope(nil, bi, bi.Images[0], mem)
 | |
| 	reflectOffs, err := scope.findGlobal("runtime", "reflectOffs")
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	reflectOffsm, err := reflectOffs.structMember("m")
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return reflectOffsm.mapAccess(newConstant(constant.MakeUint64(uint64(off)), mem))
 | |
| }
 | |
| 
 | |
| const (
 | |
| 	// flags for the name struct (see 'type name struct' in $GOROOT/src/reflect/type.go)
 | |
| 	nameflagExported = 1 << 0
 | |
| 	nameflagHasTag   = 1 << 1
 | |
| 	nameflagHasPkg   = 1 << 2
 | |
| )
 | |
| 
 | |
| func loadName(bi *BinaryInfo, addr uint64, mem MemoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
 | |
| 	off := addr
 | |
| 	namedata := make([]byte, 3)
 | |
| 	_, err = mem.ReadMemory(namedata, off)
 | |
| 	off += 3
 | |
| 	if err != nil {
 | |
| 		return "", "", 0, err
 | |
| 	}
 | |
| 
 | |
| 	namelen := uint16(namedata[1])<<8 | uint16(namedata[2])
 | |
| 
 | |
| 	rawstr := make([]byte, int(namelen))
 | |
| 	_, err = mem.ReadMemory(rawstr, off)
 | |
| 	off += uint64(namelen)
 | |
| 	if err != nil {
 | |
| 		return "", "", 0, err
 | |
| 	}
 | |
| 
 | |
| 	name = string(rawstr)
 | |
| 
 | |
| 	if namedata[0]&nameflagHasTag != 0 {
 | |
| 		taglendata := make([]byte, 2)
 | |
| 		_, err = mem.ReadMemory(taglendata, off)
 | |
| 		off += 2
 | |
| 		if err != nil {
 | |
| 			return "", "", 0, err
 | |
| 		}
 | |
| 		taglen := uint16(taglendata[0])<<8 | uint16(taglendata[1])
 | |
| 
 | |
| 		rawstr := make([]byte, int(taglen))
 | |
| 		_, err = mem.ReadMemory(rawstr, off)
 | |
| 		off += uint64(taglen)
 | |
| 		if err != nil {
 | |
| 			return "", "", 0, err
 | |
| 		}
 | |
| 
 | |
| 		tag = string(rawstr)
 | |
| 	}
 | |
| 
 | |
| 	if namedata[0]&nameflagHasPkg != 0 {
 | |
| 		pkgdata := make([]byte, 4)
 | |
| 		_, err = mem.ReadMemory(pkgdata, off)
 | |
| 		if err != nil {
 | |
| 			return "", "", 0, err
 | |
| 		}
 | |
| 
 | |
| 		// see func pkgPath in $GOROOT/src/reflect/type.go
 | |
| 		copy((*[4]byte)(unsafe.Pointer(&pkgpathoff))[:], pkgdata)
 | |
| 	}
 | |
| 
 | |
| 	return name, tag, pkgpathoff, nil
 | |
| }
 |