github.com/cilium/ebpf@v0.10.0/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  
    24  func TestLoadCollectionSpec(t *testing.T) {
    25  	cpus, err := internal.PossibleCPUs()
    26  	if err != nil {
    27  		t.Fatal(err)
    28  	}
    29  
    30  	coll := &CollectionSpec{
    31  		Maps: map[string]*MapSpec{
    32  			"hash_map": {
    33  				Name:       "hash_map",
    34  				Type:       Hash,
    35  				KeySize:    4,
    36  				ValueSize:  8,
    37  				MaxEntries: 1,
    38  				Flags:      unix.BPF_F_NO_PREALLOC,
    39  			},
    40  			"hash_map2": {
    41  				Name:       "hash_map2",
    42  				Type:       Hash,
    43  				KeySize:    4,
    44  				ValueSize:  8,
    45  				MaxEntries: 2,
    46  			},
    47  			"array_of_hash_map": {
    48  				Name:       "array_of_hash_map",
    49  				Type:       ArrayOfMaps,
    50  				KeySize:    4,
    51  				MaxEntries: 2,
    52  			},
    53  			"perf_event_array": {
    54  				Name:       "perf_event_array",
    55  				Type:       PerfEventArray,
    56  				MaxEntries: uint32(cpus),
    57  			},
    58  			// Maps prefixed by btf_ are ignored when testing ELFs
    59  			// that don't have BTF info embedded. (clang<9)
    60  			"btf_pin": {
    61  				Name:       "btf_pin",
    62  				Type:       Hash,
    63  				KeySize:    4,
    64  				ValueSize:  8,
    65  				MaxEntries: 1,
    66  				Pinning:    PinByName,
    67  			},
    68  			"btf_outer_map": {
    69  				Name:       "btf_outer_map",
    70  				Type:       ArrayOfMaps,
    71  				KeySize:    4,
    72  				ValueSize:  4,
    73  				MaxEntries: 1,
    74  				InnerMap: &MapSpec{
    75  					Name:       "btf_outer_map_inner",
    76  					Type:       Hash,
    77  					KeySize:    4,
    78  					ValueSize:  4,
    79  					MaxEntries: 1,
    80  				},
    81  			},
    82  			"btf_outer_map_anon": {
    83  				Name:       "btf_outer_map_anon",
    84  				Type:       ArrayOfMaps,
    85  				KeySize:    4,
    86  				ValueSize:  4,
    87  				MaxEntries: 1,
    88  				InnerMap: &MapSpec{
    89  					Name:       "btf_outer_map_anon_inner",
    90  					Type:       Hash,
    91  					KeySize:    4,
    92  					ValueSize:  4,
    93  					MaxEntries: 1,
    94  				},
    95  			},
    96  		},
    97  		Programs: map[string]*ProgramSpec{
    98  			"xdp_prog": {
    99  				Name:        "xdp_prog",
   100  				Type:        XDP,
   101  				SectionName: "xdp",
   102  				License:     "MIT",
   103  			},
   104  			"no_relocation": {
   105  				Name:        "no_relocation",
   106  				Type:        SocketFilter,
   107  				SectionName: "socket",
   108  				License:     "MIT",
   109  			},
   110  			"asm_relocation": {
   111  				Name:        "asm_relocation",
   112  				Type:        SocketFilter,
   113  				SectionName: "socket/2",
   114  				License:     "MIT",
   115  			},
   116  			"data_sections": {
   117  				Name:        "data_sections",
   118  				Type:        SocketFilter,
   119  				SectionName: "socket/3",
   120  				License:     "MIT",
   121  			},
   122  			"global_fn3": {
   123  				Name:        "global_fn3",
   124  				Type:        UnspecifiedProgram,
   125  				SectionName: "other",
   126  				License:     "MIT",
   127  			},
   128  			"static_fn": {
   129  				Name:        "static_fn",
   130  				Type:        UnspecifiedProgram,
   131  				SectionName: "static",
   132  				License:     "MIT",
   133  			},
   134  			"anon_const": {
   135  				Name:        "anon_const",
   136  				Type:        SocketFilter,
   137  				SectionName: "socket/4",
   138  				License:     "MIT",
   139  			},
   140  		},
   141  	}
   142  
   143  	defaultOpts := cmp.Options{
   144  		// Dummy Comparer that works with empty readers to support test cases.
   145  		cmp.Comparer(func(a, b bytes.Reader) bool {
   146  			if a.Len() == 0 && b.Len() == 0 {
   147  				return true
   148  			}
   149  			return false
   150  		}),
   151  		cmpopts.IgnoreTypes(new(btf.Spec)),
   152  		cmpopts.IgnoreFields(CollectionSpec{}, "ByteOrder", "Types"),
   153  		cmpopts.IgnoreFields(ProgramSpec{}, "Instructions", "ByteOrder"),
   154  		cmpopts.IgnoreFields(MapSpec{}, "Key", "Value"),
   155  		cmpopts.IgnoreUnexported(ProgramSpec{}),
   156  		cmpopts.IgnoreMapEntries(func(key string, _ *MapSpec) bool {
   157  			if key == ".bss" || key == ".data" || strings.HasPrefix(key, ".rodata") {
   158  				return true
   159  			}
   160  			return false
   161  		}),
   162  	}
   163  
   164  	ignoreBTFOpts := append(defaultOpts,
   165  		cmpopts.IgnoreMapEntries(func(key string, _ *MapSpec) bool {
   166  			return strings.HasPrefix(key, "btf_")
   167  		}),
   168  	)
   169  
   170  	testutils.Files(t, testutils.Glob(t, "testdata/loader-*.elf"), func(t *testing.T, file string) {
   171  		have, err := LoadCollectionSpec(file)
   172  		if err != nil {
   173  			t.Fatal("Can't parse ELF:", err)
   174  		}
   175  
   176  		opts := defaultOpts
   177  		if have.Types != nil {
   178  			err := have.RewriteConstants(map[string]interface{}{
   179  				"arg":  uint32(1),
   180  				"arg2": uint32(2),
   181  			})
   182  			if err != nil {
   183  				t.Fatal("Can't rewrite constant:", err)
   184  			}
   185  
   186  			err = have.RewriteConstants(map[string]interface{}{
   187  				"totallyBogus": uint32(1),
   188  			})
   189  			if err == nil {
   190  				t.Error("Rewriting a bogus constant doesn't fail")
   191  			}
   192  		} else {
   193  			opts = ignoreBTFOpts
   194  		}
   195  
   196  		if diff := cmp.Diff(coll, have, opts...); diff != "" {
   197  			t.Errorf("MapSpec mismatch (-want +got):\n%s", diff)
   198  		}
   199  
   200  		if have.ByteOrder != internal.NativeEndian {
   201  			return
   202  		}
   203  
   204  		have.Maps["array_of_hash_map"].InnerMap = have.Maps["hash_map"]
   205  		coll, err := NewCollectionWithOptions(have, CollectionOptions{
   206  			Maps: MapOptions{
   207  				PinPath: testutils.TempBPFFS(t),
   208  			},
   209  			Programs: ProgramOptions{
   210  				LogLevel: LogLevelBranch,
   211  			},
   212  		})
   213  		testutils.SkipIfNotSupported(t, err)
   214  		if err != nil {
   215  			t.Fatal(err)
   216  		}
   217  		defer coll.Close()
   218  
   219  		ret, _, err := coll.Programs["xdp_prog"].Test(internal.EmptyBPFContext)
   220  		if err != nil {
   221  			t.Fatal("Can't run program:", err)
   222  		}
   223  
   224  		if ret != 7 {
   225  			t.Error("Expected return value to be 5, got", ret)
   226  		}
   227  	})
   228  }
   229  
   230  func BenchmarkELFLoader(b *testing.B) {
   231  	b.ReportAllocs()
   232  
   233  	for i := 0; i < b.N; i++ {
   234  		_, _ = LoadCollectionSpec("testdata/loader-el.elf")
   235  	}
   236  }
   237  
   238  func TestDataSections(t *testing.T) {
   239  	file := fmt.Sprintf("testdata/loader-%s.elf", internal.ClangEndian)
   240  	coll, err := LoadCollectionSpec(file)
   241  	if err != nil {
   242  		t.Fatal(err)
   243  	}
   244  
   245  	t.Log(coll.Programs["data_sections"].Instructions)
   246  
   247  	var obj struct {
   248  		Program *Program `ebpf:"data_sections"`
   249  	}
   250  
   251  	err = coll.LoadAndAssign(&obj, nil)
   252  	testutils.SkipIfNotSupported(t, err)
   253  	if err != nil {
   254  		t.Fatal(err)
   255  	}
   256  	defer obj.Program.Close()
   257  
   258  	ret, _, err := obj.Program.Test(internal.EmptyBPFContext)
   259  	if err != nil {
   260  		t.Fatal(err)
   261  	}
   262  
   263  	if ret != 0 {
   264  		t.Error("BPF assertion failed on line", ret)
   265  	}
   266  }
   267  
   268  func TestInlineASMConstant(t *testing.T) {
   269  	file := fmt.Sprintf("testdata/loader-%s.elf", internal.ClangEndian)
   270  	coll, err := LoadCollectionSpec(file)
   271  	if err != nil {
   272  		t.Fatal(err)
   273  	}
   274  
   275  	spec := coll.Programs["asm_relocation"]
   276  	if spec.Instructions[0].Reference() != "MY_CONST" {
   277  		t.Fatal("First instruction is not a reference to MY_CONST")
   278  	}
   279  
   280  	// -1 is used by the loader to find unrewritten maps.
   281  	spec.Instructions[0].Constant = -1
   282  
   283  	t.Log(spec.Instructions)
   284  
   285  	var obj struct {
   286  		Program *Program `ebpf:"asm_relocation"`
   287  	}
   288  
   289  	err = coll.LoadAndAssign(&obj, nil)
   290  	testutils.SkipIfNotSupported(t, err)
   291  	if err != nil {
   292  		t.Fatal(err)
   293  	}
   294  	obj.Program.Close()
   295  }
   296  
   297  func TestCollectionSpecDetach(t *testing.T) {
   298  	coll := Collection{
   299  		Maps: map[string]*Map{
   300  			"foo": new(Map),
   301  		},
   302  		Programs: map[string]*Program{
   303  			"bar": new(Program),
   304  		},
   305  	}
   306  
   307  	foo := coll.DetachMap("foo")
   308  	if foo == nil {
   309  		t.Error("Program not returned from DetachMap")
   310  	}
   311  
   312  	if _, ok := coll.Programs["foo"]; ok {
   313  		t.Error("DetachMap doesn't remove map from Maps")
   314  	}
   315  
   316  	bar := coll.DetachProgram("bar")
   317  	if bar == nil {
   318  		t.Fatal("Program not returned from DetachProgram")
   319  	}
   320  
   321  	if _, ok := coll.Programs["bar"]; ok {
   322  		t.Error("DetachProgram doesn't remove program from Programs")
   323  	}
   324  }
   325  
   326  func TestLoadInvalidMap(t *testing.T) {
   327  	testutils.Files(t, testutils.Glob(t, "testdata/invalid_map-*.elf"), func(t *testing.T, file string) {
   328  		cs, err := LoadCollectionSpec(file)
   329  		if err != nil {
   330  			t.Fatal("Can't load CollectionSpec", err)
   331  		}
   332  
   333  		ms, ok := cs.Maps["invalid_map"]
   334  		if !ok {
   335  			t.Fatal("invalid_map not found in CollectionSpec")
   336  		}
   337  
   338  		m, err := NewMap(ms)
   339  		t.Log(err)
   340  		if err == nil {
   341  			m.Close()
   342  			t.Fatal("Creating a Map from a MapSpec with non-zero Extra is expected to fail.")
   343  		}
   344  	})
   345  }
   346  
   347  func TestLoadInvalidMapMissingSymbol(t *testing.T) {
   348  	testutils.Files(t, testutils.Glob(t, "testdata/invalid_map_static-el.elf"), func(t *testing.T, file string) {
   349  		_, err := LoadCollectionSpec(file)
   350  		t.Log(err)
   351  		if err == nil {
   352  			t.Fatal("Loading a map with static qualifier should fail")
   353  		}
   354  	})
   355  }
   356  
   357  func TestLoadInitializedBTFMap(t *testing.T) {
   358  	testutils.Files(t, testutils.Glob(t, "testdata/btf_map_init-*.elf"), func(t *testing.T, file string) {
   359  		coll, err := LoadCollectionSpec(file)
   360  		if err != nil {
   361  			t.Fatal(err)
   362  		}
   363  
   364  		t.Run("NewCollection", func(t *testing.T) {
   365  			if coll.ByteOrder != internal.NativeEndian {
   366  				t.Skipf("Skipping %s collection", coll.ByteOrder)
   367  			}
   368  
   369  			tmp, err := NewCollection(coll)
   370  			testutils.SkipIfNotSupported(t, err)
   371  			if err != nil {
   372  				t.Fatal("NewCollection failed:", err)
   373  			}
   374  			tmp.Close()
   375  		})
   376  
   377  		t.Run("prog_array", func(t *testing.T) {
   378  			m, ok := coll.Maps["prog_array_init"]
   379  			if !ok {
   380  				t.Fatal("map prog_array_init not found in program")
   381  			}
   382  
   383  			if len(m.Contents) != 1 {
   384  				t.Error("expecting exactly 1 item in MapSpec contents")
   385  			}
   386  
   387  			p := m.Contents[0]
   388  			if cmp.Equal(p.Key, 1) {
   389  				t.Errorf("expecting MapSpec entry Key to equal 1, got %v", p.Key)
   390  			}
   391  
   392  			if _, ok := p.Value.(string); !ok {
   393  				t.Errorf("expecting MapSpec entry Value to be a string, got %T", p.Value)
   394  			}
   395  
   396  			if p.Value != "tail_1" {
   397  				t.Errorf("expected MapSpec entry Value 'tail_1', got: %s", p.Value)
   398  			}
   399  		})
   400  
   401  		t.Run("array_of_maps", func(t *testing.T) {
   402  			m, ok := coll.Maps["outer_map_init"]
   403  			if !ok {
   404  				t.Fatal("map outer_map_init not found in program")
   405  			}
   406  
   407  			if len(m.Contents) != 1 {
   408  				t.Error("expecting exactly 1 item in MapSpec contents")
   409  			}
   410  
   411  			if m.Key == nil {
   412  				t.Error("Expected non-nil key")
   413  			}
   414  
   415  			if m.Value == nil {
   416  				t.Error("Expected non-nil value")
   417  			}
   418  
   419  			if m.InnerMap.Key == nil {
   420  				t.Error("Expected non-nil InnerMap key")
   421  			}
   422  
   423  			if m.InnerMap.Value == nil {
   424  				t.Error("Expected non-nil InnerMap value")
   425  			}
   426  
   427  			p := m.Contents[0]
   428  			if cmp.Equal(p.Key, 1) {
   429  				t.Errorf("expecting MapSpec entry Key to equal 1, got %v", p.Key)
   430  			}
   431  
   432  			if _, ok := p.Value.(string); !ok {
   433  				t.Errorf("expecting MapSpec entry Value to be a string, got %T", p.Value)
   434  			}
   435  
   436  			if p.Value != "inner_map" {
   437  				t.Errorf("expected MapSpec entry Value 'inner_map', got: %s", p.Value)
   438  			}
   439  		})
   440  	})
   441  }
   442  
   443  func TestLoadInvalidInitializedBTFMap(t *testing.T) {
   444  	testutils.Files(t, testutils.Glob(t, "testdata/invalid_btf_map_init-*.elf"), func(t *testing.T, file string) {
   445  		_, err := LoadCollectionSpec(file)
   446  		t.Log(err)
   447  		if !errors.Is(err, internal.ErrNotSupported) {
   448  			t.Fatal("Loading an initialized BTF map should be unsupported")
   449  		}
   450  	})
   451  }
   452  
   453  func TestStringSection(t *testing.T) {
   454  	file := fmt.Sprintf("testdata/strings-%s.elf", internal.ClangEndian)
   455  	spec, err := LoadCollectionSpec(file)
   456  	if err != nil {
   457  		t.Fatalf("load collection spec: %s", err)
   458  	}
   459  
   460  	for name := range spec.Maps {
   461  		t.Log(name)
   462  	}
   463  
   464  	strMap := spec.Maps[".rodata.str1.1"]
   465  	if strMap == nil {
   466  		t.Fatal("Unable to find map '.rodata.str1.1' in loaded collection")
   467  	}
   468  
   469  	if !strMap.Freeze {
   470  		t.Fatal("Read only data maps should be frozen")
   471  	}
   472  
   473  	if strMap.Flags != unix.BPF_F_RDONLY_PROG {
   474  		t.Fatal("Read only data maps should have the prog-read-only flag set")
   475  	}
   476  
   477  	coll, err := NewCollection(spec)
   478  	testutils.SkipIfNotSupported(t, err)
   479  	if err != nil {
   480  		t.Fatalf("new collection: %s", err)
   481  	}
   482  
   483  	prog := coll.Programs["filter"]
   484  	if prog == nil {
   485  		t.Fatal("program not found")
   486  	}
   487  
   488  	testMap := coll.Maps["my_map"]
   489  	if testMap == nil {
   490  		t.Fatal("test map not found")
   491  	}
   492  
   493  	_, err = prog.Run(&RunOptions{
   494  		Data: internal.EmptyBPFContext, // Min size for XDP programs
   495  	})
   496  	if err != nil {
   497  		t.Fatalf("prog run: %s", err)
   498  	}
   499  
   500  	key := []byte("This string is allocated in the string section\n\x00")
   501  	var value uint32
   502  	if err = testMap.Lookup(&key, &value); err != nil {
   503  		t.Fatalf("test map lookup: %s", err)
   504  	}
   505  
   506  	if value != 1 {
   507  		t.Fatal("Test map value not 1!")
   508  	}
   509  }
   510  
   511  func TestLoadRawTracepoint(t *testing.T) {
   512  	testutils.SkipOnOldKernel(t, "4.17", "BPF_RAW_TRACEPOINT API")
   513  
   514  	testutils.Files(t, testutils.Glob(t, "testdata/raw_tracepoint-*.elf"), func(t *testing.T, file string) {
   515  		spec, err := LoadCollectionSpec(file)
   516  		if err != nil {
   517  			t.Fatal("Can't parse ELF:", err)
   518  		}
   519  
   520  		if spec.ByteOrder != internal.NativeEndian {
   521  			return
   522  		}
   523  
   524  		coll, err := NewCollectionWithOptions(spec, CollectionOptions{
   525  			Programs: ProgramOptions{
   526  				LogLevel: LogLevelBranch,
   527  			},
   528  		})
   529  		testutils.SkipIfNotSupported(t, err)
   530  		if err != nil {
   531  			t.Fatal("Can't create collection:", err)
   532  		}
   533  
   534  		coll.Close()
   535  	})
   536  }
   537  
   538  func TestTailCall(t *testing.T) {
   539  	testutils.Files(t, testutils.Glob(t, "testdata/btf_map_init-*.elf"), func(t *testing.T, file string) {
   540  		spec, err := LoadCollectionSpec(file)
   541  		if err != nil {
   542  			t.Fatal(err)
   543  		}
   544  
   545  		if spec.ByteOrder != internal.NativeEndian {
   546  			return
   547  		}
   548  
   549  		var obj struct {
   550  			TailMain  *Program `ebpf:"tail_main"`
   551  			ProgArray *Map     `ebpf:"prog_array_init"`
   552  		}
   553  
   554  		err = spec.LoadAndAssign(&obj, nil)
   555  		testutils.SkipIfNotSupported(t, err)
   556  		if err != nil {
   557  			t.Fatal(err)
   558  		}
   559  		defer obj.TailMain.Close()
   560  		defer obj.ProgArray.Close()
   561  
   562  		ret, _, err := obj.TailMain.Test(internal.EmptyBPFContext)
   563  		testutils.SkipIfNotSupported(t, err)
   564  		if err != nil {
   565  			t.Fatal(err)
   566  		}
   567  
   568  		// Expect the tail_1 tail call to be taken, returning value 42.
   569  		if ret != 42 {
   570  			t.Fatalf("Expected tail call to return value 42, got %d", ret)
   571  		}
   572  	})
   573  }
   574  
   575  func TestSubprogRelocation(t *testing.T) {
   576  	testutils.SkipOnOldKernel(t, "5.13", "bpf_for_each_map_elem")
   577  
   578  	testutils.Files(t, testutils.Glob(t, "testdata/subprog_reloc-*.elf"), func(t *testing.T, file string) {
   579  		spec, err := LoadCollectionSpec(file)
   580  		if err != nil {
   581  			t.Fatal(err)
   582  		}
   583  
   584  		if spec.ByteOrder != internal.NativeEndian {
   585  			return
   586  		}
   587  
   588  		var obj struct {
   589  			Main    *Program `ebpf:"fp_relocation"`
   590  			HashMap *Map     `ebpf:"hash_map"`
   591  		}
   592  
   593  		err = spec.LoadAndAssign(&obj, nil)
   594  		testutils.SkipIfNotSupported(t, err)
   595  		if err != nil {
   596  			t.Fatal(err)
   597  		}
   598  		defer obj.Main.Close()
   599  		defer obj.HashMap.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  		if ret != 42 {
   608  			t.Fatalf("Expected subprog reloc to return value 42, got %d", ret)
   609  		}
   610  	})
   611  }
   612  
   613  func TestUnassignedProgArray(t *testing.T) {
   614  	testutils.Files(t, testutils.Glob(t, "testdata/btf_map_init-*.elf"), func(t *testing.T, file string) {
   615  		spec, err := LoadCollectionSpec(file)
   616  		if err != nil {
   617  			t.Fatal(err)
   618  		}
   619  
   620  		if spec.ByteOrder != internal.NativeEndian {
   621  			return
   622  		}
   623  
   624  		// tail_main references a ProgArray that is not being assigned
   625  		// to this struct. Normally, this would clear all its entries
   626  		// and make any tail calls into the ProgArray result in a miss.
   627  		// The library needs to explicitly refuse such operations.
   628  		var obj struct {
   629  			TailMain *Program `ebpf:"tail_main"`
   630  			// ProgArray *Map     `ebpf:"prog_array_init"`
   631  		}
   632  
   633  		err = spec.LoadAndAssign(&obj, nil)
   634  		testutils.SkipIfNotSupported(t, err)
   635  		if err == nil {
   636  			obj.TailMain.Close()
   637  			t.Fatal("Expecting LoadAndAssign to return error")
   638  		}
   639  	})
   640  }
   641  
   642  func TestIPRoute2Compat(t *testing.T) {
   643  	testutils.Files(t, testutils.Glob(t, "testdata/iproute2_map_compat-*.elf"), func(t *testing.T, file string) {
   644  		spec, err := LoadCollectionSpec(file)
   645  		if err != nil {
   646  			t.Fatal("Can't parse ELF:", err)
   647  		}
   648  
   649  		if spec.ByteOrder != internal.NativeEndian {
   650  			return
   651  		}
   652  
   653  		ms, ok := spec.Maps["hash_map"]
   654  		if !ok {
   655  			t.Fatal("Map hash_map not found")
   656  		}
   657  
   658  		var id, pinning, innerID, innerIndex uint32
   659  
   660  		if ms.Extra == nil {
   661  			t.Fatal("missing extra bytes")
   662  		}
   663  
   664  		switch {
   665  		case binary.Read(ms.Extra, spec.ByteOrder, &id) != nil:
   666  			t.Fatal("missing id")
   667  		case binary.Read(ms.Extra, spec.ByteOrder, &pinning) != nil:
   668  			t.Fatal("missing pinning")
   669  		case binary.Read(ms.Extra, spec.ByteOrder, &innerID) != nil:
   670  			t.Fatal("missing inner_id")
   671  		case binary.Read(ms.Extra, spec.ByteOrder, &innerIndex) != nil:
   672  			t.Fatal("missing inner_idx")
   673  		}
   674  
   675  		if id != 0 || innerID != 0 || innerIndex != 0 {
   676  			t.Fatal("expecting id, inner_id and inner_idx to be zero")
   677  		}
   678  
   679  		if pinning != 2 {
   680  			t.Fatal("expecting pinning field to be 2 (PIN_GLOBAL_NS)")
   681  		}
   682  
   683  		// iproute2 (tc) pins maps in /sys/fs/bpf/tc/globals with PIN_GLOBAL_NS,
   684  		// which needs to be be configured in this library using MapOptions.PinPath.
   685  		// For the sake of the test, we use a tempdir on bpffs below.
   686  		ms.Pinning = PinByName
   687  
   688  		coll, err := NewCollectionWithOptions(spec, CollectionOptions{
   689  			Maps: MapOptions{
   690  				PinPath: testutils.TempBPFFS(t),
   691  			},
   692  		})
   693  		testutils.SkipIfNotSupported(t, err)
   694  		if err != nil {
   695  			t.Fatal("Can't create collection:", err)
   696  		}
   697  
   698  		coll.Close()
   699  	})
   700  }
   701  
   702  var (
   703  	elfPath    = flag.String("elfs", os.Getenv("KERNEL_SELFTESTS"), "`Path` containing libbpf-compatible ELFs (defaults to $KERNEL_SELFTESTS)")
   704  	elfPattern = flag.String("elf-pattern", "*.o", "Glob `pattern` for object files that should be tested")
   705  )
   706  
   707  func TestLibBPFCompat(t *testing.T) {
   708  	if *elfPath == "" {
   709  		// Specify the path to the directory containing the eBPF for
   710  		// the kernel's selftests if you want to run this test.
   711  		// As of 5.2 that is tools/testing/selftests/bpf/
   712  		t.Skip("No path specified")
   713  	}
   714  
   715  	load := func(t *testing.T, spec *CollectionSpec, opts CollectionOptions, valid bool) {
   716  		// Disable retrying a program load with the log enabled, it leads
   717  		// to OOM kills.
   718  		opts.Programs.LogDisabled = true
   719  
   720  		for name, p := range spec.Programs {
   721  			if p.Type != Extension {
   722  				continue
   723  			}
   724  
   725  			targetProg, targetColl := loadTargetProgram(t, name, opts)
   726  			defer targetColl.Close()
   727  			p.AttachTarget = targetProg
   728  		}
   729  
   730  		coll, err := NewCollectionWithOptions(spec, opts)
   731  		testutils.SkipIfNotSupported(t, err)
   732  		var errno syscall.Errno
   733  		if errors.As(err, &errno) {
   734  			// This error is most likely from a syscall and caused by us not
   735  			// replicating some fixups done in the selftests or the test
   736  			// intentionally failing. This is expected, so skip the test
   737  			// instead of failing.
   738  			t.Skip("Skipping since the kernel rejected the program:", err)
   739  		}
   740  		if err == nil {
   741  			coll.Close()
   742  		}
   743  		if !valid {
   744  			if err == nil {
   745  				t.Fatal("Expected an error during load")
   746  			}
   747  		} else if err != nil {
   748  			t.Fatal("Error during loading:", err)
   749  		}
   750  	}
   751  
   752  	files := testutils.Glob(t, filepath.Join(*elfPath, *elfPattern),
   753  		// These files are only used as a source of btf.
   754  		"btf__core_reloc_*",
   755  	)
   756  
   757  	testutils.Files(t, files, func(t *testing.T, path string) {
   758  		file := filepath.Base(path)
   759  		switch file {
   760  		case "test_map_in_map.o", "test_map_in_map.linked3.o",
   761  			"test_select_reuseport_kern.o", "test_select_reuseport_kern.linked3.o":
   762  			t.Skip("Skipping due to missing InnerMap in map definition")
   763  		case "test_core_autosize.o":
   764  			t.Skip("Skipping since the test generates dynamic BTF")
   765  		case "test_static_linked.linked3.o":
   766  			t.Skip("Skipping since .text contains 'subprog' twice")
   767  		case "linked_maps.linked3.o", "linked_funcs.linked3.o":
   768  			t.Skip("Skipping since weak relocations are not supported")
   769  		case "bloom_filter_map.o", "bloom_filter_map.linked3.o",
   770  			"bloom_filter_bench.o", "bloom_filter_bench.linked3.o":
   771  			t.Skip("Skipping due to missing MapExtra field in MapSpec")
   772  		case "netif_receive_skb.linked3.o":
   773  			t.Skip("Skipping due to possible bug in upstream CO-RE generation")
   774  		case "test_usdt.o", "test_usdt.linked3.o", "test_urandom_usdt.o", "test_urandom_usdt.linked3.o", "test_usdt_multispec.o":
   775  			t.Skip("Skipping due to missing support for usdt.bpf.h")
   776  		}
   777  
   778  		t.Parallel()
   779  
   780  		spec, err := LoadCollectionSpec(path)
   781  		testutils.SkipIfNotSupported(t, err)
   782  		if err != nil {
   783  			t.Fatalf("Can't read %s: %s", file, err)
   784  		}
   785  
   786  		switch file {
   787  		case "test_sk_assign.o":
   788  			// Test contains a legacy iproute2 bpf_elf_map definition.
   789  			for _, m := range spec.Maps {
   790  				if m.Extra == nil || m.Extra.Len() == 0 {
   791  					t.Fatalf("Expected extra bytes in map %s", m.Name)
   792  				}
   793  				m.Extra = nil
   794  			}
   795  		}
   796  
   797  		var opts CollectionOptions
   798  		for _, mapSpec := range spec.Maps {
   799  			if mapSpec.Pinning != PinNone {
   800  				opts.Maps.PinPath = testutils.TempBPFFS(t)
   801  				break
   802  			}
   803  		}
   804  
   805  		coreFiles := sourceOfBTF(t, path)
   806  		if len(coreFiles) == 0 {
   807  			// NB: test_core_reloc_kernel.o doesn't have dedicated BTF and
   808  			// therefore goes via this code path.
   809  			load(t, spec, opts, true)
   810  			return
   811  		}
   812  
   813  		for _, coreFile := range coreFiles {
   814  			name := filepath.Base(coreFile)
   815  			t.Run(name, func(t *testing.T) {
   816  				// Some files like btf__core_reloc_arrays___err_too_small.o
   817  				// trigger an error on purpose. Use the name to infer whether
   818  				// the test should succeed.
   819  				var valid bool
   820  				switch name {
   821  				case "btf__core_reloc_existence___err_wrong_arr_kind.o",
   822  					"btf__core_reloc_existence___err_wrong_arr_value_type.o",
   823  					"btf__core_reloc_existence___err_wrong_int_kind.o",
   824  					"btf__core_reloc_existence___err_wrong_int_sz.o",
   825  					"btf__core_reloc_existence___err_wrong_int_type.o",
   826  					"btf__core_reloc_existence___err_wrong_struct_type.o":
   827  					// These tests are buggy upstream, see https://lore.kernel.org/bpf/20210420111639.155580-1-lmb@cloudflare.com/
   828  					valid = true
   829  				case "btf__core_reloc_ints___err_wrong_sz_16.o",
   830  					"btf__core_reloc_ints___err_wrong_sz_32.o",
   831  					"btf__core_reloc_ints___err_wrong_sz_64.o",
   832  					"btf__core_reloc_ints___err_wrong_sz_8.o",
   833  					"btf__core_reloc_arrays___err_wrong_val_type1.o",
   834  					"btf__core_reloc_arrays___err_wrong_val_type2.o":
   835  					// These tests are valid according to current libbpf behaviour,
   836  					// see commit 42765ede5c54ca915de5bfeab83be97207e46f68.
   837  					valid = true
   838  				case "btf__core_reloc_type_id___missing_targets.o",
   839  					"btf__core_reloc_flavors__err_wrong_name.o":
   840  					valid = false
   841  				case "btf__core_reloc_ints___err_bitfield.o":
   842  					// Bitfields are now valid.
   843  					valid = true
   844  				default:
   845  					valid = !strings.Contains(name, "___err_")
   846  				}
   847  
   848  				fh, err := os.Open(coreFile)
   849  				if err != nil {
   850  					t.Fatal(err)
   851  				}
   852  				defer fh.Close()
   853  
   854  				btfSpec, err := btf.LoadSpec(coreFile)
   855  				if err != nil {
   856  					t.Fatal(err)
   857  				}
   858  
   859  				opts := opts // copy
   860  				opts.Programs.KernelTypes = btfSpec
   861  				load(t, spec, opts, valid)
   862  			})
   863  		}
   864  	})
   865  }
   866  
   867  func loadTargetProgram(tb testing.TB, name string, opts CollectionOptions) (*Program, *Collection) {
   868  	file := "test_pkt_access.o"
   869  	program := "test_pkt_access"
   870  	switch name {
   871  	case "new_connect_v4_prog":
   872  		file = "connect4_prog.o"
   873  		program = "connect_v4_prog"
   874  	case "new_do_bind":
   875  		file = "connect4_prog.o"
   876  		program = "connect_v4_prog"
   877  	case "freplace_cls_redirect_test":
   878  		file = "test_cls_redirect.o"
   879  		program = "cls_redirect"
   880  	case "new_handle_kprobe":
   881  		file = "test_attach_probe.o"
   882  		program = "handle_kprobe"
   883  	case "test_pkt_md_access_new":
   884  		file = "test_pkt_md_access.o"
   885  		program = "test_pkt_md_access"
   886  	default:
   887  	}
   888  
   889  	spec, err := LoadCollectionSpec(filepath.Join(*elfPath, file))
   890  	if err != nil {
   891  		tb.Fatalf("Can't read %s: %s", file, err)
   892  	}
   893  
   894  	coll, err := NewCollectionWithOptions(spec, opts)
   895  	if err != nil {
   896  		tb.Fatalf("Can't load target: %s", err)
   897  	}
   898  
   899  	return coll.Programs[program], coll
   900  }
   901  
   902  func sourceOfBTF(tb testing.TB, path string) []string {
   903  	const testPrefix = "test_core_reloc_"
   904  	const btfPrefix = "btf__core_reloc_"
   905  
   906  	dir, base := filepath.Split(path)
   907  	if !strings.HasPrefix(base, testPrefix) {
   908  		return nil
   909  	}
   910  
   911  	base = strings.TrimSuffix(base[len(testPrefix):], ".o")
   912  	switch base {
   913  	case "bitfields_direct", "bitfields_probed":
   914  		base = "bitfields"
   915  	}
   916  
   917  	return testutils.Glob(tb, filepath.Join(dir, btfPrefix+base+"*.o"))
   918  }
   919  
   920  func TestGetProgType(t *testing.T) {
   921  	type progTypeTestData struct {
   922  		Pt ProgramType
   923  		At AttachType
   924  		Fl uint32
   925  		To string
   926  	}
   927  
   928  	testcases := map[string]progTypeTestData{
   929  		"socket/garbage": {
   930  			Pt: SocketFilter,
   931  			At: AttachNone,
   932  			To: "",
   933  		},
   934  		"kprobe/func": {
   935  			Pt: Kprobe,
   936  			At: AttachNone,
   937  			To: "func",
   938  		},
   939  		"xdp/foo": {
   940  			Pt: XDP,
   941  			At: AttachNone,
   942  			To: "",
   943  		},
   944  		"xdp_devmap/foo": {
   945  			Pt: XDP,
   946  			At: AttachXDPDevMap,
   947  			To: "foo",
   948  		},
   949  		"cgroup_skb/ingress": {
   950  			Pt: CGroupSKB,
   951  			At: AttachCGroupInetIngress,
   952  			To: "",
   953  		},
   954  		"iter/bpf_map": {
   955  			Pt: Tracing,
   956  			At: AttachTraceIter,
   957  			To: "bpf_map",
   958  		},
   959  		"lsm.s/file_ioctl_sleepable": {
   960  			Pt: LSM,
   961  			At: AttachLSMMac,
   962  			To: "file_ioctl_sleepable",
   963  			Fl: unix.BPF_F_SLEEPABLE,
   964  		},
   965  		"lsm/file_ioctl": {
   966  			Pt: LSM,
   967  			At: AttachLSMMac,
   968  			To: "file_ioctl",
   969  		},
   970  		"sk_skb/stream_verdict/foo": {
   971  			Pt: SkSKB,
   972  			At: AttachSkSKBStreamVerdict,
   973  			To: "",
   974  		},
   975  		"sk_skb/bar": {
   976  			Pt: SkSKB,
   977  			At: AttachNone,
   978  			To: "",
   979  		},
   980  	}
   981  
   982  	for section, want := range testcases {
   983  		pt, at, fl, to := getProgType(section)
   984  
   985  		if diff := cmp.Diff(want, progTypeTestData{Pt: pt, At: at, Fl: fl, To: to}); diff != "" {
   986  			t.Errorf("getProgType mismatch (-want +got):\n%s", diff)
   987  		}
   988  	}
   989  }