sbf: hello world

This commit is contained in:
Richard Patel 2022-09-04 18:39:45 +02:00
parent c3c4f02ef1
commit 196e826d6b
9 changed files with 135 additions and 30 deletions

View File

@ -56,11 +56,11 @@ func (i *Interpreter) Run() (err error) {
// TODO step to next instruction
mainLoop:
for {
// Fetch
ins := i.getSlot(pc)
// Execute
pc++
switch ins.Op() {
case OpLdxb:
vma := uint64(int64(r[ins.Src()]) + int64(ins.Off()))
@ -373,15 +373,20 @@ func (i *Interpreter) Run() (err error) {
case OpCallx:
panic("callx not implemented")
case OpExit:
return nil
// TODO implement function returns
break mainLoop
default:
panic(fmt.Sprintf("unimplemented opcode %#02x", ins.Op()))
}
// Post execute
if err != nil {
// TODO return CPU exception error type here
return err
}
pc++
}
return nil
}
func (i *Interpreter) getSlot(pc int64) Slot {

View File

@ -13,6 +13,7 @@ func clampAddUint64(x uint64, y uint64) uint64 {
return z
}
/*
func clampSubUint64(x uint64, y uint64) uint64 {
z, borrow := bits.Sub64(x, y, 0)
if borrow != 0 {
@ -20,6 +21,7 @@ func clampSubUint64(x uint64, y uint64) uint64 {
}
return z
}
*/
type addrRange struct {
min, max uint64

View File

@ -49,8 +49,7 @@ type Loader struct {
entrypoint uint64 // program counter
// Symbols
funcs map[uint32]uint64
syscalls map[uint32]string
funcs map[uint32]uint64
}
// Bounds checks

View File

@ -6,7 +6,6 @@ import (
"fmt"
"github.com/certusone/radiance/pkg/sbf"
"github.com/spaolacci/murmur3"
)
// relocate applies ELF relocations (for syscalls and position-independent code).
@ -57,7 +56,7 @@ func (l *Loader) fixupRelativeCalls() error {
}
func (l *Loader) registerFunc(target uint64) (uint32, error) {
hash := PCHash(target)
hash := sbf.PCHash(target)
// TODO check for collision with syscalls
if _, ok := l.funcs[hash]; ok {
return 0, fmt.Errorf("symbol hash collision")
@ -155,7 +154,7 @@ func (l *Loader) applyReloc(reloc *elf.Rel64) error {
}
} else {
// Syscall
hash = SymbolHash(name)
hash = sbf.SymbolHash(name)
// TODO check whether syscall is known
}
@ -175,24 +174,6 @@ func (l *Loader) getEntrypoint() error {
return nil
}
const (
// EntrypointHash equals SymbolHash("entrypoint")
EntrypointHash = uint32(0x71e3cf81)
)
// SymbolHash returns the murmur3 32-bit hash of a symbol name.
func SymbolHash(s string) uint32 {
return murmur3.Sum32([]byte(s))
}
// PCHash returns the murmur3 32-bit hash of a program counter.
func PCHash(addr uint64) uint32 {
// TODO this is kinda pointless …
var key [8]byte
binary.LittleEndian.PutUint64(key[:], addr)
return murmur3.Sum32(key[:])
}
// Relocation types for eBPF.
type R_BPF int

View File

@ -41,12 +41,12 @@ func (s Slot) Op() uint8 {
// Dst returns the destination register field.
func (s Slot) Dst() uint8 {
return uint8(s>>12) & 0xF
return uint8(s>>8) & 0xF
}
// Src returns the source register field.
func (s Slot) Src() uint8 {
return uint8(s>>8) & 0xF
return uint8(s>>12) & 0xF
}
// Off returns the offset field.

43
pkg/sbf/syscalls.go Normal file
View File

@ -0,0 +1,43 @@
package sbf
import (
"encoding/binary"
"github.com/spaolacci/murmur3"
)
const (
// EntrypointHash equals SymbolHash("entrypoint")
EntrypointHash = uint32(0x71e3cf81)
)
// SymbolHash returns the murmur3 32-bit hash of a symbol name.
func SymbolHash(s string) uint32 {
return murmur3.Sum32([]byte(s))
}
// PCHash returns the murmur3 32-bit hash of a program counter.
//
// Used by VM for non-syscall functions
func PCHash(addr uint64) uint32 {
// TODO this is kinda pointless …
var key [8]byte
binary.LittleEndian.PutUint64(key[:], addr)
return murmur3.Sum32(key[:])
}
type SyscallRegistry map[uint32]Syscall
func NewSyscallRegistry() SyscallRegistry {
return make(SyscallRegistry)
}
func (s SyscallRegistry) Register(name string, syscall Syscall) (hash uint32, ok bool) {
hash = SymbolHash(name)
if _, exist := s[hash]; exist {
return 0, false // collision or duplicate
}
s[hash] = syscall
ok = true
return
}

View File

@ -27,7 +27,7 @@ type VMOpts struct {
// Machine parameters
StackSize int
HeapSize int
Syscalls map[uint32]Syscall
Syscalls SyscallRegistry
// Execution parameters
Context any // passed to syscalls
@ -65,3 +65,41 @@ func NewExcBadAccess(addr uint64, size uint32, write bool, reason string) ExcBad
func (e ExcBadAccess) Error() string {
return fmt.Sprintf("bad memory access at %#x (size=%d write=%v), reason: %s", e.Addr, e.Size, e.Write, e.Reason)
}
// Convenience Methods
type SyscallFunc0 func(vm VM, cuIn int64) (r0 uint64, cuOut int64, err error)
func (f SyscallFunc0) Invoke(vm VM, _, _, _, _, _ uint64, cuIn int64) (r0 uint64, cuOut int64, err error) {
return f(vm, cuIn)
}
type SyscallFunc1 func(vm VM, r1 uint64, cuIn int64) (r0 uint64, cuOut int64, err error)
func (f SyscallFunc1) Invoke(vm VM, r1, _, _, _, _ uint64, cuIn int64) (r0 uint64, cuOut int64, err error) {
return f(vm, r1, cuIn)
}
type SyscallFunc2 func(vm VM, r1, r2 uint64, cuIn int64) (r0 uint64, cuOut int64, err error)
func (f SyscallFunc2) Invoke(vm VM, r1, r2, _, _, _ uint64, cuIn int64) (r0 uint64, cuOut int64, err error) {
return f(vm, r1, r2, cuIn)
}
type SyscallFunc3 func(vm VM, r1, r2, r3 uint64, cuIn int64) (r0 uint64, cuOut int64, err error)
func (f SyscallFunc3) Invoke(vm VM, r1, r2, r3, _, _ uint64, cuIn int64) (r0 uint64, cuOut int64, err error) {
return f(vm, r1, r2, r3, cuIn)
}
type SyscallFunc4 func(vm VM, r1, r2, r3, r4 uint64, cuIn int64) (r0 uint64, cuOut int64, err error)
func (f SyscallFunc4) Invoke(vm VM, r1, r2, r3, r4, _ uint64, cuIn int64) (r0 uint64, cuOut int64, err error) {
return f(vm, r1, r2, r3, r4, cuIn)
}
type SyscallFunc5 func(vm VM, r1, r2, r3, r4, r5 uint64, cuIn int64) (r0 uint64, cuOut int64, err error)
func (f SyscallFunc5) Invoke(vm VM, r1, r2, r3, r4, r5 uint64, cuIn int64) (r0 uint64, cuOut int64, err error) {
return f(vm, r1, r2, r3, r4, r5, cuIn)
}

View File

@ -1,15 +1,18 @@
package loader
package sealevel
import (
_ "embed"
"testing"
"github.com/certusone/radiance/fixtures"
"github.com/certusone/radiance/pkg/sbf"
"github.com/certusone/radiance/pkg/sbf/loader"
"github.com/stretchr/testify/require"
)
func TestInterpreter_Noop(t *testing.T) {
loader, err := NewLoaderFromBytes(soNoop)
// TODO simplify API?
loader, err := loader.NewLoaderFromBytes(fixtures.SBF(t, "noop.so"))
require.NotNil(t, loader)
require.NoError(t, err)
@ -19,11 +22,16 @@ func TestInterpreter_Noop(t *testing.T) {
require.NoError(t, program.Verify())
syscalls := sbf.NewSyscallRegistry()
syscalls.Register("log", SyscallLog)
syscalls.Register("log_64", SyscallLog64)
interpreter := sbf.NewInterpreter(program, &sbf.VMOpts{
StackSize: 1024,
HeapSize: 1024, // TODO
Input: nil,
MaxCU: 10000,
Syscalls: syscalls,
})
require.NotNil(t, interpreter)

29
pkg/sealevel/log.go Normal file
View File

@ -0,0 +1,29 @@
package sealevel
import (
"fmt"
"strings"
"github.com/certusone/radiance/pkg/sbf"
)
// TODO These are naive stubs
func SyscallLogImpl(vm sbf.VM, r1, r2 uint64, cuIn int64) (r0 uint64, cuOut int64, err error) {
buf := make([]byte, r2)
if err = vm.Read(r1, buf); err != nil {
return
}
fmt.Println("Program Log:", strings.Trim(string(buf), " \t\x00"))
//panic("log syscall unimplemented")
return
}
var SyscallLog = sbf.SyscallFunc2(SyscallLogImpl)
func SyscallLog64Impl(vm sbf.VM, r1, r2, r3, r4, r5 uint64, cuIn int64) (r0 uint64, cuOut int64, err error) {
fmt.Printf("Program Log: r1=%#x r2=%#x r3=%#x r4=%#x r5=%#x\n", r1, r2, r3, r4, r5)
return
}
var SyscallLog64 = sbf.SyscallFunc5(SyscallLog64Impl)