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 }