opcode.go 5.3 KB

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