提交 8f16b371 编写于 作者: A aarzilli 提交者: Derek Parker

proc/eval: support more type casts

* string to []rune
* string to []byte
* []rune to string
* []byte to string
* any pointer to uintptr

The string, []rune, []byte conversion pairs aligns this to the go
language.
The pointer -> uintptr conversion pair is symmetric to the uintptr ->
pointer that we already have.

Also lets the user specify any size for byte array types instead of
just the ones already used by the program, this can be used to read
arbitrary memory.

Fixes #548, #867
上级 94c3b401
......@@ -5,7 +5,8 @@ Delve can evaluate a subset of go expression language, specifically the followin
- All (binary and unary) on basic types except <-, ++ and --
- Comparison operators on any type
- Type casts between numeric types
- Type casts of integer constants into any pointer type
- Type casts of integer constants into any pointer type and vice versa
- Type casts between string, []byte and []rune
- Struct member access (i.e. `somevar.memberfield`)
- Slicing and indexing operators on arrays, slices and strings
- Map access
......
......@@ -235,6 +235,9 @@ func main() {
emptyslice := []string{}
emptymap := make(map[string]string)
byteslice := []byte{116, 195, 168, 115, 116}
runeslice := []rune{116, 232, 115, 116}
var amb1 = 1
runtime.Breakpoint()
for amb1 := 0; amb1 < 10; amb1++ {
......@@ -242,5 +245,5 @@ func main() {
}
runtime.Breakpoint()
fmt.Println(i1, i2, i3, p1, amb1, s1, s3, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, up1, i4, i5, i6, err1, err2, errnil, iface1, iface2, ifacenil, arr1, parr, cpx1, const1, iface3, iface4, recursive1, recursive1.x, iface5, iface2fn1, iface2fn2, bencharr, benchparr, mapinf, mainMenu, b, b2, sd, anonstruct1, anonstruct2, anoniface1, anonfunc, mapanonstruct1, ifacearr, efacearr, ni8, ni16, ni32, pinf, ninf, nan, zsvmap, zsslice, zsvar, tm, errtypednil, emptyslice, emptymap)
fmt.Println(i1, i2, i3, p1, amb1, s1, s3, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, up1, i4, i5, i6, err1, err2, errnil, iface1, iface2, ifacenil, arr1, parr, cpx1, const1, iface3, iface4, recursive1, recursive1.x, iface5, iface2fn1, iface2fn2, bencharr, benchparr, mapinf, mainMenu, b, b2, sd, anonstruct1, anonstruct2, anoniface1, anonfunc, mapanonstruct1, ifacearr, efacearr, ni8, ni16, ni32, pinf, ninf, nan, zsvmap, zsslice, zsvar, tm, errtypednil, emptyslice, emptymap, byteslice, runeslice)
}
......@@ -26,7 +26,10 @@ func (scope *EvalScope) EvalExpression(expr string, cfg LoadConfig) (*Variable,
return nil, err
}
ev, err := scope.evalAST(t)
ev, err := scope.evalToplevelTypeCast(t, cfg)
if ev == nil && err == nil {
ev, err = scope.evalAST(t)
}
if err != nil {
return nil, err
}
......@@ -37,6 +40,113 @@ func (scope *EvalScope) EvalExpression(expr string, cfg LoadConfig) (*Variable,
return ev, nil
}
// evalToplevelTypeCast implements certain type casts that we only support
// at the outermost levels of an expression.
func (scope *EvalScope) evalToplevelTypeCast(t ast.Expr, cfg LoadConfig) (*Variable, error) {
call, _ := t.(*ast.CallExpr)
if call == nil || len(call.Args) != 1 {
return nil, nil
}
targetTypeStr := exprToString(removeParen(call.Fun))
var targetType godwarf.Type
switch targetTypeStr {
case "[]byte", "[]uint8":
targetType = fakeSliceType(&godwarf.IntType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 1, Name: "uint8"}, BitSize: 8, BitOffset: 0}})
case "[]int32", "[]rune":
targetType = fakeSliceType(&godwarf.IntType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 1, Name: "int32"}, BitSize: 32, BitOffset: 0}})
case "string":
var err error
targetType, err = scope.BinInfo.findType("string")
if err != nil {
return nil, err
}
default:
return nil, nil
}
argv, err := scope.evalToplevelTypeCast(call.Args[0], cfg)
if argv == nil && err == nil {
argv, err = scope.evalAST(call.Args[0])
}
if err != nil {
return nil, err
}
argv.loadValue(cfg)
if argv.Unreadable != nil {
return nil, argv.Unreadable
}
v := newVariable("", 0, targetType, scope.BinInfo, scope.Mem)
v.loaded = true
converr := fmt.Errorf("can not convert %q to %s", exprToString(call.Args[0]), targetTypeStr)
switch targetTypeStr {
case "[]byte", "[]uint8":
if argv.Kind != reflect.String {
return nil, converr
}
for i, ch := range []byte(constant.StringVal(argv.Value)) {
e := scope.newVariable("", argv.Addr+uintptr(i), targetType.(*godwarf.SliceType).ElemType)
e.loaded = true
e.Value = constant.MakeInt64(int64(ch))
v.Children = append(v.Children, *e)
}
v.Len = int64(len(v.Children))
v.Cap = v.Len
return v, nil
case "[]int32", "[]rune":
if argv.Kind != reflect.String {
return nil, converr
}
for i, ch := range constant.StringVal(argv.Value) {
e := scope.newVariable("", argv.Addr+uintptr(i), targetType.(*godwarf.SliceType).ElemType)
e.loaded = true
e.Value = constant.MakeInt64(int64(ch))
v.Children = append(v.Children, *e)
}
v.Len = int64(len(v.Children))
v.Cap = v.Len
return v, nil
case "string":
if argv.Kind != reflect.Slice {
return nil, nil
}
switch elemType := argv.RealType.(*godwarf.SliceType).ElemType.(type) {
case *godwarf.UintType:
if elemType.Name != "uint8" && elemType.Name != "byte" {
return nil, nil
}
bytes := make([]byte, len(argv.Children))
for i := range argv.Children {
n, _ := constant.Int64Val(argv.Children[i].Value)
bytes[i] = byte(n)
}
v.Value = constant.MakeString(string(bytes))
case *godwarf.IntType:
if elemType.Name != "int32" && elemType.Name != "rune" {
return nil, nil
}
runes := make([]rune, len(argv.Children))
for i := range argv.Children {
n, _ := constant.Int64Val(argv.Children[i].Value)
runes[i] = rune(n)
}
v.Value = constant.MakeString(string(runes))
default:
return nil, nil
}
v.Len = int64(len(constant.StringVal(v.Value)))
return v, nil
}
return nil, nil
}
func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
switch node := t.(type) {
case *ast.CallExpr:
......@@ -125,6 +235,17 @@ func exprToString(t ast.Expr) string {
return buf.String()
}
func removeParen(n ast.Expr) ast.Expr {
for {
p, ok := n.(*ast.ParenExpr)
if !ok {
break
}
n = p.X
}
return n
}
// Eval type cast expressions
func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
argv, err := scope.evalAST(node.Args[0])
......@@ -139,13 +260,7 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
fnnode := node.Fun
// remove all enclosing parenthesis from the type name
for {
p, ok := fnnode.(*ast.ParenExpr)
if !ok {
break
}
fnnode = p.X
}
fnnode = removeParen(fnnode)
styp, err := scope.BinInfo.findTypeExpr(fnnode)
if err != nil {
......@@ -188,6 +303,9 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
x, _ := constant.Float64Val(argv.Value)
v.Value = constant.MakeUint64(uint64(x))
return v, nil
case reflect.Ptr:
v.Value = constant.MakeUint64(uint64(argv.Children[0].Addr))
return v, nil
}
case *godwarf.IntType:
switch argv.Kind {
......@@ -1126,18 +1244,7 @@ func (v *Variable) reslice(low int64, high int64) (*Variable, error) {
typ := v.DwarfType
if _, isarr := v.DwarfType.(*godwarf.ArrayType); isarr {
typ = &godwarf.SliceType{
StructType: godwarf.StructType{
CommonType: godwarf.CommonType{
ByteSize: 24,
Name: "",
},
StructName: fmt.Sprintf("[]%s", v.fieldType.Common().Name),
Kind: "struct",
Field: nil,
},
ElemType: v.fieldType,
}
typ = fakeSliceType(v.fieldType)
}
r := v.newVariable("", 0, typ)
......@@ -1149,3 +1256,18 @@ func (v *Variable) reslice(low int64, high int64) (*Variable, error) {
return r, nil
}
func fakeSliceType(fieldType godwarf.Type) godwarf.Type {
return &godwarf.SliceType{
StructType: godwarf.StructType{
CommonType: godwarf.CommonType{
ByteSize: 24,
Name: "",
},
StructName: fmt.Sprintf("[]%s", fieldType.Common().Name),
Kind: "struct",
Field: nil,
},
ElemType: fieldType,
}
}
......@@ -81,6 +81,32 @@ func (bi *BinaryInfo) findTypeExpr(expr ast.Expr) (godwarf.Type, error) {
}
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))
}
......
......@@ -34,7 +34,7 @@ func (v *Variable) writeTo(buf io.Writer, top, newlines, includeType bool, inden
return
}
if !top && v.Addr == 0 {
if !top && v.Addr == 0 && v.Value == "" {
if includeType && v.Type != "void" {
fmt.Fprintf(buf, "%s nil", v.Type)
} else {
......
......@@ -714,6 +714,19 @@ func TestEvalExpression(t *testing.T) {
{"emptyslice", false, `[]string len: 0, cap: 0, []`, `[]string len: 0, cap: 0, []`, "[]string", nil},
{"emptymap", false, `map[string]string []`, `map[string]string []`, "map[string]string", nil},
{"mnil", false, `map[string]main.astruct nil`, `map[string]main.astruct nil`, "map[string]main.astruct", nil},
// conversions between string/[]byte/[]rune (issue #548)
{"runeslice", true, `[]int32 len: 4, cap: 4, [116,232,115,116]`, `[]int32 len: 4, cap: 4, [...]`, "[]int32", nil},
{"byteslice", true, `[]uint8 len: 5, cap: 5, [116,195,168,115,116]`, `[]uint8 len: 5, cap: 5, [...]`, "[]uint8", nil},
{"[]byte(str1)", false, `[]uint8 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, `[]uint8 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, "[]uint8", nil},
{"[]uint8(str1)", false, `[]uint8 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, `[]uint8 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, "[]uint8", nil},
{"[]rune(str1)", false, `[]int32 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, `[]int32 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, "[]int32", nil},
{"[]int32(str1)", false, `[]int32 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, `[]int32 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, "[]int32", nil},
{"string(byteslice)", false, `"tèst"`, `""`, "string", nil},
{"[]int32(string(byteslice))", false, `[]int32 len: 4, cap: 4, [116,232,115,116]`, `[]int32 len: 0, cap: 0, nil`, "[]int32", nil},
{"string(runeslice)", false, `"tèst"`, `""`, "string", nil},
{"[]byte(string(runeslice))", false, `[]uint8 len: 5, cap: 5, [116,195,168,115,116]`, `[]uint8 len: 0, cap: 0, nil`, "[]uint8", nil},
{"*(*[5]byte)(uintptr(&byteslice[0]))", false, `[5]uint8 [116,195,168,115,116]`, `[5]uint8 [...]`, "[5]uint8", nil},
}
ver, _ := goversion.Parse(runtime.Version())
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册