github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/net/bpf/instructions_test.go (about)

     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  
     5  package bpf
     6  
     7  import (
     8  	"fmt"
     9  	"io/ioutil"
    10  	"reflect"
    11  	"strconv"
    12  	"strings"
    13  	"testing"
    14  )
    15  
    16  // This is a direct translation of the program in
    17  // testdata/all_instructions.txt.
    18  var allInstructions = []Instruction{
    19  	LoadConstant{Dst: RegA, Val: 42},
    20  	LoadConstant{Dst: RegX, Val: 42},
    21  
    22  	LoadScratch{Dst: RegA, N: 3},
    23  	LoadScratch{Dst: RegX, N: 3},
    24  
    25  	LoadAbsolute{Off: 42, Size: 1},
    26  	LoadAbsolute{Off: 42, Size: 2},
    27  	LoadAbsolute{Off: 42, Size: 4},
    28  
    29  	LoadIndirect{Off: 42, Size: 1},
    30  	LoadIndirect{Off: 42, Size: 2},
    31  	LoadIndirect{Off: 42, Size: 4},
    32  
    33  	LoadMemShift{Off: 42},
    34  
    35  	LoadExtension{Num: ExtLen},
    36  	LoadExtension{Num: ExtProto},
    37  	LoadExtension{Num: ExtType},
    38  	LoadExtension{Num: ExtRand},
    39  
    40  	StoreScratch{Src: RegA, N: 3},
    41  	StoreScratch{Src: RegX, N: 3},
    42  
    43  	ALUOpConstant{Op: ALUOpAdd, Val: 42},
    44  	ALUOpConstant{Op: ALUOpSub, Val: 42},
    45  	ALUOpConstant{Op: ALUOpMul, Val: 42},
    46  	ALUOpConstant{Op: ALUOpDiv, Val: 42},
    47  	ALUOpConstant{Op: ALUOpOr, Val: 42},
    48  	ALUOpConstant{Op: ALUOpAnd, Val: 42},
    49  	ALUOpConstant{Op: ALUOpShiftLeft, Val: 42},
    50  	ALUOpConstant{Op: ALUOpShiftRight, Val: 42},
    51  	ALUOpConstant{Op: ALUOpMod, Val: 42},
    52  	ALUOpConstant{Op: ALUOpXor, Val: 42},
    53  
    54  	ALUOpX{Op: ALUOpAdd},
    55  	ALUOpX{Op: ALUOpSub},
    56  	ALUOpX{Op: ALUOpMul},
    57  	ALUOpX{Op: ALUOpDiv},
    58  	ALUOpX{Op: ALUOpOr},
    59  	ALUOpX{Op: ALUOpAnd},
    60  	ALUOpX{Op: ALUOpShiftLeft},
    61  	ALUOpX{Op: ALUOpShiftRight},
    62  	ALUOpX{Op: ALUOpMod},
    63  	ALUOpX{Op: ALUOpXor},
    64  
    65  	NegateA{},
    66  
    67  	Jump{Skip: 17},
    68  	JumpIf{Cond: JumpEqual, Val: 42, SkipTrue: 15, SkipFalse: 16},
    69  	JumpIf{Cond: JumpNotEqual, Val: 42, SkipTrue: 15},
    70  	JumpIf{Cond: JumpLessThan, Val: 42, SkipTrue: 14},
    71  	JumpIf{Cond: JumpLessOrEqual, Val: 42, SkipTrue: 13},
    72  	JumpIf{Cond: JumpGreaterThan, Val: 42, SkipTrue: 11, SkipFalse: 12},
    73  	JumpIf{Cond: JumpGreaterOrEqual, Val: 42, SkipTrue: 10, SkipFalse: 11},
    74  	JumpIf{Cond: JumpBitsSet, Val: 42, SkipTrue: 9, SkipFalse: 10},
    75  
    76  	JumpIfX{Cond: JumpEqual, SkipTrue: 8, SkipFalse: 9},
    77  	JumpIfX{Cond: JumpNotEqual, SkipTrue: 8},
    78  	JumpIfX{Cond: JumpLessThan, SkipTrue: 7},
    79  	JumpIfX{Cond: JumpLessOrEqual, SkipTrue: 6},
    80  	JumpIfX{Cond: JumpGreaterThan, SkipTrue: 4, SkipFalse: 5},
    81  	JumpIfX{Cond: JumpGreaterOrEqual, SkipTrue: 3, SkipFalse: 4},
    82  	JumpIfX{Cond: JumpBitsSet, SkipTrue: 2, SkipFalse: 3},
    83  
    84  	TAX{},
    85  	TXA{},
    86  
    87  	RetA{},
    88  	RetConstant{Val: 42},
    89  }
    90  var allInstructionsExpected = "testdata/all_instructions.bpf"
    91  
    92  // Check that we produce the same output as the canonical bpf_asm
    93  // linux kernel tool.
    94  func TestInterop(t *testing.T) {
    95  	out, err := Assemble(allInstructions)
    96  	if err != nil {
    97  		t.Fatalf("assembly of allInstructions program failed: %s", err)
    98  	}
    99  	t.Logf("Assembled program is %d instructions long", len(out))
   100  
   101  	bs, err := ioutil.ReadFile(allInstructionsExpected)
   102  	if err != nil {
   103  		t.Fatalf("reading %s: %s", allInstructionsExpected, err)
   104  	}
   105  	// First statement is the number of statements, last statement is
   106  	// empty. We just ignore both and rely on slice length.
   107  	stmts := strings.Split(string(bs), ",")
   108  	if len(stmts)-2 != len(out) {
   109  		t.Fatalf("test program lengths don't match: %s has %d, Go implementation has %d", allInstructionsExpected, len(stmts)-2, len(allInstructions))
   110  	}
   111  
   112  	for i, stmt := range stmts[1 : len(stmts)-2] {
   113  		nums := strings.Split(stmt, " ")
   114  		if len(nums) != 4 {
   115  			t.Fatalf("malformed instruction %d in %s: %s", i+1, allInstructionsExpected, stmt)
   116  		}
   117  
   118  		actual := out[i]
   119  
   120  		op, err := strconv.ParseUint(nums[0], 10, 16)
   121  		if err != nil {
   122  			t.Fatalf("malformed opcode %s in instruction %d of %s", nums[0], i+1, allInstructionsExpected)
   123  		}
   124  		if actual.Op != uint16(op) {
   125  			t.Errorf("opcode mismatch on instruction %d (%#v): got 0x%02x, want 0x%02x", i+1, allInstructions[i], actual.Op, op)
   126  		}
   127  
   128  		jt, err := strconv.ParseUint(nums[1], 10, 8)
   129  		if err != nil {
   130  			t.Fatalf("malformed jt offset %s in instruction %d of %s", nums[1], i+1, allInstructionsExpected)
   131  		}
   132  		if actual.Jt != uint8(jt) {
   133  			t.Errorf("jt mismatch on instruction %d (%#v): got %d, want %d", i+1, allInstructions[i], actual.Jt, jt)
   134  		}
   135  
   136  		jf, err := strconv.ParseUint(nums[2], 10, 8)
   137  		if err != nil {
   138  			t.Fatalf("malformed jf offset %s in instruction %d of %s", nums[2], i+1, allInstructionsExpected)
   139  		}
   140  		if actual.Jf != uint8(jf) {
   141  			t.Errorf("jf mismatch on instruction %d (%#v): got %d, want %d", i+1, allInstructions[i], actual.Jf, jf)
   142  		}
   143  
   144  		k, err := strconv.ParseUint(nums[3], 10, 32)
   145  		if err != nil {
   146  			t.Fatalf("malformed constant %s in instruction %d of %s", nums[3], i+1, allInstructionsExpected)
   147  		}
   148  		if actual.K != uint32(k) {
   149  			t.Errorf("constant mismatch on instruction %d (%#v): got %d, want %d", i+1, allInstructions[i], actual.K, k)
   150  		}
   151  	}
   152  }
   153  
   154  // Check that assembly and disassembly match each other.
   155  func TestAsmDisasm(t *testing.T) {
   156  	prog1, err := Assemble(allInstructions)
   157  	if err != nil {
   158  		t.Fatalf("assembly of allInstructions program failed: %s", err)
   159  	}
   160  	t.Logf("Assembled program is %d instructions long", len(prog1))
   161  
   162  	got, allDecoded := Disassemble(prog1)
   163  	if !allDecoded {
   164  		t.Errorf("Disassemble(Assemble(allInstructions)) produced unrecognized instructions:")
   165  		for i, inst := range got {
   166  			if r, ok := inst.(RawInstruction); ok {
   167  				t.Logf("  insn %d, %#v --> %#v", i+1, allInstructions[i], r)
   168  			}
   169  		}
   170  	}
   171  
   172  	if len(allInstructions) != len(got) {
   173  		t.Fatalf("disassembly changed program size: %d insns before, %d insns after", len(allInstructions), len(got))
   174  	}
   175  	if !reflect.DeepEqual(allInstructions, got) {
   176  		t.Errorf("program mutated by disassembly:")
   177  		for i := range got {
   178  			if !reflect.DeepEqual(allInstructions[i], got[i]) {
   179  				t.Logf("  insn %d, s: %#v, p1: %#v, got: %#v", i+1, allInstructions[i], prog1[i], got[i])
   180  			}
   181  		}
   182  	}
   183  }
   184  
   185  type InvalidInstruction struct{}
   186  
   187  func (a InvalidInstruction) Assemble() (RawInstruction, error) {
   188  	return RawInstruction{}, fmt.Errorf("Invalid Instruction")
   189  }
   190  
   191  func (a InvalidInstruction) String() string {
   192  	return fmt.Sprintf("unknown instruction: %#v", a)
   193  }
   194  
   195  func TestString(t *testing.T) {
   196  	testCases := []struct {
   197  		instruction Instruction
   198  		assembler   string
   199  	}{
   200  		{
   201  			instruction: LoadConstant{Dst: RegA, Val: 42},
   202  			assembler:   "ld #42",
   203  		},
   204  		{
   205  			instruction: LoadConstant{Dst: RegX, Val: 42},
   206  			assembler:   "ldx #42",
   207  		},
   208  		{
   209  			instruction: LoadConstant{Dst: 0xffff, Val: 42},
   210  			assembler:   "unknown instruction: bpf.LoadConstant{Dst:0xffff, Val:0x2a}",
   211  		},
   212  		{
   213  			instruction: LoadScratch{Dst: RegA, N: 3},
   214  			assembler:   "ld M[3]",
   215  		},
   216  		{
   217  			instruction: LoadScratch{Dst: RegX, N: 3},
   218  			assembler:   "ldx M[3]",
   219  		},
   220  		{
   221  			instruction: LoadScratch{Dst: 0xffff, N: 3},
   222  			assembler:   "unknown instruction: bpf.LoadScratch{Dst:0xffff, N:3}",
   223  		},
   224  		{
   225  			instruction: LoadAbsolute{Off: 42, Size: 1},
   226  			assembler:   "ldb [42]",
   227  		},
   228  		{
   229  			instruction: LoadAbsolute{Off: 42, Size: 2},
   230  			assembler:   "ldh [42]",
   231  		},
   232  		{
   233  			instruction: LoadAbsolute{Off: 42, Size: 4},
   234  			assembler:   "ld [42]",
   235  		},
   236  		{
   237  			instruction: LoadAbsolute{Off: 42, Size: -1},
   238  			assembler:   "unknown instruction: bpf.LoadAbsolute{Off:0x2a, Size:-1}",
   239  		},
   240  		{
   241  			instruction: LoadIndirect{Off: 42, Size: 1},
   242  			assembler:   "ldb [x + 42]",
   243  		},
   244  		{
   245  			instruction: LoadIndirect{Off: 42, Size: 2},
   246  			assembler:   "ldh [x + 42]",
   247  		},
   248  		{
   249  			instruction: LoadIndirect{Off: 42, Size: 4},
   250  			assembler:   "ld [x + 42]",
   251  		},
   252  		{
   253  			instruction: LoadIndirect{Off: 42, Size: -1},
   254  			assembler:   "unknown instruction: bpf.LoadIndirect{Off:0x2a, Size:-1}",
   255  		},
   256  		{
   257  			instruction: LoadMemShift{Off: 42},
   258  			assembler:   "ldx 4*([42]&0xf)",
   259  		},
   260  		{
   261  			instruction: LoadExtension{Num: ExtLen},
   262  			assembler:   "ld #len",
   263  		},
   264  		{
   265  			instruction: LoadExtension{Num: ExtProto},
   266  			assembler:   "ld #proto",
   267  		},
   268  		{
   269  			instruction: LoadExtension{Num: ExtType},
   270  			assembler:   "ld #type",
   271  		},
   272  		{
   273  			instruction: LoadExtension{Num: ExtPayloadOffset},
   274  			assembler:   "ld #poff",
   275  		},
   276  		{
   277  			instruction: LoadExtension{Num: ExtInterfaceIndex},
   278  			assembler:   "ld #ifidx",
   279  		},
   280  		{
   281  			instruction: LoadExtension{Num: ExtNetlinkAttr},
   282  			assembler:   "ld #nla",
   283  		},
   284  		{
   285  			instruction: LoadExtension{Num: ExtNetlinkAttrNested},
   286  			assembler:   "ld #nlan",
   287  		},
   288  		{
   289  			instruction: LoadExtension{Num: ExtMark},
   290  			assembler:   "ld #mark",
   291  		},
   292  		{
   293  			instruction: LoadExtension{Num: ExtQueue},
   294  			assembler:   "ld #queue",
   295  		},
   296  		{
   297  			instruction: LoadExtension{Num: ExtLinkLayerType},
   298  			assembler:   "ld #hatype",
   299  		},
   300  		{
   301  			instruction: LoadExtension{Num: ExtRXHash},
   302  			assembler:   "ld #rxhash",
   303  		},
   304  		{
   305  			instruction: LoadExtension{Num: ExtCPUID},
   306  			assembler:   "ld #cpu",
   307  		},
   308  		{
   309  			instruction: LoadExtension{Num: ExtVLANTag},
   310  			assembler:   "ld #vlan_tci",
   311  		},
   312  		{
   313  			instruction: LoadExtension{Num: ExtVLANTagPresent},
   314  			assembler:   "ld #vlan_avail",
   315  		},
   316  		{
   317  			instruction: LoadExtension{Num: ExtVLANProto},
   318  			assembler:   "ld #vlan_tpid",
   319  		},
   320  		{
   321  			instruction: LoadExtension{Num: ExtRand},
   322  			assembler:   "ld #rand",
   323  		},
   324  		{
   325  			instruction: LoadAbsolute{Off: 0xfffff038, Size: 4},
   326  			assembler:   "ld #rand",
   327  		},
   328  		{
   329  			instruction: LoadExtension{Num: 0xfff},
   330  			assembler:   "unknown instruction: bpf.LoadExtension{Num:4095}",
   331  		},
   332  		{
   333  			instruction: StoreScratch{Src: RegA, N: 3},
   334  			assembler:   "st M[3]",
   335  		},
   336  		{
   337  			instruction: StoreScratch{Src: RegX, N: 3},
   338  			assembler:   "stx M[3]",
   339  		},
   340  		{
   341  			instruction: StoreScratch{Src: 0xffff, N: 3},
   342  			assembler:   "unknown instruction: bpf.StoreScratch{Src:0xffff, N:3}",
   343  		},
   344  		{
   345  			instruction: ALUOpConstant{Op: ALUOpAdd, Val: 42},
   346  			assembler:   "add #42",
   347  		},
   348  		{
   349  			instruction: ALUOpConstant{Op: ALUOpSub, Val: 42},
   350  			assembler:   "sub #42",
   351  		},
   352  		{
   353  			instruction: ALUOpConstant{Op: ALUOpMul, Val: 42},
   354  			assembler:   "mul #42",
   355  		},
   356  		{
   357  			instruction: ALUOpConstant{Op: ALUOpDiv, Val: 42},
   358  			assembler:   "div #42",
   359  		},
   360  		{
   361  			instruction: ALUOpConstant{Op: ALUOpOr, Val: 42},
   362  			assembler:   "or #42",
   363  		},
   364  		{
   365  			instruction: ALUOpConstant{Op: ALUOpAnd, Val: 42},
   366  			assembler:   "and #42",
   367  		},
   368  		{
   369  			instruction: ALUOpConstant{Op: ALUOpShiftLeft, Val: 42},
   370  			assembler:   "lsh #42",
   371  		},
   372  		{
   373  			instruction: ALUOpConstant{Op: ALUOpShiftRight, Val: 42},
   374  			assembler:   "rsh #42",
   375  		},
   376  		{
   377  			instruction: ALUOpConstant{Op: ALUOpMod, Val: 42},
   378  			assembler:   "mod #42",
   379  		},
   380  		{
   381  			instruction: ALUOpConstant{Op: ALUOpXor, Val: 42},
   382  			assembler:   "xor #42",
   383  		},
   384  		{
   385  			instruction: ALUOpConstant{Op: 0xffff, Val: 42},
   386  			assembler:   "unknown instruction: bpf.ALUOpConstant{Op:0xffff, Val:0x2a}",
   387  		},
   388  		{
   389  			instruction: ALUOpX{Op: ALUOpAdd},
   390  			assembler:   "add x",
   391  		},
   392  		{
   393  			instruction: ALUOpX{Op: ALUOpSub},
   394  			assembler:   "sub x",
   395  		},
   396  		{
   397  			instruction: ALUOpX{Op: ALUOpMul},
   398  			assembler:   "mul x",
   399  		},
   400  		{
   401  			instruction: ALUOpX{Op: ALUOpDiv},
   402  			assembler:   "div x",
   403  		},
   404  		{
   405  			instruction: ALUOpX{Op: ALUOpOr},
   406  			assembler:   "or x",
   407  		},
   408  		{
   409  			instruction: ALUOpX{Op: ALUOpAnd},
   410  			assembler:   "and x",
   411  		},
   412  		{
   413  			instruction: ALUOpX{Op: ALUOpShiftLeft},
   414  			assembler:   "lsh x",
   415  		},
   416  		{
   417  			instruction: ALUOpX{Op: ALUOpShiftRight},
   418  			assembler:   "rsh x",
   419  		},
   420  		{
   421  			instruction: ALUOpX{Op: ALUOpMod},
   422  			assembler:   "mod x",
   423  		},
   424  		{
   425  			instruction: ALUOpX{Op: ALUOpXor},
   426  			assembler:   "xor x",
   427  		},
   428  		{
   429  			instruction: ALUOpX{Op: 0xffff},
   430  			assembler:   "unknown instruction: bpf.ALUOpX{Op:0xffff}",
   431  		},
   432  		{
   433  			instruction: NegateA{},
   434  			assembler:   "neg",
   435  		},
   436  		{
   437  			instruction: Jump{Skip: 10},
   438  			assembler:   "ja 10",
   439  		},
   440  		{
   441  			instruction: JumpIf{Cond: JumpEqual, Val: 42, SkipTrue: 8, SkipFalse: 9},
   442  			assembler:   "jeq #42,8,9",
   443  		},
   444  		{
   445  			instruction: JumpIf{Cond: JumpEqual, Val: 42, SkipTrue: 8},
   446  			assembler:   "jeq #42,8",
   447  		},
   448  		{
   449  			instruction: JumpIf{Cond: JumpEqual, Val: 42, SkipFalse: 8},
   450  			assembler:   "jneq #42,8",
   451  		},
   452  		{
   453  			instruction: JumpIf{Cond: JumpNotEqual, Val: 42, SkipTrue: 8},
   454  			assembler:   "jneq #42,8",
   455  		},
   456  		{
   457  			instruction: JumpIf{Cond: JumpLessThan, Val: 42, SkipTrue: 7},
   458  			assembler:   "jlt #42,7",
   459  		},
   460  		{
   461  			instruction: JumpIf{Cond: JumpLessOrEqual, Val: 42, SkipTrue: 6},
   462  			assembler:   "jle #42,6",
   463  		},
   464  		{
   465  			instruction: JumpIf{Cond: JumpGreaterThan, Val: 42, SkipTrue: 4, SkipFalse: 5},
   466  			assembler:   "jgt #42,4,5",
   467  		},
   468  		{
   469  			instruction: JumpIf{Cond: JumpGreaterThan, Val: 42, SkipTrue: 4},
   470  			assembler:   "jgt #42,4",
   471  		},
   472  		{
   473  			instruction: JumpIf{Cond: JumpGreaterOrEqual, Val: 42, SkipTrue: 3, SkipFalse: 4},
   474  			assembler:   "jge #42,3,4",
   475  		},
   476  		{
   477  			instruction: JumpIf{Cond: JumpGreaterOrEqual, Val: 42, SkipTrue: 3},
   478  			assembler:   "jge #42,3",
   479  		},
   480  		{
   481  			instruction: JumpIf{Cond: JumpBitsSet, Val: 42, SkipTrue: 2, SkipFalse: 3},
   482  			assembler:   "jset #42,2,3",
   483  		},
   484  		{
   485  			instruction: JumpIf{Cond: JumpBitsSet, Val: 42, SkipTrue: 2},
   486  			assembler:   "jset #42,2",
   487  		},
   488  		{
   489  			instruction: JumpIf{Cond: JumpBitsNotSet, Val: 42, SkipTrue: 2, SkipFalse: 3},
   490  			assembler:   "jset #42,3,2",
   491  		},
   492  		{
   493  			instruction: JumpIf{Cond: JumpBitsNotSet, Val: 42, SkipTrue: 2},
   494  			assembler:   "jset #42,0,2",
   495  		},
   496  		{
   497  			instruction: JumpIf{Cond: 0xffff, Val: 42, SkipTrue: 1, SkipFalse: 2},
   498  			assembler:   "unknown JumpTest 0xffff",
   499  		},
   500  		{
   501  			instruction: JumpIfX{Cond: JumpEqual, SkipTrue: 8, SkipFalse: 9},
   502  			assembler:   "jeq x,8,9",
   503  		},
   504  		{
   505  			instruction: JumpIfX{Cond: JumpEqual, SkipTrue: 8},
   506  			assembler:   "jeq x,8",
   507  		},
   508  		{
   509  			instruction: JumpIfX{Cond: JumpEqual, SkipFalse: 8},
   510  			assembler:   "jneq x,8",
   511  		},
   512  		{
   513  			instruction: JumpIfX{Cond: JumpNotEqual, SkipTrue: 8},
   514  			assembler:   "jneq x,8",
   515  		},
   516  		{
   517  			instruction: JumpIfX{Cond: JumpLessThan, SkipTrue: 7},
   518  			assembler:   "jlt x,7",
   519  		},
   520  		{
   521  			instruction: JumpIfX{Cond: JumpLessOrEqual, SkipTrue: 6},
   522  			assembler:   "jle x,6",
   523  		},
   524  		{
   525  			instruction: JumpIfX{Cond: JumpGreaterThan, SkipTrue: 4, SkipFalse: 5},
   526  			assembler:   "jgt x,4,5",
   527  		},
   528  		{
   529  			instruction: JumpIfX{Cond: JumpGreaterThan, SkipTrue: 4},
   530  			assembler:   "jgt x,4",
   531  		},
   532  		{
   533  			instruction: JumpIfX{Cond: JumpGreaterOrEqual, SkipTrue: 3, SkipFalse: 4},
   534  			assembler:   "jge x,3,4",
   535  		},
   536  		{
   537  			instruction: JumpIfX{Cond: JumpGreaterOrEqual, SkipTrue: 3},
   538  			assembler:   "jge x,3",
   539  		},
   540  		{
   541  			instruction: JumpIfX{Cond: JumpBitsSet, SkipTrue: 2, SkipFalse: 3},
   542  			assembler:   "jset x,2,3",
   543  		},
   544  		{
   545  			instruction: JumpIfX{Cond: JumpBitsSet, SkipTrue: 2},
   546  			assembler:   "jset x,2",
   547  		},
   548  		{
   549  			instruction: JumpIfX{Cond: JumpBitsNotSet, SkipTrue: 2, SkipFalse: 3},
   550  			assembler:   "jset x,3,2",
   551  		},
   552  		{
   553  			instruction: JumpIfX{Cond: JumpBitsNotSet, SkipTrue: 2},
   554  			assembler:   "jset x,0,2",
   555  		},
   556  		{
   557  			instruction: JumpIfX{Cond: 0xffff, SkipTrue: 1, SkipFalse: 2},
   558  			assembler:   "unknown JumpTest 0xffff",
   559  		},
   560  		{
   561  			instruction: TAX{},
   562  			assembler:   "tax",
   563  		},
   564  		{
   565  			instruction: TXA{},
   566  			assembler:   "txa",
   567  		},
   568  		{
   569  			instruction: RetA{},
   570  			assembler:   "ret a",
   571  		},
   572  		{
   573  			instruction: RetConstant{Val: 42},
   574  			assembler:   "ret #42",
   575  		},
   576  		// Invalid instruction
   577  		{
   578  			instruction: InvalidInstruction{},
   579  			assembler:   "unknown instruction: bpf.InvalidInstruction{}",
   580  		},
   581  	}
   582  
   583  	for _, testCase := range testCases {
   584  		if input, ok := testCase.instruction.(fmt.Stringer); ok {
   585  			got := input.String()
   586  			if got != testCase.assembler {
   587  				t.Errorf("String did not return expected assembler notation, expected: %s, got: %s", testCase.assembler, got)
   588  			}
   589  		} else {
   590  			t.Errorf("Instruction %#v is not a fmt.Stringer", testCase.instruction)
   591  		}
   592  	}
   593  }