Extended Berkeley Packet Filter (eBPF) assembler and virtual machine
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

257 lines
6.0KB

  1. package ebpf
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "fmt"
  6. "io"
  7. )
  8. type Instruction interface {
  9. // Assemble assembles the Instruction into a RawInstruction.
  10. Assemble() (RawInstruction, error)
  11. // String returns the assembly.
  12. String() string
  13. }
  14. // A RawInstruction is a raw BPF virtual machine instruction.
  15. type RawInstruction struct {
  16. // Operation to execute.
  17. Op Opcode
  18. Dst, Src Register
  19. Offset int16
  20. Immediate uint64
  21. }
  22. // Assemble implements the Instruction Assemble method.
  23. func (ri RawInstruction) Assemble() (RawInstruction, error) { return ri, nil }
  24. // Disassemble parses ri into an Instruction and returns it. If ri is
  25. // not recognized by this package, ri itself is returned.
  26. func (ri RawInstruction) Disassemble() Instruction {
  27. // log.Printf("op class %#02x", ri.Op&opMaskClass)
  28. switch class := ri.Op & opMaskClass; class {
  29. case opClassLoad, opClassLoadX:
  30. var size uint8
  31. switch ri.Op & opMaskSize {
  32. case sizeWord:
  33. size = 4
  34. case sizeDoubleWord:
  35. size = 8
  36. case sizeHalfWord:
  37. size = 2
  38. case sizeByte:
  39. size = 1
  40. default:
  41. return ri
  42. }
  43. switch ri.Op & opMaskMode {
  44. case modeImmediate: // 0x00
  45. if class == opClassLoad {
  46. if size != 4 && size != 8 {
  47. return ri
  48. }
  49. return LoadConstant{Dst: ri.Dst, Size: size, Value: ri.Immediate}
  50. }
  51. case modeAbsolute: // 0x20
  52. if class == opClassLoad {
  53. return LoadAbsolute{
  54. Dst: ri.Dst,
  55. Src: ri.Src,
  56. Value: ri.Immediate,
  57. Size: size,
  58. }
  59. }
  60. case modeIndirect: // 0x40
  61. if class == opClassLoad {
  62. return LoadIndirect{
  63. Dst: ri.Dst,
  64. Src: ri.Src,
  65. Value: ri.Immediate,
  66. Size: size,
  67. }
  68. }
  69. case modeMemory: // 0x60
  70. if class == opClassLoadX {
  71. return LoadRegister{
  72. Dst: ri.Dst,
  73. Src: ri.Src,
  74. Offset: ri.Offset,
  75. Size: size,
  76. }
  77. }
  78. case modeExclusiveAdd: // 0xc0
  79. }
  80. case opClassStore, opClassStoreX:
  81. var size uint8
  82. switch ri.Op & opMaskSize {
  83. case sizeWord:
  84. size = 4
  85. case sizeDoubleWord:
  86. size = 8
  87. case sizeHalfWord:
  88. size = 2
  89. case sizeByte:
  90. size = 1
  91. default:
  92. return ri
  93. }
  94. switch ri.Op & opMaskMode {
  95. case modeMemory: // 0x60
  96. if class == opClassStore {
  97. return StoreImmediate{
  98. Dst: ri.Dst,
  99. Offset: ri.Offset,
  100. Value: uint32(ri.Immediate),
  101. Size: size,
  102. }
  103. }
  104. return StoreRegister{
  105. Dst: ri.Dst,
  106. Src: ri.Src,
  107. Offset: ri.Offset,
  108. Size: size,
  109. }
  110. }
  111. case opClassALU:
  112. switch op := ALUOp(ri.Op & opMaskOperand); op {
  113. case ALUOpAdd, ALUOpSub, ALUOpMul, ALUOpDiv, ALUOpOr, ALUOpAnd, ALUOpShiftLeft, ALUOpShiftRight, ALUOpMod, ALUOpXor, ALUOpMove, ALUOpArithmicShiftRight:
  114. switch ri.Op & opMaskSource {
  115. case aluSourceImmediate:
  116. return ALUOpConstant{
  117. Op: op,
  118. Dst: ri.Dst,
  119. Value: uint32(ri.Immediate),
  120. }
  121. case aluSourceX:
  122. return ALUOpRegister{
  123. Op: op,
  124. Dst: ri.Dst,
  125. Src: ri.Src,
  126. }
  127. }
  128. case aluOpNegate:
  129. return Negate{ri.Dst}
  130. case aluOpByteSwap:
  131. var byteOrder binary.ByteOrder = binary.LittleEndian
  132. if ri.Op&opMaskSource == aluSourceX {
  133. byteOrder = binary.BigEndian
  134. }
  135. return ByteSwap{
  136. Dst: ri.Dst,
  137. ByteOrder: byteOrder,
  138. Size: uint8(ri.Immediate),
  139. }
  140. }
  141. case opClassALU64:
  142. switch op := ALUOp(ri.Op & opMaskOperand); op {
  143. case ALUOpAdd, ALUOpSub, ALUOpMul, ALUOpDiv, ALUOpOr, ALUOpAnd, ALUOpShiftLeft, ALUOpShiftRight, ALUOpMod, ALUOpXor, ALUOpMove, ALUOpArithmicShiftRight:
  144. switch ri.Op & opMaskSource {
  145. case aluSourceImmediate:
  146. return ALU64OpConstant{
  147. Op: op,
  148. Dst: ri.Dst,
  149. Value: uint32(ri.Immediate),
  150. }
  151. case aluSourceX:
  152. return ALU64OpRegister{
  153. Op: op,
  154. Dst: ri.Dst,
  155. Src: ri.Src,
  156. }
  157. }
  158. case aluOpNegate:
  159. return Negate64{ri.Dst}
  160. }
  161. case opClassJump:
  162. switch op := ri.Op & 0xf0; op {
  163. case jumpOp:
  164. return Jump{uint16(ri.Offset)}
  165. case jumpOpEqual, jumpOpGreater, jumpOpGreaterOrEqual, jumpOpSet, jumpOpNotEqual,
  166. jumpOpSignedGreater, jumpOpSignedGreaterOrEqual, jumpOpLess, jumpOpLessOrEqual,
  167. jumpOpSignedLess, jumpOpSignedLessOrEqual:
  168. cond := jumpOpToTest(op)
  169. if ri.Op&0x08 == jumpSourceImmediate {
  170. return JumpIf{
  171. Cond: cond,
  172. Dst: ri.Dst,
  173. Value: uint32(ri.Immediate),
  174. Offset: uint16(ri.Offset),
  175. }
  176. }
  177. return JumpIfX{
  178. Cond: cond,
  179. Dst: ri.Dst,
  180. Src: ri.Src,
  181. Offset: uint16(ri.Offset),
  182. }
  183. case jumpOpExit:
  184. return Exit{}
  185. case jumpOpCall:
  186. return Call(ri.Immediate)
  187. }
  188. }
  189. return ri
  190. }
  191. func (ri RawInstruction) String() string {
  192. return fmt.Sprintf("# invalid opcode = %#02x, class = %#02x", ri.Op, ri.Op&opMaskClass)
  193. }
  194. // Disassemble a slice of bytes containing an eBPF program.
  195. func Disassemble(data []byte) (Program, error) {
  196. if l := len(data); l%8 != 0 {
  197. return nil, fmt.Errorf("ebpf: raw data size %d does not align with 64-bit boundary", l)
  198. }
  199. return DisassembleReader(bytes.NewBuffer(data))
  200. }
  201. // DisassembleReader disassembles an eBPF program from reader using the default byte order.
  202. func DisassembleReader(r io.Reader) (Program, error) {
  203. return DisassembleReaderOrder(r, binary.LittleEndian)
  204. }
  205. type marshaledInstruction struct {
  206. Op Opcode
  207. DstSrc uint8
  208. Offset int16
  209. Immediate uint32
  210. }
  211. // DisassembleReaderOrder is like DisassembleReader with a custom byte order.
  212. func DisassembleReaderOrder(r io.Reader, byteOrder binary.ByteOrder) (Program, error) {
  213. var (
  214. out Program
  215. )
  216. for {
  217. var load marshaledInstruction
  218. if err := binary.Read(r, byteOrder, &load); err == io.EOF {
  219. break
  220. } else if err != nil {
  221. return nil, err
  222. }
  223. ri := RawInstruction{
  224. Op: load.Op,
  225. Dst: Register(load.DstSrc & 0x0f),
  226. Src: Register(load.DstSrc >> 4),
  227. Offset: load.Offset,
  228. Immediate: uint64(load.Immediate),
  229. }
  230. if ri.Op == 0x18 {
  231. // LDDW takes two opcodes
  232. var next marshaledInstruction
  233. if err := binary.Read(r, byteOrder, &next); err != nil {
  234. return nil, err
  235. }
  236. ri.Immediate |= uint64(next.Immediate) << 32
  237. }
  238. out = append(out, ri.Disassemble())
  239. }
  240. return out, nil
  241. }