gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

opcode.go (6346B)


      1 package asm
      2 
      3 import (
      4 	"fmt"
      5 	"strings"
      6 )
      7 
      8 //go:generate stringer -output opcode_string.go -type=Class
      9 
     10 // Class of operations
     11 //
     12 //    msb      lsb
     13 //    +---+--+---+
     14 //    |  ??  |CLS|
     15 //    +---+--+---+
     16 type Class uint8
     17 
     18 const classMask OpCode = 0x07
     19 
     20 const (
     21 	// LdClass loads immediate values into registers.
     22 	// Also used for non-standard load operations from cBPF.
     23 	LdClass Class = 0x00
     24 	// LdXClass loads memory into registers.
     25 	LdXClass Class = 0x01
     26 	// StClass stores immediate values to memory.
     27 	StClass Class = 0x02
     28 	// StXClass stores registers to memory.
     29 	StXClass Class = 0x03
     30 	// ALUClass describes arithmetic operators.
     31 	ALUClass Class = 0x04
     32 	// JumpClass describes jump operators.
     33 	JumpClass Class = 0x05
     34 	// Jump32Class describes jump operators with 32-bit comparisons.
     35 	// Requires kernel 5.1.
     36 	Jump32Class Class = 0x06
     37 	// ALU64Class describes arithmetic operators in 64-bit mode.
     38 	ALU64Class Class = 0x07
     39 )
     40 
     41 // IsLoad checks if this is either LdClass or LdXClass.
     42 func (cls Class) IsLoad() bool {
     43 	return cls == LdClass || cls == LdXClass
     44 }
     45 
     46 // IsStore checks if this is either StClass or StXClass.
     47 func (cls Class) IsStore() bool {
     48 	return cls == StClass || cls == StXClass
     49 }
     50 
     51 func (cls Class) isLoadOrStore() bool {
     52 	return cls.IsLoad() || cls.IsStore()
     53 }
     54 
     55 // IsALU checks if this is either ALUClass or ALU64Class.
     56 func (cls Class) IsALU() bool {
     57 	return cls == ALUClass || cls == ALU64Class
     58 }
     59 
     60 // IsJump checks if this is either JumpClass or Jump32Class.
     61 func (cls Class) IsJump() bool {
     62 	return cls == JumpClass || cls == Jump32Class
     63 }
     64 
     65 func (cls Class) isJumpOrALU() bool {
     66 	return cls.IsJump() || cls.IsALU()
     67 }
     68 
     69 // OpCode is a packed eBPF opcode.
     70 //
     71 // Its encoding is defined by a Class value:
     72 //
     73 //    msb      lsb
     74 //    +----+-+---+
     75 //    | ???? |CLS|
     76 //    +----+-+---+
     77 type OpCode uint8
     78 
     79 // InvalidOpCode is returned by setters on OpCode
     80 const InvalidOpCode OpCode = 0xff
     81 
     82 // rawInstructions returns the number of BPF instructions required
     83 // to encode this opcode.
     84 func (op OpCode) rawInstructions() int {
     85 	if op.IsDWordLoad() {
     86 		return 2
     87 	}
     88 	return 1
     89 }
     90 
     91 func (op OpCode) IsDWordLoad() bool {
     92 	return op == LoadImmOp(DWord)
     93 }
     94 
     95 // Class returns the class of operation.
     96 func (op OpCode) Class() Class {
     97 	return Class(op & classMask)
     98 }
     99 
    100 // Mode returns the mode for load and store operations.
    101 func (op OpCode) Mode() Mode {
    102 	if !op.Class().isLoadOrStore() {
    103 		return InvalidMode
    104 	}
    105 	return Mode(op & modeMask)
    106 }
    107 
    108 // Size returns the size for load and store operations.
    109 func (op OpCode) Size() Size {
    110 	if !op.Class().isLoadOrStore() {
    111 		return InvalidSize
    112 	}
    113 	return Size(op & sizeMask)
    114 }
    115 
    116 // Source returns the source for branch and ALU operations.
    117 func (op OpCode) Source() Source {
    118 	if !op.Class().isJumpOrALU() || op.ALUOp() == Swap {
    119 		return InvalidSource
    120 	}
    121 	return Source(op & sourceMask)
    122 }
    123 
    124 // ALUOp returns the ALUOp.
    125 func (op OpCode) ALUOp() ALUOp {
    126 	if !op.Class().IsALU() {
    127 		return InvalidALUOp
    128 	}
    129 	return ALUOp(op & aluMask)
    130 }
    131 
    132 // Endianness returns the Endianness for a byte swap instruction.
    133 func (op OpCode) Endianness() Endianness {
    134 	if op.ALUOp() != Swap {
    135 		return InvalidEndian
    136 	}
    137 	return Endianness(op & endianMask)
    138 }
    139 
    140 // JumpOp returns the JumpOp.
    141 // Returns InvalidJumpOp if it doesn't encode a jump.
    142 func (op OpCode) JumpOp() JumpOp {
    143 	if !op.Class().IsJump() {
    144 		return InvalidJumpOp
    145 	}
    146 
    147 	jumpOp := JumpOp(op & jumpMask)
    148 
    149 	// Some JumpOps are only supported by JumpClass, not Jump32Class.
    150 	if op.Class() == Jump32Class && (jumpOp == Exit || jumpOp == Call || jumpOp == Ja) {
    151 		return InvalidJumpOp
    152 	}
    153 
    154 	return jumpOp
    155 }
    156 
    157 // SetMode sets the mode on load and store operations.
    158 //
    159 // Returns InvalidOpCode if op is of the wrong class.
    160 func (op OpCode) SetMode(mode Mode) OpCode {
    161 	if !op.Class().isLoadOrStore() || !valid(OpCode(mode), modeMask) {
    162 		return InvalidOpCode
    163 	}
    164 	return (op & ^modeMask) | OpCode(mode)
    165 }
    166 
    167 // SetSize sets the size on load and store operations.
    168 //
    169 // Returns InvalidOpCode if op is of the wrong class.
    170 func (op OpCode) SetSize(size Size) OpCode {
    171 	if !op.Class().isLoadOrStore() || !valid(OpCode(size), sizeMask) {
    172 		return InvalidOpCode
    173 	}
    174 	return (op & ^sizeMask) | OpCode(size)
    175 }
    176 
    177 // SetSource sets the source on jump and ALU operations.
    178 //
    179 // Returns InvalidOpCode if op is of the wrong class.
    180 func (op OpCode) SetSource(source Source) OpCode {
    181 	if !op.Class().isJumpOrALU() || !valid(OpCode(source), sourceMask) {
    182 		return InvalidOpCode
    183 	}
    184 	return (op & ^sourceMask) | OpCode(source)
    185 }
    186 
    187 // SetALUOp sets the ALUOp on ALU operations.
    188 //
    189 // Returns InvalidOpCode if op is of the wrong class.
    190 func (op OpCode) SetALUOp(alu ALUOp) OpCode {
    191 	if !op.Class().IsALU() || !valid(OpCode(alu), aluMask) {
    192 		return InvalidOpCode
    193 	}
    194 	return (op & ^aluMask) | OpCode(alu)
    195 }
    196 
    197 // SetJumpOp sets the JumpOp on jump operations.
    198 //
    199 // Returns InvalidOpCode if op is of the wrong class.
    200 func (op OpCode) SetJumpOp(jump JumpOp) OpCode {
    201 	if !op.Class().IsJump() || !valid(OpCode(jump), jumpMask) {
    202 		return InvalidOpCode
    203 	}
    204 
    205 	newOp := (op & ^jumpMask) | OpCode(jump)
    206 
    207 	// Check newOp is legal.
    208 	if newOp.JumpOp() == InvalidJumpOp {
    209 		return InvalidOpCode
    210 	}
    211 
    212 	return newOp
    213 }
    214 
    215 func (op OpCode) String() string {
    216 	var f strings.Builder
    217 
    218 	switch class := op.Class(); {
    219 	case class.isLoadOrStore():
    220 		f.WriteString(strings.TrimSuffix(class.String(), "Class"))
    221 
    222 		mode := op.Mode()
    223 		f.WriteString(strings.TrimSuffix(mode.String(), "Mode"))
    224 
    225 		switch op.Size() {
    226 		case DWord:
    227 			f.WriteString("DW")
    228 		case Word:
    229 			f.WriteString("W")
    230 		case Half:
    231 			f.WriteString("H")
    232 		case Byte:
    233 			f.WriteString("B")
    234 		}
    235 
    236 	case class.IsALU():
    237 		f.WriteString(op.ALUOp().String())
    238 
    239 		if op.ALUOp() == Swap {
    240 			// Width for Endian is controlled by Constant
    241 			f.WriteString(op.Endianness().String())
    242 		} else {
    243 			if class == ALUClass {
    244 				f.WriteString("32")
    245 			}
    246 
    247 			f.WriteString(strings.TrimSuffix(op.Source().String(), "Source"))
    248 		}
    249 
    250 	case class.IsJump():
    251 		f.WriteString(op.JumpOp().String())
    252 
    253 		if class == Jump32Class {
    254 			f.WriteString("32")
    255 		}
    256 
    257 		if jop := op.JumpOp(); jop != Exit && jop != Call {
    258 			f.WriteString(strings.TrimSuffix(op.Source().String(), "Source"))
    259 		}
    260 
    261 	default:
    262 		fmt.Fprintf(&f, "OpCode(%#x)", uint8(op))
    263 	}
    264 
    265 	return f.String()
    266 }
    267 
    268 // valid returns true if all bits in value are covered by mask.
    269 func valid(value, mask OpCode) bool {
    270 	return value & ^mask == 0
    271 }