Extended Berkeley Packet Filter (eBPF) assembler and virtual machine
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

248 lines
5.4 KiB

package ebpf
import "fmt"
// Jump skips the following Skip instructions in the program.
type Jump struct {
Offset uint16
}
// Assemble implements the Instruction Assemble method.
func (a Jump) Assemble() (RawInstruction, error) {
return RawInstruction{
Op: opClassJump | jumpOp,
Offset: int16(a.Offset),
}, nil
}
// String returns the instruction in assembler notation.
func (a Jump) String() string {
return fmt.Sprintf("ja +%d", a.Offset)
}
// JumpTest is a comparison operator used in conditional jumps.
type JumpTest uint8
// Supported operators for conditional jumps.
const (
JumpEqual JumpTest = iota // K == A
JumpGreater // K > A
JumpGreaterOrEqual // K >= A
JumpSet // K & A
JumpNotEqual // K != A
JumpSignedGreater // K > A
JumpSignedGreaterOrEqual // K >= A
JumpLess // K < A
JumpLessOrEqual // K <= A
JumpSignedLess // K < A
JumpSignedLessOrEqual // K <= A
)
func (cond JumpTest) Match(dst, src uint64) bool {
switch cond {
case JumpEqual:
return dst == src
case JumpGreater:
return dst > src
case JumpGreaterOrEqual:
return dst >= src
case JumpSet:
return dst&src != 0
case JumpNotEqual:
return dst != src
case JumpSignedGreater:
return int64(dst) > int64(src)
case JumpSignedGreaterOrEqual:
return int64(dst) >= int64(src)
case JumpLess:
return dst < src
case JumpLessOrEqual:
return dst <= src
case JumpSignedLess:
return int64(dst) < int64(src)
case JumpSignedLessOrEqual:
return int64(dst) <= int64(src)
default:
return false
}
}
func (cond JumpTest) String() string {
switch cond {
case JumpEqual:
return "jeq"
case JumpGreater:
return "jgt"
case JumpGreaterOrEqual:
return "jge"
case JumpSet:
return "jset"
case JumpNotEqual:
return "jne"
case JumpSignedGreater:
return "jsgt"
case JumpSignedGreaterOrEqual:
return "jsge"
case JumpLess:
return "jlt"
case JumpLessOrEqual:
return "jle"
case JumpSignedLess:
return "jslt"
case JumpSignedLessOrEqual:
return "jsle"
default:
return ""
}
}
func (cond JumpTest) CFormat() string {
switch cond {
case JumpEqual:
return "%s == %s"
case JumpGreater:
return "%s > %s"
case JumpGreaterOrEqual:
return "%s >= %s"
case JumpSet:
return "%s & %s"
case JumpNotEqual:
return "%s != %s"
case JumpSignedGreater:
return "(int64_t)(%s) > (int64_t)(%s)"
case JumpSignedGreaterOrEqual:
return "(int64_t)(%s) >= (int64_t)(%s)"
case JumpLess:
return "%s < %s"
case JumpLessOrEqual:
return "%s <= %s"
case JumpSignedLess:
return "(int64_t)(%s) < (int64_t)(%s)"
case JumpSignedLessOrEqual:
return "(int64_t)(%s) <= (int64_t)(%s)"
default:
return ""
}
}
func jumpOpToTest(op Opcode) JumpTest {
switch op {
case jumpOpEqual:
return JumpEqual
case jumpOpGreater:
return JumpGreater
case jumpOpGreaterOrEqual:
return JumpGreaterOrEqual
case jumpOpSet:
return JumpSet
case jumpOpNotEqual:
return JumpNotEqual
case jumpOpSignedGreater:
return JumpSignedGreater
case jumpOpSignedGreaterOrEqual:
return JumpSignedGreaterOrEqual
case jumpOpLess:
return JumpLess
case jumpOpLessOrEqual:
return JumpLessOrEqual
case jumpOpSignedLess:
return JumpSignedLess
case jumpOpSignedLessOrEqual:
return JumpLessOrEqual
default:
return 0xff
}
}
// JumpIf performs a conditional jump.
type JumpIf struct {
Cond JumpTest
Dst Register
Value uint32
Offset uint16
}
func (a JumpIf) Assemble() (RawInstruction, error) {
return jumpToRaw(a.Cond, true, a.Dst, a.Value, a.Offset)
}
func (a JumpIf) String() string {
return fmt.Sprintf("%s %s, %d, +%d", a.Cond, a.Dst, a.Value, a.Offset)
}
type JumpIfX struct {
Cond JumpTest
Dst, Src Register
Offset uint16
}
func (a JumpIfX) Assemble() (RawInstruction, error) {
return jumpToRaw(a.Cond, false, a.Dst, uint32(a.Src), a.Offset)
}
func (a JumpIfX) String() string {
return fmt.Sprintf("%s %s, %s, +%d", a.Cond, a.Dst, a.Src, a.Offset)
}
func jumpToRaw(test JumpTest, immediate bool, dst Register, value uint32, offset uint16) (RawInstruction, error) {
var (
cond Opcode
)
switch test {
case JumpEqual:
cond = jumpOpEqual
case JumpGreater:
cond = jumpOpGreater
case JumpGreaterOrEqual:
cond = jumpOpGreaterOrEqual
case JumpNotEqual:
cond = jumpOpNotEqual
case JumpSignedGreater:
cond = jumpOpSignedGreater
case JumpSignedGreaterOrEqual:
cond = jumpOpSignedGreaterOrEqual
case JumpLess:
cond = jumpOpLess
case JumpLessOrEqual:
cond = jumpOpLessOrEqual
case JumpSignedLess:
cond = jumpOpSignedLess
case JumpSignedLessOrEqual:
cond = jumpOpSignedLessOrEqual
default:
return RawInstruction{}, fmt.Errorf("ebpf: unknown JumpTest %v", test)
}
if immediate {
return RawInstruction{
Op: opClassJump | cond | jumpSourceImmediate,
Dst: dst,
Offset: int16(offset),
Immediate: uint64(value),
}, nil
}
return RawInstruction{
Op: opClassJump | cond | jumpSourceX,
Dst: dst,
Src: Register(value & 0x0f),
Offset: int16(offset),
}, nil
}
// Exit the eBPF program and return the value of R0.
type Exit struct{}
func (a Exit) Assemble() (RawInstruction, error) {
return RawInstruction{
Op: opClassJump | jumpOpExit,
}, nil
}
func (Exit) String() string {
return "exit"
}
var (
_ Instruction = (*Jump)(nil)
_ Instruction = (*Exit)(nil)
)