github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/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  	"github.com/go-quicktest/qt"
    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  	file := testutils.NativeFile(t, "testdata/fwd_decl-%s.elf")
    63  	coll, err := LoadCollectionSpec(file)
    64  	if err != nil {
    65  		t.Fatal(err)
    66  	}
    67  
    68  	spec := coll.Programs["call_fwd"]
    69  
    70  	// This program calls an unimplemented forward function declaration.
    71  	_, err = NewProgram(spec)
    72  	if !errors.Is(err, asm.ErrUnsatisfiedProgramReference) {
    73  		t.Fatal("Expected an error wrapping ErrUnsatisfiedProgramReference, got:", err)
    74  	}
    75  
    76  	// Append the implementation of fwd().
    77  	spec.Instructions = append(spec.Instructions,
    78  		asm.Mov.Imm32(asm.R0, 23).WithSymbol("fwd"),
    79  		asm.Return(),
    80  	)
    81  
    82  	// The body of the subprog we appended does not come with BTF func_infos,
    83  	// so the verifier will reject it. Load without BTF.
    84  	for i, ins := range spec.Instructions {
    85  		if btf.FuncMetadata(&ins) != nil || ins.Source() != nil {
    86  			sym := ins.Symbol()
    87  			ref := ins.Reference()
    88  			ins.Metadata = asm.Metadata{}
    89  			spec.Instructions[i] = ins.WithSymbol(sym).WithReference(ref)
    90  		}
    91  	}
    92  
    93  	prog, err := NewProgram(spec)
    94  	testutils.SkipIfNotSupported(t, err)
    95  	if err != nil {
    96  		t.Fatalf("%+v", err)
    97  	}
    98  	defer prog.Close()
    99  
   100  	ret, _, err := prog.Test(internal.EmptyBPFContext)
   101  	if err != nil {
   102  		t.Fatal("Running program:", err)
   103  	}
   104  	if ret != 23 {
   105  		t.Fatalf("Expected 23, got %d", ret)
   106  	}
   107  }
   108  
   109  func TestSplitSymbols(t *testing.T) {
   110  
   111  	// Splitting an empty insns results in an error.
   112  	_, err := splitSymbols(asm.Instructions{})
   113  	qt.Assert(t, qt.IsNotNil(err), qt.Commentf("empty insns"))
   114  
   115  	// Splitting non-empty insns without a leading Symbol is an error.
   116  	_, err = splitSymbols(asm.Instructions{
   117  		asm.Return(),
   118  	})
   119  	qt.Assert(t, qt.IsNotNil(err), qt.Commentf("insns without leading Symbol"))
   120  
   121  	// Non-empty insns with a single Instruction that is a Symbol.
   122  	insns := asm.Instructions{
   123  		asm.Return().WithSymbol("sym"),
   124  	}
   125  	m, err := splitSymbols(insns)
   126  	qt.Assert(t, qt.IsNil(err), qt.Commentf("insns with a single Symbol"))
   127  
   128  	qt.Assert(t, qt.HasLen(m, 1))
   129  	qt.Assert(t, qt.HasLen(m["sym"], 1))
   130  
   131  	// Insns containing duplicate Symbols.
   132  	_, err = splitSymbols(asm.Instructions{
   133  		asm.Return().WithSymbol("sym"),
   134  		asm.Return().WithSymbol("sym"),
   135  	})
   136  	qt.Assert(t, qt.IsNotNil(err), qt.Commentf("insns containing duplicate Symbols"))
   137  
   138  	// Insns with multiple Symbols and subprogs of various lengths.
   139  	m, err = splitSymbols(asm.Instructions{
   140  		asm.Return().WithSymbol("sym1"),
   141  
   142  		asm.Mov.Imm(asm.R0, 0).WithSymbol("sym2"),
   143  		asm.Return(),
   144  
   145  		asm.Mov.Imm(asm.R0, 0).WithSymbol("sym3"),
   146  		asm.Mov.Imm(asm.R0, 1),
   147  		asm.Return(),
   148  
   149  		asm.Mov.Imm(asm.R0, 0).WithSymbol("sym4"),
   150  		asm.Mov.Imm(asm.R0, 1),
   151  		asm.Mov.Imm(asm.R0, 2),
   152  		asm.Return(),
   153  	})
   154  	qt.Assert(t, qt.IsNil(err), qt.Commentf("insns with multiple Symbols"))
   155  
   156  	qt.Assert(t, qt.HasLen(m, 4))
   157  	qt.Assert(t, qt.HasLen(m["sym1"], 1))
   158  	qt.Assert(t, qt.HasLen(m["sym2"], 2))
   159  	qt.Assert(t, qt.HasLen(m["sym3"], 3))
   160  	qt.Assert(t, qt.HasLen(m["sym4"], 4))
   161  }