opcode.go 6.2 KB

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