instructions_test.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. // Copyright 2016 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package bpf
  5. import (
  6. "io/ioutil"
  7. "reflect"
  8. "strconv"
  9. "strings"
  10. "testing"
  11. )
  12. // This is a direct translation of the program in
  13. // testdata/all_instructions.txt.
  14. var allInstructions = []Instruction{
  15. LoadConstant{Dst: RegA, Val: 42},
  16. LoadConstant{Dst: RegX, Val: 42},
  17. LoadScratch{Dst: RegA, N: 3},
  18. LoadScratch{Dst: RegX, N: 3},
  19. LoadAbsolute{Off: 42, Size: 1},
  20. LoadAbsolute{Off: 42, Size: 2},
  21. LoadAbsolute{Off: 42, Size: 4},
  22. LoadIndirect{Off: 42, Size: 1},
  23. LoadIndirect{Off: 42, Size: 2},
  24. LoadIndirect{Off: 42, Size: 4},
  25. LoadMemShift{Off: 42},
  26. LoadExtension{Num: ExtLen},
  27. LoadExtension{Num: ExtProto},
  28. LoadExtension{Num: ExtType},
  29. LoadExtension{Num: ExtRand},
  30. StoreScratch{Src: RegA, N: 3},
  31. StoreScratch{Src: RegX, N: 3},
  32. ALUOpConstant{Op: ALUOpAdd, Val: 42},
  33. ALUOpConstant{Op: ALUOpSub, Val: 42},
  34. ALUOpConstant{Op: ALUOpMul, Val: 42},
  35. ALUOpConstant{Op: ALUOpDiv, Val: 42},
  36. ALUOpConstant{Op: ALUOpOr, Val: 42},
  37. ALUOpConstant{Op: ALUOpAnd, Val: 42},
  38. ALUOpConstant{Op: ALUOpShiftLeft, Val: 42},
  39. ALUOpConstant{Op: ALUOpShiftRight, Val: 42},
  40. ALUOpConstant{Op: ALUOpMod, Val: 42},
  41. ALUOpConstant{Op: ALUOpXor, Val: 42},
  42. ALUOpX{Op: ALUOpAdd},
  43. ALUOpX{Op: ALUOpSub},
  44. ALUOpX{Op: ALUOpMul},
  45. ALUOpX{Op: ALUOpDiv},
  46. ALUOpX{Op: ALUOpOr},
  47. ALUOpX{Op: ALUOpAnd},
  48. ALUOpX{Op: ALUOpShiftLeft},
  49. ALUOpX{Op: ALUOpShiftRight},
  50. ALUOpX{Op: ALUOpMod},
  51. ALUOpX{Op: ALUOpXor},
  52. NegateA{},
  53. Jump{Skip: 10},
  54. JumpIf{Cond: JumpEqual, Val: 42, SkipTrue: 8, SkipFalse: 9},
  55. JumpIf{Cond: JumpNotEqual, Val: 42, SkipTrue: 8},
  56. JumpIf{Cond: JumpLessThan, Val: 42, SkipTrue: 7},
  57. JumpIf{Cond: JumpLessOrEqual, Val: 42, SkipTrue: 6},
  58. JumpIf{Cond: JumpGreaterThan, Val: 42, SkipTrue: 4, SkipFalse: 5},
  59. JumpIf{Cond: JumpGreaterOrEqual, Val: 42, SkipTrue: 3, SkipFalse: 4},
  60. JumpIf{Cond: JumpBitsSet, Val: 42, SkipTrue: 2, SkipFalse: 3},
  61. TAX{},
  62. TXA{},
  63. RetA{},
  64. RetConstant{Val: 42},
  65. }
  66. var allInstructionsExpected = "testdata/all_instructions.bpf"
  67. // Check that we produce the same output as the canonical bpf_asm
  68. // linux kernel tool.
  69. func TestInterop(t *testing.T) {
  70. out, err := Assemble(allInstructions)
  71. if err != nil {
  72. t.Fatalf("assembly of allInstructions program failed: %s", err)
  73. }
  74. t.Logf("Assembled program is %d instructions long", len(out))
  75. bs, err := ioutil.ReadFile(allInstructionsExpected)
  76. if err != nil {
  77. t.Fatalf("reading %s: %s", allInstructionsExpected, err)
  78. }
  79. // First statement is the number of statements, last statement is
  80. // empty. We just ignore both and rely on slice length.
  81. stmts := strings.Split(string(bs), ",")
  82. if len(stmts)-2 != len(out) {
  83. t.Fatalf("test program lengths don't match: %s has %d, Go implementation has %d", allInstructionsExpected, len(stmts)-2, len(allInstructions))
  84. }
  85. for i, stmt := range stmts[1 : len(stmts)-2] {
  86. nums := strings.Split(stmt, " ")
  87. if len(nums) != 4 {
  88. t.Fatalf("malformed instruction %d in %s: %s", i+1, allInstructionsExpected, stmt)
  89. }
  90. actual := out[i]
  91. op, err := strconv.ParseUint(nums[0], 10, 16)
  92. if err != nil {
  93. t.Fatalf("malformed opcode %s in instruction %d of %s", nums[0], i+1, allInstructionsExpected)
  94. }
  95. if actual.Op != uint16(op) {
  96. t.Errorf("opcode mismatch on instruction %d (%#v): got 0x%02x, want 0x%02x", i+1, allInstructions[i], actual.Op, op)
  97. }
  98. jt, err := strconv.ParseUint(nums[1], 10, 8)
  99. if err != nil {
  100. t.Fatalf("malformed jt offset %s in instruction %d of %s", nums[1], i+1, allInstructionsExpected)
  101. }
  102. if actual.Jt != uint8(jt) {
  103. t.Errorf("jt mismatch on instruction %d (%#v): got %d, want %d", i+1, allInstructions[i], actual.Jt, jt)
  104. }
  105. jf, err := strconv.ParseUint(nums[2], 10, 8)
  106. if err != nil {
  107. t.Fatalf("malformed jf offset %s in instruction %d of %s", nums[2], i+1, allInstructionsExpected)
  108. }
  109. if actual.Jf != uint8(jf) {
  110. t.Errorf("jf mismatch on instruction %d (%#v): got %d, want %d", i+1, allInstructions[i], actual.Jf, jf)
  111. }
  112. k, err := strconv.ParseUint(nums[3], 10, 32)
  113. if err != nil {
  114. t.Fatalf("malformed constant %s in instruction %d of %s", nums[3], i+1, allInstructionsExpected)
  115. }
  116. if actual.K != uint32(k) {
  117. t.Errorf("constant mismatch on instruction %d (%#v): got %d, want %d", i+1, allInstructions[i], actual.K, k)
  118. }
  119. }
  120. }
  121. // Check that assembly and disassembly match each other.
  122. //
  123. // Because we offer "fake" jump conditions that don't appear in the
  124. // machine code, disassembly won't be a 1:1 match with the original
  125. // source, although the behavior will be identical. However,
  126. // reassembling the disassembly should produce an identical program.
  127. func TestAsmDisasm(t *testing.T) {
  128. prog1, err := Assemble(allInstructions)
  129. if err != nil {
  130. t.Fatalf("assembly of allInstructions program failed: %s", err)
  131. }
  132. t.Logf("Assembled program is %d instructions long", len(prog1))
  133. src, allDecoded := Disassemble(prog1)
  134. if !allDecoded {
  135. t.Errorf("Disassemble(Assemble(allInstructions)) produced unrecognized instructions:")
  136. for i, inst := range src {
  137. if r, ok := inst.(RawInstruction); ok {
  138. t.Logf(" insn %d, %#v --> %#v", i+1, allInstructions[i], r)
  139. }
  140. }
  141. }
  142. prog2, err := Assemble(src)
  143. if err != nil {
  144. t.Fatalf("assembly of Disassemble(Assemble(allInstructions)) failed: %s", err)
  145. }
  146. if len(prog2) != len(prog1) {
  147. t.Fatalf("disassembly changed program size: %d insns before, %d insns after", len(prog1), len(prog2))
  148. }
  149. if !reflect.DeepEqual(prog1, prog2) {
  150. t.Errorf("program mutated by disassembly:")
  151. for i := range prog2 {
  152. if !reflect.DeepEqual(prog1[i], prog2[i]) {
  153. t.Logf(" insn %d, s: %#v, p1: %#v, p2: %#v", i+1, allInstructions[i], prog1[i], prog2[i])
  154. }
  155. }
  156. }
  157. }