github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/asm/opcode.go (about) 1 package asm 2 3 import ( 4 "fmt" 5 "strings" 6 ) 7 8 //go:generate go run golang.org/x/tools/cmd/stringer@latest -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 represents a single operation. 70 // It is not a 1:1 mapping to real eBPF opcodes. 71 // 72 // The encoding varies based on a 3-bit Class: 73 // 74 // 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 75 // ??? | CLS 76 // 77 // For ALUClass and ALUCLass32: 78 // 79 // 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 80 // OPC |S| CLS 81 // 82 // For LdClass, LdXclass, StClass and StXClass: 83 // 84 // 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 85 // 0 | MDE |SIZ| CLS 86 // 87 // For JumpClass, Jump32Class: 88 // 89 // 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 90 // 0 | OPC |S| CLS 91 type OpCode uint16 92 93 // InvalidOpCode is returned by setters on OpCode 94 const InvalidOpCode OpCode = 0xffff 95 96 // bpfOpCode returns the actual BPF opcode. 97 func (op OpCode) bpfOpCode() (byte, error) { 98 const opCodeMask = 0xff 99 100 if !valid(op, opCodeMask) { 101 return 0, fmt.Errorf("invalid opcode %x", op) 102 } 103 104 return byte(op & opCodeMask), nil 105 } 106 107 // rawInstructions returns the number of BPF instructions required 108 // to encode this opcode. 109 func (op OpCode) rawInstructions() int { 110 if op.IsDWordLoad() { 111 return 2 112 } 113 return 1 114 } 115 116 func (op OpCode) IsDWordLoad() bool { 117 return op == LoadImmOp(DWord) 118 } 119 120 // Class returns the class of operation. 121 func (op OpCode) Class() Class { 122 return Class(op & classMask) 123 } 124 125 // Mode returns the mode for load and store operations. 126 func (op OpCode) Mode() Mode { 127 if !op.Class().isLoadOrStore() { 128 return InvalidMode 129 } 130 return Mode(op & modeMask) 131 } 132 133 // Size returns the size for load and store operations. 134 func (op OpCode) Size() Size { 135 if !op.Class().isLoadOrStore() { 136 return InvalidSize 137 } 138 return Size(op & sizeMask) 139 } 140 141 // Source returns the source for branch and ALU operations. 142 func (op OpCode) Source() Source { 143 if !op.Class().isJumpOrALU() || op.ALUOp() == Swap { 144 return InvalidSource 145 } 146 return Source(op & sourceMask) 147 } 148 149 // ALUOp returns the ALUOp. 150 func (op OpCode) ALUOp() ALUOp { 151 if !op.Class().IsALU() { 152 return InvalidALUOp 153 } 154 return ALUOp(op & aluMask) 155 } 156 157 // Endianness returns the Endianness for a byte swap instruction. 158 func (op OpCode) Endianness() Endianness { 159 if op.ALUOp() != Swap { 160 return InvalidEndian 161 } 162 return Endianness(op & endianMask) 163 } 164 165 // JumpOp returns the JumpOp. 166 // Returns InvalidJumpOp if it doesn't encode a jump. 167 func (op OpCode) JumpOp() JumpOp { 168 if !op.Class().IsJump() { 169 return InvalidJumpOp 170 } 171 172 jumpOp := JumpOp(op & jumpMask) 173 174 // Some JumpOps are only supported by JumpClass, not Jump32Class. 175 if op.Class() == Jump32Class && (jumpOp == Exit || jumpOp == Call) { 176 return InvalidJumpOp 177 } 178 179 return jumpOp 180 } 181 182 // SetMode sets the mode on load and store operations. 183 // 184 // Returns InvalidOpCode if op is of the wrong class. 185 func (op OpCode) SetMode(mode Mode) OpCode { 186 if !op.Class().isLoadOrStore() || !valid(OpCode(mode), modeMask) { 187 return InvalidOpCode 188 } 189 return (op & ^modeMask) | OpCode(mode) 190 } 191 192 // SetSize sets the size on load and store operations. 193 // 194 // Returns InvalidOpCode if op is of the wrong class. 195 func (op OpCode) SetSize(size Size) OpCode { 196 if !op.Class().isLoadOrStore() || !valid(OpCode(size), sizeMask) { 197 return InvalidOpCode 198 } 199 return (op & ^sizeMask) | OpCode(size) 200 } 201 202 // SetSource sets the source on jump and ALU operations. 203 // 204 // Returns InvalidOpCode if op is of the wrong class. 205 func (op OpCode) SetSource(source Source) OpCode { 206 if !op.Class().isJumpOrALU() || !valid(OpCode(source), sourceMask) { 207 return InvalidOpCode 208 } 209 return (op & ^sourceMask) | OpCode(source) 210 } 211 212 // SetALUOp sets the ALUOp on ALU operations. 213 // 214 // Returns InvalidOpCode if op is of the wrong class. 215 func (op OpCode) SetALUOp(alu ALUOp) OpCode { 216 if !op.Class().IsALU() || !valid(OpCode(alu), aluMask) { 217 return InvalidOpCode 218 } 219 return (op & ^aluMask) | OpCode(alu) 220 } 221 222 // SetJumpOp sets the JumpOp on jump operations. 223 // 224 // Returns InvalidOpCode if op is of the wrong class. 225 func (op OpCode) SetJumpOp(jump JumpOp) OpCode { 226 if !op.Class().IsJump() || !valid(OpCode(jump), jumpMask) { 227 return InvalidOpCode 228 } 229 230 newOp := (op & ^jumpMask) | OpCode(jump) 231 232 // Check newOp is legal. 233 if newOp.JumpOp() == InvalidJumpOp { 234 return InvalidOpCode 235 } 236 237 return newOp 238 } 239 240 func (op OpCode) String() string { 241 var f strings.Builder 242 243 switch class := op.Class(); { 244 case class.isLoadOrStore(): 245 f.WriteString(strings.TrimSuffix(class.String(), "Class")) 246 247 mode := op.Mode() 248 f.WriteString(strings.TrimSuffix(mode.String(), "Mode")) 249 250 switch op.Size() { 251 case DWord: 252 f.WriteString("DW") 253 case Word: 254 f.WriteString("W") 255 case Half: 256 f.WriteString("H") 257 case Byte: 258 f.WriteString("B") 259 } 260 261 case class.IsALU(): 262 if op.ALUOp() == Swap && op.Class() == ALU64Class { 263 // B to make BSwap, uncontitional byte swap 264 f.WriteString("B") 265 } 266 267 f.WriteString(op.ALUOp().String()) 268 269 if op.ALUOp() == Swap { 270 if op.Class() == ALUClass { 271 // Width for Endian is controlled by Constant 272 f.WriteString(op.Endianness().String()) 273 } 274 } else { 275 f.WriteString(strings.TrimSuffix(op.Source().String(), "Source")) 276 277 if class == ALUClass { 278 f.WriteString("32") 279 } 280 } 281 282 case class.IsJump(): 283 f.WriteString(op.JumpOp().String()) 284 285 if class == Jump32Class { 286 f.WriteString("32") 287 } 288 289 if jop := op.JumpOp(); jop != Exit && jop != Call && jop != Ja { 290 f.WriteString(strings.TrimSuffix(op.Source().String(), "Source")) 291 } 292 293 default: 294 fmt.Fprintf(&f, "OpCode(%#x)", uint8(op)) 295 } 296 297 return f.String() 298 } 299 300 // valid returns true if all bits in value are covered by mask. 301 func valid(value, mask OpCode) bool { 302 return value & ^mask == 0 303 }