github.com/kubeshark/ebpf@v0.9.2/asm/opcode.go (about)

     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  }