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

     1  package btf
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"slices"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/google/go-cmp/cmp"
    12  
    13  	"github.com/cilium/ebpf/internal"
    14  	"github.com/cilium/ebpf/internal/testutils"
    15  
    16  	"github.com/go-quicktest/qt"
    17  )
    18  
    19  func TestCheckTypeCompatibility(t *testing.T) {
    20  	tests := []struct {
    21  		a, b       Type
    22  		compatible bool
    23  	}{
    24  		{&Void{}, &Void{}, true},
    25  		{&Struct{Name: "a"}, &Struct{Name: "b"}, true},
    26  		{&Union{Name: "a"}, &Union{Name: "b"}, true},
    27  		{&Union{Name: "a"}, &Struct{Name: "b"}, false},
    28  		{&Enum{Name: "a"}, &Enum{Name: "b"}, true},
    29  		{&Fwd{Name: "a"}, &Fwd{Name: "b"}, true},
    30  		{&Int{Name: "a", Size: 2}, &Int{Name: "b", Size: 4}, true},
    31  		{&Pointer{Target: &Void{}}, &Pointer{Target: &Void{}}, true},
    32  		{&Pointer{Target: &Void{}}, &Void{}, false},
    33  		{&Array{Index: &Void{}, Type: &Void{}}, &Array{Index: &Void{}, Type: &Void{}}, true},
    34  		{&Array{Index: &Void{}, Type: &Int{}}, &Array{Index: &Void{}, Type: &Void{}}, false},
    35  		{&FuncProto{Return: &Int{}}, &FuncProto{Return: &Void{}}, false},
    36  		{
    37  			&FuncProto{Return: &Void{}, Params: []FuncParam{{Name: "a", Type: &Void{}}}},
    38  			&FuncProto{Return: &Void{}, Params: []FuncParam{{Name: "b", Type: &Void{}}}},
    39  			true,
    40  		},
    41  		{
    42  			&FuncProto{Return: &Void{}, Params: []FuncParam{{Type: &Void{}}}},
    43  			&FuncProto{Return: &Void{}, Params: []FuncParam{{Type: &Int{}}}},
    44  			false,
    45  		},
    46  		{
    47  			&FuncProto{Return: &Void{}, Params: []FuncParam{{Type: &Void{}}, {Type: &Void{}}}},
    48  			&FuncProto{Return: &Void{}, Params: []FuncParam{{Type: &Void{}}}},
    49  			false,
    50  		},
    51  		{&FuncProto{Return: &Typedef{Type: &Int{}}}, &FuncProto{Return: &Int{}}, true},
    52  		{&FuncProto{Return: &Typedef{Type: &Int{}}}, &FuncProto{Return: &Void{}}, false},
    53  	}
    54  
    55  	for _, test := range tests {
    56  		err := CheckTypeCompatibility(test.a, test.b)
    57  		if test.compatible {
    58  			if err != nil {
    59  				t.Errorf("Expected types to be compatible: %s\na = %#v\nb = %#v", err, test.a, test.b)
    60  				continue
    61  			}
    62  		} else {
    63  			if !errors.Is(err, errIncompatibleTypes) {
    64  				t.Errorf("Expected types to be incompatible: %s\na = %#v\nb = %#v", err, test.a, test.b)
    65  				continue
    66  			}
    67  		}
    68  
    69  		err = CheckTypeCompatibility(test.b, test.a)
    70  		if test.compatible {
    71  			if err != nil {
    72  				t.Errorf("Expected reversed types to be compatible: %s\na = %#v\nb = %#v", err, test.a, test.b)
    73  			}
    74  		} else {
    75  			if !errors.Is(err, errIncompatibleTypes) {
    76  				t.Errorf("Expected reversed types to be incompatible: %s\na = %#v\nb = %#v", err, test.a, test.b)
    77  			}
    78  		}
    79  	}
    80  
    81  	for _, invalid := range []Type{&Var{}, &Datasec{}} {
    82  		err := CheckTypeCompatibility(invalid, invalid)
    83  		if errors.Is(err, errIncompatibleTypes) {
    84  			t.Errorf("Expected an error for %T, not errIncompatibleTypes", invalid)
    85  		} else if err == nil {
    86  			t.Errorf("Expected an error for %T", invalid)
    87  		}
    88  	}
    89  }
    90  
    91  func TestCOREAreMembersCompatible(t *testing.T) {
    92  	tests := []struct {
    93  		a, b       Type
    94  		compatible bool
    95  	}{
    96  		{&Struct{Name: "a"}, &Struct{Name: "b"}, true},
    97  		{&Union{Name: "a"}, &Union{Name: "b"}, true},
    98  		{&Union{Name: "a"}, &Struct{Name: "b"}, true},
    99  		{&Enum{Name: "a"}, &Enum{Name: "b"}, false},
   100  		{&Enum{Name: "a"}, &Enum{Name: "a___foo"}, true},
   101  		{&Enum{Name: "a"}, &Enum{Name: ""}, true},
   102  		{&Fwd{Name: "a"}, &Fwd{Name: "b"}, false},
   103  		{&Fwd{Name: "a"}, &Fwd{Name: "a___foo"}, true},
   104  		{&Fwd{Name: "a"}, &Fwd{Name: ""}, true},
   105  		{&Int{Name: "a", Size: 2}, &Int{Name: "b", Size: 4}, true},
   106  		{&Pointer{Target: &Void{}}, &Pointer{Target: &Void{}}, true},
   107  		{&Pointer{Target: &Void{}}, &Void{}, false},
   108  		{&Array{Type: &Int{Size: 1}}, &Array{Type: &Int{Encoding: Signed}}, true},
   109  		{&Float{Size: 2}, &Float{Size: 4}, true},
   110  	}
   111  
   112  	for _, test := range tests {
   113  		err := coreAreMembersCompatible(test.a, test.b)
   114  		if test.compatible {
   115  			if err != nil {
   116  				t.Errorf("Expected members to be compatible: %s\na = %#v\nb = %#v", err, test.a, test.b)
   117  				continue
   118  			}
   119  		} else {
   120  			if !errors.Is(err, errImpossibleRelocation) {
   121  				t.Errorf("Expected members to be incompatible: %s\na = %#v\nb = %#v", err, test.a, test.b)
   122  				continue
   123  			}
   124  		}
   125  
   126  		err = coreAreMembersCompatible(test.b, test.a)
   127  		if test.compatible {
   128  			if err != nil {
   129  				t.Errorf("Expected reversed members to be compatible: %s\na = %#v\nb = %#v", err, test.a, test.b)
   130  			}
   131  		} else {
   132  			if !errors.Is(err, errImpossibleRelocation) {
   133  				t.Errorf("Expected reversed members to be incompatible: %s\na = %#v\nb = %#v", err, test.a, test.b)
   134  			}
   135  		}
   136  	}
   137  
   138  	for _, invalid := range []Type{&Void{}, &FuncProto{}, &Var{}, &Datasec{}} {
   139  		err := coreAreMembersCompatible(invalid, invalid)
   140  		if errors.Is(err, errImpossibleRelocation) {
   141  			t.Errorf("Expected an error for %T, not errImpossibleRelocation", invalid)
   142  		} else if err == nil {
   143  			t.Errorf("Expected an error for %T", invalid)
   144  		}
   145  	}
   146  }
   147  
   148  func TestCOREAccessor(t *testing.T) {
   149  	for _, valid := range []string{
   150  		"0",
   151  		"1:0",
   152  		"1:0:3:34:10:1",
   153  	} {
   154  		_, err := parseCOREAccessor(valid)
   155  		if err != nil {
   156  			t.Errorf("Parse %q: %s", valid, err)
   157  		}
   158  	}
   159  
   160  	for _, invalid := range []string{
   161  		"",
   162  		"-1",
   163  		":",
   164  		"0:",
   165  		":12",
   166  		"4294967296",
   167  	} {
   168  		_, err := parseCOREAccessor(invalid)
   169  		if err == nil {
   170  			t.Errorf("Accepted invalid accessor %q", invalid)
   171  		}
   172  	}
   173  }
   174  
   175  func TestCOREFindEnumValue(t *testing.T) {
   176  	a := &Enum{Values: []EnumValue{{"foo", 23}, {"bar", 42}}}
   177  	b := &Enum{Values: []EnumValue{
   178  		{"foo___flavour", 0},
   179  		{"bar", 123},
   180  		{"garbage", 3},
   181  	}}
   182  
   183  	invalid := []struct {
   184  		name   string
   185  		local  Type
   186  		target Type
   187  		acc    coreAccessor
   188  		err    error
   189  	}{
   190  		{"o-o-b accessor", a, b, coreAccessor{len(a.Values)}, nil},
   191  		{"long accessor", a, b, coreAccessor{0, 1}, nil},
   192  		{"wrong target", a, &Void{}, coreAccessor{0, 1}, nil},
   193  		{
   194  			"no matching value",
   195  			b, a,
   196  			coreAccessor{2},
   197  			errImpossibleRelocation,
   198  		},
   199  	}
   200  
   201  	for _, test := range invalid {
   202  		t.Run(test.name, func(t *testing.T) {
   203  			_, _, err := coreFindEnumValue(test.local, test.acc, test.target)
   204  			if test.err != nil && !errors.Is(err, test.err) {
   205  				t.Fatalf("Expected %s, got %s", test.err, err)
   206  			}
   207  			if err == nil {
   208  				t.Fatal("Accepted invalid case")
   209  			}
   210  		})
   211  	}
   212  
   213  	valid := []struct {
   214  		name                    string
   215  		local, target           Type
   216  		acc                     coreAccessor
   217  		localValue, targetValue uint64
   218  	}{
   219  		{"a to b", a, b, coreAccessor{0}, 23, 0},
   220  		{"b to a", b, a, coreAccessor{1}, 123, 42},
   221  	}
   222  
   223  	for _, test := range valid {
   224  		t.Run(test.name, func(t *testing.T) {
   225  			local, target, err := coreFindEnumValue(test.local, test.acc, test.target)
   226  			qt.Assert(t, qt.IsNil(err))
   227  			qt.Check(t, qt.Equals(local.Value, test.localValue))
   228  			qt.Check(t, qt.Equals(target.Value, test.targetValue))
   229  		})
   230  	}
   231  }
   232  
   233  func TestCOREFindField(t *testing.T) {
   234  	ptr := &Pointer{}
   235  	u16 := &Int{Size: 2}
   236  	u32 := &Int{Size: 4}
   237  	aFields := []Member{
   238  		{Name: "foo", Type: ptr, Offset: 8},
   239  		{Name: "bar", Type: u16, Offset: 16},
   240  		{Name: "baz", Type: u32, Offset: 32, BitfieldSize: 3},
   241  		{Name: "quux", Type: u32, Offset: 35, BitfieldSize: 10},
   242  		{Name: "quuz", Type: u32, Offset: 45, BitfieldSize: 8},
   243  	}
   244  	bFields := []Member{
   245  		{Name: "foo", Type: ptr, Offset: 16},
   246  		{Name: "bar", Type: u32, Offset: 8},
   247  		{Name: "other", Offset: 4},
   248  		// baz is separated out from the other bitfields
   249  		{Name: "baz", Type: u32, Offset: 64, BitfieldSize: 3},
   250  		// quux's type changes u32->u16
   251  		{Name: "quux", Type: u16, Offset: 96, BitfieldSize: 10},
   252  		// quuz becomes a normal field
   253  		{Name: "quuz", Type: u16, Offset: 112},
   254  	}
   255  
   256  	aStruct := &Struct{Members: aFields, Size: 48}
   257  	bStruct := &Struct{Members: bFields, Size: 80}
   258  	aArray := &Array{Nelems: 4, Type: u16}
   259  	bArray := &Array{Nelems: 3, Type: u32}
   260  
   261  	invalid := []struct {
   262  		name          string
   263  		local, target Type
   264  		acc           coreAccessor
   265  		err           error
   266  	}{
   267  		{
   268  			"unsupported type",
   269  			&Void{}, &Void{},
   270  			coreAccessor{0, 0},
   271  			ErrNotSupported,
   272  		},
   273  		{
   274  			"different types",
   275  			&Union{}, &Array{Type: u16},
   276  			coreAccessor{0},
   277  			errImpossibleRelocation,
   278  		},
   279  		{
   280  			"invalid composite accessor",
   281  			aStruct, aStruct,
   282  			coreAccessor{0, len(aStruct.Members)},
   283  			nil,
   284  		},
   285  		{
   286  			"invalid array accessor",
   287  			aArray, aArray,
   288  			coreAccessor{0, int(aArray.Nelems)},
   289  			nil,
   290  		},
   291  		{
   292  			"o-o-b array accessor",
   293  			aArray, bArray,
   294  			coreAccessor{0, int(bArray.Nelems)},
   295  			errImpossibleRelocation,
   296  		},
   297  		{
   298  			"no match",
   299  			bStruct, aStruct,
   300  			coreAccessor{0, 2},
   301  			errImpossibleRelocation,
   302  		},
   303  		{
   304  			"incompatible match",
   305  			&Union{Members: []Member{{Name: "foo", Type: &Pointer{}}}},
   306  			&Union{Members: []Member{{Name: "foo", Type: &Int{}}}},
   307  			coreAccessor{0, 0},
   308  			errImpossibleRelocation,
   309  		},
   310  		{
   311  			"unsized type",
   312  			bStruct, &Func{},
   313  			// non-zero accessor to force calculating the offset.
   314  			coreAccessor{1},
   315  			errImpossibleRelocation,
   316  		},
   317  	}
   318  
   319  	for _, test := range invalid {
   320  		t.Run(test.name, func(t *testing.T) {
   321  			_, _, err := coreFindField(test.local, test.acc, test.target)
   322  			if test.err != nil && !errors.Is(err, test.err) {
   323  				t.Fatalf("Expected %s, got %s", test.err, err)
   324  			}
   325  			if err == nil {
   326  				t.Fatal("Accepted invalid case")
   327  			}
   328  			t.Log(err)
   329  		})
   330  	}
   331  
   332  	bytes := func(typ Type) uint32 {
   333  		sz, err := Sizeof(typ)
   334  		if err != nil {
   335  			t.Fatal(err)
   336  		}
   337  		return uint32(sz)
   338  	}
   339  
   340  	anon := func(t Type, offset Bits) []Member {
   341  		return []Member{{Type: t, Offset: offset}}
   342  	}
   343  
   344  	anonStruct := func(m ...Member) Member {
   345  		return Member{Type: &Struct{Members: m}}
   346  	}
   347  
   348  	anonUnion := func(m ...Member) Member {
   349  		return Member{Type: &Union{Members: m}}
   350  	}
   351  
   352  	valid := []struct {
   353  		name                    string
   354  		local                   Type
   355  		target                  Type
   356  		acc                     coreAccessor
   357  		localField, targetField coreField
   358  	}{
   359  		{
   360  			"array[0]",
   361  			aArray,
   362  			bArray,
   363  			coreAccessor{0, 0},
   364  			coreField{u16, 0, 0, 0},
   365  			coreField{u32, 0, 0, 0},
   366  		},
   367  		{
   368  			"array[1]",
   369  			aArray,
   370  			bArray,
   371  			coreAccessor{0, 1},
   372  			coreField{u16, bytes(aArray.Type), 0, 0},
   373  			coreField{u32, bytes(bArray.Type), 0, 0},
   374  		},
   375  		{
   376  			"array[0] with base offset",
   377  			aArray,
   378  			bArray,
   379  			coreAccessor{1, 0},
   380  			coreField{u16, bytes(aArray), 0, 0},
   381  			coreField{u32, bytes(bArray), 0, 0},
   382  		},
   383  		{
   384  			"array[2] with base offset",
   385  			aArray,
   386  			bArray,
   387  			coreAccessor{1, 2},
   388  			coreField{u16, bytes(aArray) + 2*bytes(aArray.Type), 0, 0},
   389  			coreField{u32, bytes(bArray) + 2*bytes(bArray.Type), 0, 0},
   390  		},
   391  		{
   392  			"flex array",
   393  			&Struct{Members: []Member{{Name: "foo", Type: &Array{Nelems: 0, Type: u16}}}},
   394  			&Struct{Members: []Member{{Name: "foo", Type: &Array{Nelems: 0, Type: u32}}}},
   395  			coreAccessor{0, 0, 9000},
   396  			coreField{u16, bytes(u16) * 9000, 0, 0},
   397  			coreField{u32, bytes(u32) * 9000, 0, 0},
   398  		},
   399  		{
   400  			"struct.0",
   401  			aStruct, bStruct,
   402  			coreAccessor{0, 0},
   403  			coreField{ptr, 1, 0, 0},
   404  			coreField{ptr, 2, 0, 0},
   405  		},
   406  		{
   407  			"struct.0 anon",
   408  			aStruct, &Struct{Members: anon(bStruct, 24)},
   409  			coreAccessor{0, 0},
   410  			coreField{ptr, 1, 0, 0},
   411  			coreField{ptr, 3 + 2, 0, 0},
   412  		},
   413  		{
   414  			"struct.0 with base offset",
   415  			aStruct, bStruct,
   416  			coreAccessor{3, 0},
   417  			coreField{ptr, 3*bytes(aStruct) + 1, 0, 0},
   418  			coreField{ptr, 3*bytes(bStruct) + 2, 0, 0},
   419  		},
   420  		{
   421  			"struct.1",
   422  			aStruct, bStruct,
   423  			coreAccessor{0, 1},
   424  			coreField{u16, 2, 0, 0},
   425  			coreField{u32, 1, 0, 0},
   426  		},
   427  		{
   428  			"struct.1 anon",
   429  			aStruct, &Struct{Members: anon(bStruct, 24)},
   430  			coreAccessor{0, 1},
   431  			coreField{u16, 2, 0, 0},
   432  			coreField{u32, 3 + 1, 0, 0},
   433  		},
   434  		{
   435  			"union.1",
   436  			&Union{Members: aFields, Size: 32},
   437  			&Union{Members: bFields, Size: 32},
   438  			coreAccessor{0, 1},
   439  			coreField{u16, 2, 0, 0},
   440  			coreField{u32, 1, 0, 0},
   441  		},
   442  		{
   443  			"interchangeable composites",
   444  			&Struct{
   445  				Members: []Member{
   446  					anonStruct(anonUnion(Member{Name: "_1", Type: u16})),
   447  				},
   448  			},
   449  			&Struct{
   450  				Members: []Member{
   451  					anonUnion(anonStruct(Member{Name: "_1", Type: u16})),
   452  				},
   453  			},
   454  			coreAccessor{0, 0, 0, 0},
   455  			coreField{u16, 0, 0, 0},
   456  			coreField{u16, 0, 0, 0},
   457  		},
   458  		{
   459  			"struct.2 (bitfield baz)",
   460  			aStruct, bStruct,
   461  			coreAccessor{0, 2},
   462  			coreField{u32, 4, 0, 3},
   463  			coreField{u32, 8, 0, 3},
   464  		},
   465  		{
   466  			"struct.3 (bitfield quux)",
   467  			aStruct, bStruct,
   468  			coreAccessor{0, 3},
   469  			coreField{u32, 4, 3, 10},
   470  			coreField{u16, 12, 0, 10},
   471  		},
   472  		{
   473  			"struct.4 (bitfield quuz)",
   474  			aStruct, bStruct,
   475  			coreAccessor{0, 4},
   476  			coreField{u32, 4, 13, 8},
   477  			coreField{u16, 14, 0, 0},
   478  		},
   479  	}
   480  
   481  	allowCoreField := cmp.AllowUnexported(coreField{})
   482  
   483  	checkCOREField := func(t *testing.T, which string, got, want coreField) {
   484  		t.Helper()
   485  		if diff := cmp.Diff(want, got, allowCoreField); diff != "" {
   486  			t.Errorf("%s mismatch (-want +got):\n%s", which, diff)
   487  		}
   488  	}
   489  
   490  	for _, test := range valid {
   491  		t.Run(test.name, func(t *testing.T) {
   492  			localField, targetField, err := coreFindField(test.local, test.acc, test.target)
   493  			qt.Assert(t, qt.IsNil(err))
   494  			checkCOREField(t, "local", localField, test.localField)
   495  			checkCOREField(t, "target", targetField, test.targetField)
   496  		})
   497  	}
   498  }
   499  
   500  func TestCOREFindFieldCyclical(t *testing.T) {
   501  	members := []Member{{Name: "foo", Type: &Pointer{}}}
   502  
   503  	cyclicStruct := &Struct{}
   504  	cyclicStruct.Members = []Member{{Type: cyclicStruct}}
   505  
   506  	cyclicUnion := &Union{}
   507  	cyclicUnion.Members = []Member{{Type: cyclicUnion}}
   508  
   509  	cyclicArray := &Array{Nelems: 1}
   510  	cyclicArray.Type = &Pointer{Target: cyclicArray}
   511  
   512  	tests := []struct {
   513  		name          string
   514  		local, cyclic Type
   515  	}{
   516  		{"struct", &Struct{Members: members}, cyclicStruct},
   517  		{"union", &Union{Members: members}, cyclicUnion},
   518  		{"array", &Array{Nelems: 2, Type: &Int{}}, cyclicArray},
   519  	}
   520  
   521  	for _, test := range tests {
   522  		t.Run(test.name, func(t *testing.T) {
   523  			_, _, err := coreFindField(test.local, coreAccessor{0, 0}, test.cyclic)
   524  			if !errors.Is(err, errImpossibleRelocation) {
   525  				t.Fatal("Should return errImpossibleRelocation, got", err)
   526  			}
   527  		})
   528  	}
   529  }
   530  
   531  func TestCORERelocation(t *testing.T) {
   532  	testutils.Files(t, testutils.Glob(t, "testdata/*.elf"), func(t *testing.T, file string) {
   533  		rd, err := os.Open(file)
   534  		if err != nil {
   535  			t.Fatal(err)
   536  		}
   537  		defer rd.Close()
   538  
   539  		spec, extInfos, err := LoadSpecAndExtInfosFromReader(rd)
   540  		if err != nil {
   541  			t.Fatal(err)
   542  		}
   543  
   544  		if extInfos == nil {
   545  			t.Skip("No ext_infos")
   546  		}
   547  
   548  		errs := map[string]error{
   549  			"err_ambiguous":         errAmbiguousRelocation,
   550  			"err_ambiguous_flavour": errAmbiguousRelocation,
   551  		}
   552  
   553  		for section := range extInfos.funcInfos {
   554  			name := strings.TrimPrefix(section, "socket/")
   555  			t.Run(name, func(t *testing.T) {
   556  				var relos []*CORERelocation
   557  				for _, reloInfo := range extInfos.relocationInfos[section].infos {
   558  					relos = append(relos, reloInfo.relo)
   559  				}
   560  
   561  				fixups, err := CORERelocate(relos, []*Spec{spec}, spec.imm.byteOrder, spec.TypeID)
   562  				if want := errs[name]; want != nil {
   563  					if !errors.Is(err, want) {
   564  						t.Fatal("Expected", want, "got", err)
   565  					}
   566  					return
   567  				}
   568  
   569  				if err != nil {
   570  					t.Fatal("Can't relocate against itself:", err)
   571  				}
   572  
   573  				for offset, fixup := range fixups {
   574  					if want := fixup.local; !fixup.skipLocalValidation && want != fixup.target {
   575  						// Since we're relocating against ourselves both values
   576  						// should match.
   577  						t.Errorf("offset %d: local %v doesn't match target %d (kind %s)", offset, fixup.local, fixup.target, fixup.kind)
   578  					}
   579  				}
   580  			})
   581  		}
   582  	})
   583  }
   584  
   585  func TestCOREReloFieldSigned(t *testing.T) {
   586  	for _, typ := range []Type{&Int{}, &Enum{}} {
   587  		t.Run(fmt.Sprintf("%T with invalid target", typ), func(t *testing.T) {
   588  			relo := &CORERelocation{
   589  				typ, coreAccessor{0}, reloFieldSigned, 0,
   590  			}
   591  			fixup, err := coreCalculateFixup(relo, &Void{}, internal.NativeEndian, dummyTypeID)
   592  			qt.Assert(t, qt.IsTrue(fixup.poison))
   593  			qt.Assert(t, qt.IsNil(err))
   594  		})
   595  	}
   596  
   597  	t.Run("type without signedness", func(t *testing.T) {
   598  		relo := &CORERelocation{
   599  			&Array{}, coreAccessor{0}, reloFieldSigned, 0,
   600  		}
   601  		_, err := coreCalculateFixup(relo, &Array{}, internal.NativeEndian, dummyTypeID)
   602  		qt.Assert(t, qt.ErrorIs(err, errNoSignedness))
   603  	})
   604  }
   605  
   606  func TestCOREReloFieldShiftU64(t *testing.T) {
   607  	typ := &Struct{
   608  		Members: []Member{
   609  			{Name: "A", Type: &Fwd{}},
   610  		},
   611  	}
   612  
   613  	for _, relo := range []*CORERelocation{
   614  		{typ, coreAccessor{0, 0}, reloFieldRShiftU64, 1},
   615  		{typ, coreAccessor{0, 0}, reloFieldLShiftU64, 1},
   616  	} {
   617  		t.Run(relo.kind.String(), func(t *testing.T) {
   618  			_, err := coreCalculateFixup(relo, typ, internal.NativeEndian, dummyTypeID)
   619  			qt.Assert(t, qt.ErrorIs(err, errUnsizedType))
   620  		})
   621  	}
   622  }
   623  
   624  func TestCORERelosKmodTypeID(t *testing.T) {
   625  	a := &Int{Name: "a"}
   626  	b := &Int{Name: "b"}
   627  
   628  	relos := []*CORERelocation{
   629  		{&Int{}, coreAccessor{0}, reloTypeIDTarget, 0},
   630  	}
   631  
   632  	typeID := func(t Type) (TypeID, error) {
   633  		if t == a {
   634  			return 42, nil
   635  		}
   636  		return 0, ErrNotFound
   637  	}
   638  
   639  	fixups, err := coreCalculateFixups(
   640  		relos,
   641  		[]Type{a, b},
   642  		internal.NativeEndian,
   643  		typeID,
   644  	)
   645  	qt.Assert(t, qt.IsNil(err))
   646  	qt.Assert(t, qt.IsFalse(fixups[0].poison))
   647  	qt.Assert(t, qt.Equals(fixups[0].target, 42))
   648  
   649  	fixups, err = coreCalculateFixups(
   650  		relos,
   651  		[]Type{b},
   652  		internal.NativeEndian,
   653  		typeID,
   654  	)
   655  	qt.Assert(t, qt.IsNil(err))
   656  	qt.Assert(t, qt.IsTrue(fixups[0].poison))
   657  }
   658  
   659  func BenchmarkCORESkBuff(b *testing.B) {
   660  	spec := vmlinuxTestdataSpec(b)
   661  
   662  	var skb *Struct
   663  	err := spec.TypeByName("sk_buff", &skb)
   664  	qt.Assert(b, qt.IsNil(err))
   665  
   666  	skbID, err := spec.TypeID(skb)
   667  	qt.Assert(b, qt.IsNil(err))
   668  
   669  	lenIndex := slices.IndexFunc(skb.Members, func(m Member) bool {
   670  		return m.Name == "len"
   671  	})
   672  	qt.Assert(b, qt.Not(qt.Equals(lenIndex, -1)))
   673  
   674  	var pktHashTypes *Enum
   675  	err = spec.TypeByName("pkt_hash_types", &pktHashTypes)
   676  	qt.Assert(b, qt.IsNil(err))
   677  
   678  	pktHashTypesID, err := spec.TypeID(pktHashTypes)
   679  	qt.Assert(b, qt.IsNil(err))
   680  
   681  	for _, relo := range []*CORERelocation{
   682  		{skb, coreAccessor{0, lenIndex}, reloFieldByteOffset, skbID},
   683  		{skb, coreAccessor{0, lenIndex}, reloFieldByteSize, skbID},
   684  		{skb, coreAccessor{0, lenIndex}, reloFieldExists, skbID},
   685  		{skb, coreAccessor{0, lenIndex}, reloFieldSigned, skbID},
   686  		{skb, coreAccessor{0, lenIndex}, reloFieldLShiftU64, skbID},
   687  		{skb, coreAccessor{0, lenIndex}, reloFieldRShiftU64, skbID},
   688  		{skb, coreAccessor{0}, reloTypeIDLocal, skbID},
   689  		{skb, coreAccessor{0}, reloTypeIDTarget, skbID},
   690  		{skb, coreAccessor{0}, reloTypeExists, skbID},
   691  		{skb, coreAccessor{0}, reloTypeSize, skbID},
   692  		{pktHashTypes, coreAccessor{0}, reloEnumvalExists, pktHashTypesID},
   693  		{pktHashTypes, coreAccessor{0}, reloEnumvalValue, pktHashTypesID},
   694  	} {
   695  		b.Run(relo.kind.String(), func(b *testing.B) {
   696  			b.ReportAllocs()
   697  
   698  			for i := 0; i < b.N; i++ {
   699  				_, err = CORERelocate([]*CORERelocation{relo}, []*Spec{spec}, spec.imm.byteOrder, spec.TypeID)
   700  				if err != nil {
   701  					b.Fatal(err)
   702  				}
   703  			}
   704  		})
   705  	}
   706  }
   707  
   708  func TestCORETypesMatch(t *testing.T) {
   709  	tests := []struct {
   710  		a, b       Type
   711  		match      bool
   712  		reversible bool
   713  	}{
   714  		{&Void{}, &Void{}, true, true},
   715  		{&Int{Size: 32}, &Int{Size: 32}, true, true},
   716  		{&Int{Size: 64}, &Int{Size: 32}, false, true},
   717  		{&Int{Size: 32}, &Int{Size: 32, Encoding: Signed}, false, true},
   718  		{&Fwd{Name: "a"}, &Fwd{Name: "a"}, true, true},
   719  		{&Fwd{Name: "a"}, &Fwd{Name: "b___new"}, false, true},
   720  		{&Fwd{Name: "a"}, &Fwd{Name: "a___new"}, true, true},
   721  		{&Fwd{Name: "a"}, &Struct{Name: "a___new"}, false, true},
   722  		{&Fwd{Name: "a"}, &Union{Name: "a___new"}, false, true},
   723  		{&Fwd{Name: "a", Kind: FwdStruct}, &Fwd{Name: "a___new", Kind: FwdUnion}, false, true},
   724  		{&Pointer{&Fwd{Name: "a", Kind: FwdStruct}}, &Pointer{&Struct{Name: "a___new"}}, true, true},
   725  		{&Pointer{&Fwd{Name: "a", Kind: FwdUnion}}, &Pointer{&Union{Name: "a___new"}}, true, true},
   726  		{&Pointer{&Fwd{Name: "a", Kind: FwdStruct}}, &Pointer{&Union{Name: "a___new"}}, false, true},
   727  		{&Struct{Name: "a___new"}, &Union{Name: "a___new"}, false, true},
   728  		{&Pointer{&Struct{Name: "a"}}, &Pointer{&Union{Name: "a___new"}}, false, true},
   729  		{
   730  			&Struct{Name: "a", Members: []Member{
   731  				{Name: "foo", Type: &Int{}},
   732  			}},
   733  			&Struct{Name: "a___new", Members: []Member{
   734  				{Name: "foo", Type: &Int{}},
   735  			}},
   736  			true,
   737  			true,
   738  		},
   739  		{
   740  			&Struct{Name: "a", Members: []Member{
   741  				{Name: "foo", Type: &Int{}},
   742  			}},
   743  			&Struct{Name: "a___new", Members: []Member{
   744  				{Name: "foo", Type: &Int{}},
   745  				{Name: "bar", Type: &Int{}},
   746  			}},
   747  			true,
   748  			false,
   749  		},
   750  		{
   751  			&Struct{Name: "a", Members: []Member{
   752  				{Name: "foo", Type: &Int{}},
   753  				{Name: "bar", Type: &Int{}},
   754  			}},
   755  			&Struct{Name: "a___new", Members: []Member{
   756  				{Name: "foo", Type: &Int{}},
   757  			}},
   758  			false,
   759  			false,
   760  		},
   761  		{
   762  			&Struct{Name: "a", Members: []Member{
   763  				{Name: "bar", Type: &Int{}},
   764  			}},
   765  			&Struct{Name: "a___new", Members: []Member{
   766  				{Name: "foo", Type: &Int{}},
   767  			}},
   768  			false,
   769  			false,
   770  		},
   771  		{
   772  			&Struct{Name: "a", Members: []Member{
   773  				{Name: "foo", Type: &Int{Encoding: Signed}},
   774  			}},
   775  			&Struct{Name: "a___new", Members: []Member{
   776  				{Name: "foo", Type: &Int{}},
   777  			}},
   778  			false,
   779  			false,
   780  		},
   781  		{
   782  			&Enum{Name: "a", Values: []EnumValue{
   783  				{"foo", 1},
   784  			}},
   785  			&Enum{Name: "a___new", Values: []EnumValue{
   786  				{"foo", 1},
   787  			}},
   788  			true,
   789  			true,
   790  		},
   791  		{
   792  			&Enum{Name: "a", Values: []EnumValue{
   793  				{"foo", 1},
   794  			}},
   795  			&Enum{Name: "a___new", Values: []EnumValue{
   796  				{"foo", 1},
   797  				{"bar", 2},
   798  			}},
   799  			true,
   800  			false,
   801  		},
   802  		{
   803  			&Enum{Name: "a", Values: []EnumValue{
   804  				{"foo", 1},
   805  				{"bar", 2},
   806  			}},
   807  			&Enum{Name: "a___new", Values: []EnumValue{
   808  				{"foo", 1},
   809  			}},
   810  			false,
   811  			false,
   812  		},
   813  		{
   814  			&Enum{Name: "a", Values: []EnumValue{
   815  				{"foo", 1},
   816  			}},
   817  			&Enum{Name: "a___new", Values: []EnumValue{
   818  				{"bar", 1},
   819  			}},
   820  			false,
   821  			false,
   822  		},
   823  		{
   824  			&Enum{Name: "a", Values: []EnumValue{
   825  				{"foo", 1},
   826  			}, Size: 1},
   827  			&Enum{Name: "a___new", Values: []EnumValue{
   828  				{"foo", 1},
   829  			}, Size: 2},
   830  			false,
   831  			false,
   832  		},
   833  		{
   834  			&Array{Type: &Int{}, Nelems: 2},
   835  			&Array{Type: &Int{}, Nelems: 2},
   836  			true,
   837  			true,
   838  		},
   839  		{
   840  			&Array{Type: &Int{}, Nelems: 3},
   841  			&Array{Type: &Int{}, Nelems: 2},
   842  			false,
   843  			true,
   844  		},
   845  		{
   846  			&Array{Type: &Void{}, Nelems: 2},
   847  			&Array{Type: &Int{}, Nelems: 2},
   848  			false,
   849  			true,
   850  		},
   851  		{
   852  			&FuncProto{Return: &Int{}, Params: []FuncParam{
   853  				{Name: "foo", Type: &Int{}},
   854  			}},
   855  			&FuncProto{Return: &Int{}, Params: []FuncParam{
   856  				{Name: "bar", Type: &Int{}},
   857  			}},
   858  			true,
   859  			true,
   860  		},
   861  		{
   862  			&FuncProto{Return: &Int{}, Params: []FuncParam{
   863  				{Name: "foo", Type: &Int{}},
   864  			}},
   865  			&FuncProto{Return: &Int{}, Params: []FuncParam{
   866  				{Name: "bar", Type: &Int{}},
   867  				{Name: "baz", Type: &Int{}},
   868  			}},
   869  			false,
   870  			true,
   871  		},
   872  		{
   873  			&FuncProto{Return: &Void{}, Params: []FuncParam{
   874  				{Name: "foo", Type: &Int{}},
   875  			}},
   876  			&FuncProto{Return: &Int{}, Params: []FuncParam{
   877  				{Name: "bar", Type: &Int{}},
   878  			}},
   879  			false,
   880  			true,
   881  		},
   882  		{
   883  			&FuncProto{Return: &Void{}, Params: []FuncParam{
   884  				{Name: "bar", Type: &Int{Encoding: Signed}},
   885  			}},
   886  			&FuncProto{Return: &Int{}, Params: []FuncParam{
   887  				{Name: "bar", Type: &Int{}},
   888  			}},
   889  			false,
   890  			true,
   891  		},
   892  	}
   893  
   894  	for _, test := range tests {
   895  		err := coreTypesMatch(test.a, test.b, nil)
   896  		if test.match {
   897  			if err != nil {
   898  				t.Errorf("Expected types to match: %s\na = %#v\nb = %#v", err, test.a, test.b)
   899  				continue
   900  			}
   901  		} else {
   902  			if !errors.Is(err, errIncompatibleTypes) {
   903  				t.Errorf("Expected types to be incompatible: \na = %#v\nb = %#v", test.a, test.b)
   904  				continue
   905  			}
   906  		}
   907  
   908  		if test.reversible {
   909  			err = coreTypesMatch(test.b, test.a, nil)
   910  			if test.match {
   911  				if err != nil {
   912  					t.Errorf("Expected reversed types to match: %s\na = %#v\nb = %#v", err, test.a, test.b)
   913  				}
   914  			} else {
   915  				if !errors.Is(err, errIncompatibleTypes) {
   916  					t.Errorf("Expected reversed types to be incompatible: %s\na = %#v\nb = %#v", err, test.a, test.b)
   917  				}
   918  			}
   919  		}
   920  	}
   921  
   922  	for _, invalid := range []Type{&Var{}, &Datasec{}} {
   923  		err := coreTypesMatch(invalid, invalid, nil)
   924  		if errors.Is(err, errIncompatibleTypes) {
   925  			t.Errorf("Expected an error for %T, not errIncompatibleTypes", invalid)
   926  		} else if err == nil {
   927  			t.Errorf("Expected an error for %T", invalid)
   928  		}
   929  	}
   930  }
   931  
   932  // dummyTypeID returns 0, nil for any passed type.
   933  func dummyTypeID(Type) (TypeID, error) {
   934  	return 0, nil
   935  }