github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/elf_reader_test.go (about)

     1  package ebpf
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"flag"
     8  	"fmt"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  	"syscall"
    13  	"testing"
    14  
    15  	"github.com/cilium/ebpf/btf"
    16  	"github.com/cilium/ebpf/internal"
    17  	"github.com/cilium/ebpf/internal/testutils"
    18  	"github.com/cilium/ebpf/internal/unix"
    19  
    20  	"github.com/google/go-cmp/cmp"
    21  	"github.com/google/go-cmp/cmp/cmpopts"
    22  
    23  	"github.com/go-quicktest/qt"
    24  )
    25  
    26  func TestLoadCollectionSpec(t *testing.T) {
    27  	coll := &CollectionSpec{
    28  		Maps: map[string]*MapSpec{
    29  			"hash_map": {
    30  				Name:       "hash_map",
    31  				Type:       Hash,
    32  				KeySize:    4,
    33  				ValueSize:  8,
    34  				MaxEntries: 1,
    35  				Flags:      unix.BPF_F_NO_PREALLOC,
    36  			},
    37  			"hash_map2": {
    38  				Name:       "hash_map2",
    39  				Type:       Hash,
    40  				KeySize:    4,
    41  				ValueSize:  8,
    42  				MaxEntries: 2,
    43  			},
    44  			"array_of_hash_map": {
    45  				Name:       "array_of_hash_map",
    46  				Type:       ArrayOfMaps,
    47  				KeySize:    4,
    48  				MaxEntries: 2,
    49  			},
    50  			"perf_event_array": {
    51  				Name:       "perf_event_array",
    52  				Type:       PerfEventArray,
    53  				MaxEntries: 4096,
    54  			},
    55  			"btf_pin": {
    56  				Name:       "btf_pin",
    57  				Type:       Hash,
    58  				KeySize:    4,
    59  				ValueSize:  8,
    60  				MaxEntries: 1,
    61  				Pinning:    PinByName,
    62  			},
    63  			"btf_outer_map": {
    64  				Name:       "btf_outer_map",
    65  				Type:       ArrayOfMaps,
    66  				KeySize:    4,
    67  				ValueSize:  4,
    68  				MaxEntries: 1,
    69  				InnerMap: &MapSpec{
    70  					Name:       "btf_outer_map_inner",
    71  					Type:       Hash,
    72  					KeySize:    4,
    73  					ValueSize:  4,
    74  					MaxEntries: 1,
    75  				},
    76  			},
    77  			"btf_outer_map_anon": {
    78  				Name:       "btf_outer_map_anon",
    79  				Type:       ArrayOfMaps,
    80  				KeySize:    4,
    81  				ValueSize:  4,
    82  				MaxEntries: 1,
    83  				InnerMap: &MapSpec{
    84  					Name:       "btf_outer_map_anon_inner",
    85  					Type:       Hash,
    86  					KeySize:    4,
    87  					ValueSize:  4,
    88  					MaxEntries: 1,
    89  				},
    90  			},
    91  			"btf_typedef_map": {
    92  				Name:       "btf_typedef_map",
    93  				Type:       Array,
    94  				KeySize:    4,
    95  				ValueSize:  8,
    96  				MaxEntries: 1,
    97  			},
    98  		},
    99  		Programs: map[string]*ProgramSpec{
   100  			"xdp_prog": {
   101  				Name:        "xdp_prog",
   102  				Type:        XDP,
   103  				SectionName: "xdp",
   104  				License:     "MIT",
   105  			},
   106  			"no_relocation": {
   107  				Name:        "no_relocation",
   108  				Type:        SocketFilter,
   109  				SectionName: "socket",
   110  				License:     "MIT",
   111  			},
   112  			"asm_relocation": {
   113  				Name:        "asm_relocation",
   114  				Type:        SocketFilter,
   115  				SectionName: "socket/2",
   116  				License:     "MIT",
   117  			},
   118  			"data_sections": {
   119  				Name:        "data_sections",
   120  				Type:        SocketFilter,
   121  				SectionName: "socket/3",
   122  				License:     "MIT",
   123  			},
   124  			"global_fn3": {
   125  				Name:        "global_fn3",
   126  				Type:        UnspecifiedProgram,
   127  				SectionName: "other",
   128  				License:     "MIT",
   129  			},
   130  			"static_fn": {
   131  				Name:        "static_fn",
   132  				Type:        UnspecifiedProgram,
   133  				SectionName: "static",
   134  				License:     "MIT",
   135  			},
   136  			"anon_const": {
   137  				Name:        "anon_const",
   138  				Type:        SocketFilter,
   139  				SectionName: "socket/4",
   140  				License:     "MIT",
   141  			},
   142  		},
   143  	}
   144  
   145  	cmpOpts := cmp.Options{
   146  		// Dummy Comparer that works with empty readers to support test cases.
   147  		cmp.Comparer(func(a, b bytes.Reader) bool {
   148  			if a.Len() == 0 && b.Len() == 0 {
   149  				return true
   150  			}
   151  			return false
   152  		}),
   153  		cmpopts.IgnoreTypes(new(btf.Spec)),
   154  		cmpopts.IgnoreFields(CollectionSpec{}, "ByteOrder", "Types"),
   155  		cmpopts.IgnoreFields(ProgramSpec{}, "Instructions", "ByteOrder"),
   156  		cmpopts.IgnoreFields(MapSpec{}, "Key", "Value"),
   157  		cmpopts.IgnoreUnexported(ProgramSpec{}),
   158  		cmpopts.IgnoreMapEntries(func(key string, _ *MapSpec) bool {
   159  			if key == ".bss" || key == ".data" || strings.HasPrefix(key, ".rodata") {
   160  				return true
   161  			}
   162  			return false
   163  		}),
   164  	}
   165  
   166  	testutils.Files(t, testutils.Glob(t, "testdata/loader-*.elf"), func(t *testing.T, file string) {
   167  		have, err := LoadCollectionSpec(file)
   168  		if err != nil {
   169  			t.Fatal("Can't parse ELF:", err)
   170  		}
   171  
   172  		err = have.RewriteConstants(map[string]interface{}{
   173  			"arg":  uint32(1),
   174  			"arg2": uint32(2),
   175  		})
   176  		if err != nil {
   177  			t.Fatal("Can't rewrite constant:", err)
   178  		}
   179  
   180  		err = have.RewriteConstants(map[string]interface{}{
   181  			"totallyBogus": uint32(1),
   182  		})
   183  		if err == nil {
   184  			t.Error("Rewriting a bogus constant doesn't fail")
   185  		}
   186  
   187  		if diff := cmp.Diff(coll, have, cmpOpts...); diff != "" {
   188  			t.Errorf("MapSpec mismatch (-want +got):\n%s", diff)
   189  		}
   190  
   191  		if have.ByteOrder != internal.NativeEndian {
   192  			return
   193  		}
   194  
   195  		have.Maps["array_of_hash_map"].InnerMap = have.Maps["hash_map"]
   196  		coll, err := NewCollectionWithOptions(have, CollectionOptions{
   197  			Maps: MapOptions{
   198  				PinPath: testutils.TempBPFFS(t),
   199  			},
   200  			Programs: ProgramOptions{
   201  				LogLevel: LogLevelBranch,
   202  			},
   203  		})
   204  
   205  		testutils.SkipIfNotSupported(t, err)
   206  		if err != nil {
   207  			t.Fatal(err)
   208  		}
   209  		defer coll.Close()
   210  
   211  		ret, _, err := coll.Programs["xdp_prog"].Test(internal.EmptyBPFContext)
   212  		if err != nil {
   213  			t.Fatal("Can't run program:", err)
   214  		}
   215  
   216  		if ret != 7 {
   217  			t.Error("Unexpected return value:", ret)
   218  		}
   219  	})
   220  }
   221  
   222  func BenchmarkELFLoader(b *testing.B) {
   223  	b.ReportAllocs()
   224  
   225  	for i := 0; i < b.N; i++ {
   226  		_, _ = LoadCollectionSpec("testdata/loader-el.elf")
   227  	}
   228  }
   229  
   230  func TestDataSections(t *testing.T) {
   231  	file := testutils.NativeFile(t, "testdata/loader-%s.elf")
   232  	coll, err := LoadCollectionSpec(file)
   233  	if err != nil {
   234  		t.Fatal(err)
   235  	}
   236  
   237  	t.Log(coll.Programs["data_sections"].Instructions)
   238  
   239  	var obj struct {
   240  		Program *Program `ebpf:"data_sections"`
   241  	}
   242  
   243  	err = coll.LoadAndAssign(&obj, nil)
   244  	testutils.SkipIfNotSupported(t, err)
   245  	if err != nil {
   246  		t.Fatal(err)
   247  	}
   248  	defer obj.Program.Close()
   249  
   250  	ret, _, err := obj.Program.Test(internal.EmptyBPFContext)
   251  	if err != nil {
   252  		t.Fatal(err)
   253  	}
   254  
   255  	if ret != 0 {
   256  		t.Error("BPF assertion failed on line", ret)
   257  	}
   258  }
   259  
   260  func TestInlineASMConstant(t *testing.T) {
   261  	file := testutils.NativeFile(t, "testdata/loader-%s.elf")
   262  	coll, err := LoadCollectionSpec(file)
   263  	if err != nil {
   264  		t.Fatal(err)
   265  	}
   266  
   267  	spec := coll.Programs["asm_relocation"]
   268  	if spec.Instructions[0].Reference() != "MY_CONST" {
   269  		t.Fatal("First instruction is not a reference to MY_CONST")
   270  	}
   271  
   272  	// -1 is used by the loader to find unrewritten maps.
   273  	spec.Instructions[0].Constant = -1
   274  
   275  	t.Log(spec.Instructions)
   276  
   277  	var obj struct {
   278  		Program *Program `ebpf:"asm_relocation"`
   279  	}
   280  
   281  	err = coll.LoadAndAssign(&obj, nil)
   282  	testutils.SkipIfNotSupported(t, err)
   283  	if err != nil {
   284  		t.Fatal(err)
   285  	}
   286  	obj.Program.Close()
   287  }
   288  
   289  func TestFreezeRodata(t *testing.T) {
   290  	testutils.SkipOnOldKernel(t, "5.9", "sk_lookup program type")
   291  
   292  	file := testutils.NativeFile(t, "testdata/constants-%s.elf")
   293  	spec, err := LoadCollectionSpec(file)
   294  	if err != nil {
   295  		t.Fatal(err)
   296  	}
   297  
   298  	var obj struct {
   299  		Program *Program `ebpf:"freeze_rodata"`
   300  	}
   301  
   302  	if err := spec.RewriteConstants(map[string]interface{}{
   303  		"ret": uint32(1),
   304  	}); err != nil {
   305  		t.Fatal(err)
   306  	}
   307  
   308  	err = spec.LoadAndAssign(&obj, nil)
   309  	testutils.SkipIfNotSupported(t, err)
   310  	if err != nil {
   311  		t.Fatal(err)
   312  	}
   313  	defer obj.Program.Close()
   314  }
   315  
   316  func TestCollectionSpecDetach(t *testing.T) {
   317  	coll := Collection{
   318  		Maps: map[string]*Map{
   319  			"foo": new(Map),
   320  		},
   321  		Programs: map[string]*Program{
   322  			"bar": new(Program),
   323  		},
   324  	}
   325  
   326  	foo := coll.DetachMap("foo")
   327  	if foo == nil {
   328  		t.Error("Program not returned from DetachMap")
   329  	}
   330  
   331  	if _, ok := coll.Programs["foo"]; ok {
   332  		t.Error("DetachMap doesn't remove map from Maps")
   333  	}
   334  
   335  	bar := coll.DetachProgram("bar")
   336  	if bar == nil {
   337  		t.Fatal("Program not returned from DetachProgram")
   338  	}
   339  
   340  	if _, ok := coll.Programs["bar"]; ok {
   341  		t.Error("DetachProgram doesn't remove program from Programs")
   342  	}
   343  }
   344  
   345  func TestLoadInvalidMap(t *testing.T) {
   346  	file := testutils.NativeFile(t, "testdata/invalid_map-%s.elf")
   347  	cs, err := LoadCollectionSpec(file)
   348  	if err != nil {
   349  		t.Fatal("Can't load CollectionSpec", err)
   350  	}
   351  
   352  	ms, ok := cs.Maps["invalid_map"]
   353  	if !ok {
   354  		t.Fatal("invalid_map not found in CollectionSpec")
   355  	}
   356  
   357  	m, err := NewMap(ms)
   358  	t.Log(err)
   359  	if err == nil {
   360  		m.Close()
   361  		t.Fatal("Creating a Map from a MapSpec with non-zero Extra is expected to fail.")
   362  	}
   363  }
   364  
   365  func TestLoadInvalidMapMissingSymbol(t *testing.T) {
   366  	file := testutils.NativeFile(t, "testdata/invalid_map_static-%s.elf")
   367  	_, err := LoadCollectionSpec(file)
   368  	t.Log(err)
   369  	if err == nil {
   370  		t.Fatal("Loading a map with static qualifier should fail")
   371  	}
   372  }
   373  
   374  func TestLoadInitializedBTFMap(t *testing.T) {
   375  	testutils.Files(t, testutils.Glob(t, "testdata/btf_map_init-*.elf"), func(t *testing.T, file string) {
   376  		coll, err := LoadCollectionSpec(file)
   377  		if err != nil {
   378  			t.Fatal(err)
   379  		}
   380  
   381  		t.Run("NewCollection", func(t *testing.T) {
   382  			if coll.ByteOrder != internal.NativeEndian {
   383  				t.Skipf("Skipping %s collection", coll.ByteOrder)
   384  			}
   385  
   386  			tmp, err := NewCollection(coll)
   387  			testutils.SkipIfNotSupported(t, err)
   388  			if err != nil {
   389  				t.Fatal("NewCollection failed:", err)
   390  			}
   391  			tmp.Close()
   392  		})
   393  
   394  		t.Run("prog_array", func(t *testing.T) {
   395  			m, ok := coll.Maps["prog_array_init"]
   396  			if !ok {
   397  				t.Fatal("map prog_array_init not found in program")
   398  			}
   399  
   400  			if len(m.Contents) != 1 {
   401  				t.Error("expecting exactly 1 item in MapSpec contents")
   402  			}
   403  
   404  			p := m.Contents[0]
   405  			if cmp.Equal(p.Key, 1) {
   406  				t.Errorf("expecting MapSpec entry Key to equal 1, got %v", p.Key)
   407  			}
   408  
   409  			if _, ok := p.Value.(string); !ok {
   410  				t.Errorf("expecting MapSpec entry Value to be a string, got %T", p.Value)
   411  			}
   412  
   413  			if p.Value != "tail_1" {
   414  				t.Errorf("expected MapSpec entry Value 'tail_1', got: %s", p.Value)
   415  			}
   416  		})
   417  
   418  		t.Run("array_of_maps", func(t *testing.T) {
   419  			m, ok := coll.Maps["outer_map_init"]
   420  			if !ok {
   421  				t.Fatal("map outer_map_init not found in program")
   422  			}
   423  
   424  			if len(m.Contents) != 1 {
   425  				t.Error("expecting exactly 1 item in MapSpec contents")
   426  			}
   427  
   428  			if m.Key == nil {
   429  				t.Error("Expected non-nil key")
   430  			}
   431  
   432  			if m.Value == nil {
   433  				t.Error("Expected non-nil value")
   434  			}
   435  
   436  			if m.InnerMap.Key == nil {
   437  				t.Error("Expected non-nil InnerMap key")
   438  			}
   439  
   440  			if m.InnerMap.Value == nil {
   441  				t.Error("Expected non-nil InnerMap value")
   442  			}
   443  
   444  			p := m.Contents[0]
   445  			if cmp.Equal(p.Key, 1) {
   446  				t.Errorf("expecting MapSpec entry Key to equal 1, got %v", p.Key)
   447  			}
   448  
   449  			if _, ok := p.Value.(string); !ok {
   450  				t.Errorf("expecting MapSpec entry Value to be a string, got %T", p.Value)
   451  			}
   452  
   453  			if p.Value != "inner_map" {
   454  				t.Errorf("expected MapSpec entry Value 'inner_map', got: %s", p.Value)
   455  			}
   456  		})
   457  	})
   458  }
   459  
   460  func TestLoadInvalidInitializedBTFMap(t *testing.T) {
   461  	file := testutils.NativeFile(t, "testdata/invalid_btf_map_init-%s.elf")
   462  	_, err := LoadCollectionSpec(file)
   463  	t.Log(err)
   464  	if !errors.Is(err, internal.ErrNotSupported) {
   465  		t.Fatal("Loading an initialized BTF map should be unsupported")
   466  	}
   467  }
   468  
   469  func TestStringSection(t *testing.T) {
   470  	file := testutils.NativeFile(t, "testdata/strings-%s.elf")
   471  	spec, err := LoadCollectionSpec(file)
   472  	if err != nil {
   473  		t.Fatalf("load collection spec: %s", err)
   474  	}
   475  
   476  	for name := range spec.Maps {
   477  		t.Log(name)
   478  	}
   479  
   480  	strMap := spec.Maps[".rodata.str1.1"]
   481  	if strMap == nil {
   482  		t.Fatal("Unable to find map '.rodata.str1.1' in loaded collection")
   483  	}
   484  
   485  	if !strMap.Freeze {
   486  		t.Fatal("Read only data maps should be frozen")
   487  	}
   488  
   489  	if strMap.Flags != unix.BPF_F_RDONLY_PROG {
   490  		t.Fatal("Read only data maps should have the prog-read-only flag set")
   491  	}
   492  
   493  	coll, err := NewCollection(spec)
   494  	testutils.SkipIfNotSupported(t, err)
   495  	if err != nil {
   496  		t.Fatalf("new collection: %s", err)
   497  	}
   498  	defer coll.Close()
   499  
   500  	prog := coll.Programs["filter"]
   501  	if prog == nil {
   502  		t.Fatal("program not found")
   503  	}
   504  
   505  	testMap := coll.Maps["my_map"]
   506  	if testMap == nil {
   507  		t.Fatal("test map not found")
   508  	}
   509  
   510  	_, err = prog.Run(&RunOptions{
   511  		Data: internal.EmptyBPFContext, // Min size for XDP programs
   512  	})
   513  	if err != nil {
   514  		t.Fatalf("prog run: %s", err)
   515  	}
   516  
   517  	key := []byte("This string is allocated in the string section\n\x00")
   518  	var value uint32
   519  	if err = testMap.Lookup(&key, &value); err != nil {
   520  		t.Fatalf("test map lookup: %s", err)
   521  	}
   522  
   523  	if value != 1 {
   524  		t.Fatal("Test map value not 1!")
   525  	}
   526  }
   527  
   528  func TestLoadRawTracepoint(t *testing.T) {
   529  	testutils.SkipOnOldKernel(t, "4.17", "BPF_RAW_TRACEPOINT API")
   530  
   531  	file := testutils.NativeFile(t, "testdata/raw_tracepoint-%s.elf")
   532  	spec, err := LoadCollectionSpec(file)
   533  	if err != nil {
   534  		t.Fatal("Can't parse ELF:", err)
   535  	}
   536  
   537  	coll, err := NewCollectionWithOptions(spec, CollectionOptions{
   538  		Programs: ProgramOptions{
   539  			LogLevel: LogLevelBranch,
   540  		},
   541  	})
   542  	testutils.SkipIfNotSupported(t, err)
   543  	if err != nil {
   544  		t.Fatal("Can't create collection:", err)
   545  	}
   546  
   547  	coll.Close()
   548  }
   549  
   550  func TestTailCall(t *testing.T) {
   551  	file := testutils.NativeFile(t, "testdata/btf_map_init-%s.elf")
   552  	spec, err := LoadCollectionSpec(file)
   553  	if err != nil {
   554  		t.Fatal(err)
   555  	}
   556  
   557  	var obj struct {
   558  		TailMain  *Program `ebpf:"tail_main"`
   559  		ProgArray *Map     `ebpf:"prog_array_init"`
   560  	}
   561  
   562  	err = spec.LoadAndAssign(&obj, nil)
   563  	testutils.SkipIfNotSupported(t, err)
   564  	if err != nil {
   565  		t.Fatal(err)
   566  	}
   567  	defer obj.TailMain.Close()
   568  	defer obj.ProgArray.Close()
   569  
   570  	ret, _, err := obj.TailMain.Test(internal.EmptyBPFContext)
   571  	testutils.SkipIfNotSupported(t, err)
   572  	if err != nil {
   573  		t.Fatal(err)
   574  	}
   575  
   576  	// Expect the tail_1 tail call to be taken, returning value 42.
   577  	if ret != 42 {
   578  		t.Fatalf("Expected tail call to return value 42, got %d", ret)
   579  	}
   580  }
   581  
   582  func TestKconfigKernelVersion(t *testing.T) {
   583  	file := testutils.NativeFile(t, "testdata/kconfig-%s.elf")
   584  	spec, err := LoadCollectionSpec(file)
   585  	if err != nil {
   586  		t.Fatal(err)
   587  	}
   588  
   589  	var obj struct {
   590  		Main *Program `ebpf:"kernel_version"`
   591  	}
   592  
   593  	testutils.SkipOnOldKernel(t, "5.2", "readonly maps")
   594  
   595  	err = spec.LoadAndAssign(&obj, nil)
   596  	if err != nil {
   597  		t.Fatal(err)
   598  	}
   599  	defer obj.Main.Close()
   600  
   601  	ret, _, err := obj.Main.Test(internal.EmptyBPFContext)
   602  	testutils.SkipIfNotSupported(t, err)
   603  	if err != nil {
   604  		t.Fatal(err)
   605  	}
   606  
   607  	v, err := internal.KernelVersion()
   608  	if err != nil {
   609  		t.Fatalf("getting kernel version: %s", err)
   610  	}
   611  
   612  	version := v.Kernel()
   613  	if ret != version {
   614  		t.Fatalf("Expected eBPF to return value %d, got %d", version, ret)
   615  	}
   616  }
   617  
   618  func TestKconfigSyscallWrapper(t *testing.T) {
   619  	file := testutils.NativeFile(t, "testdata/kconfig-%s.elf")
   620  	spec, err := LoadCollectionSpec(file)
   621  	if err != nil {
   622  		t.Fatal(err)
   623  	}
   624  
   625  	var obj struct {
   626  		Main *Program `ebpf:"syscall_wrapper"`
   627  	}
   628  
   629  	err = spec.LoadAndAssign(&obj, nil)
   630  	testutils.SkipIfNotSupported(t, err)
   631  	if err != nil {
   632  		t.Fatal(err)
   633  	}
   634  	defer obj.Main.Close()
   635  
   636  	ret, _, err := obj.Main.Test(internal.EmptyBPFContext)
   637  	testutils.SkipIfNotSupported(t, err)
   638  	if err != nil {
   639  		t.Fatal(err)
   640  	}
   641  
   642  	var expected uint32
   643  	if testutils.IsKernelLessThan(t, "4.17") {
   644  		expected = 0
   645  	} else {
   646  		expected = 1
   647  	}
   648  
   649  	if ret != expected {
   650  		t.Fatalf("Expected eBPF to return value %d, got %d", expected, ret)
   651  	}
   652  }
   653  
   654  func TestKconfigConfig(t *testing.T) {
   655  	file := testutils.NativeFile(t, "testdata/kconfig_config-%s.elf")
   656  	spec, err := LoadCollectionSpec(file)
   657  	if err != nil {
   658  		t.Fatal(err)
   659  	}
   660  
   661  	var obj struct {
   662  		Main     *Program `ebpf:"kconfig"`
   663  		ArrayMap *Map     `ebpf:"array_map"`
   664  	}
   665  
   666  	err = spec.LoadAndAssign(&obj, nil)
   667  	testutils.SkipIfNotSupported(t, err)
   668  	if err != nil {
   669  		t.Fatal(err)
   670  	}
   671  	defer obj.Main.Close()
   672  	defer obj.ArrayMap.Close()
   673  
   674  	_, _, err = obj.Main.Test(internal.EmptyBPFContext)
   675  	testutils.SkipIfNotSupported(t, err)
   676  	if err != nil {
   677  		t.Fatal(err)
   678  	}
   679  
   680  	var value uint64
   681  	err = obj.ArrayMap.Lookup(uint32(0), &value)
   682  	if err != nil {
   683  		t.Fatal(err)
   684  	}
   685  
   686  	// CONFIG_HZ must have a value.
   687  	qt.Assert(t, qt.Not(qt.Equals(value, 0)))
   688  }
   689  
   690  func TestKfunc(t *testing.T) {
   691  	testutils.SkipOnOldKernel(t, "5.18", "kfunc support")
   692  	file := testutils.NativeFile(t, "testdata/kfunc-%s.elf")
   693  	spec, err := LoadCollectionSpec(file)
   694  	if err != nil {
   695  		t.Fatal(err)
   696  	}
   697  
   698  	var obj struct {
   699  		Main *Program `ebpf:"call_kfunc"`
   700  	}
   701  
   702  	err = spec.LoadAndAssign(&obj, nil)
   703  	testutils.SkipIfNotSupported(t, err)
   704  	if err != nil {
   705  		t.Fatalf("%+v", err)
   706  	}
   707  	defer obj.Main.Close()
   708  
   709  	ret, _, err := obj.Main.Test(internal.EmptyBPFContext)
   710  	testutils.SkipIfNotSupported(t, err)
   711  	if err != nil {
   712  		t.Fatal(err)
   713  	}
   714  
   715  	if ret != 1 {
   716  		t.Fatalf("Expected kfunc to return value 1, got %d", ret)
   717  	}
   718  }
   719  
   720  func TestWeakKfunc(t *testing.T) {
   721  	testutils.SkipOnOldKernel(t, "5.18", "kfunc support")
   722  	file := testutils.NativeFile(t, "testdata/kfunc-%s.elf")
   723  	spec, err := LoadCollectionSpec(file)
   724  	if err != nil {
   725  		t.Fatal(err)
   726  	}
   727  
   728  	var obj struct {
   729  		Missing *Program `ebpf:"weak_kfunc_missing"`
   730  		Calling *Program `ebpf:"call_weak_kfunc"`
   731  	}
   732  
   733  	err = spec.LoadAndAssign(&obj, nil)
   734  	testutils.SkipIfNotSupported(t, err)
   735  	if err != nil {
   736  		t.Fatalf("%+v", err)
   737  	}
   738  	defer obj.Missing.Close()
   739  	defer obj.Calling.Close()
   740  }
   741  
   742  func TestInvalidKfunc(t *testing.T) {
   743  	testutils.SkipOnOldKernel(t, "5.18", "kfunc support")
   744  
   745  	if !haveTestmod(t) {
   746  		t.Skip("bpf_testmod not loaded")
   747  	}
   748  
   749  	file := testutils.NativeFile(t, "testdata/invalid-kfunc-%s.elf")
   750  	coll, err := LoadCollection(file)
   751  	if err == nil {
   752  		coll.Close()
   753  		t.Fatal("Expected an error")
   754  	}
   755  
   756  	var ike *incompatibleKfuncError
   757  	if !errors.As(err, &ike) {
   758  		t.Fatalf("Expected an error wrapping incompatibleKfuncError, got %s", err)
   759  	}
   760  }
   761  
   762  func TestKfuncKmod(t *testing.T) {
   763  	testutils.SkipOnOldKernel(t, "5.18", "Kernel module function calls")
   764  
   765  	if !haveTestmod(t) {
   766  		t.Skip("bpf_testmod not loaded")
   767  	}
   768  
   769  	file := testutils.NativeFile(t, "testdata/kfunc-kmod-%s.elf")
   770  	spec, err := LoadCollectionSpec(file)
   771  	if err != nil {
   772  		t.Fatal(err)
   773  	}
   774  
   775  	var obj struct {
   776  		Main *Program `ebpf:"call_kfunc"`
   777  	}
   778  
   779  	err = spec.LoadAndAssign(&obj, nil)
   780  	testutils.SkipIfNotSupported(t, err)
   781  	if err != nil {
   782  		t.Fatalf("%v+", err)
   783  	}
   784  	defer obj.Main.Close()
   785  
   786  	ret, _, err := obj.Main.Test(internal.EmptyBPFContext)
   787  	testutils.SkipIfNotSupported(t, err)
   788  	if err != nil {
   789  		t.Fatal(err)
   790  	}
   791  
   792  	if ret != 1 {
   793  		t.Fatalf("Expected kfunc to return value 1, got %d", ret)
   794  	}
   795  }
   796  
   797  func TestSubprogRelocation(t *testing.T) {
   798  	testutils.SkipOnOldKernel(t, "5.13", "bpf_for_each_map_elem")
   799  
   800  	file := testutils.NativeFile(t, "testdata/subprog_reloc-%s.elf")
   801  	spec, err := LoadCollectionSpec(file)
   802  	if err != nil {
   803  		t.Fatal(err)
   804  	}
   805  
   806  	var obj struct {
   807  		Main    *Program `ebpf:"fp_relocation"`
   808  		HashMap *Map     `ebpf:"hash_map"`
   809  	}
   810  
   811  	err = spec.LoadAndAssign(&obj, nil)
   812  	testutils.SkipIfNotSupported(t, err)
   813  	if err != nil {
   814  		t.Fatal(err)
   815  	}
   816  	defer obj.Main.Close()
   817  	defer obj.HashMap.Close()
   818  
   819  	ret, _, err := obj.Main.Test(internal.EmptyBPFContext)
   820  	testutils.SkipIfNotSupported(t, err)
   821  	if err != nil {
   822  		t.Fatal(err)
   823  	}
   824  
   825  	if ret != 42 {
   826  		t.Fatalf("Expected subprog reloc to return value 42, got %d", ret)
   827  	}
   828  }
   829  
   830  func TestUnassignedProgArray(t *testing.T) {
   831  	file := testutils.NativeFile(t, "testdata/btf_map_init-%s.elf")
   832  	spec, err := LoadCollectionSpec(file)
   833  	if err != nil {
   834  		t.Fatal(err)
   835  	}
   836  
   837  	// tail_main references a ProgArray that is not being assigned
   838  	// to this struct. Normally, this would clear all its entries
   839  	// and make any tail calls into the ProgArray result in a miss.
   840  	// The library needs to explicitly refuse such operations.
   841  	var obj struct {
   842  		TailMain *Program `ebpf:"tail_main"`
   843  		// ProgArray *Map     `ebpf:"prog_array_init"`
   844  	}
   845  
   846  	err = spec.LoadAndAssign(&obj, nil)
   847  	testutils.SkipIfNotSupported(t, err)
   848  	if err == nil {
   849  		obj.TailMain.Close()
   850  		t.Fatal("Expecting LoadAndAssign to return error")
   851  	}
   852  }
   853  
   854  func TestIPRoute2Compat(t *testing.T) {
   855  	file := testutils.NativeFile(t, "testdata/iproute2_map_compat-%s.elf")
   856  	spec, err := LoadCollectionSpec(file)
   857  	if err != nil {
   858  		t.Fatal("Can't parse ELF:", err)
   859  	}
   860  
   861  	ms, ok := spec.Maps["hash_map"]
   862  	if !ok {
   863  		t.Fatal("Map hash_map not found")
   864  	}
   865  
   866  	var id, pinning, innerID, innerIndex uint32
   867  
   868  	if ms.Extra == nil {
   869  		t.Fatal("missing extra bytes")
   870  	}
   871  
   872  	switch {
   873  	case binary.Read(ms.Extra, spec.ByteOrder, &id) != nil:
   874  		t.Fatal("missing id")
   875  	case binary.Read(ms.Extra, spec.ByteOrder, &pinning) != nil:
   876  		t.Fatal("missing pinning")
   877  	case binary.Read(ms.Extra, spec.ByteOrder, &innerID) != nil:
   878  		t.Fatal("missing inner_id")
   879  	case binary.Read(ms.Extra, spec.ByteOrder, &innerIndex) != nil:
   880  		t.Fatal("missing inner_idx")
   881  	}
   882  
   883  	if id != 0 || innerID != 0 || innerIndex != 0 {
   884  		t.Fatal("expecting id, inner_id and inner_idx to be zero")
   885  	}
   886  
   887  	if pinning != 2 {
   888  		t.Fatal("expecting pinning field to be 2 (PIN_GLOBAL_NS)")
   889  	}
   890  
   891  	// iproute2 (tc) pins maps in /sys/fs/bpf/tc/globals with PIN_GLOBAL_NS,
   892  	// which needs to be configured in this library using MapOptions.PinPath.
   893  	// For the sake of the test, we use a tempdir on bpffs below.
   894  	ms.Pinning = PinByName
   895  
   896  	coll, err := NewCollectionWithOptions(spec, CollectionOptions{
   897  		Maps: MapOptions{
   898  			PinPath: testutils.TempBPFFS(t),
   899  		},
   900  	})
   901  	testutils.SkipIfNotSupported(t, err)
   902  	if err != nil {
   903  		t.Fatal("Can't create collection:", err)
   904  	}
   905  
   906  	coll.Close()
   907  }
   908  
   909  var (
   910  	elfPath    = flag.String("elfs", os.Getenv("CI_KERNEL_SELFTESTS"), "`Path` containing libbpf-compatible ELFs (defaults to $CI_KERNEL_SELFTESTS)")
   911  	elfPattern = flag.String("elf-pattern", "*.o", "Glob `pattern` for object files that should be tested")
   912  )
   913  
   914  func TestLibBPFCompat(t *testing.T) {
   915  	if *elfPath == "" {
   916  		// Specify the path to the directory containing the eBPF for
   917  		// the kernel's selftests if you want to run this test.
   918  		// As of 5.2 that is tools/testing/selftests/bpf/
   919  		t.Skip("No path specified")
   920  	}
   921  
   922  	load := func(t *testing.T, spec *CollectionSpec, opts CollectionOptions, valid bool) {
   923  		// Disable retrying a program load with the log enabled, it leads
   924  		// to OOM kills.
   925  		opts.Programs.LogDisabled = true
   926  
   927  		coll, err := NewCollectionWithOptions(spec, opts)
   928  		testutils.SkipIfNotSupported(t, err)
   929  		var errno syscall.Errno
   930  		if errors.As(err, &errno) {
   931  			// This error is most likely from a syscall and caused by us not
   932  			// replicating some fixups done in the selftests or the test
   933  			// intentionally failing. This is expected, so skip the test
   934  			// instead of failing.
   935  			t.Skip("Skipping since the kernel rejected the program:", err)
   936  		}
   937  		if err == nil {
   938  			coll.Close()
   939  		}
   940  		if !valid {
   941  			if err == nil {
   942  				t.Fatal("Expected an error during load")
   943  			}
   944  		} else if err != nil {
   945  			t.Fatal("Error during loading:", err)
   946  		}
   947  	}
   948  
   949  	files := testutils.Glob(t, filepath.Join(*elfPath, *elfPattern),
   950  		// These files are only used as a source of btf.
   951  		"btf__core_reloc_*",
   952  	)
   953  
   954  	testutils.Files(t, files, func(t *testing.T, path string) {
   955  		name := selftestName(path)
   956  		switch name {
   957  		case "test_map_in_map", "test_select_reuseport_kern":
   958  			t.Skip("Skipping due to missing InnerMap in map definition")
   959  		case "test_core_autosize":
   960  			t.Skip("Skipping since the test generates dynamic BTF")
   961  		case "test_static_linked":
   962  			t.Skip("Skipping since .text contains 'subprog' twice")
   963  		case "bloom_filter_map", "bloom_filter_bench":
   964  			t.Skip("Skipping due to missing MapExtra field in MapSpec")
   965  		case "netif_receive_skb",
   966  			"local_kptr_stash",
   967  			"local_kptr_stash_fail",
   968  			"type_cast",
   969  			"preempted_bpf_ma_op",
   970  			"percpu_alloc_fail":
   971  			// Error message like
   972  			//    fixup for CORERelocation(local_type_id, Struct:"bin_data"[0],
   973  			//    local_id=27): invalid immediate 31, expected 27 (fixup: local_type_id=27->1)
   974  			// See https://github.com/cilium/ebpf/issues/739
   975  			t.Skip("Skipping due to bug in libbpf type deduplication")
   976  		case "test_usdt", "test_urandom_usdt", "test_usdt_multispec":
   977  			t.Skip("Skipping due to missing support for usdt.bpf.h")
   978  		case "lsm_cgroup", "bpf_iter_ipv6_route", "test_core_extern",
   979  			"profiler1", "profiler2", "profiler3":
   980  			t.Skip("Skipping due to using weak CONFIG_* variables")
   981  		case "linked_maps", "linked_maps1", "linked_maps2", "linked_funcs1", "linked_funcs2",
   982  			"test_subskeleton", "test_subskeleton_lib":
   983  			t.Skip("Skipping due to relying on cross ELF linking")
   984  		case "test_log_fixup":
   985  			t.Skip("Skipping due to intentionally broken CO-RE relocations")
   986  		}
   987  
   988  		t.Parallel()
   989  
   990  		spec, err := LoadCollectionSpec(path)
   991  		testutils.SkipIfNotSupported(t, err)
   992  		if errors.Is(err, errUnsupportedBinding) {
   993  			t.Skip(err)
   994  		}
   995  		if err != nil {
   996  			t.Fatal(err)
   997  		}
   998  
   999  		switch name {
  1000  		case "test_sk_assign":
  1001  			// Test contains a legacy iproute2 bpf_elf_map definition.
  1002  			for _, m := range spec.Maps {
  1003  				if m.Extra == nil || m.Extra.Len() == 0 {
  1004  					t.Fatalf("Expected extra bytes in map %s", m.Name)
  1005  				}
  1006  				m.Extra = nil
  1007  			}
  1008  
  1009  		case "fexit_bpf2bpf",
  1010  			"freplace_get_constant",
  1011  			"freplace_global_func":
  1012  			loadTargetProgram(t, spec, "test_pkt_access.bpf.o", "test_pkt_access")
  1013  
  1014  		case "freplace_cls_redirect":
  1015  			loadTargetProgram(t, spec, "test_cls_redirect.bpf.o", "cls_redirect")
  1016  
  1017  		case "test_trace_ext":
  1018  			loadTargetProgram(t, spec, "test_pkt_md_access.bpf.o", "test_pkt_md_access")
  1019  
  1020  		case "freplace_progmap":
  1021  			loadTargetProgram(t, spec, "xdp_dummy.bpf.o", "xdp_dummy_prog")
  1022  
  1023  			if prog := spec.Programs["xdp_cpumap_prog"]; prog.AttachTo == "" {
  1024  				prog.AttachTo = "xdp_dummy_prog"
  1025  			}
  1026  
  1027  		case "freplace_attach_probe":
  1028  			// Looks like the test should have a target, but 6.6 selftests don't
  1029  			// seem to be using it.
  1030  		}
  1031  
  1032  		var opts CollectionOptions
  1033  		for _, mapSpec := range spec.Maps {
  1034  			if mapSpec.Pinning != PinNone {
  1035  				opts.Maps.PinPath = testutils.TempBPFFS(t)
  1036  				break
  1037  			}
  1038  		}
  1039  
  1040  		coreFiles := sourceOfBTF(t, path)
  1041  		if len(coreFiles) == 0 {
  1042  			// NB: test_core_reloc_kernel.o doesn't have dedicated BTF and
  1043  			// therefore goes via this code path.
  1044  			load(t, spec, opts, true)
  1045  			return
  1046  		}
  1047  
  1048  		for _, coreFile := range coreFiles {
  1049  			name := selftestName(coreFile)
  1050  			t.Run(name, func(t *testing.T) {
  1051  				// Some files like btf__core_reloc_arrays___err_too_small.o
  1052  				// trigger an error on purpose. Use the name to infer whether
  1053  				// the test should succeed.
  1054  				var valid bool
  1055  				switch name {
  1056  				case "btf__core_reloc_existence___err_wrong_arr_kind",
  1057  					"btf__core_reloc_existence___err_wrong_arr_value_type",
  1058  					"btf__core_reloc_existence___err_wrong_int_kind",
  1059  					"btf__core_reloc_existence___err_wrong_int_sz",
  1060  					"btf__core_reloc_existence___err_wrong_int_type",
  1061  					"btf__core_reloc_existence___err_wrong_struct_type":
  1062  					// These tests are buggy upstream, see https://lore.kernel.org/bpf/20210420111639.155580-1-lmb@cloudflare.com/
  1063  					valid = true
  1064  				case "btf__core_reloc_ints___err_wrong_sz_16",
  1065  					"btf__core_reloc_ints___err_wrong_sz_32",
  1066  					"btf__core_reloc_ints___err_wrong_sz_64",
  1067  					"btf__core_reloc_ints___err_wrong_sz_8",
  1068  					"btf__core_reloc_arrays___err_wrong_val_type1",
  1069  					"btf__core_reloc_arrays___err_wrong_val_type2":
  1070  					// These tests are valid according to current libbpf behaviour,
  1071  					// see commit 42765ede5c54ca915de5bfeab83be97207e46f68.
  1072  					valid = true
  1073  				case "btf__core_reloc_type_id___missing_targets",
  1074  					"btf__core_reloc_flavors__err_wrong_name":
  1075  					valid = false
  1076  				case "btf__core_reloc_ints___err_bitfield":
  1077  					// Bitfields are now valid.
  1078  					valid = true
  1079  				default:
  1080  					valid = !strings.Contains(name, "___err_")
  1081  				}
  1082  
  1083  				fh, err := os.Open(coreFile)
  1084  				if err != nil {
  1085  					t.Fatal(err)
  1086  				}
  1087  				defer fh.Close()
  1088  
  1089  				btfSpec, err := btf.LoadSpec(coreFile)
  1090  				if err != nil {
  1091  					t.Fatal(err)
  1092  				}
  1093  
  1094  				opts := opts // copy
  1095  				opts.Programs.KernelTypes = btfSpec
  1096  				load(t, spec, opts, valid)
  1097  			})
  1098  		}
  1099  	})
  1100  }
  1101  
  1102  func loadTargetProgram(tb testing.TB, spec *CollectionSpec, file, program string) {
  1103  	targetSpec, err := LoadCollectionSpec(filepath.Join(*elfPath, file))
  1104  	if errors.Is(err, os.ErrNotExist) && strings.HasSuffix(file, ".bpf.o") {
  1105  		// Prior to v6.1 BPF ELF used a plain .o suffix.
  1106  		file = strings.TrimSuffix(file, ".bpf.o") + ".o"
  1107  		targetSpec, err = LoadCollectionSpec(filepath.Join(*elfPath, file))
  1108  	}
  1109  	if err != nil {
  1110  		tb.Fatalf("Can't read %s: %s", file, err)
  1111  	}
  1112  
  1113  	qt.Assert(tb, qt.IsNotNil(targetSpec.Programs[program]))
  1114  
  1115  	coll, err := NewCollectionWithOptions(targetSpec, CollectionOptions{
  1116  		Programs: ProgramOptions{LogDisabled: true},
  1117  	})
  1118  	if err != nil {
  1119  		tb.Fatalf("Can't load target: %s", err)
  1120  	}
  1121  	tb.Cleanup(func() { coll.Close() })
  1122  
  1123  	target := coll.Programs[program]
  1124  	for _, prog := range spec.Programs {
  1125  		if prog.Type == Extension && prog.AttachType == AttachNone {
  1126  			prog.AttachTarget = target
  1127  			continue
  1128  		}
  1129  
  1130  		if prog.Type == Tracing {
  1131  			switch prog.AttachType {
  1132  			case AttachTraceFEntry, AttachTraceFExit, AttachModifyReturn:
  1133  				prog.AttachTarget = target
  1134  				continue
  1135  			}
  1136  		}
  1137  	}
  1138  }
  1139  
  1140  func sourceOfBTF(tb testing.TB, path string) []string {
  1141  	const testPrefix = "test_core_reloc_"
  1142  	const btfPrefix = "btf__core_reloc_"
  1143  
  1144  	dir, base := filepath.Split(path)
  1145  	if !strings.HasPrefix(base, testPrefix) {
  1146  		return nil
  1147  	}
  1148  
  1149  	base = strings.TrimSuffix(base[len(testPrefix):], ".o")
  1150  	switch base {
  1151  	case "bitfields_direct", "bitfields_probed":
  1152  		base = "bitfields"
  1153  	}
  1154  
  1155  	return testutils.Glob(tb, filepath.Join(dir, btfPrefix+base+"*.o"))
  1156  }
  1157  
  1158  func TestELFSectionProgramTypes(t *testing.T) {
  1159  	type testcase struct {
  1160  		Section     string
  1161  		ProgramType ProgramType
  1162  		AttachType  AttachType
  1163  		Flags       uint32
  1164  		Extra       string
  1165  	}
  1166  
  1167  	testcases := []testcase{
  1168  		{"socket", SocketFilter, AttachNone, 0, ""},
  1169  		{"socket/garbage", SocketFilter, AttachNone, 0, ""},
  1170  		{"sk_reuseport/migrate", SkReuseport, AttachSkReuseportSelectOrMigrate, 0, ""},
  1171  		{"sk_reuseport", SkReuseport, AttachSkReuseportSelect, 0, ""},
  1172  		{"kprobe/", Kprobe, AttachNone, 0, ""},
  1173  		{"kprobe/func", Kprobe, AttachNone, 0, "func"},
  1174  		{"uprobe/", Kprobe, AttachNone, 0, ""},
  1175  		{"kretprobe/", Kprobe, AttachNone, 0, ""},
  1176  		{"uretprobe/", Kprobe, AttachNone, 0, ""},
  1177  		{"tc", SchedCLS, AttachNone, 0, ""},
  1178  		{"classifier", SchedCLS, AttachNone, 0, ""},
  1179  		{"action", SchedACT, AttachNone, 0, ""},
  1180  		{"tracepoint/", TracePoint, AttachNone, 0, ""},
  1181  		{"tp/", TracePoint, AttachNone, 0, ""},
  1182  		{"raw_tracepoint/", RawTracepoint, AttachNone, 0, ""},
  1183  		{"raw_tp/", RawTracepoint, AttachNone, 0, ""},
  1184  		{"raw_tracepoint.w/", RawTracepointWritable, AttachNone, 0, ""},
  1185  		{"raw_tp.w/", RawTracepointWritable, AttachNone, 0, ""},
  1186  		{"tp_btf/", Tracing, AttachTraceRawTp, 0, ""},
  1187  		{"fentry/", Tracing, AttachTraceFEntry, 0, ""},
  1188  		{"fmod_ret/", Tracing, AttachModifyReturn, 0, ""},
  1189  		{"fexit/", Tracing, AttachTraceFExit, 0, ""},
  1190  		{"fentry.s/", Tracing, AttachTraceFEntry, unix.BPF_F_SLEEPABLE, ""},
  1191  		{"fmod_ret.s/", Tracing, AttachModifyReturn, unix.BPF_F_SLEEPABLE, ""},
  1192  		{"fexit.s/", Tracing, AttachTraceFExit, unix.BPF_F_SLEEPABLE, ""},
  1193  		{"freplace/", Extension, AttachNone, 0, ""},
  1194  		{"lsm/foo", LSM, AttachLSMMac, 0, "foo"},
  1195  		{"lsm.s/foo", LSM, AttachLSMMac, unix.BPF_F_SLEEPABLE, "foo"},
  1196  		{"iter/bpf_map", Tracing, AttachTraceIter, 0, "bpf_map"},
  1197  		{"iter.s/", Tracing, AttachTraceIter, unix.BPF_F_SLEEPABLE, ""},
  1198  		// Was missing sleepable.
  1199  		{"syscall", Syscall, AttachNone, unix.BPF_F_SLEEPABLE, ""},
  1200  		{"xdp.frags_devmap/foo", XDP, AttachXDPDevMap, unix.BPF_F_XDP_HAS_FRAGS, "foo"},
  1201  		{"xdp_devmap/foo", XDP, AttachXDPDevMap, 0, "foo"},
  1202  		{"xdp.frags_cpumap/", XDP, AttachXDPCPUMap, unix.BPF_F_XDP_HAS_FRAGS, ""},
  1203  		{"xdp_cpumap/", XDP, AttachXDPCPUMap, 0, ""},
  1204  		// Used incorrect attach type.
  1205  		{"xdp.frags/foo", XDP, AttachXDP, unix.BPF_F_XDP_HAS_FRAGS, ""},
  1206  		{"xdp/foo", XDP, AttachNone, 0, ""},
  1207  		{"perf_event", PerfEvent, AttachNone, 0, ""},
  1208  		{"lwt_in", LWTIn, AttachNone, 0, ""},
  1209  		{"lwt_out", LWTOut, AttachNone, 0, ""},
  1210  		{"lwt_xmit", LWTXmit, AttachNone, 0, ""},
  1211  		{"lwt_seg6local", LWTSeg6Local, AttachNone, 0, ""},
  1212  		{"cgroup_skb/ingress", CGroupSKB, AttachCGroupInetIngress, 0, ""},
  1213  		{"cgroup_skb/egress", CGroupSKB, AttachCGroupInetEgress, 0, ""},
  1214  		{"cgroup/skb", CGroupSKB, AttachNone, 0, ""},
  1215  		{"cgroup/sock_create", CGroupSock, AttachCGroupInetSockCreate, 0, ""},
  1216  		{"cgroup/sock_release", CGroupSock, AttachCgroupInetSockRelease, 0, ""},
  1217  		{"cgroup/sock", CGroupSock, AttachCGroupInetSockCreate, 0, ""},
  1218  		{"cgroup/post_bind4", CGroupSock, AttachCGroupInet4PostBind, 0, ""},
  1219  		{"cgroup/post_bind6", CGroupSock, AttachCGroupInet6PostBind, 0, ""},
  1220  		{"cgroup/dev", CGroupDevice, AttachCGroupDevice, 0, ""},
  1221  		{"sockops", SockOps, AttachCGroupSockOps, 0, ""},
  1222  		{"sk_skb/stream_parser", SkSKB, AttachSkSKBStreamParser, 0, ""},
  1223  		{"sk_skb/stream_verdict", SkSKB, AttachSkSKBStreamVerdict, 0, ""},
  1224  		{"sk_skb/stream_verdict/foo", SkSKB, AttachSkSKBStreamVerdict, 0, ""},
  1225  		{"sk_skb", SkSKB, AttachNone, 0, ""},
  1226  		{"sk_skb/bar", SkSKB, AttachNone, 0, ""},
  1227  		{"sk_msg", SkMsg, AttachSkMsgVerdict, 0, ""},
  1228  		{"lirc_mode2", LircMode2, AttachLircMode2, 0, ""},
  1229  		{"flow_dissector", FlowDissector, AttachFlowDissector, 0, ""},
  1230  		{"cgroup/bind4", CGroupSockAddr, AttachCGroupInet4Bind, 0, ""},
  1231  		{"cgroup/bind6", CGroupSockAddr, AttachCGroupInet6Bind, 0, ""},
  1232  		{"cgroup/connect4", CGroupSockAddr, AttachCGroupInet4Connect, 0, ""},
  1233  		{"cgroup/connect6", CGroupSockAddr, AttachCGroupInet6Connect, 0, ""},
  1234  		{"cgroup/sendmsg4", CGroupSockAddr, AttachCGroupUDP4Sendmsg, 0, ""},
  1235  		{"cgroup/sendmsg6", CGroupSockAddr, AttachCGroupUDP6Sendmsg, 0, ""},
  1236  		{"cgroup/recvmsg4", CGroupSockAddr, AttachCGroupUDP4Recvmsg, 0, ""},
  1237  		{"cgroup/recvmsg6", CGroupSockAddr, AttachCGroupUDP6Recvmsg, 0, ""},
  1238  		{"cgroup/getpeername4", CGroupSockAddr, AttachCgroupInet4GetPeername, 0, ""},
  1239  		{"cgroup/getpeername6", CGroupSockAddr, AttachCgroupInet6GetPeername, 0, ""},
  1240  		{"cgroup/getsockname4", CGroupSockAddr, AttachCgroupInet4GetSockname, 0, ""},
  1241  		{"cgroup/getsockname6", CGroupSockAddr, AttachCgroupInet6GetSockname, 0, ""},
  1242  		{"cgroup/sysctl", CGroupSysctl, AttachCGroupSysctl, 0, ""},
  1243  		{"cgroup/getsockopt", CGroupSockopt, AttachCGroupGetsockopt, 0, ""},
  1244  		{"cgroup/setsockopt", CGroupSockopt, AttachCGroupSetsockopt, 0, ""},
  1245  		// Bogus pattern means it never matched anything.
  1246  		// {"struct_ops+", StructOps, AttachNone, 0, ""},
  1247  		{"sk_lookup/", SkLookup, AttachSkLookup, 0, ""},
  1248  		{"seccomp", SocketFilter, AttachNone, 0, ""},
  1249  		{"kprobe.multi", Kprobe, AttachTraceKprobeMulti, 0, ""},
  1250  		{"kretprobe.multi", Kprobe, AttachTraceKprobeMulti, 0, ""},
  1251  	}
  1252  
  1253  	for _, tc := range testcases {
  1254  		t.Run(tc.Section, func(t *testing.T) {
  1255  			pt, at, fl, extra := getProgType(tc.Section)
  1256  			have := testcase{tc.Section, pt, at, fl, extra}
  1257  			qt.Assert(t, qt.DeepEquals(have, tc))
  1258  		})
  1259  	}
  1260  }
  1261  
  1262  func TestMatchSectionName(t *testing.T) {
  1263  	for _, testcase := range []struct {
  1264  		pattern string
  1265  		input   string
  1266  		matches bool
  1267  		extra   string
  1268  	}{
  1269  		{"prefix/", "prefix/", true, ""},
  1270  		{"prefix/", "prefix/a", true, "a"},
  1271  		{"prefix/", "prefix/b", true, "b"},
  1272  		{"prefix/", "prefix", false, ""},
  1273  		{"prefix/", "junk", false, ""},
  1274  
  1275  		{"prefix+", "prefix/", true, ""},
  1276  		{"prefix+", "prefix/a", true, "a"},
  1277  		{"prefix+", "prefix/b", true, "b"},
  1278  		{"prefix+", "prefix", true, ""},
  1279  		{"prefix+", "junk", false, ""},
  1280  
  1281  		{"exact", "exact", true, ""},
  1282  		{"exact", "exact/", true, ""},
  1283  		{"exact", "exact/a", true, ""},
  1284  		{"exact", "exactement", true, ""},
  1285  		{"exact", "junk", false, ""},
  1286  	} {
  1287  		name := fmt.Sprintf("%s:%s", testcase.pattern, testcase.input)
  1288  		t.Run(name, func(t *testing.T) {
  1289  			extra, matches := matchSectionName(testcase.input, testcase.pattern)
  1290  			qt.Assert(t, qt.Equals(matches, testcase.matches))
  1291  			if testcase.matches {
  1292  				qt.Assert(t, qt.Equals(extra, testcase.extra))
  1293  			}
  1294  		})
  1295  	}
  1296  }
  1297  
  1298  // selftestName takes a path to a file and derives a canonical name from it.
  1299  //
  1300  // It strips various suffixes used by the selftest build system.
  1301  func selftestName(path string) string {
  1302  	file := filepath.Base(path)
  1303  
  1304  	name := strings.TrimSuffix(file, ".o")
  1305  	// Strip various suffixes.
  1306  	// Various linking suffixes.
  1307  	name = strings.TrimSuffix(name, ".linked3")
  1308  	name = strings.TrimSuffix(name, ".llinked1")
  1309  	name = strings.TrimSuffix(name, ".llinked2")
  1310  	name = strings.TrimSuffix(name, ".llinked3")
  1311  	// v6.1 started adding .bpf to all BPF ELF.
  1312  	name = strings.TrimSuffix(name, ".bpf")
  1313  
  1314  	return name
  1315  }