blob: 3d630f936644c678c6a030b250e21d41eb294e44 [file] [log] [blame]
// 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("<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")
}
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 "<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 := 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 "<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 := 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)
}