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