sbf: hello world
This commit is contained in:
parent
c3c4f02ef1
commit
196e826d6b
|
@ -56,11 +56,11 @@ func (i *Interpreter) Run() (err error) {
|
||||||
|
|
||||||
// TODO step to next instruction
|
// TODO step to next instruction
|
||||||
|
|
||||||
|
mainLoop:
|
||||||
for {
|
for {
|
||||||
// Fetch
|
// Fetch
|
||||||
ins := i.getSlot(pc)
|
ins := i.getSlot(pc)
|
||||||
// Execute
|
// Execute
|
||||||
pc++
|
|
||||||
switch ins.Op() {
|
switch ins.Op() {
|
||||||
case OpLdxb:
|
case OpLdxb:
|
||||||
vma := uint64(int64(r[ins.Src()]) + int64(ins.Off()))
|
vma := uint64(int64(r[ins.Src()]) + int64(ins.Off()))
|
||||||
|
@ -373,15 +373,20 @@ func (i *Interpreter) Run() (err error) {
|
||||||
case OpCallx:
|
case OpCallx:
|
||||||
panic("callx not implemented")
|
panic("callx not implemented")
|
||||||
case OpExit:
|
case OpExit:
|
||||||
return nil
|
// TODO implement function returns
|
||||||
|
break mainLoop
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unimplemented opcode %#02x", ins.Op()))
|
panic(fmt.Sprintf("unimplemented opcode %#02x", ins.Op()))
|
||||||
}
|
}
|
||||||
|
// Post execute
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO return CPU exception error type here
|
// TODO return CPU exception error type here
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
pc++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Interpreter) getSlot(pc int64) Slot {
|
func (i *Interpreter) getSlot(pc int64) Slot {
|
||||||
|
|
|
@ -13,6 +13,7 @@ func clampAddUint64(x uint64, y uint64) uint64 {
|
||||||
return z
|
return z
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
func clampSubUint64(x uint64, y uint64) uint64 {
|
func clampSubUint64(x uint64, y uint64) uint64 {
|
||||||
z, borrow := bits.Sub64(x, y, 0)
|
z, borrow := bits.Sub64(x, y, 0)
|
||||||
if borrow != 0 {
|
if borrow != 0 {
|
||||||
|
@ -20,6 +21,7 @@ func clampSubUint64(x uint64, y uint64) uint64 {
|
||||||
}
|
}
|
||||||
return z
|
return z
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
type addrRange struct {
|
type addrRange struct {
|
||||||
min, max uint64
|
min, max uint64
|
||||||
|
|
|
@ -49,8 +49,7 @@ type Loader struct {
|
||||||
entrypoint uint64 // program counter
|
entrypoint uint64 // program counter
|
||||||
|
|
||||||
// Symbols
|
// Symbols
|
||||||
funcs map[uint32]uint64
|
funcs map[uint32]uint64
|
||||||
syscalls map[uint32]string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bounds checks
|
// Bounds checks
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/certusone/radiance/pkg/sbf"
|
"github.com/certusone/radiance/pkg/sbf"
|
||||||
"github.com/spaolacci/murmur3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// relocate applies ELF relocations (for syscalls and position-independent code).
|
// 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) {
|
func (l *Loader) registerFunc(target uint64) (uint32, error) {
|
||||||
hash := PCHash(target)
|
hash := sbf.PCHash(target)
|
||||||
// TODO check for collision with syscalls
|
// TODO check for collision with syscalls
|
||||||
if _, ok := l.funcs[hash]; ok {
|
if _, ok := l.funcs[hash]; ok {
|
||||||
return 0, fmt.Errorf("symbol hash collision")
|
return 0, fmt.Errorf("symbol hash collision")
|
||||||
|
@ -155,7 +154,7 @@ func (l *Loader) applyReloc(reloc *elf.Rel64) error {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Syscall
|
// Syscall
|
||||||
hash = SymbolHash(name)
|
hash = sbf.SymbolHash(name)
|
||||||
// TODO check whether syscall is known
|
// TODO check whether syscall is known
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,24 +174,6 @@ func (l *Loader) getEntrypoint() error {
|
||||||
return nil
|
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.
|
// Relocation types for eBPF.
|
||||||
type R_BPF int
|
type R_BPF int
|
||||||
|
|
||||||
|
|
|
@ -41,12 +41,12 @@ func (s Slot) Op() uint8 {
|
||||||
|
|
||||||
// Dst returns the destination register field.
|
// Dst returns the destination register field.
|
||||||
func (s Slot) Dst() uint8 {
|
func (s Slot) Dst() uint8 {
|
||||||
return uint8(s>>12) & 0xF
|
return uint8(s>>8) & 0xF
|
||||||
}
|
}
|
||||||
|
|
||||||
// Src returns the source register field.
|
// Src returns the source register field.
|
||||||
func (s Slot) Src() uint8 {
|
func (s Slot) Src() uint8 {
|
||||||
return uint8(s>>8) & 0xF
|
return uint8(s>>12) & 0xF
|
||||||
}
|
}
|
||||||
|
|
||||||
// Off returns the offset field.
|
// Off returns the offset field.
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -27,7 +27,7 @@ type VMOpts struct {
|
||||||
// Machine parameters
|
// Machine parameters
|
||||||
StackSize int
|
StackSize int
|
||||||
HeapSize int
|
HeapSize int
|
||||||
Syscalls map[uint32]Syscall
|
Syscalls SyscallRegistry
|
||||||
|
|
||||||
// Execution parameters
|
// Execution parameters
|
||||||
Context any // passed to syscalls
|
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 {
|
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)
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
package loader
|
package sealevel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/certusone/radiance/fixtures"
|
||||||
"github.com/certusone/radiance/pkg/sbf"
|
"github.com/certusone/radiance/pkg/sbf"
|
||||||
|
"github.com/certusone/radiance/pkg/sbf/loader"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInterpreter_Noop(t *testing.T) {
|
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.NotNil(t, loader)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -19,11 +22,16 @@ func TestInterpreter_Noop(t *testing.T) {
|
||||||
|
|
||||||
require.NoError(t, program.Verify())
|
require.NoError(t, program.Verify())
|
||||||
|
|
||||||
|
syscalls := sbf.NewSyscallRegistry()
|
||||||
|
syscalls.Register("log", SyscallLog)
|
||||||
|
syscalls.Register("log_64", SyscallLog64)
|
||||||
|
|
||||||
interpreter := sbf.NewInterpreter(program, &sbf.VMOpts{
|
interpreter := sbf.NewInterpreter(program, &sbf.VMOpts{
|
||||||
StackSize: 1024,
|
StackSize: 1024,
|
||||||
HeapSize: 1024, // TODO
|
HeapSize: 1024, // TODO
|
||||||
Input: nil,
|
Input: nil,
|
||||||
MaxCU: 10000,
|
MaxCU: 10000,
|
||||||
|
Syscalls: syscalls,
|
||||||
})
|
})
|
||||||
require.NotNil(t, interpreter)
|
require.NotNil(t, interpreter)
|
||||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue