package ebpf
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
type Instruction interface {
|
|
// Assemble assembles the Instruction into a RawInstruction.
|
|
Assemble() (RawInstruction, error)
|
|
|
|
// String returns the assembly.
|
|
String() string
|
|
}
|
|
|
|
// A RawInstruction is a raw BPF virtual machine instruction.
|
|
type RawInstruction struct {
|
|
// Operation to execute.
|
|
Op Opcode
|
|
Dst, Src Register
|
|
Offset int16
|
|
Immediate uint64
|
|
}
|
|
|
|
// Assemble implements the Instruction Assemble method.
|
|
func (ri RawInstruction) Assemble() (RawInstruction, error) { return ri, nil }
|
|
|
|
// Disassemble parses ri into an Instruction and returns it. If ri is
|
|
// not recognized by this package, ri itself is returned.
|
|
func (ri RawInstruction) Disassemble() Instruction {
|
|
// log.Printf("op class %#02x", ri.Op&opMaskClass)
|
|
switch class := ri.Op & opMaskClass; class {
|
|
case opClassLoad, opClassLoadX:
|
|
var size uint8
|
|
switch ri.Op & opMaskSize {
|
|
case sizeWord:
|
|
size = 4
|
|
case sizeDoubleWord:
|
|
size = 8
|
|
case sizeHalfWord:
|
|
size = 2
|
|
case sizeByte:
|
|
size = 1
|
|
default:
|
|
return ri
|
|
}
|
|
switch ri.Op & opMaskMode {
|
|
case modeImmediate: // 0x00
|
|
if class == opClassLoad {
|
|
if size != 4 && size != 8 {
|
|
return ri
|
|
}
|
|
return LoadConstant{Dst: ri.Dst, Size: size, Value: ri.Immediate}
|
|
}
|
|
case modeAbsolute: // 0x20
|
|
if class == opClassLoad {
|
|
return LoadAbsolute{
|
|
Dst: ri.Dst,
|
|
Src: ri.Src,
|
|
Value: ri.Immediate,
|
|
Size: size,
|
|
}
|
|
}
|
|
case modeIndirect: // 0x40
|
|
if class == opClassLoad {
|
|
return LoadIndirect{
|
|
Dst: ri.Dst,
|
|
Src: ri.Src,
|
|
Value: ri.Immediate,
|
|
Size: size,
|
|
}
|
|
}
|
|
case modeMemory: // 0x60
|
|
if class == opClassLoadX {
|
|
return LoadRegister{
|
|
Dst: ri.Dst,
|
|
Src: ri.Src,
|
|
Offset: ri.Offset,
|
|
Size: size,
|
|
}
|
|
}
|
|
case modeExclusiveAdd: // 0xc0
|
|
}
|
|
|
|
case opClassStore, opClassStoreX:
|
|
var size uint8
|
|
switch ri.Op & opMaskSize {
|
|
case sizeWord:
|
|
size = 4
|
|
case sizeDoubleWord:
|
|
size = 8
|
|
case sizeHalfWord:
|
|
size = 2
|
|
case sizeByte:
|
|
size = 1
|
|
default:
|
|
return ri
|
|
}
|
|
switch ri.Op & opMaskMode {
|
|
case modeMemory: // 0x60
|
|
if class == opClassStore {
|
|
return StoreImmediate{
|
|
Dst: ri.Dst,
|
|
Offset: ri.Offset,
|
|
Value: uint32(ri.Immediate),
|
|
Size: size,
|
|
}
|
|
}
|
|
return StoreRegister{
|
|
Dst: ri.Dst,
|
|
Src: ri.Src,
|
|
Offset: ri.Offset,
|
|
Size: size,
|
|
}
|
|
}
|
|
|
|
case opClassALU:
|
|
switch op := ALUOp(ri.Op & opMaskOperand); op {
|
|
case ALUOpAdd, ALUOpSub, ALUOpMul, ALUOpDiv, ALUOpOr, ALUOpAnd, ALUOpShiftLeft, ALUOpShiftRight, ALUOpMod, ALUOpXor, ALUOpMove, ALUOpArithmicShiftRight:
|
|
switch ri.Op & opMaskSource {
|
|
case aluSourceImmediate:
|
|
return ALUOpConstant{
|
|
Op: op,
|
|
Dst: ri.Dst,
|
|
Value: uint32(ri.Immediate),
|
|
}
|
|
case aluSourceX:
|
|
return ALUOpRegister{
|
|
Op: op,
|
|
Dst: ri.Dst,
|
|
Src: ri.Src,
|
|
}
|
|
}
|
|
case aluOpNegate:
|
|
return Negate{ri.Dst}
|
|
case aluOpByteSwap:
|
|
var byteOrder binary.ByteOrder = binary.LittleEndian
|
|
if ri.Op&opMaskSource == aluSourceX {
|
|
byteOrder = binary.BigEndian
|
|
}
|
|
return ByteSwap{
|
|
Dst: ri.Dst,
|
|
ByteOrder: byteOrder,
|
|
Size: uint8(ri.Immediate),
|
|
}
|
|
}
|
|
|
|
case opClassALU64:
|
|
switch op := ALUOp(ri.Op & opMaskOperand); op {
|
|
case ALUOpAdd, ALUOpSub, ALUOpMul, ALUOpDiv, ALUOpOr, ALUOpAnd, ALUOpShiftLeft, ALUOpShiftRight, ALUOpMod, ALUOpXor, ALUOpMove, ALUOpArithmicShiftRight:
|
|
switch ri.Op & opMaskSource {
|
|
case aluSourceImmediate:
|
|
return ALU64OpConstant{
|
|
Op: op,
|
|
Dst: ri.Dst,
|
|
Value: uint32(ri.Immediate),
|
|
}
|
|
case aluSourceX:
|
|
return ALU64OpRegister{
|
|
Op: op,
|
|
Dst: ri.Dst,
|
|
Src: ri.Src,
|
|
}
|
|
}
|
|
case aluOpNegate:
|
|
return Negate64{ri.Dst}
|
|
}
|
|
|
|
case opClassJump:
|
|
switch op := ri.Op & 0xf0; op {
|
|
case jumpOp:
|
|
return Jump{uint16(ri.Offset)}
|
|
case jumpOpEqual, jumpOpGreater, jumpOpGreaterOrEqual, jumpOpSet, jumpOpNotEqual,
|
|
jumpOpSignedGreater, jumpOpSignedGreaterOrEqual, jumpOpLess, jumpOpLessOrEqual,
|
|
jumpOpSignedLess, jumpOpSignedLessOrEqual:
|
|
cond := jumpOpToTest(op)
|
|
if ri.Op&0x08 == jumpSourceImmediate {
|
|
return JumpIf{
|
|
Cond: cond,
|
|
Dst: ri.Dst,
|
|
Value: uint32(ri.Immediate),
|
|
Offset: uint16(ri.Offset),
|
|
}
|
|
}
|
|
return JumpIfX{
|
|
Cond: cond,
|
|
Dst: ri.Dst,
|
|
Src: ri.Src,
|
|
Offset: uint16(ri.Offset),
|
|
}
|
|
case jumpOpExit:
|
|
return Exit{}
|
|
case jumpOpCall:
|
|
return Call(ri.Immediate)
|
|
}
|
|
}
|
|
return ri
|
|
}
|
|
|
|
func (ri RawInstruction) String() string {
|
|
return fmt.Sprintf("# invalid opcode = %#02x, class = %#02x", ri.Op, ri.Op&opMaskClass)
|
|
}
|
|
|
|
// Disassemble a slice of bytes containing an eBPF program.
|
|
func Disassemble(data []byte) (Program, error) {
|
|
if l := len(data); l%8 != 0 {
|
|
return nil, fmt.Errorf("ebpf: raw data size %d does not align with 64-bit boundary", l)
|
|
}
|
|
return DisassembleReader(bytes.NewBuffer(data))
|
|
}
|
|
|
|
// DisassembleReader disassembles an eBPF program from reader using the default byte order.
|
|
func DisassembleReader(r io.Reader) (Program, error) {
|
|
return DisassembleReaderOrder(r, binary.LittleEndian)
|
|
}
|
|
|
|
type marshaledInstruction struct {
|
|
Op Opcode
|
|
DstSrc uint8
|
|
Offset int16
|
|
Immediate uint32
|
|
}
|
|
|
|
// DisassembleReaderOrder is like DisassembleReader with a custom byte order.
|
|
func DisassembleReaderOrder(r io.Reader, byteOrder binary.ByteOrder) (Program, error) {
|
|
var (
|
|
out Program
|
|
)
|
|
for {
|
|
var load marshaledInstruction
|
|
if err := binary.Read(r, byteOrder, &load); err == io.EOF {
|
|
break
|
|
} else if err != nil {
|
|
return nil, err
|
|
}
|
|
ri := RawInstruction{
|
|
Op: load.Op,
|
|
Dst: Register(load.DstSrc & 0x0f),
|
|
Src: Register(load.DstSrc >> 4),
|
|
Offset: load.Offset,
|
|
Immediate: uint64(load.Immediate),
|
|
}
|
|
if ri.Op == 0x18 {
|
|
// LDDW takes two opcodes
|
|
var next marshaledInstruction
|
|
if err := binary.Read(r, byteOrder, &next); err != nil {
|
|
return nil, err
|
|
}
|
|
ri.Immediate |= uint64(next.Immediate) << 32
|
|
}
|
|
out = append(out, ri.Disassemble())
|
|
}
|
|
return out, nil
|
|
}
|