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 }