...

Source file src/github.com/cilium/ebpf/elf_reader_test.go

Documentation: github.com/cilium/ebpf

     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: 1,
   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(make([]byte, 14))
   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(make([]byte, 14))
   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("prog_array", func(t *testing.T) {
   365  			m, ok := coll.Maps["prog_array_init"]
   366  			if !ok {
   367  				t.Fatal("map prog_array_init not found in program")
   368  			}
   369  
   370  			if len(m.Contents) != 1 {
   371  				t.Error("expecting exactly 1 item in MapSpec contents")
   372  			}
   373  
   374  			p := m.Contents[0]
   375  			if cmp.Equal(p.Key, 1) {
   376  				t.Errorf("expecting MapSpec entry Key to equal 1, got %v", p.Key)
   377  			}
   378  
   379  			if _, ok := p.Value.(string); !ok {
   380  				t.Errorf("expecting MapSpec entry Value to be a string, got %T", p.Value)
   381  			}
   382  
   383  			if p.Value != "tail_1" {
   384  				t.Errorf("expected MapSpec entry Value 'tail_1', got: %s", p.Value)
   385  			}
   386  		})
   387  
   388  		t.Run("array_of_maps", func(t *testing.T) {
   389  			m, ok := coll.Maps["outer_map_init"]
   390  			if !ok {
   391  				t.Fatal("map outer_map_init not found in program")
   392  			}
   393  
   394  			if len(m.Contents) != 1 {
   395  				t.Error("expecting exactly 1 item in MapSpec contents")
   396  			}
   397  
   398  			p := m.Contents[0]
   399  			if cmp.Equal(p.Key, 1) {
   400  				t.Errorf("expecting MapSpec entry Key to equal 1, got %v", p.Key)
   401  			}
   402  
   403  			if _, ok := p.Value.(string); !ok {
   404  				t.Errorf("expecting MapSpec entry Value to be a string, got %T", p.Value)
   405  			}
   406  
   407  			if p.Value != "inner_map" {
   408  				t.Errorf("expected MapSpec entry Value 'inner_map', got: %s", p.Value)
   409  			}
   410  		})
   411  	})
   412  }
   413  
   414  func TestLoadInvalidInitializedBTFMap(t *testing.T) {
   415  	testutils.Files(t, testutils.Glob(t, "testdata/invalid_btf_map_init-*.elf"), func(t *testing.T, file string) {
   416  		_, err := LoadCollectionSpec(file)
   417  		t.Log(err)
   418  		if !errors.Is(err, internal.ErrNotSupported) {
   419  			t.Fatal("Loading an initialized BTF map should be unsupported")
   420  		}
   421  	})
   422  }
   423  
   424  func TestStringSection(t *testing.T) {
   425  	testutils.Files(t, testutils.Glob(t, "testdata/strings-*.elf"), func(t *testing.T, file string) {
   426  		_, err := LoadCollectionSpec(file)
   427  		t.Log(err)
   428  		if !errors.Is(err, ErrNotSupported) {
   429  			t.Error("References to a string section should be unsupported")
   430  		}
   431  	})
   432  }
   433  
   434  func TestLoadRawTracepoint(t *testing.T) {
   435  	testutils.SkipOnOldKernel(t, "4.17", "BPF_RAW_TRACEPOINT API")
   436  
   437  	testutils.Files(t, testutils.Glob(t, "testdata/raw_tracepoint-*.elf"), func(t *testing.T, file string) {
   438  		spec, err := LoadCollectionSpec(file)
   439  		if err != nil {
   440  			t.Fatal("Can't parse ELF:", err)
   441  		}
   442  
   443  		if spec.ByteOrder != internal.NativeEndian {
   444  			return
   445  		}
   446  
   447  		coll, err := NewCollectionWithOptions(spec, CollectionOptions{
   448  			Programs: ProgramOptions{
   449  				LogLevel: 1,
   450  			},
   451  		})
   452  		testutils.SkipIfNotSupported(t, err)
   453  		if err != nil {
   454  			t.Fatal("Can't create collection:", err)
   455  		}
   456  
   457  		coll.Close()
   458  	})
   459  }
   460  
   461  func TestTailCall(t *testing.T) {
   462  	testutils.Files(t, testutils.Glob(t, "testdata/btf_map_init-*.elf"), func(t *testing.T, file string) {
   463  		spec, err := LoadCollectionSpec(file)
   464  		if err != nil {
   465  			t.Fatal(err)
   466  		}
   467  
   468  		if spec.ByteOrder != internal.NativeEndian {
   469  			return
   470  		}
   471  
   472  		var obj struct {
   473  			TailMain  *Program `ebpf:"tail_main"`
   474  			ProgArray *Map     `ebpf:"prog_array_init"`
   475  		}
   476  
   477  		err = spec.LoadAndAssign(&obj, nil)
   478  		testutils.SkipIfNotSupported(t, err)
   479  		if err != nil {
   480  			t.Fatal(err)
   481  		}
   482  		defer obj.TailMain.Close()
   483  		defer obj.ProgArray.Close()
   484  
   485  		ret, _, err := obj.TailMain.Test(make([]byte, 14))
   486  		testutils.SkipIfNotSupported(t, err)
   487  		if err != nil {
   488  			t.Fatal(err)
   489  		}
   490  
   491  		// Expect the tail_1 tail call to be taken, returning value 42.
   492  		if ret != 42 {
   493  			t.Fatalf("Expected tail call to return value 42, got %d", ret)
   494  		}
   495  	})
   496  }
   497  
   498  func TestSubprogRelocation(t *testing.T) {
   499  	testutils.SkipOnOldKernel(t, "5.13", "bpf_for_each_map_elem")
   500  
   501  	testutils.Files(t, testutils.Glob(t, "testdata/subprog_reloc-*.elf"), func(t *testing.T, file string) {
   502  		spec, err := LoadCollectionSpec(file)
   503  		if err != nil {
   504  			t.Fatal(err)
   505  		}
   506  
   507  		if spec.ByteOrder != internal.NativeEndian {
   508  			return
   509  		}
   510  
   511  		var obj struct {
   512  			Main    *Program `ebpf:"fp_relocation"`
   513  			HashMap *Map     `ebpf:"hash_map"`
   514  		}
   515  
   516  		err = spec.LoadAndAssign(&obj, nil)
   517  		testutils.SkipIfNotSupported(t, err)
   518  		if err != nil {
   519  			t.Fatal(err)
   520  		}
   521  		defer obj.Main.Close()
   522  		defer obj.HashMap.Close()
   523  
   524  		ret, _, err := obj.Main.Test(make([]byte, 14))
   525  		testutils.SkipIfNotSupported(t, err)
   526  		if err != nil {
   527  			t.Fatal(err)
   528  		}
   529  
   530  		if ret != 42 {
   531  			t.Fatalf("Expected subprog reloc to return value 42, got %d", ret)
   532  		}
   533  	})
   534  }
   535  
   536  func TestUnassignedProgArray(t *testing.T) {
   537  	testutils.Files(t, testutils.Glob(t, "testdata/btf_map_init-*.elf"), func(t *testing.T, file string) {
   538  		spec, err := LoadCollectionSpec(file)
   539  		if err != nil {
   540  			t.Fatal(err)
   541  		}
   542  
   543  		if spec.ByteOrder != internal.NativeEndian {
   544  			return
   545  		}
   546  
   547  		// tail_main references a ProgArray that is not being assigned
   548  		// to this struct. Normally, this would clear all its entries
   549  		// and make any tail calls into the ProgArray result in a miss.
   550  		// The library needs to explicitly refuse such operations.
   551  		var obj struct {
   552  			TailMain *Program `ebpf:"tail_main"`
   553  			// ProgArray *Map     `ebpf:"prog_array_init"`
   554  		}
   555  
   556  		err = spec.LoadAndAssign(&obj, nil)
   557  		testutils.SkipIfNotSupported(t, err)
   558  		if err == nil {
   559  			obj.TailMain.Close()
   560  			t.Fatal("Expecting LoadAndAssign to return error")
   561  		}
   562  	})
   563  }
   564  
   565  func TestIPRoute2Compat(t *testing.T) {
   566  	testutils.Files(t, testutils.Glob(t, "testdata/iproute2_map_compat-*.elf"), func(t *testing.T, file string) {
   567  		spec, err := LoadCollectionSpec(file)
   568  		if err != nil {
   569  			t.Fatal("Can't parse ELF:", err)
   570  		}
   571  
   572  		if spec.ByteOrder != internal.NativeEndian {
   573  			return
   574  		}
   575  
   576  		ms, ok := spec.Maps["hash_map"]
   577  		if !ok {
   578  			t.Fatal("Map hash_map not found")
   579  		}
   580  
   581  		var id, pinning, innerID, innerIndex uint32
   582  
   583  		if ms.Extra == nil {
   584  			t.Fatal("missing extra bytes")
   585  		}
   586  
   587  		switch {
   588  		case binary.Read(ms.Extra, spec.ByteOrder, &id) != nil:
   589  			t.Fatal("missing id")
   590  		case binary.Read(ms.Extra, spec.ByteOrder, &pinning) != nil:
   591  			t.Fatal("missing pinning")
   592  		case binary.Read(ms.Extra, spec.ByteOrder, &innerID) != nil:
   593  			t.Fatal("missing inner_id")
   594  		case binary.Read(ms.Extra, spec.ByteOrder, &innerIndex) != nil:
   595  			t.Fatal("missing inner_idx")
   596  		}
   597  
   598  		if id != 0 || innerID != 0 || innerIndex != 0 {
   599  			t.Fatal("expecting id, inner_id and inner_idx to be zero")
   600  		}
   601  
   602  		if pinning != 2 {
   603  			t.Fatal("expecting pinning field to be 2 (PIN_GLOBAL_NS)")
   604  		}
   605  
   606  		// iproute2 (tc) pins maps in /sys/fs/bpf/tc/globals with PIN_GLOBAL_NS,
   607  		// which needs to be be configured in this library using MapOptions.PinPath.
   608  		// For the sake of the test, we use a tempdir on bpffs below.
   609  		ms.Pinning = PinByName
   610  
   611  		coll, err := NewCollectionWithOptions(spec, CollectionOptions{
   612  			Maps: MapOptions{
   613  				PinPath: testutils.TempBPFFS(t),
   614  			},
   615  		})
   616  		testutils.SkipIfNotSupported(t, err)
   617  		if err != nil {
   618  			t.Fatal("Can't create collection:", err)
   619  		}
   620  
   621  		coll.Close()
   622  	})
   623  }
   624  
   625  var (
   626  	elfPath    = flag.String("elfs", os.Getenv("KERNEL_SELFTESTS"), "`Path` containing libbpf-compatible ELFs (defaults to $KERNEL_SELFTESTS)")
   627  	elfPattern = flag.String("elf-pattern", "*.o", "Glob `pattern` for object files that should be tested")
   628  )
   629  
   630  func TestLibBPFCompat(t *testing.T) {
   631  	if *elfPath == "" {
   632  		// Specify the path to the directory containing the eBPF for
   633  		// the kernel's selftests if you want to run this test.
   634  		// As of 5.2 that is tools/testing/selftests/bpf/
   635  		t.Skip("No path specified")
   636  	}
   637  
   638  	load := func(t *testing.T, spec *CollectionSpec, opts CollectionOptions, valid bool) {
   639  		// Disable retrying a program load with the log enabled, it leads
   640  		// to OOM kills.
   641  		opts.Programs.LogSize = -1
   642  
   643  		for name, p := range spec.Programs {
   644  			if p.Type != Extension {
   645  				continue
   646  			}
   647  
   648  			targetProg, targetColl := loadTargetProgram(t, name, opts)
   649  			defer targetColl.Close()
   650  			p.AttachTarget = targetProg
   651  		}
   652  
   653  		coll, err := NewCollectionWithOptions(spec, opts)
   654  		testutils.SkipIfNotSupported(t, err)
   655  		var errno syscall.Errno
   656  		if errors.As(err, &errno) {
   657  			// This error is most likely from a syscall and caused by us not
   658  			// replicating some fixups done in the selftests or the test
   659  			// intentionally failing. This is expected, so skip the test
   660  			// instead of failing.
   661  			t.Skip("Skipping since the kernel rejected the program:", errno)
   662  		}
   663  		if err == nil {
   664  			coll.Close()
   665  		}
   666  		if !valid {
   667  			if err == nil {
   668  				t.Fatal("Expected an error during load")
   669  			}
   670  		} else if err != nil {
   671  			t.Fatal("Error during loading:", err)
   672  		}
   673  	}
   674  
   675  	files := testutils.Glob(t, filepath.Join(*elfPath, *elfPattern),
   676  		// These files are only used as a source of btf.
   677  		"btf__core_reloc_*",
   678  	)
   679  
   680  	testutils.Files(t, files, func(t *testing.T, path string) {
   681  		file := filepath.Base(path)
   682  		switch file {
   683  		case "test_map_in_map.o", "test_map_in_map.linked3.o",
   684  			"test_select_reuseport_kern.o", "test_select_reuseport_kern.linked3.o":
   685  			t.Skip("Skipping due to missing InnerMap in map definition")
   686  		case "test_core_autosize.o":
   687  			t.Skip("Skipping since the test generates dynamic BTF")
   688  		case "test_static_linked.linked3.o":
   689  			t.Skip("Skipping since .text contains 'subprog' twice")
   690  		case "linked_maps.linked3.o", "linked_funcs.linked3.o":
   691  			t.Skip("Skipping since weak relocations are not supported")
   692  		}
   693  
   694  		t.Parallel()
   695  
   696  		spec, err := LoadCollectionSpec(path)
   697  		testutils.SkipIfNotSupported(t, err)
   698  		if err != nil {
   699  			t.Fatalf("Can't read %s: %s", file, err)
   700  		}
   701  
   702  		switch file {
   703  		case "test_sk_assign.o":
   704  			// Test contains a legacy iproute2 bpf_elf_map definition.
   705  			for _, m := range spec.Maps {
   706  				if m.Extra == nil || m.Extra.Len() == 0 {
   707  					t.Fatalf("Expected extra bytes in map %s", m.Name)
   708  				}
   709  				m.Extra = nil
   710  			}
   711  		}
   712  
   713  		var opts CollectionOptions
   714  		for _, mapSpec := range spec.Maps {
   715  			if mapSpec.Pinning != PinNone {
   716  				opts.Maps.PinPath = testutils.TempBPFFS(t)
   717  				break
   718  			}
   719  		}
   720  
   721  		coreFiles := sourceOfBTF(t, path)
   722  		if len(coreFiles) == 0 {
   723  			// NB: test_core_reloc_kernel.o doesn't have dedicated BTF and
   724  			// therefore goes via this code path.
   725  			load(t, spec, opts, true)
   726  			return
   727  		}
   728  
   729  		for _, coreFile := range coreFiles {
   730  			name := filepath.Base(coreFile)
   731  			t.Run(name, func(t *testing.T) {
   732  				// Some files like btf__core_reloc_arrays___err_too_small.o
   733  				// trigger an error on purpose. Use the name to infer whether
   734  				// the test should succeed.
   735  				var valid bool
   736  				switch name {
   737  				case "btf__core_reloc_existence___err_wrong_arr_kind.o",
   738  					"btf__core_reloc_existence___err_wrong_arr_value_type.o",
   739  					"btf__core_reloc_existence___err_wrong_int_kind.o",
   740  					"btf__core_reloc_existence___err_wrong_int_sz.o",
   741  					"btf__core_reloc_existence___err_wrong_int_type.o",
   742  					"btf__core_reloc_existence___err_wrong_struct_type.o":
   743  					// These tests are buggy upstream, see https://lore.kernel.org/bpf/20210420111639.155580-1-lmb@cloudflare.com/
   744  					valid = true
   745  				case "btf__core_reloc_ints___err_wrong_sz_16.o",
   746  					"btf__core_reloc_ints___err_wrong_sz_32.o",
   747  					"btf__core_reloc_ints___err_wrong_sz_64.o",
   748  					"btf__core_reloc_ints___err_wrong_sz_8.o",
   749  					"btf__core_reloc_arrays___err_wrong_val_type1.o",
   750  					"btf__core_reloc_arrays___err_wrong_val_type2.o":
   751  					// These tests are valid according to current libbpf behaviour,
   752  					// see commit 42765ede5c54ca915de5bfeab83be97207e46f68.
   753  					valid = true
   754  				case "btf__core_reloc_type_id___missing_targets.o",
   755  					"btf__core_reloc_flavors__err_wrong_name.o":
   756  					valid = false
   757  				case "btf__core_reloc_ints___err_bitfield.o":
   758  					// Bitfields are now valid.
   759  					valid = true
   760  				default:
   761  					valid = !strings.Contains(name, "___err_")
   762  				}
   763  
   764  				fh, err := os.Open(coreFile)
   765  				if err != nil {
   766  					t.Fatal(err)
   767  				}
   768  				defer fh.Close()
   769  
   770  				btfSpec, err := btf.LoadSpec(coreFile)
   771  				if err != nil {
   772  					t.Fatal(err)
   773  				}
   774  
   775  				opts := opts // copy
   776  				opts.Programs.KernelTypes = btfSpec
   777  				load(t, spec, opts, valid)
   778  			})
   779  		}
   780  	})
   781  }
   782  
   783  func loadTargetProgram(tb testing.TB, name string, opts CollectionOptions) (*Program, *Collection) {
   784  	file := "test_pkt_access.o"
   785  	program := "test_pkt_access"
   786  	switch name {
   787  	case "new_connect_v4_prog":
   788  		file = "connect4_prog.o"
   789  		program = "connect_v4_prog"
   790  	case "new_do_bind":
   791  		file = "connect4_prog.o"
   792  		program = "connect_v4_prog"
   793  	case "freplace_cls_redirect_test":
   794  		file = "test_cls_redirect.o"
   795  		program = "cls_redirect"
   796  	case "new_handle_kprobe":
   797  		file = "test_attach_probe.o"
   798  		program = "handle_kprobe"
   799  	case "test_pkt_md_access_new":
   800  		file = "test_pkt_md_access.o"
   801  		program = "test_pkt_md_access"
   802  	default:
   803  	}
   804  
   805  	spec, err := LoadCollectionSpec(filepath.Join(*elfPath, file))
   806  	if err != nil {
   807  		tb.Fatalf("Can't read %s: %s", file, err)
   808  	}
   809  
   810  	coll, err := NewCollectionWithOptions(spec, opts)
   811  	if err != nil {
   812  		tb.Fatalf("Can't load target: %s", err)
   813  	}
   814  
   815  	return coll.Programs[program], coll
   816  }
   817  
   818  func sourceOfBTF(tb testing.TB, path string) []string {
   819  	const testPrefix = "test_core_reloc_"
   820  	const btfPrefix = "btf__core_reloc_"
   821  
   822  	dir, base := filepath.Split(path)
   823  	if !strings.HasPrefix(base, testPrefix) {
   824  		return nil
   825  	}
   826  
   827  	base = strings.TrimSuffix(base[len(testPrefix):], ".o")
   828  	switch base {
   829  	case "bitfields_direct", "bitfields_probed":
   830  		base = "bitfields"
   831  	}
   832  
   833  	return testutils.Glob(tb, filepath.Join(dir, btfPrefix+base+"*.o"))
   834  }
   835  
   836  func TestGetProgType(t *testing.T) {
   837  	type progTypeTestData struct {
   838  		Pt ProgramType
   839  		At AttachType
   840  		Fl uint32
   841  		To string
   842  	}
   843  
   844  	testcases := map[string]progTypeTestData{
   845  		"socket/garbage": {
   846  			Pt: SocketFilter,
   847  			At: AttachNone,
   848  			To: "",
   849  		},
   850  		"kprobe/func": {
   851  			Pt: Kprobe,
   852  			At: AttachNone,
   853  			To: "func",
   854  		},
   855  		"xdp/foo": {
   856  			Pt: XDP,
   857  			At: AttachNone,
   858  			To: "",
   859  		},
   860  		"xdp_devmap/foo": {
   861  			Pt: XDP,
   862  			At: AttachXDPDevMap,
   863  			To: "foo",
   864  		},
   865  		"cgroup_skb/ingress": {
   866  			Pt: CGroupSKB,
   867  			At: AttachCGroupInetIngress,
   868  			To: "",
   869  		},
   870  		"iter/bpf_map": {
   871  			Pt: Tracing,
   872  			At: AttachTraceIter,
   873  			To: "bpf_map",
   874  		},
   875  		"lsm.s/file_ioctl_sleepable": {
   876  			Pt: LSM,
   877  			At: AttachLSMMac,
   878  			To: "file_ioctl_sleepable",
   879  			Fl: unix.BPF_F_SLEEPABLE,
   880  		},
   881  		"lsm/file_ioctl": {
   882  			Pt: LSM,
   883  			At: AttachLSMMac,
   884  			To: "file_ioctl",
   885  		},
   886  		"sk_skb/stream_verdict/foo": {
   887  			Pt: SkSKB,
   888  			At: AttachSkSKBStreamVerdict,
   889  			To: "",
   890  		},
   891  		"sk_skb/bar": {
   892  			Pt: SkSKB,
   893  			At: AttachNone,
   894  			To: "",
   895  		},
   896  	}
   897  
   898  	for section, want := range testcases {
   899  		pt, at, fl, to := getProgType(section)
   900  
   901  		if diff := cmp.Diff(want, progTypeTestData{Pt: pt, At: at, Fl: fl, To: to}); diff != "" {
   902  			t.Errorf("getProgType mismatch (-want +got):\n%s", diff)
   903  		}
   904  	}
   905  }
   906  

View as plain text