github.com/cilium/ebpf@v0.10.0/linker_test.go (about)

     1  package ebpf
     2  
     3  import (
     4  	"errors"
     5  	"testing"
     6  
     7  	"github.com/cilium/ebpf/asm"
     8  	"github.com/cilium/ebpf/btf"
     9  	"github.com/cilium/ebpf/internal"
    10  	"github.com/cilium/ebpf/internal/testutils"
    11  
    12  	qt "github.com/frankban/quicktest"
    13  )
    14  
    15  func TestFindReferences(t *testing.T) {
    16  	progs := map[string]*ProgramSpec{
    17  		"entrypoint": {
    18  			Type: SocketFilter,
    19  			Instructions: asm.Instructions{
    20  				// Make sure the call doesn't happen at instruction 0
    21  				// to exercise the relative offset calculation.
    22  				asm.Mov.Reg(asm.R0, asm.R1),
    23  				asm.Call.Label("my_func"),
    24  				asm.Return(),
    25  			},
    26  			License: "MIT",
    27  		},
    28  		"my_other_func": {
    29  			Instructions: asm.Instructions{
    30  				asm.LoadImm(asm.R0, 1337, asm.DWord).WithSymbol("my_other_func"),
    31  				asm.Return(),
    32  			},
    33  		},
    34  		"my_func": {
    35  			Instructions: asm.Instructions{
    36  				asm.Call.Label("my_other_func").WithSymbol("my_func"),
    37  				asm.Return(),
    38  			},
    39  		},
    40  	}
    41  
    42  	flattenPrograms(progs, []string{"entrypoint"})
    43  
    44  	prog, err := NewProgram(progs["entrypoint"])
    45  	testutils.SkipIfNotSupported(t, err)
    46  	if err != nil {
    47  		t.Fatal(err)
    48  	}
    49  	defer prog.Close()
    50  
    51  	ret, _, err := prog.Test(internal.EmptyBPFContext)
    52  	if err != nil {
    53  		t.Fatal(err)
    54  	}
    55  
    56  	if ret != 1337 {
    57  		t.Errorf("Expected return code 1337, got %d", ret)
    58  	}
    59  }
    60  
    61  func TestForwardFunctionDeclaration(t *testing.T) {
    62  	testutils.Files(t, testutils.Glob(t, "testdata/fwd_decl-*.elf"), func(t *testing.T, file string) {
    63  		coll, err := LoadCollectionSpec(file)
    64  		if err != nil {
    65  			t.Fatal(err)
    66  		}
    67  
    68  		if coll.ByteOrder != internal.NativeEndian {
    69  			return
    70  		}
    71  
    72  		spec := coll.Programs["call_fwd"]
    73  
    74  		// This program calls an unimplemented forward function declaration.
    75  		_, err = NewProgram(spec)
    76  		if !errors.Is(err, asm.ErrUnsatisfiedProgramReference) {
    77  			t.Fatal("Expected an error wrapping ErrUnsatisfiedProgramReference, got:", err)
    78  		}
    79  
    80  		// Append the implementation of fwd().
    81  		spec.Instructions = append(spec.Instructions,
    82  			asm.Mov.Imm32(asm.R0, 23).WithSymbol("fwd"),
    83  			asm.Return(),
    84  		)
    85  
    86  		// The body of the subprog we appended does not come with BTF func_infos,
    87  		// so the verifier will reject it. Load without BTF.
    88  		for i, ins := range spec.Instructions {
    89  			if btf.FuncMetadata(&ins) != nil || ins.Source() != nil {
    90  				sym := ins.Symbol()
    91  				ref := ins.Reference()
    92  				ins.Metadata = asm.Metadata{}
    93  				spec.Instructions[i] = ins.WithSymbol(sym).WithReference(ref)
    94  			}
    95  		}
    96  
    97  		prog, err := NewProgram(spec)
    98  		testutils.SkipIfNotSupported(t, err)
    99  		if err != nil {
   100  			t.Fatalf("%+v", err)
   101  		}
   102  		defer prog.Close()
   103  
   104  		ret, _, err := prog.Test(internal.EmptyBPFContext)
   105  		if err != nil {
   106  			t.Fatal("Running program:", err)
   107  		}
   108  		if ret != 23 {
   109  			t.Fatalf("Expected 23, got %d", ret)
   110  		}
   111  	})
   112  }
   113  
   114  func TestSplitSymbols(t *testing.T) {
   115  	c := qt.New(t)
   116  
   117  	// Splitting an empty insns results in an error.
   118  	_, err := splitSymbols(asm.Instructions{})
   119  	c.Assert(err, qt.IsNotNil, qt.Commentf("empty insns"))
   120  
   121  	// Splitting non-empty insns without a leading Symbol is an error.
   122  	_, err = splitSymbols(asm.Instructions{
   123  		asm.Return(),
   124  	})
   125  	c.Assert(err, qt.IsNotNil, qt.Commentf("insns without leading Symbol"))
   126  
   127  	// Non-empty insns with a single Instruction that is a Symbol.
   128  	insns := asm.Instructions{
   129  		asm.Return().WithSymbol("sym"),
   130  	}
   131  	m, err := splitSymbols(insns)
   132  	c.Assert(err, qt.IsNil, qt.Commentf("insns with a single Symbol"))
   133  
   134  	c.Assert(len(m), qt.Equals, 1)
   135  	c.Assert(len(m["sym"]), qt.Equals, 1)
   136  
   137  	// Insns containing duplicate Symbols.
   138  	_, err = splitSymbols(asm.Instructions{
   139  		asm.Return().WithSymbol("sym"),
   140  		asm.Return().WithSymbol("sym"),
   141  	})
   142  	c.Assert(err, qt.IsNotNil, qt.Commentf("insns containing duplicate Symbols"))
   143  
   144  	// Insns with multiple Symbols and subprogs of various lengths.
   145  	m, err = splitSymbols(asm.Instructions{
   146  		asm.Return().WithSymbol("sym1"),
   147  
   148  		asm.Mov.Imm(asm.R0, 0).WithSymbol("sym2"),
   149  		asm.Return(),
   150  
   151  		asm.Mov.Imm(asm.R0, 0).WithSymbol("sym3"),
   152  		asm.Mov.Imm(asm.R0, 1),
   153  		asm.Return(),
   154  
   155  		asm.Mov.Imm(asm.R0, 0).WithSymbol("sym4"),
   156  		asm.Mov.Imm(asm.R0, 1),
   157  		asm.Mov.Imm(asm.R0, 2),
   158  		asm.Return(),
   159  	})
   160  	c.Assert(err, qt.IsNil, qt.Commentf("insns with multiple Symbols"))
   161  
   162  	c.Assert(len(m), qt.Equals, 4)
   163  	c.Assert(len(m["sym1"]), qt.Equals, 1)
   164  	c.Assert(len(m["sym2"]), qt.Equals, 2)
   165  	c.Assert(len(m["sym3"]), qt.Equals, 3)
   166  	c.Assert(len(m["sym4"]), qt.Equals, 4)
   167  }