| // 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"; |
| "os"; |
| "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 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 ptrace.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(ptrace.Word(x), buf); |
| _, err := v.p.Poke(v.base, buf); |
| if err != nil { |
| a.Abort(err); |
| } |
| } |
| |
| func (v remote) plus(x ptrace.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("<error: %v>", 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 ", v.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.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 := ptrace.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(ptrace.Word(int64(v.elemType.size) * i))); |
| } |
| |
| func (v remoteArray) Sub(i int64, len int64) eval.ArrayValue { |
| return remoteArray{v.r.plus(ptrace.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(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 { |
| return tryRVString(func(a aborter) string { |
| e := v.aGet(a); |
| if e == nil { |
| return "<nil>"; |
| } |
| 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 := ptrace.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 "<nil>"; |
| } |
| 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 := ptrace.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); |
| } |