]> Cypherpunks repositories - gostls13.git/commitdiff
Debugged processes, remote values, and remote type parser
authorAustin Clements <aclements@csail.mit.edu>
Sat, 29 Aug 2009 01:04:35 +0000 (18:04 -0700)
committerAustin Clements <aclements@csail.mit.edu>
Sat, 29 Aug 2009 01:04:35 +0000 (18:04 -0700)
R=rsc
APPROVED=rsc
DELTA=917  (917 added, 0 deleted, 0 changed)
OCL=34049
CL=34066

usr/austin/ogle/process.go [new file with mode: 0644]
usr/austin/ogle/rtype.go [new file with mode: 0644]
usr/austin/ogle/rvalue.go [new file with mode: 0644]

diff --git a/usr/austin/ogle/process.go b/usr/austin/ogle/process.go
new file mode 100644 (file)
index 0000000..0ca8940
--- /dev/null
@@ -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 (file)
index 0000000..8565949
--- /dev/null
@@ -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 := "<unknown>";
+               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 := "<unknown symbol>";
+               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 (file)
index 0000000..db99b63
--- /dev/null
@@ -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 "<nil>";
+       }
+       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 "<nil>";
+       }
+       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);
+}