// 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 ( "debug/proc" "exp/eval" "fmt" ) // 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 ReadOnlyError occurs when attempting to set or assign to a // read-only value. type ReadOnlyError string func (e ReadOnlyError) 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 proc.Word p *Process } func (v remote) Get(a aborter, 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.Peek(v.base, buf) if err != nil { a.Abort(err) } return uint64(v.p.ToWord(buf)) } func (v remote) Set(a aborter, size int, x uint64) { var arr [8]byte buf := arr[0:size] v.p.FromWord(proc.Word(x), buf) _, err := v.p.Poke(v.base, buf) if err != nil { a.Abort(err) } } func (v remote) plus(x proc.Word) remote { return remote{v.base + x, v.p} } func tryRVString(f func(a aborter) string) string { var s string err := try(func(a aborter) { s = f(a) }) if err != nil { return fmt.Sprintf("", err) } return s } /* * Bool */ type remoteBool struct { r remote } func (v remoteBool) String() string { return tryRVString(func(a aborter) string { return fmt.Sprintf("%v", v.aGet(a)) }) } func (v remoteBool) Assign(t *eval.Thread, o eval.Value) { v.Set(t, o.(eval.BoolValue).Get(t)) } func (v remoteBool) Get(t *eval.Thread) bool { return v.aGet(t) } func (v remoteBool) aGet(a aborter) bool { return v.r.Get(a, 1) != 0 } func (v remoteBool) Set(t *eval.Thread, x bool) { v.aSet(t, x) } func (v remoteBool) aSet(a aborter, x bool) { if x { v.r.Set(a, 1, 1) } else { v.r.Set(a, 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 tryRVString(func(a aborter) string { return fmt.Sprintf("%v", v.aGet(a)) }) } func (v remoteUint) Assign(t *eval.Thread, o eval.Value) { v.Set(t, o.(eval.UintValue).Get(t)) } func (v remoteUint) Get(t *eval.Thread) uint64 { return v.aGet(t) } func (v remoteUint) aGet(a aborter) uint64 { return v.r.Get(a, v.size) } func (v remoteUint) Set(t *eval.Thread, x uint64) { v.aSet(t, x) } func (v remoteUint) aSet(a aborter, x uint64) { v.r.Set(a, 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 tryRVString(func(a aborter) string { return fmt.Sprintf("%v", v.aGet(a)) }) } func (v remoteInt) Assign(t *eval.Thread, o eval.Value) { v.Set(t, o.(eval.IntValue).Get(t)) } func (v remoteInt) Get(t *eval.Thread) int64 { return v.aGet(t) } func (v remoteInt) aGet(a aborter) int64 { return int64(v.r.Get(a, v.size)) } func (v remoteInt) Set(t *eval.Thread, x int64) { v.aSet(t, x) } func (v remoteInt) aSet(a aborter, x int64) { v.r.Set(a, 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 tryRVString(func(a aborter) string { return fmt.Sprintf("%v", v.aGet(a)) }) } func (v remoteFloat) Assign(t *eval.Thread, o eval.Value) { v.Set(t, o.(eval.FloatValue).Get(t)) } func (v remoteFloat) Get(t *eval.Thread) float64 { return v.aGet(t) } func (v remoteFloat) aGet(a aborter) float64 { bits := v.r.Get(a, 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") } func (v remoteFloat) Set(t *eval.Thread, x float64) { v.aSet(t, x) } func (v remoteFloat) aSet(a aborter, 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.r.Set(a, 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 tryRVString(func(a aborter) string { return v.aGet(a) }) } func (v remoteString) Assign(t *eval.Thread, o eval.Value) { v.Set(t, o.(eval.StringValue).Get(t)) } func (v remoteString) Get(t *eval.Thread) string { return v.aGet(t) } func (v remoteString) aGet(a aborter) string { rs := v.r.p.runtime.String.mk(v.r).(remoteStruct) str := proc.Word(rs.field(v.r.p.f.String.Str).(remoteUint).aGet(a)) len := rs.field(v.r.p.f.String.Len).(remoteInt).aGet(a) bytes := make([]uint8, len) _, err := v.r.p.Peek(str, bytes) if err != nil { a.Abort(err) } return string(bytes) } func (v remoteString) Set(t *eval.Thread, x string) { v.aSet(t, x) } func (v remoteString) aSet(a aborter, x string) { // TODO(austin) This isn't generally possible without the // ability to allocate remote memory. a.Abort(ReadOnlyError("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(t *eval.Thread, 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(t, i).Assign(t, oa.Elem(t, i)) } } func (v remoteArray) Get(t *eval.Thread) eval.ArrayValue { return v } func (v remoteArray) Elem(t *eval.Thread, i int64) eval.Value { return v.elem(i) } func (v remoteArray) elem(i int64) eval.Value { return v.elemType.mk(v.r.plus(proc.Word(int64(v.elemType.size) * i))) } func (v remoteArray) Sub(i int64, len int64) eval.ArrayValue { return remoteArray{v.r.plus(proc.Word(int64(v.elemType.size) * i)), len, 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(t *eval.Thread, 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(t, i).Assign(t, oa.Field(t, i)) } } func (v remoteStruct) Get(t *eval.Thread) eval.StructValue { return v } func (v remoteStruct) Field(t *eval.Thread, i int) eval.Value { return v.field(i) } func (v remoteStruct) field(i int) eval.Value { f := &v.layout[i] return f.fieldType.mk(v.r.plus(proc.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 { return tryRVString(func(a aborter) string { e := v.aGet(a) if e == nil { return "" } return "&" + e.String() }) } func (v remotePtr) Assign(t *eval.Thread, o eval.Value) { v.Set(t, o.(eval.PtrValue).Get(t)) } func (v remotePtr) Get(t *eval.Thread) eval.Value { return v.aGet(t) } func (v remotePtr) aGet(a aborter) eval.Value { addr := proc.Word(v.r.Get(a, v.r.p.PtrSize())) if addr == 0 { return nil } return v.elemType.mk(remote{addr, v.r.p}) } func (v remotePtr) Set(t *eval.Thread, x eval.Value) { v.aSet(t, x) } func (v remotePtr) aSet(a aborter, x eval.Value) { if x == nil { v.r.Set(a, v.r.p.PtrSize(), 0) return } xr, ok := x.(remoteValue) if !ok || v.r.p != xr.addr().p { a.Abort(RemoteMismatchError("remote pointer must point within the same process")) } v.r.Set(a, 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 { return tryRVString(func(a aborter) string { b := v.aGet(a).Base if b == nil { return "" } return b.String() }) } func (v remoteSlice) Assign(t *eval.Thread, o eval.Value) { v.Set(t, o.(eval.SliceValue).Get(t)) } func (v remoteSlice) Get(t *eval.Thread) eval.Slice { return v.aGet(t) } func (v remoteSlice) aGet(a aborter) eval.Slice { rs := v.r.p.runtime.Slice.mk(v.r).(remoteStruct) base := proc.Word(rs.field(v.r.p.f.Slice.Array).(remoteUint).aGet(a)) nel := rs.field(v.r.p.f.Slice.Len).(remoteInt).aGet(a) cap := rs.field(v.r.p.f.Slice.Cap).(remoteInt).aGet(a) 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(t *eval.Thread, x eval.Slice) { v.aSet(t, x) } func (v remoteSlice) aSet(a aborter, 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).aSet(a, 0) } else { ar, ok := x.Base.(remoteArray) if !ok || v.r.p != ar.r.p { a.Abort(RemoteMismatchError("remote slice must point within the same process")) } rs.field(v.r.p.f.Slice.Array).(remoteUint).aSet(a, uint64(ar.r.base)) } rs.field(v.r.p.f.Slice.Len).(remoteInt).aSet(a, x.Len) rs.field(v.r.p.f.Slice.Cap).(remoteInt).aSet(a, x.Cap) }