From 345c1bd4736c0fc1c638c3d4da8551811e013c71 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 28 Aug 2009 18:04:35 -0700 Subject: [PATCH] Debugged processes, remote values, and remote type parser R=rsc APPROVED=rsc DELTA=917 (917 added, 0 deleted, 0 changed) OCL=34049 CL=34066 --- usr/austin/ogle/process.go | 129 ++++++++++ usr/austin/ogle/rtype.go | 306 +++++++++++++++++++++++ usr/austin/ogle/rvalue.go | 494 +++++++++++++++++++++++++++++++++++++ 3 files changed, 929 insertions(+) create mode 100644 usr/austin/ogle/process.go create mode 100644 usr/austin/ogle/rtype.go create mode 100644 usr/austin/ogle/rvalue.go diff --git a/usr/austin/ogle/process.go b/usr/austin/ogle/process.go new file mode 100644 index 0000000000..0ca8940729 --- /dev/null +++ b/usr/austin/ogle/process.go @@ -0,0 +1,129 @@ +// Copyright 2009 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 ogle + +import ( + "eval"; + "ptrace"; + "reflect"; + "os"; + "sym"; +) + +// A FormatError indicates a failure to process information in or +// about a remote process, such as unexpected or missing information +// in the object file or runtime structures. +type FormatError string + +func (e FormatError) String() string { + return string(e); +} + +// An UnknownArchitecture occurs when trying to load an object file +// that indicates an architecture not supported by the debugger. +type UnknownArchitecture sym.ElfMachine + +func (e UnknownArchitecture) String() string { + return "unknown architecture: " + sym.ElfMachine(e).String(); +} + +// A Process represents a remote attached process. +type Process struct { + Arch; + ptrace.Process; + + // The symbol table of this process + syms *sym.GoSymTable; + + // Current thread + thread ptrace.Thread; + // Current frame, or nil if the current thread is not stopped + frame *frame; + + // Types parsed from the remote process + types map[ptrace.Word] *remoteType; + + // Types and values from the remote runtime package + runtime runtimeValues; + + // Runtime field indexes + f runtimeIndexes; +} + +// NewProcess constructs a new remote process around a ptrace'd +// process, an architecture, and a symbol table. +func NewProcess(proc ptrace.Process, arch Arch, syms *sym.GoSymTable) *Process { + p := &Process{ + Arch: arch, + Process: proc, + syms: syms, + thread: proc.Threads()[0], + types: make(map[ptrace.Word] *remoteType), + }; + + // TODO(austin) Set p.frame if proc is stopped + + p.bootstrap(); + return p; +} + +// NewProcessElf constructs a new remote process around a ptrace'd +// process and the process' ELF object. +func NewProcessElf(proc ptrace.Process, elf *sym.Elf) (*Process, os.Error) { + syms, err := sym.ElfGoSyms(elf); + if err != nil { + return nil, err; + } + if syms == nil { + return nil, FormatError("Failed to find symbol table"); + } + var arch Arch; + switch elf.Machine { + case sym.ElfX86_64: + arch = Amd64; + default: + return nil, UnknownArchitecture(elf.Machine); + } + return NewProcess(proc, arch, syms), nil; +} + +// bootstrap constructs the runtime structure of a remote process. +func (p *Process) bootstrap() { + // Manually construct runtime types + p.runtime.String = newManualType(eval.TypeOfNative(rt1String{}), p.Arch); + p.runtime.Slice = newManualType(eval.TypeOfNative(rt1Slice{}), p.Arch); + p.runtime.Eface = newManualType(eval.TypeOfNative(rt1Eface{}), p.Arch); + + p.runtime.Type = newManualType(eval.TypeOfNative(rt1Type{}), p.Arch); + p.runtime.CommonType = newManualType(eval.TypeOfNative(rt1CommonType{}), p.Arch); + p.runtime.UncommonType = newManualType(eval.TypeOfNative(rt1UncommonType{}), p.Arch); + p.runtime.StructField = newManualType(eval.TypeOfNative(rt1StructField{}), p.Arch); + p.runtime.StructType = newManualType(eval.TypeOfNative(rt1StructType{}), p.Arch); + p.runtime.PtrType = newManualType(eval.TypeOfNative(rt1PtrType{}), p.Arch); + p.runtime.ArrayType = newManualType(eval.TypeOfNative(rt1ArrayType{}), p.Arch); + p.runtime.SliceType = newManualType(eval.TypeOfNative(rt1SliceType{}), p.Arch); + + p.runtime.Stktop = newManualType(eval.TypeOfNative(rt1Stktop{}), p.Arch); + p.runtime.Gobuf = newManualType(eval.TypeOfNative(rt1Gobuf{}), p.Arch); + p.runtime.G = newManualType(eval.TypeOfNative(rt1G{}), p.Arch); + + // Get addresses of type·*runtime.XType for discrimination. + rtv := reflect.Indirect(reflect.NewValue(&p.runtime)).(*reflect.StructValue); + rtvt := rtv.Type().(*reflect.StructType); + for i := 0; i < rtv.NumField(); i++ { + n := rtvt.Field(i).Name; + if n[0] != 'P' || n[1] < 'A' || n[1] > 'Z' { + continue; + } + sym := p.syms.SymFromName("type·*runtime." + n[1:len(n)]); + if sym == nil { + continue; + } + rtv.Field(i).(*reflect.Uint64Value).Set(sym.Common().Value); + } + + // Get field indexes + fillRuntimeIndexes(&p.runtime, &p.f); +} diff --git a/usr/austin/ogle/rtype.go b/usr/austin/ogle/rtype.go new file mode 100644 index 0000000000..8565949369 --- /dev/null +++ b/usr/austin/ogle/rtype.go @@ -0,0 +1,306 @@ +// Copyright 2009 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 ogle + +import ( + "eval"; + "fmt"; + "log"; + "ptrace"; +) + +const debugParseRemoteType = false + +// A remoteType is the local representation of a type in a remote process. +type remoteType struct { + eval.Type; + // The size of values of this type in bytes. + size int; + // The field alignment of this type. Only used for + // manually-constructed types. + fieldAlign int; + // The maker function to turn a remote address of a value of + // this type into an interpreter Value. + mk maker; +} + +var manualTypes = make(map[Arch] map[eval.Type] *remoteType) + +// newManualType constructs a remote type from an interpreter Type +// using the size and alignment properties of the given architecture. +// Most types are parsed directly out of the remote process, but to do +// so we need to layout the structures that describe those types ourselves. +func newManualType(t eval.Type, arch Arch) *remoteType { + if nt, ok := t.(*eval.NamedType); ok { + t = nt.Def; + } + + // Get the type map for this architecture + typeMap, ok := manualTypes[arch]; + if typeMap == nil { + typeMap = make(map[eval.Type] *remoteType); + manualTypes[arch] = typeMap; + + // Construct basic types for this architecture + basicType := func(t eval.Type, mk maker, size int, fieldAlign int) { + t = t.(*eval.NamedType).Def; + if fieldAlign == 0 { + fieldAlign = size; + } + typeMap[t] = &remoteType{t, size, fieldAlign, mk}; + }; + basicType(eval.Uint8Type, mkUint8, 1, 0); + basicType(eval.Uint32Type, mkUint32, 4, 0); + basicType(eval.UintptrType, mkUintptr, arch.PtrSize(), 0); + basicType(eval.Int32Type, mkInt32, 4, 0); + basicType(eval.IntType, mkInt, arch.IntSize(), 0); + basicType(eval.StringType, mkString, arch.PtrSize() + arch.IntSize(), arch.PtrSize()); + } + + if rt, ok := typeMap[t]; ok { + return rt; + } + + var rt *remoteType; + switch t := t.(type) { + case *eval.PtrType: + var elem *remoteType; + mk := func(r remote) eval.Value { + return remotePtr{r, elem}; + }; + rt = &remoteType{t, arch.PtrSize(), arch.PtrSize(), mk}; + // Construct the element type after registering the + // type to break cycles. + typeMap[t] = rt; + elem = newManualType(t.Elem, arch); + + case *eval.ArrayType: + elem := newManualType(t.Elem, arch); + mk := func(r remote) eval.Value { + return remoteArray{r, t.Len, elem}; + }; + rt = &remoteType{t, elem.size*int(t.Len), elem.fieldAlign, mk}; + + case *eval.SliceType: + elem := newManualType(t.Elem, arch); + mk := func(r remote) eval.Value { + return remoteSlice{r, elem}; + }; + rt = &remoteType{t, arch.PtrSize() + 2*arch.IntSize(), arch.PtrSize(), mk}; + + case *eval.StructType: + layout := make([]remoteStructField, len(t.Elems)); + offset := 0; + fieldAlign := 0; + for i, f := range t.Elems { + elem := newManualType(f.Type, arch); + if fieldAlign == 0 { + fieldAlign = elem.fieldAlign; + } + offset = arch.Align(offset, elem.fieldAlign); + layout[i].offset = offset; + layout[i].fieldType = elem; + offset += elem.size; + } + mk := func(r remote) eval.Value { + return remoteStruct{r, layout}; + }; + rt = &remoteType{t, offset, fieldAlign, mk}; + + default: + log.Crashf("cannot manually construct type %T", t); + } + + typeMap[t] = rt; + return rt; +} + +var prtIndent = ""; + +// parseRemoteType parses a Type structure in a remote process to +// construct the corresponding interpreter type and remote type. +func parseRemoteType(rs remoteStruct) *remoteType { + addr := rs.addr().base; + p := rs.addr().p; + + // We deal with circular types by discovering cycles at + // NamedTypes. If a type cycles back to something other than + // a named type, we're guaranteed that there will be a named + // type somewhere in that cycle. Thus, we continue down, + // re-parsing types until we reach the named type in the + // cycle. In order to still create one remoteType per remote + // type, we insert an empty remoteType in the type map the + // first time we encounter the type and re-use that structure + // the second time we encounter it. + + rt, ok := p.types[addr]; + if ok && rt.Type != nil { + return rt; + } else if !ok { + rt = &remoteType{}; + p.types[addr] = rt; + } + + if debugParseRemoteType { + sym := p.syms.SymFromAddr(uint64(addr)); + name := ""; + if sym != nil { + name = sym.Common().Name; + } + log.Stderrf("%sParsing type at %#x (%s)", prtIndent, addr, name); + prtIndent += " "; + defer func() { prtIndent = prtIndent[0:len(prtIndent)-1] }(); + } + + // Get Type header + itype := ptrace.Word(rs.Field(p.f.Type.Typ).(remoteUint).Get()); + typ := rs.Field(p.f.Type.Ptr).(remotePtr).Get().(remoteStruct); + + // Is this a named type? + var nt *eval.NamedType; + uncommon := typ.Field(p.f.CommonType.UncommonType).(remotePtr).Get(); + if uncommon != nil { + name := uncommon.(remoteStruct).Field(p.f.UncommonType.Name).(remotePtr).Get(); + if name != nil { + // TODO(austin) Declare type in appropriate remote package + nt = eval.NewNamedType(name.(remoteString).Get()); + rt.Type = nt; + } + } + + // Create type + var t eval.Type; + var mk maker; + switch itype { + case p.runtime.PBoolType: + t = eval.BoolType; + mk = mkBool; + case p.runtime.PUint8Type: + t = eval.Uint8Type; + mk = mkUint8; + case p.runtime.PUint16Type: + t = eval.Uint16Type; + mk = mkUint16; + case p.runtime.PUint32Type: + t = eval.Uint32Type; + mk = mkUint32; + case p.runtime.PUint64Type: + t = eval.Uint64Type; + mk = mkUint64; + case p.runtime.PUintType: + t = eval.UintType; + mk = mkUint; + case p.runtime.PUintptrType: + t = eval.UintptrType; + mk = mkUintptr; + case p.runtime.PInt8Type: + t = eval.Int8Type; + mk = mkInt8; + case p.runtime.PInt16Type: + t = eval.Int16Type; + mk = mkInt16; + case p.runtime.PInt32Type: + t = eval.Int32Type; + mk = mkInt32; + case p.runtime.PInt64Type: + t = eval.Int64Type; + mk = mkInt64; + case p.runtime.PIntType: + t = eval.IntType; + mk = mkInt; + case p.runtime.PFloat32Type: + t = eval.Float32Type; + mk = mkFloat32; + case p.runtime.PFloat64Type: + t = eval.Float64Type; + mk = mkFloat64; + case p.runtime.PFloatType: + t = eval.FloatType; + mk = mkFloat; + case p.runtime.PStringType: + t = eval.StringType; + mk = mkString; + + case p.runtime.PArrayType: + // Cast to an ArrayType + typ := p.runtime.ArrayType.mk(typ.addr()).(remoteStruct); + len := int64(typ.Field(p.f.ArrayType.Len).(remoteUint).Get()); + elem := parseRemoteType(typ.Field(p.f.ArrayType.Elem).(remotePtr).Get().(remoteStruct)); + t = eval.NewArrayType(len, elem.Type); + mk = func(r remote) eval.Value { + return remoteArray{r, len, elem}; + }; + + case p.runtime.PStructType: + // Cast to a StructType + typ := p.runtime.StructType.mk(typ.addr()).(remoteStruct); + fs := typ.Field(p.f.StructType.Fields).(remoteSlice).Get(); + + fields := make([]eval.StructField, fs.Len); + layout := make([]remoteStructField, fs.Len); + for i := range fields { + f := fs.Base.Elem(int64(i)).(remoteStruct); + elemrs := f.Field(p.f.StructField.Typ).(remotePtr).Get().(remoteStruct); + elem := parseRemoteType(elemrs); + fields[i].Type = elem.Type; + name := f.Field(p.f.StructField.Name).(remotePtr).Get(); + if name == nil { + fields[i].Anonymous = true; + } else { + fields[i].Name = name.(remoteString).Get(); + } + layout[i].offset = int(f.Field(p.f.StructField.Offset).(remoteUint).Get()); + layout[i].fieldType = elem; + } + + t = eval.NewStructType(fields); + mk = func(r remote) eval.Value { + return remoteStruct{r, layout}; + }; + + case p.runtime.PPtrType: + // Cast to a PtrType + typ := p.runtime.PtrType.mk(typ.addr()).(remoteStruct); + elem := parseRemoteType(typ.Field(p.f.PtrType.Elem).(remotePtr).Get().(remoteStruct)); + t = eval.NewPtrType(elem.Type); + mk = func(r remote) eval.Value { + return remotePtr{r, elem}; + }; + + case p.runtime.PSliceType: + // Cast to a SliceType + typ := p.runtime.SliceType.mk(typ.addr()).(remoteStruct); + elem := parseRemoteType(typ.Field(p.f.SliceType.Elem).(remotePtr).Get().(remoteStruct)); + t = eval.NewSliceType(elem.Type); + mk = func(r remote) eval.Value { + return remoteSlice{r, elem}; + }; + + case p.runtime.PMapType, p.runtime.PChanType, p.runtime.PFuncType, p.runtime.PInterfaceType, p.runtime.PUnsafePointerType, p.runtime.PDotDotDotType: + // TODO(austin) + t = eval.UintptrType; + mk = mkUintptr; + + default: + sym := p.syms.SymFromAddr(uint64(itype)); + name := ""; + if sym != nil { + name = sym.Common().Name; + } + err := fmt.Sprintf("runtime type at %#x has unexpected type %#x (%s)", addr, itype, name); + eval.Abort(FormatError(err)); + } + + // Fill in the remote type + if nt != nil { + nt.Complete(t); + } else { + rt.Type = t; + } + rt.size = int(typ.Field(p.f.CommonType.Size).(remoteUint).Get()); + rt.mk = mk; + + return rt; +} diff --git a/usr/austin/ogle/rvalue.go b/usr/austin/ogle/rvalue.go new file mode 100644 index 0000000000..db99b63b90 --- /dev/null +++ b/usr/austin/ogle/rvalue.go @@ -0,0 +1,494 @@ +// Copyright 2009 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 ogle + +import ( + "eval"; + "fmt"; + "ptrace"; +) + +// A RemoteMismatchError occurs when an operation that requires two +// identical remote processes is given different process. For +// example, this occurs when trying to set a pointer in one process to +// point to something in another process. +type RemoteMismatchError string + +func (e RemoteMismatchError) String() string { + return string(e); +} + +// A maker is a function that converts a remote address into an +// interpreter Value. +type maker func(remote) eval.Value + +type remoteValue interface { + addr() remote; +} + +// remote represents an address in a remote process. +type remote struct { + base ptrace.Word; + p *Process; +} + +func (v remote) Get(size int) uint64 { + // TODO(austin) This variable might temporarily be in a + // register. We could trace the assembly back from the + // current PC, looking for the beginning of the function or a + // call (both of which guarantee that the variable is in + // memory), or an instruction that loads the variable into a + // register. + // + // TODO(austin) If this is a local variable, it might not be + // live at this PC. In fact, because the compiler reuses + // slots, there might even be a different local variable at + // this location right now. A simple solution to both + // problems is to include the range of PC's over which a local + // variable is live in the symbol table. + // + // TODO(austin) We need to prevent the remote garbage + // collector from collecting objects out from under us. + var arr [8]byte; + buf := arr[0:size]; + _, err := v.p.thread.Peek(v.base, buf); + if err != nil { + eval.Abort(err); + } + return uint64(v.p.ToWord(buf)); +} + +func (v remote) Set(size int, x uint64) { + var arr [8]byte; + buf := arr[0:size]; + v.p.FromWord(ptrace.Word(x), buf); + _, err := v.p.thread.Poke(v.base, buf); + if err != nil { + eval.Abort(err); + } +} + +func (v remote) plus(x ptrace.Word) remote { + return remote{v.base + x, v.p}; +} + +/* + * Bool + */ + +type remoteBool struct { + r remote; +} + +func (v remoteBool) String() string { + return fmt.Sprintf("%v", v.Get()); +} + +func (v remoteBool) Assign(o eval.Value) { + v.Set(o.(eval.BoolValue).Get()); +} + +func (v remoteBool) Get() bool { + return v.r.Get(1) != 0; +} + +func (v remoteBool) Set(x bool) { + if x { + v.r.Set(1, 1); + } else { + v.r.Set(1, 0); + } +} + +func (v remoteBool) addr() remote { + return v.r; +} + +func mkBool(r remote) eval.Value { + return remoteBool{r}; +} + +/* + * Uint + */ + +type remoteUint struct { + r remote; + size int; +} + +func (v remoteUint) String() string { + return fmt.Sprintf("%v", v.Get()); +} + +func (v remoteUint) Assign(o eval.Value) { + v.Set(o.(eval.UintValue).Get()); +} + +func (v remoteUint) Get() uint64 { + return v.r.Get(v.size); +} + +func (v remoteUint) Set(x uint64) { + v.r.Set(v.size, x); +} + +func (v remoteUint) addr() remote { + return v.r; +} + +func mkUint8(r remote) eval.Value { + return remoteUint{r, 1}; +} + +func mkUint16(r remote) eval.Value { + return remoteUint{r, 2}; +} + +func mkUint32(r remote) eval.Value { + return remoteUint{r, 4}; +} + +func mkUint64(r remote) eval.Value { + return remoteUint{r, 8}; +} + +func mkUint(r remote) eval.Value { + return remoteUint{r, r.p.IntSize()}; +} + +func mkUintptr(r remote) eval.Value { + return remoteUint{r, r.p.PtrSize()}; +} + +/* + * Int + */ + +type remoteInt struct { + r remote; + size int; +} + +func (v remoteInt) String() string { + return fmt.Sprintf("%v", v.Get()); +} + +func (v remoteInt) Assign(o eval.Value) { + v.Set(o.(eval.IntValue).Get()); +} + +func (v remoteInt) Get() int64 { + return int64(v.r.Get(v.size)); +} + +func (v remoteInt) Set(x int64) { + v.r.Set(v.size, uint64(x)); +} + +func (v remoteInt) addr() remote { + return v.r; +} + +func mkInt8(r remote) eval.Value { + return remoteInt{r, 1}; +} + +func mkInt16(r remote) eval.Value { + return remoteInt{r, 2}; +} + +func mkInt32(r remote) eval.Value { + return remoteInt{r, 4}; +} + +func mkInt64(r remote) eval.Value { + return remoteInt{r, 8}; +} + +func mkInt(r remote) eval.Value { + return remoteInt{r, r.p.IntSize()}; +} + +/* + * Float + */ + +type remoteFloat struct { + r remote; + size int; +} + +func (v remoteFloat) String() string { + return fmt.Sprintf("%v", v.Get()); +} + +func (v remoteFloat) Assign(o eval.Value) { + v.Set(o.(eval.FloatValue).Get()); +} + +func (v remoteFloat) Get() float64 { + bits := v.r.Get(v.size); + switch v.size { + case 4: + return float64(v.r.p.ToFloat32(uint32(bits))); + case 8: + return v.r.p.ToFloat64(bits); + } + panic("Unexpected float size ", v.size); +} + +func (v remoteFloat) Set(x float64) { + var bits uint64; + switch v.size{ + case 4: + bits = uint64(v.r.p.FromFloat32(float32(x))); + case 8: + bits = v.r.p.FromFloat64(x); + default: + panic("Unexpected float size ", v.size); + } + v.r.Set(v.size, bits); +} + +func (v remoteFloat) addr() remote { + return v.r; +} + +func mkFloat32(r remote) eval.Value { + return remoteFloat{r, 4}; +} + +func mkFloat64(r remote) eval.Value { + return remoteFloat{r, 8}; +} + +func mkFloat(r remote) eval.Value { + return remoteFloat{r, r.p.FloatSize()}; +} + +/* + * String + */ + +type remoteString struct { + r remote; +} + +func (v remoteString) String() string { + return v.Get(); +} + +func (v remoteString) Assign(o eval.Value) { + v.Set(o.(eval.StringValue).Get()); +} + +func (v remoteString) Get() string { + rs := v.r.p.runtime.String.mk(v.r).(remoteStruct); + str := ptrace.Word(rs.Field(v.r.p.f.String.Str).(remoteUint).Get()); + len := rs.Field(v.r.p.f.String.Len).(remoteInt).Get(); + + bytes := make([]uint8, len); + _, err := v.r.p.thread.Peek(str, bytes); + if err != nil { + eval.Abort(err); + } + return string(bytes); +} + +func (v remoteString) Set(x string) { + // TODO(austin) This isn't generally possible without the + // ability to allocate remote memory. + eval.Abort(RemoteMismatchError("remote strings cannot be assigned to")); +} + +func mkString(r remote) eval.Value { + return remoteString{r}; +} + +/* + * Array + */ + +type remoteArray struct { + r remote; + len int64; + elemType *remoteType; +} + +func (v remoteArray) String() string { + res := "{"; + for i := int64(0); i < v.len; i++ { + if i > 0 { + res += ", "; + } + res += v.Elem(i).String(); + } + return res + "}"; +} + +func (v remoteArray) Assign(o eval.Value) { + // TODO(austin) Could do a bigger memcpy if o is a + // remoteArray in the same Process. + oa := o.(eval.ArrayValue); + for i := int64(0); i < v.len; i++ { + v.Elem(i).Assign(oa.Elem(i)); + } +} + +func (v remoteArray) Get() eval.ArrayValue { + return v; +} + +func (v remoteArray) Elem(i int64) eval.Value { + return v.elemType.mk(v.r.plus(ptrace.Word(int64(v.elemType.size) * i))); +} + +func (v remoteArray) From(i int64) eval.ArrayValue { + return remoteArray{v.r.plus(ptrace.Word(int64(v.elemType.size) * i)), v.len - i, v.elemType}; +} + +/* + * Struct + */ + +type remoteStruct struct { + r remote; + layout []remoteStructField; +} + +type remoteStructField struct { + offset int; + fieldType *remoteType; +} + +func (v remoteStruct) String() string { + res := "{"; + for i := range v.layout { + if i > 0 { + res += ", "; + } + res += v.Field(i).String(); + } + return res + "}"; +} + +func (v remoteStruct) Assign(o eval.Value) { + // TODO(austin) Could do a bigger memcpy. + oa := o.(eval.StructValue); + l := len(v.layout); + for i := 0; i < l; i++ { + v.Field(i).Assign(oa.Field(i)); + } +} + +func (v remoteStruct) Get() eval.StructValue { + return v; +} + +func (v remoteStruct) Field(i int) eval.Value { + f := &v.layout[i]; + return f.fieldType.mk(v.r.plus(ptrace.Word(f.offset))); +} + +func (v remoteStruct) addr() remote { + return v.r; +} + +/* + * Pointer + */ + +// TODO(austin) Comparing two remote pointers for equality in the +// interpreter will crash it because the Value's returned from +// remotePtr.Get() will be structs. + +type remotePtr struct { + r remote; + elemType *remoteType; +} + +func (v remotePtr) String() string { + e := v.Get(); + if e == nil { + return ""; + } + return "&" + e.String(); +} + +func (v remotePtr) Assign(o eval.Value) { + v.Set(o.(eval.PtrValue).Get()); +} + +func (v remotePtr) Get() eval.Value { + addr := ptrace.Word(v.r.Get(v.r.p.PtrSize())); + if addr == 0 { + return nil; + } + return v.elemType.mk(remote{addr, v.r.p}); +} + +func (v remotePtr) Set(x eval.Value) { + if x == nil { + v.r.Set(v.r.p.PtrSize(), 0); + return; + } + xr, ok := x.(remoteValue); + if !ok || v.r.p != xr.addr().p { + eval.Abort(RemoteMismatchError("remote pointer must point within the same process")); + } + v.r.Set(v.r.p.PtrSize(), uint64(xr.addr().base)); +} + +func (v remotePtr) addr() remote { + return v.r; +} + +/* + * Slice + */ + +type remoteSlice struct { + r remote; + elemType *remoteType; +} + +func (v remoteSlice) String() string { + b := v.Get().Base; + if b == nil { + return ""; + } + return b.String(); +} + +func (v remoteSlice) Assign(o eval.Value) { + v.Set(o.(eval.SliceValue).Get()); +} + +func (v remoteSlice) Get() eval.Slice { + rs := v.r.p.runtime.Slice.mk(v.r).(remoteStruct); + base := ptrace.Word(rs.Field(v.r.p.f.Slice.Array).(remoteUint).Get()); + nel := rs.Field(v.r.p.f.Slice.Len).(remoteInt).Get(); + cap := rs.Field(v.r.p.f.Slice.Cap).(remoteInt).Get(); + if base == 0 { + return eval.Slice{nil, nel, cap}; + } + return eval.Slice{remoteArray{remote{base, v.r.p}, nel, v.elemType}, nel, cap}; +} + +func (v remoteSlice) Set(x eval.Slice) { + rs := v.r.p.runtime.Slice.mk(v.r).(remoteStruct); + if x.Base == nil { + rs.Field(v.r.p.f.Slice.Array).(remoteUint).Set(0); + } else { + ar, ok := x.Base.(remoteArray); + if !ok || v.r.p != ar.r.p { + eval.Abort(RemoteMismatchError("remote slice must point within the same process")); + } + rs.Field(v.r.p.f.Slice.Array).(remoteUint).Set(uint64(ar.r.base)); + } + rs.Field(v.r.p.f.Slice.Len).(remoteInt).Set(x.Len); + rs.Field(v.r.p.f.Slice.Cap).(remoteInt).Set(x.Cap); +} -- 2.48.1