/
packunpack.go
100 lines (91 loc) · 2.86 KB
/
packunpack.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package syscall
import (
"context"
"fmt"
"log/slog"
"reflect"
"runtime/debug"
"unsafe"
apishared "github.com/iansmith/parigot/api/shared"
"github.com/iansmith/parigot/g/syscall/v1"
"google.golang.org/protobuf/proto"
)
// ClientSide does the marshalling and unmarshalling needed to read the T given,
// write the U given, and return the KernelErrId properly. It does these
// manipulations so you can call a lower level function that is implemented by
// the host. The final bool is a meta indicator about if we detected a crash and
// the client side of the program should exit.
func ClientSide[T proto.Message, U proto.Message](ctx context.Context, t T, u U, fn func(int32, int32, int32, int32) int64) (outU U, outId int32, signal bool) {
var outErr int32
outProtoPtr := u
outErrPtr := &outErr
var nilU U
buf, err := proto.Marshal(t)
if err != nil {
return nilU, int32(syscall.KernelErr_MarshalFailed), false
}
sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
length := int32(len(buf))
req := int32(sh.Data)
val := reflect.ValueOf(u)
if val.Kind() != reflect.Ptr {
panic("client side of syscall passed a proto.Message that is not a pointer")
}
outBuf := make([]byte, apishared.GuestReceiveBufferSize)
sh = (*reflect.SliceHeader)(unsafe.Pointer(&outBuf))
out := int32(sh.Data)
errPtr := int32(uintptr(unsafe.Pointer(outErrPtr)))
defer func() {
if r := recover(); r != nil {
print("---------------\n")
print("trapped panic:", r, "\n")
debug.PrintStack()
print("---------------\n")
signal = true
}
}()
wrapped := fn(length, req, out, errPtr)
if int32(outErr) != 0 {
if outErr&0x7fffff00 == 0x7fffff00 {
return nilU, outErr & 0xff, true
}
return nilU, outErr, false
}
l, ptr := unwindLenAndPtr(uint64(wrapped))
if l == 0 {
return nilU, int32(syscall.KernelErr_NoError), false
}
if int32(ptr) != out { //sanity
print(fmt.Sprintf("WARNING! mismatched pointers in host call/return %x, %x\n", ptr, out))
}
// a := asPtr(u)
// log.Printf("xxxx checking asPtr %T,%p, %v", u, u, a)
// if unsafe.Pointer(a) == nil {
// log.Printf("xxx client side 2: %T is nil", u)
// return u, int32(syscall.KernelErr_NoError), false
// }
if l > 0 {
if err := proto.Unmarshal(outBuf[:l], u); err != nil {
slog.Error("found a nil, expected empty, result buffer", "size in bytes", l, "type", fmt.Sprintf("%T", u))
myId := syscall.KernelErr_UnmarshalFailed
outErr = int32(myId)
}
}
return outProtoPtr, int32(0), false
}
func asPtr[T proto.Message](t T) uintptr {
val := reflect.ValueOf(t)
if val.Kind() != reflect.Pointer {
panic("should never call the standard processing of a client side syscall with a value, always use a pointer")
}
return val.Pointer()
}
func unwindLenAndPtr(ret uint64) (uint32, uint32) {
len64 := ret
len64 >>= 32
len32 := uint32(len64)
ptr64 := ret
ptr64 &= 0xffffffff
ptr32 := uint32(ptr64)
return len32, ptr32
}