2023-01-17 23:59:04 +03:00
|
|
|
package asm
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
//go:generate stringer -output opcode_string.go -type=Class
|
|
|
|
|
|
|
|
// Class of operations
|
|
|
|
//
|
|
|
|
// msb lsb
|
|
|
|
// +---+--+---+
|
|
|
|
// | ?? |CLS|
|
|
|
|
// +---+--+---+
|
|
|
|
type Class uint8
|
|
|
|
|
|
|
|
const classMask OpCode = 0x07
|
|
|
|
|
|
|
|
const (
|
2023-04-03 12:16:17 +03:00
|
|
|
// LdClass loads immediate values into registers.
|
|
|
|
// Also used for non-standard load operations from cBPF.
|
2023-01-17 23:59:04 +03:00
|
|
|
LdClass Class = 0x00
|
2023-04-03 12:16:17 +03:00
|
|
|
// LdXClass loads memory into registers.
|
2023-01-17 23:59:04 +03:00
|
|
|
LdXClass Class = 0x01
|
2023-04-03 12:16:17 +03:00
|
|
|
// StClass stores immediate values to memory.
|
2023-01-17 23:59:04 +03:00
|
|
|
StClass Class = 0x02
|
2023-04-03 12:16:17 +03:00
|
|
|
// StXClass stores registers to memory.
|
2023-01-17 23:59:04 +03:00
|
|
|
StXClass Class = 0x03
|
2023-04-03 12:16:17 +03:00
|
|
|
// ALUClass describes arithmetic operators.
|
2023-01-17 23:59:04 +03:00
|
|
|
ALUClass Class = 0x04
|
2023-04-03 12:16:17 +03:00
|
|
|
// JumpClass describes jump operators.
|
2023-01-17 23:59:04 +03:00
|
|
|
JumpClass Class = 0x05
|
2023-04-03 12:16:17 +03:00
|
|
|
// Jump32Class describes jump operators with 32-bit comparisons.
|
|
|
|
// Requires kernel 5.1.
|
|
|
|
Jump32Class Class = 0x06
|
|
|
|
// ALU64Class describes arithmetic operators in 64-bit mode.
|
2023-01-17 23:59:04 +03:00
|
|
|
ALU64Class Class = 0x07
|
|
|
|
)
|
|
|
|
|
2023-04-03 12:16:17 +03:00
|
|
|
// IsLoad checks if this is either LdClass or LdXClass.
|
|
|
|
func (cls Class) IsLoad() bool {
|
|
|
|
return cls == LdClass || cls == LdXClass
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsStore checks if this is either StClass or StXClass.
|
|
|
|
func (cls Class) IsStore() bool {
|
|
|
|
return cls == StClass || cls == StXClass
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cls Class) isLoadOrStore() bool {
|
|
|
|
return cls.IsLoad() || cls.IsStore()
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsALU checks if this is either ALUClass or ALU64Class.
|
|
|
|
func (cls Class) IsALU() bool {
|
|
|
|
return cls == ALUClass || cls == ALU64Class
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsJump checks if this is either JumpClass or Jump32Class.
|
|
|
|
func (cls Class) IsJump() bool {
|
|
|
|
return cls == JumpClass || cls == Jump32Class
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cls Class) isJumpOrALU() bool {
|
|
|
|
return cls.IsJump() || cls.IsALU()
|
2023-01-17 23:59:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// OpCode is a packed eBPF opcode.
|
|
|
|
//
|
|
|
|
// Its encoding is defined by a Class value:
|
|
|
|
//
|
|
|
|
// msb lsb
|
|
|
|
// +----+-+---+
|
|
|
|
// | ???? |CLS|
|
|
|
|
// +----+-+---+
|
|
|
|
type OpCode uint8
|
|
|
|
|
|
|
|
// InvalidOpCode is returned by setters on OpCode
|
|
|
|
const InvalidOpCode OpCode = 0xff
|
|
|
|
|
|
|
|
// rawInstructions returns the number of BPF instructions required
|
|
|
|
// to encode this opcode.
|
|
|
|
func (op OpCode) rawInstructions() int {
|
2023-04-03 12:16:17 +03:00
|
|
|
if op.IsDWordLoad() {
|
2023-01-17 23:59:04 +03:00
|
|
|
return 2
|
|
|
|
}
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
2023-04-03 12:16:17 +03:00
|
|
|
func (op OpCode) IsDWordLoad() bool {
|
2023-01-17 23:59:04 +03:00
|
|
|
return op == LoadImmOp(DWord)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Class returns the class of operation.
|
|
|
|
func (op OpCode) Class() Class {
|
|
|
|
return Class(op & classMask)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mode returns the mode for load and store operations.
|
|
|
|
func (op OpCode) Mode() Mode {
|
2023-04-03 12:16:17 +03:00
|
|
|
if !op.Class().isLoadOrStore() {
|
2023-01-17 23:59:04 +03:00
|
|
|
return InvalidMode
|
|
|
|
}
|
|
|
|
return Mode(op & modeMask)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Size returns the size for load and store operations.
|
|
|
|
func (op OpCode) Size() Size {
|
2023-04-03 12:16:17 +03:00
|
|
|
if !op.Class().isLoadOrStore() {
|
2023-01-17 23:59:04 +03:00
|
|
|
return InvalidSize
|
|
|
|
}
|
|
|
|
return Size(op & sizeMask)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Source returns the source for branch and ALU operations.
|
|
|
|
func (op OpCode) Source() Source {
|
2023-04-03 12:16:17 +03:00
|
|
|
if !op.Class().isJumpOrALU() || op.ALUOp() == Swap {
|
2023-01-17 23:59:04 +03:00
|
|
|
return InvalidSource
|
|
|
|
}
|
|
|
|
return Source(op & sourceMask)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ALUOp returns the ALUOp.
|
|
|
|
func (op OpCode) ALUOp() ALUOp {
|
2023-04-03 12:16:17 +03:00
|
|
|
if !op.Class().IsALU() {
|
2023-01-17 23:59:04 +03:00
|
|
|
return InvalidALUOp
|
|
|
|
}
|
|
|
|
return ALUOp(op & aluMask)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Endianness returns the Endianness for a byte swap instruction.
|
|
|
|
func (op OpCode) Endianness() Endianness {
|
|
|
|
if op.ALUOp() != Swap {
|
|
|
|
return InvalidEndian
|
|
|
|
}
|
|
|
|
return Endianness(op & endianMask)
|
|
|
|
}
|
|
|
|
|
|
|
|
// JumpOp returns the JumpOp.
|
2023-04-03 12:16:17 +03:00
|
|
|
// Returns InvalidJumpOp if it doesn't encode a jump.
|
2023-01-17 23:59:04 +03:00
|
|
|
func (op OpCode) JumpOp() JumpOp {
|
2023-04-03 12:16:17 +03:00
|
|
|
if !op.Class().IsJump() {
|
2023-01-17 23:59:04 +03:00
|
|
|
return InvalidJumpOp
|
|
|
|
}
|
2023-04-03 12:16:17 +03:00
|
|
|
|
|
|
|
jumpOp := JumpOp(op & jumpMask)
|
|
|
|
|
|
|
|
// Some JumpOps are only supported by JumpClass, not Jump32Class.
|
|
|
|
if op.Class() == Jump32Class && (jumpOp == Exit || jumpOp == Call || jumpOp == Ja) {
|
|
|
|
return InvalidJumpOp
|
|
|
|
}
|
|
|
|
|
|
|
|
return jumpOp
|
2023-01-17 23:59:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetMode sets the mode on load and store operations.
|
|
|
|
//
|
|
|
|
// Returns InvalidOpCode if op is of the wrong class.
|
|
|
|
func (op OpCode) SetMode(mode Mode) OpCode {
|
2023-04-03 12:16:17 +03:00
|
|
|
if !op.Class().isLoadOrStore() || !valid(OpCode(mode), modeMask) {
|
2023-01-17 23:59:04 +03:00
|
|
|
return InvalidOpCode
|
|
|
|
}
|
|
|
|
return (op & ^modeMask) | OpCode(mode)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetSize sets the size on load and store operations.
|
|
|
|
//
|
|
|
|
// Returns InvalidOpCode if op is of the wrong class.
|
|
|
|
func (op OpCode) SetSize(size Size) OpCode {
|
2023-04-03 12:16:17 +03:00
|
|
|
if !op.Class().isLoadOrStore() || !valid(OpCode(size), sizeMask) {
|
2023-01-17 23:59:04 +03:00
|
|
|
return InvalidOpCode
|
|
|
|
}
|
|
|
|
return (op & ^sizeMask) | OpCode(size)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetSource sets the source on jump and ALU operations.
|
|
|
|
//
|
|
|
|
// Returns InvalidOpCode if op is of the wrong class.
|
|
|
|
func (op OpCode) SetSource(source Source) OpCode {
|
2023-04-03 12:16:17 +03:00
|
|
|
if !op.Class().isJumpOrALU() || !valid(OpCode(source), sourceMask) {
|
2023-01-17 23:59:04 +03:00
|
|
|
return InvalidOpCode
|
|
|
|
}
|
|
|
|
return (op & ^sourceMask) | OpCode(source)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetALUOp sets the ALUOp on ALU operations.
|
|
|
|
//
|
|
|
|
// Returns InvalidOpCode if op is of the wrong class.
|
|
|
|
func (op OpCode) SetALUOp(alu ALUOp) OpCode {
|
2023-04-03 12:16:17 +03:00
|
|
|
if !op.Class().IsALU() || !valid(OpCode(alu), aluMask) {
|
2023-01-17 23:59:04 +03:00
|
|
|
return InvalidOpCode
|
|
|
|
}
|
|
|
|
return (op & ^aluMask) | OpCode(alu)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetJumpOp sets the JumpOp on jump operations.
|
|
|
|
//
|
|
|
|
// Returns InvalidOpCode if op is of the wrong class.
|
|
|
|
func (op OpCode) SetJumpOp(jump JumpOp) OpCode {
|
2023-04-03 12:16:17 +03:00
|
|
|
if !op.Class().IsJump() || !valid(OpCode(jump), jumpMask) {
|
|
|
|
return InvalidOpCode
|
|
|
|
}
|
|
|
|
|
|
|
|
newOp := (op & ^jumpMask) | OpCode(jump)
|
|
|
|
|
|
|
|
// Check newOp is legal.
|
|
|
|
if newOp.JumpOp() == InvalidJumpOp {
|
2023-01-17 23:59:04 +03:00
|
|
|
return InvalidOpCode
|
|
|
|
}
|
2023-04-03 12:16:17 +03:00
|
|
|
|
|
|
|
return newOp
|
2023-01-17 23:59:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (op OpCode) String() string {
|
|
|
|
var f strings.Builder
|
|
|
|
|
2023-04-03 12:16:17 +03:00
|
|
|
switch class := op.Class(); {
|
|
|
|
case class.isLoadOrStore():
|
2023-01-17 23:59:04 +03:00
|
|
|
f.WriteString(strings.TrimSuffix(class.String(), "Class"))
|
|
|
|
|
|
|
|
mode := op.Mode()
|
|
|
|
f.WriteString(strings.TrimSuffix(mode.String(), "Mode"))
|
|
|
|
|
|
|
|
switch op.Size() {
|
|
|
|
case DWord:
|
|
|
|
f.WriteString("DW")
|
|
|
|
case Word:
|
|
|
|
f.WriteString("W")
|
|
|
|
case Half:
|
|
|
|
f.WriteString("H")
|
|
|
|
case Byte:
|
|
|
|
f.WriteString("B")
|
|
|
|
}
|
|
|
|
|
2023-04-03 12:16:17 +03:00
|
|
|
case class.IsALU():
|
2023-01-17 23:59:04 +03:00
|
|
|
f.WriteString(op.ALUOp().String())
|
|
|
|
|
|
|
|
if op.ALUOp() == Swap {
|
|
|
|
// Width for Endian is controlled by Constant
|
|
|
|
f.WriteString(op.Endianness().String())
|
|
|
|
} else {
|
|
|
|
if class == ALUClass {
|
|
|
|
f.WriteString("32")
|
|
|
|
}
|
|
|
|
|
|
|
|
f.WriteString(strings.TrimSuffix(op.Source().String(), "Source"))
|
|
|
|
}
|
|
|
|
|
2023-04-03 12:16:17 +03:00
|
|
|
case class.IsJump():
|
2023-01-17 23:59:04 +03:00
|
|
|
f.WriteString(op.JumpOp().String())
|
2023-04-03 12:16:17 +03:00
|
|
|
|
|
|
|
if class == Jump32Class {
|
|
|
|
f.WriteString("32")
|
|
|
|
}
|
|
|
|
|
2023-01-17 23:59:04 +03:00
|
|
|
if jop := op.JumpOp(); jop != Exit && jop != Call {
|
|
|
|
f.WriteString(strings.TrimSuffix(op.Source().String(), "Source"))
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
fmt.Fprintf(&f, "OpCode(%#x)", uint8(op))
|
|
|
|
}
|
|
|
|
|
|
|
|
return f.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// valid returns true if all bits in value are covered by mask.
|
|
|
|
func valid(value, mask OpCode) bool {
|
|
|
|
return value & ^mask == 0
|
|
|
|
}
|