github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/collection.go (about) 1 package ebpf 2 3 import ( 4 "encoding/binary" 5 "errors" 6 "fmt" 7 "reflect" 8 "strings" 9 10 "github.com/cilium/ebpf/asm" 11 "github.com/cilium/ebpf/btf" 12 "github.com/cilium/ebpf/internal" 13 "github.com/cilium/ebpf/internal/kconfig" 14 "github.com/cilium/ebpf/internal/sysenc" 15 ) 16 17 // CollectionOptions control loading a collection into the kernel. 18 // 19 // Maps and Programs are passed to NewMapWithOptions and NewProgramsWithOptions. 20 type CollectionOptions struct { 21 Maps MapOptions 22 Programs ProgramOptions 23 24 // MapReplacements takes a set of Maps that will be used instead of 25 // creating new ones when loading the CollectionSpec. 26 // 27 // For each given Map, there must be a corresponding MapSpec in 28 // CollectionSpec.Maps, and its type, key/value size, max entries and flags 29 // must match the values of the MapSpec. 30 // 31 // The given Maps are Clone()d before being used in the Collection, so the 32 // caller can Close() them freely when they are no longer needed. 33 MapReplacements map[string]*Map 34 } 35 36 // CollectionSpec describes a collection. 37 type CollectionSpec struct { 38 Maps map[string]*MapSpec 39 Programs map[string]*ProgramSpec 40 41 // Types holds type information about Maps and Programs. 42 // Modifications to Types are currently undefined behaviour. 43 Types *btf.Spec 44 45 // ByteOrder specifies whether the ELF was compiled for 46 // big-endian or little-endian architectures. 47 ByteOrder binary.ByteOrder 48 } 49 50 // Copy returns a recursive copy of the spec. 51 func (cs *CollectionSpec) Copy() *CollectionSpec { 52 if cs == nil { 53 return nil 54 } 55 56 cpy := CollectionSpec{ 57 Maps: make(map[string]*MapSpec, len(cs.Maps)), 58 Programs: make(map[string]*ProgramSpec, len(cs.Programs)), 59 ByteOrder: cs.ByteOrder, 60 Types: cs.Types, 61 } 62 63 for name, spec := range cs.Maps { 64 cpy.Maps[name] = spec.Copy() 65 } 66 67 for name, spec := range cs.Programs { 68 cpy.Programs[name] = spec.Copy() 69 } 70 71 return &cpy 72 } 73 74 // RewriteMaps replaces all references to specific maps. 75 // 76 // Use this function to use pre-existing maps instead of creating new ones 77 // when calling NewCollection. Any named maps are removed from CollectionSpec.Maps. 78 // 79 // Returns an error if a named map isn't used in at least one program. 80 // 81 // Deprecated: Pass CollectionOptions.MapReplacements when loading the Collection 82 // instead. 83 func (cs *CollectionSpec) RewriteMaps(maps map[string]*Map) error { 84 for symbol, m := range maps { 85 // have we seen a program that uses this symbol / map 86 seen := false 87 for progName, progSpec := range cs.Programs { 88 err := progSpec.Instructions.AssociateMap(symbol, m) 89 90 switch { 91 case err == nil: 92 seen = true 93 94 case errors.Is(err, asm.ErrUnreferencedSymbol): 95 // Not all programs need to use the map 96 97 default: 98 return fmt.Errorf("program %s: %w", progName, err) 99 } 100 } 101 102 if !seen { 103 return fmt.Errorf("map %s not referenced by any programs", symbol) 104 } 105 106 // Prevent NewCollection from creating rewritten maps 107 delete(cs.Maps, symbol) 108 } 109 110 return nil 111 } 112 113 // MissingConstantsError is returned by [CollectionSpec.RewriteConstants]. 114 type MissingConstantsError struct { 115 // The constants missing from .rodata. 116 Constants []string 117 } 118 119 func (m *MissingConstantsError) Error() string { 120 return fmt.Sprintf("some constants are missing from .rodata: %s", strings.Join(m.Constants, ", ")) 121 } 122 123 // RewriteConstants replaces the value of multiple constants. 124 // 125 // The constant must be defined like so in the C program: 126 // 127 // volatile const type foobar; 128 // volatile const type foobar = default; 129 // 130 // Replacement values must be of the same length as the C sizeof(type). 131 // If necessary, they are marshalled according to the same rules as 132 // map values. 133 // 134 // From Linux 5.5 the verifier will use constants to eliminate dead code. 135 // 136 // Returns an error wrapping [MissingConstantsError] if a constant doesn't exist. 137 func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error { 138 replaced := make(map[string]bool) 139 140 for name, spec := range cs.Maps { 141 if !strings.HasPrefix(name, ".rodata") { 142 continue 143 } 144 145 b, ds, err := spec.dataSection() 146 if errors.Is(err, errMapNoBTFValue) { 147 // Data sections without a BTF Datasec are valid, but don't support 148 // constant replacements. 149 continue 150 } 151 if err != nil { 152 return fmt.Errorf("map %s: %w", name, err) 153 } 154 155 // MapSpec.Copy() performs a shallow copy. Fully copy the byte slice 156 // to avoid any changes affecting other copies of the MapSpec. 157 cpy := make([]byte, len(b)) 158 copy(cpy, b) 159 160 for _, v := range ds.Vars { 161 vname := v.Type.TypeName() 162 replacement, ok := consts[vname] 163 if !ok { 164 continue 165 } 166 167 if _, ok := v.Type.(*btf.Var); !ok { 168 return fmt.Errorf("section %s: unexpected type %T for variable %s", name, v.Type, vname) 169 } 170 171 if replaced[vname] { 172 return fmt.Errorf("section %s: duplicate variable %s", name, vname) 173 } 174 175 if int(v.Offset+v.Size) > len(cpy) { 176 return fmt.Errorf("section %s: offset %d(+%d) for variable %s is out of bounds", name, v.Offset, v.Size, vname) 177 } 178 179 b, err := sysenc.Marshal(replacement, int(v.Size)) 180 if err != nil { 181 return fmt.Errorf("marshaling constant replacement %s: %w", vname, err) 182 } 183 184 b.CopyTo(cpy[v.Offset : v.Offset+v.Size]) 185 186 replaced[vname] = true 187 } 188 189 spec.Contents[0] = MapKV{Key: uint32(0), Value: cpy} 190 } 191 192 var missing []string 193 for c := range consts { 194 if !replaced[c] { 195 missing = append(missing, c) 196 } 197 } 198 199 if len(missing) != 0 { 200 return fmt.Errorf("rewrite constants: %w", &MissingConstantsError{Constants: missing}) 201 } 202 203 return nil 204 } 205 206 // Assign the contents of a CollectionSpec to a struct. 207 // 208 // This function is a shortcut to manually checking the presence 209 // of maps and programs in a CollectionSpec. Consider using bpf2go 210 // if this sounds useful. 211 // 212 // 'to' must be a pointer to a struct. A field of the 213 // struct is updated with values from Programs or Maps if it 214 // has an `ebpf` tag and its type is *ProgramSpec or *MapSpec. 215 // The tag's value specifies the name of the program or map as 216 // found in the CollectionSpec. 217 // 218 // struct { 219 // Foo *ebpf.ProgramSpec `ebpf:"xdp_foo"` 220 // Bar *ebpf.MapSpec `ebpf:"bar_map"` 221 // Ignored int 222 // } 223 // 224 // Returns an error if any of the eBPF objects can't be found, or 225 // if the same MapSpec or ProgramSpec is assigned multiple times. 226 func (cs *CollectionSpec) Assign(to interface{}) error { 227 // Assign() only supports assigning ProgramSpecs and MapSpecs, 228 // so doesn't load any resources into the kernel. 229 getValue := func(typ reflect.Type, name string) (interface{}, error) { 230 switch typ { 231 232 case reflect.TypeOf((*ProgramSpec)(nil)): 233 if p := cs.Programs[name]; p != nil { 234 return p, nil 235 } 236 return nil, fmt.Errorf("missing program %q", name) 237 238 case reflect.TypeOf((*MapSpec)(nil)): 239 if m := cs.Maps[name]; m != nil { 240 return m, nil 241 } 242 return nil, fmt.Errorf("missing map %q", name) 243 244 default: 245 return nil, fmt.Errorf("unsupported type %s", typ) 246 } 247 } 248 249 return assignValues(to, getValue) 250 } 251 252 // LoadAndAssign loads Maps and Programs into the kernel and assigns them 253 // to a struct. 254 // 255 // Omitting Map/Program.Close() during application shutdown is an error. 256 // See the package documentation for details around Map and Program lifecycle. 257 // 258 // This function is a shortcut to manually checking the presence 259 // of maps and programs in a CollectionSpec. Consider using bpf2go 260 // if this sounds useful. 261 // 262 // 'to' must be a pointer to a struct. A field of the struct is updated with 263 // a Program or Map if it has an `ebpf` tag and its type is *Program or *Map. 264 // The tag's value specifies the name of the program or map as found in the 265 // CollectionSpec. Before updating the struct, the requested objects and their 266 // dependent resources are loaded into the kernel and populated with values if 267 // specified. 268 // 269 // struct { 270 // Foo *ebpf.Program `ebpf:"xdp_foo"` 271 // Bar *ebpf.Map `ebpf:"bar_map"` 272 // Ignored int 273 // } 274 // 275 // opts may be nil. 276 // 277 // Returns an error if any of the fields can't be found, or 278 // if the same Map or Program is assigned multiple times. 279 func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) error { 280 loader, err := newCollectionLoader(cs, opts) 281 if err != nil { 282 return err 283 } 284 defer loader.close() 285 286 // Support assigning Programs and Maps, lazy-loading the required objects. 287 assignedMaps := make(map[string]bool) 288 assignedProgs := make(map[string]bool) 289 290 getValue := func(typ reflect.Type, name string) (interface{}, error) { 291 switch typ { 292 293 case reflect.TypeOf((*Program)(nil)): 294 assignedProgs[name] = true 295 return loader.loadProgram(name) 296 297 case reflect.TypeOf((*Map)(nil)): 298 assignedMaps[name] = true 299 return loader.loadMap(name) 300 301 default: 302 return nil, fmt.Errorf("unsupported type %s", typ) 303 } 304 } 305 306 // Load the Maps and Programs requested by the annotated struct. 307 if err := assignValues(to, getValue); err != nil { 308 return err 309 } 310 311 // Populate the requested maps. Has a chance of lazy-loading other dependent maps. 312 if err := loader.populateDeferredMaps(); err != nil { 313 return err 314 } 315 316 // Evaluate the loader's objects after all (lazy)loading has taken place. 317 for n, m := range loader.maps { 318 switch m.typ { 319 case ProgramArray: 320 // Require all lazy-loaded ProgramArrays to be assigned to the given object. 321 // The kernel empties a ProgramArray once the last user space reference 322 // to it closes, which leads to failed tail calls. Combined with the library 323 // closing map fds via GC finalizers this can lead to surprising behaviour. 324 // Only allow unassigned ProgramArrays when the library hasn't pre-populated 325 // any entries from static value declarations. At this point, we know the map 326 // is empty and there's no way for the caller to interact with the map going 327 // forward. 328 if !assignedMaps[n] && len(cs.Maps[n].Contents) > 0 { 329 return fmt.Errorf("ProgramArray %s must be assigned to prevent missed tail calls", n) 330 } 331 } 332 } 333 334 // Prevent loader.cleanup() from closing assigned Maps and Programs. 335 for m := range assignedMaps { 336 delete(loader.maps, m) 337 } 338 for p := range assignedProgs { 339 delete(loader.programs, p) 340 } 341 342 return nil 343 } 344 345 // Collection is a collection of Programs and Maps associated 346 // with their symbols 347 type Collection struct { 348 Programs map[string]*Program 349 Maps map[string]*Map 350 } 351 352 // NewCollection creates a Collection from the given spec, creating and 353 // loading its declared resources into the kernel. 354 // 355 // Omitting Collection.Close() during application shutdown is an error. 356 // See the package documentation for details around Map and Program lifecycle. 357 func NewCollection(spec *CollectionSpec) (*Collection, error) { 358 return NewCollectionWithOptions(spec, CollectionOptions{}) 359 } 360 361 // NewCollectionWithOptions creates a Collection from the given spec using 362 // options, creating and loading its declared resources into the kernel. 363 // 364 // Omitting Collection.Close() during application shutdown is an error. 365 // See the package documentation for details around Map and Program lifecycle. 366 func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Collection, error) { 367 loader, err := newCollectionLoader(spec, &opts) 368 if err != nil { 369 return nil, err 370 } 371 defer loader.close() 372 373 // Create maps first, as their fds need to be linked into programs. 374 for mapName := range spec.Maps { 375 if _, err := loader.loadMap(mapName); err != nil { 376 return nil, err 377 } 378 } 379 380 for progName, prog := range spec.Programs { 381 if prog.Type == UnspecifiedProgram { 382 continue 383 } 384 385 if _, err := loader.loadProgram(progName); err != nil { 386 return nil, err 387 } 388 } 389 390 // Maps can contain Program and Map stubs, so populate them after 391 // all Maps and Programs have been successfully loaded. 392 if err := loader.populateDeferredMaps(); err != nil { 393 return nil, err 394 } 395 396 // Prevent loader.cleanup from closing maps and programs. 397 maps, progs := loader.maps, loader.programs 398 loader.maps, loader.programs = nil, nil 399 400 return &Collection{ 401 progs, 402 maps, 403 }, nil 404 } 405 406 type collectionLoader struct { 407 coll *CollectionSpec 408 opts *CollectionOptions 409 maps map[string]*Map 410 programs map[string]*Program 411 } 412 413 func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collectionLoader, error) { 414 if opts == nil { 415 opts = &CollectionOptions{} 416 } 417 418 // Check for existing MapSpecs in the CollectionSpec for all provided replacement maps. 419 for name, m := range opts.MapReplacements { 420 spec, ok := coll.Maps[name] 421 if !ok { 422 return nil, fmt.Errorf("replacement map %s not found in CollectionSpec", name) 423 } 424 425 if err := spec.Compatible(m); err != nil { 426 return nil, fmt.Errorf("using replacement map %s: %w", spec.Name, err) 427 } 428 } 429 430 return &collectionLoader{ 431 coll, 432 opts, 433 make(map[string]*Map), 434 make(map[string]*Program), 435 }, nil 436 } 437 438 // close all resources left over in the collectionLoader. 439 func (cl *collectionLoader) close() { 440 for _, m := range cl.maps { 441 m.Close() 442 } 443 for _, p := range cl.programs { 444 p.Close() 445 } 446 } 447 448 func (cl *collectionLoader) loadMap(mapName string) (*Map, error) { 449 if m := cl.maps[mapName]; m != nil { 450 return m, nil 451 } 452 453 mapSpec := cl.coll.Maps[mapName] 454 if mapSpec == nil { 455 return nil, fmt.Errorf("missing map %s", mapName) 456 } 457 458 if replaceMap, ok := cl.opts.MapReplacements[mapName]; ok { 459 // Clone the map to avoid closing user's map later on. 460 m, err := replaceMap.Clone() 461 if err != nil { 462 return nil, err 463 } 464 465 cl.maps[mapName] = m 466 return m, nil 467 } 468 469 m, err := newMapWithOptions(mapSpec, cl.opts.Maps) 470 if err != nil { 471 return nil, fmt.Errorf("map %s: %w", mapName, err) 472 } 473 474 // Finalize 'scalar' maps that don't refer to any other eBPF resources 475 // potentially pending creation. This is needed for frozen maps like .rodata 476 // that need to be finalized before invoking the verifier. 477 if !mapSpec.Type.canStoreMapOrProgram() { 478 if err := m.finalize(mapSpec); err != nil { 479 return nil, fmt.Errorf("finalizing map %s: %w", mapName, err) 480 } 481 } 482 483 cl.maps[mapName] = m 484 return m, nil 485 } 486 487 func (cl *collectionLoader) loadProgram(progName string) (*Program, error) { 488 if prog := cl.programs[progName]; prog != nil { 489 return prog, nil 490 } 491 492 progSpec := cl.coll.Programs[progName] 493 if progSpec == nil { 494 return nil, fmt.Errorf("unknown program %s", progName) 495 } 496 497 // Bail out early if we know the kernel is going to reject the program. 498 // This skips loading map dependencies, saving some cleanup work later. 499 if progSpec.Type == UnspecifiedProgram { 500 return nil, fmt.Errorf("cannot load program %s: program type is unspecified", progName) 501 } 502 503 progSpec = progSpec.Copy() 504 505 // Rewrite any reference to a valid map in the program's instructions, 506 // which includes all of its dependencies. 507 for i := range progSpec.Instructions { 508 ins := &progSpec.Instructions[i] 509 510 if !ins.IsLoadFromMap() || ins.Reference() == "" { 511 continue 512 } 513 514 // Don't overwrite map loads containing non-zero map fd's, 515 // they can be manually included by the caller. 516 // Map FDs/IDs are placed in the lower 32 bits of Constant. 517 if int32(ins.Constant) > 0 { 518 continue 519 } 520 521 m, err := cl.loadMap(ins.Reference()) 522 if err != nil { 523 return nil, fmt.Errorf("program %s: %w", progName, err) 524 } 525 526 if err := ins.AssociateMap(m); err != nil { 527 return nil, fmt.Errorf("program %s: map %s: %w", progName, ins.Reference(), err) 528 } 529 } 530 531 prog, err := newProgramWithOptions(progSpec, cl.opts.Programs) 532 if err != nil { 533 return nil, fmt.Errorf("program %s: %w", progName, err) 534 } 535 536 cl.programs[progName] = prog 537 return prog, nil 538 } 539 540 // populateDeferredMaps iterates maps holding programs or other maps and loads 541 // any dependencies. Populates all maps in cl and freezes them if specified. 542 func (cl *collectionLoader) populateDeferredMaps() error { 543 for mapName, m := range cl.maps { 544 mapSpec, ok := cl.coll.Maps[mapName] 545 if !ok { 546 return fmt.Errorf("missing map spec %s", mapName) 547 } 548 549 // Scalar maps without Map or Program references are finalized during 550 // creation. Don't finalize them again. 551 if !mapSpec.Type.canStoreMapOrProgram() { 552 continue 553 } 554 555 mapSpec = mapSpec.Copy() 556 557 // MapSpecs that refer to inner maps or programs within the same 558 // CollectionSpec do so using strings. These strings are used as the key 559 // to look up the respective object in the Maps or Programs fields. 560 // Resolve those references to actual Map or Program resources that 561 // have been loaded into the kernel. 562 for i, kv := range mapSpec.Contents { 563 objName, ok := kv.Value.(string) 564 if !ok { 565 continue 566 } 567 568 switch t := mapSpec.Type; { 569 case t.canStoreProgram(): 570 // loadProgram is idempotent and could return an existing Program. 571 prog, err := cl.loadProgram(objName) 572 if err != nil { 573 return fmt.Errorf("loading program %s, for map %s: %w", objName, mapName, err) 574 } 575 mapSpec.Contents[i] = MapKV{kv.Key, prog} 576 577 case t.canStoreMap(): 578 // loadMap is idempotent and could return an existing Map. 579 innerMap, err := cl.loadMap(objName) 580 if err != nil { 581 return fmt.Errorf("loading inner map %s, for map %s: %w", objName, mapName, err) 582 } 583 mapSpec.Contents[i] = MapKV{kv.Key, innerMap} 584 } 585 } 586 587 // Populate and freeze the map if specified. 588 if err := m.finalize(mapSpec); err != nil { 589 return fmt.Errorf("populating map %s: %w", mapName, err) 590 } 591 } 592 593 return nil 594 } 595 596 // resolveKconfig resolves all variables declared in .kconfig and populates 597 // m.Contents. Does nothing if the given m.Contents is non-empty. 598 func resolveKconfig(m *MapSpec) error { 599 ds, ok := m.Value.(*btf.Datasec) 600 if !ok { 601 return errors.New("map value is not a Datasec") 602 } 603 604 type configInfo struct { 605 offset uint32 606 typ btf.Type 607 } 608 609 configs := make(map[string]configInfo) 610 611 data := make([]byte, ds.Size) 612 for _, vsi := range ds.Vars { 613 v := vsi.Type.(*btf.Var) 614 n := v.TypeName() 615 616 switch n { 617 case "LINUX_KERNEL_VERSION": 618 if integer, ok := v.Type.(*btf.Int); !ok || integer.Size != 4 { 619 return fmt.Errorf("variable %s must be a 32 bits integer, got %s", n, v.Type) 620 } 621 622 kv, err := internal.KernelVersion() 623 if err != nil { 624 return fmt.Errorf("getting kernel version: %w", err) 625 } 626 internal.NativeEndian.PutUint32(data[vsi.Offset:], kv.Kernel()) 627 628 case "LINUX_HAS_SYSCALL_WRAPPER": 629 integer, ok := v.Type.(*btf.Int) 630 if !ok { 631 return fmt.Errorf("variable %s must be an integer, got %s", n, v.Type) 632 } 633 var value uint64 = 1 634 if err := haveSyscallWrapper(); errors.Is(err, ErrNotSupported) { 635 value = 0 636 } else if err != nil { 637 return fmt.Errorf("unable to derive a value for LINUX_HAS_SYSCALL_WRAPPER: %w", err) 638 } 639 640 if err := kconfig.PutInteger(data[vsi.Offset:], integer, value); err != nil { 641 return fmt.Errorf("set LINUX_HAS_SYSCALL_WRAPPER: %w", err) 642 } 643 644 default: // Catch CONFIG_*. 645 configs[n] = configInfo{ 646 offset: vsi.Offset, 647 typ: v.Type, 648 } 649 } 650 } 651 652 // We only parse kconfig file if a CONFIG_* variable was found. 653 if len(configs) > 0 { 654 f, err := kconfig.Find() 655 if err != nil { 656 return fmt.Errorf("cannot find a kconfig file: %w", err) 657 } 658 defer f.Close() 659 660 filter := make(map[string]struct{}, len(configs)) 661 for config := range configs { 662 filter[config] = struct{}{} 663 } 664 665 kernelConfig, err := kconfig.Parse(f, filter) 666 if err != nil { 667 return fmt.Errorf("cannot parse kconfig file: %w", err) 668 } 669 670 for n, info := range configs { 671 value, ok := kernelConfig[n] 672 if !ok { 673 return fmt.Errorf("config option %q does not exists for this kernel", n) 674 } 675 676 err := kconfig.PutValue(data[info.offset:], info.typ, value) 677 if err != nil { 678 return fmt.Errorf("problem adding value for %s: %w", n, err) 679 } 680 } 681 } 682 683 m.Contents = []MapKV{{uint32(0), data}} 684 685 return nil 686 } 687 688 // LoadCollection reads an object file and creates and loads its declared 689 // resources into the kernel. 690 // 691 // Omitting Collection.Close() during application shutdown is an error. 692 // See the package documentation for details around Map and Program lifecycle. 693 func LoadCollection(file string) (*Collection, error) { 694 spec, err := LoadCollectionSpec(file) 695 if err != nil { 696 return nil, err 697 } 698 return NewCollection(spec) 699 } 700 701 // Assign the contents of a Collection to a struct. 702 // 703 // This function bridges functionality between bpf2go generated 704 // code and any functionality better implemented in Collection. 705 // 706 // 'to' must be a pointer to a struct. A field of the 707 // struct is updated with values from Programs or Maps if it 708 // has an `ebpf` tag and its type is *Program or *Map. 709 // The tag's value specifies the name of the program or map as 710 // found in the CollectionSpec. 711 // 712 // struct { 713 // Foo *ebpf.Program `ebpf:"xdp_foo"` 714 // Bar *ebpf.Map `ebpf:"bar_map"` 715 // Ignored int 716 // } 717 // 718 // Returns an error if any of the eBPF objects can't be found, or 719 // if the same Map or Program is assigned multiple times. 720 // 721 // Ownership and Close()ing responsibility is transferred to `to` 722 // for any successful assigns. On error `to` is left in an undefined state. 723 func (coll *Collection) Assign(to interface{}) error { 724 assignedMaps := make(map[string]bool) 725 assignedProgs := make(map[string]bool) 726 727 // Assign() only transfers already-loaded Maps and Programs. No extra 728 // loading is done. 729 getValue := func(typ reflect.Type, name string) (interface{}, error) { 730 switch typ { 731 732 case reflect.TypeOf((*Program)(nil)): 733 if p := coll.Programs[name]; p != nil { 734 assignedProgs[name] = true 735 return p, nil 736 } 737 return nil, fmt.Errorf("missing program %q", name) 738 739 case reflect.TypeOf((*Map)(nil)): 740 if m := coll.Maps[name]; m != nil { 741 assignedMaps[name] = true 742 return m, nil 743 } 744 return nil, fmt.Errorf("missing map %q", name) 745 746 default: 747 return nil, fmt.Errorf("unsupported type %s", typ) 748 } 749 } 750 751 if err := assignValues(to, getValue); err != nil { 752 return err 753 } 754 755 // Finalize ownership transfer 756 for p := range assignedProgs { 757 delete(coll.Programs, p) 758 } 759 for m := range assignedMaps { 760 delete(coll.Maps, m) 761 } 762 763 return nil 764 } 765 766 // Close frees all maps and programs associated with the collection. 767 // 768 // The collection mustn't be used afterwards. 769 func (coll *Collection) Close() { 770 for _, prog := range coll.Programs { 771 prog.Close() 772 } 773 for _, m := range coll.Maps { 774 m.Close() 775 } 776 } 777 778 // DetachMap removes the named map from the Collection. 779 // 780 // This means that a later call to Close() will not affect this map. 781 // 782 // Returns nil if no map of that name exists. 783 func (coll *Collection) DetachMap(name string) *Map { 784 m := coll.Maps[name] 785 delete(coll.Maps, name) 786 return m 787 } 788 789 // DetachProgram removes the named program from the Collection. 790 // 791 // This means that a later call to Close() will not affect this program. 792 // 793 // Returns nil if no program of that name exists. 794 func (coll *Collection) DetachProgram(name string) *Program { 795 p := coll.Programs[name] 796 delete(coll.Programs, name) 797 return p 798 } 799 800 // structField represents a struct field containing the ebpf struct tag. 801 type structField struct { 802 reflect.StructField 803 value reflect.Value 804 } 805 806 // ebpfFields extracts field names tagged with 'ebpf' from a struct type. 807 // Keep track of visited types to avoid infinite recursion. 808 func ebpfFields(structVal reflect.Value, visited map[reflect.Type]bool) ([]structField, error) { 809 if visited == nil { 810 visited = make(map[reflect.Type]bool) 811 } 812 813 structType := structVal.Type() 814 if structType.Kind() != reflect.Struct { 815 return nil, fmt.Errorf("%s is not a struct", structType) 816 } 817 818 if visited[structType] { 819 return nil, fmt.Errorf("recursion on type %s", structType) 820 } 821 822 fields := make([]structField, 0, structType.NumField()) 823 for i := 0; i < structType.NumField(); i++ { 824 field := structField{structType.Field(i), structVal.Field(i)} 825 826 // If the field is tagged, gather it and move on. 827 name := field.Tag.Get("ebpf") 828 if name != "" { 829 fields = append(fields, field) 830 continue 831 } 832 833 // If the field does not have an ebpf tag, but is a struct or a pointer 834 // to a struct, attempt to gather its fields as well. 835 var v reflect.Value 836 switch field.Type.Kind() { 837 case reflect.Ptr: 838 if field.Type.Elem().Kind() != reflect.Struct { 839 continue 840 } 841 842 if field.value.IsNil() { 843 return nil, fmt.Errorf("nil pointer to %s", structType) 844 } 845 846 // Obtain the destination type of the pointer. 847 v = field.value.Elem() 848 849 case reflect.Struct: 850 // Reference the value's type directly. 851 v = field.value 852 853 default: 854 continue 855 } 856 857 inner, err := ebpfFields(v, visited) 858 if err != nil { 859 return nil, fmt.Errorf("field %s: %w", field.Name, err) 860 } 861 862 fields = append(fields, inner...) 863 } 864 865 return fields, nil 866 } 867 868 // assignValues attempts to populate all fields of 'to' tagged with 'ebpf'. 869 // 870 // getValue is called for every tagged field of 'to' and must return the value 871 // to be assigned to the field with the given typ and name. 872 func assignValues(to interface{}, 873 getValue func(typ reflect.Type, name string) (interface{}, error)) error { 874 875 toValue := reflect.ValueOf(to) 876 if toValue.Type().Kind() != reflect.Ptr { 877 return fmt.Errorf("%T is not a pointer to struct", to) 878 } 879 880 if toValue.IsNil() { 881 return fmt.Errorf("nil pointer to %T", to) 882 } 883 884 fields, err := ebpfFields(toValue.Elem(), nil) 885 if err != nil { 886 return err 887 } 888 889 type elem struct { 890 // Either *Map or *Program 891 typ reflect.Type 892 name string 893 } 894 895 assigned := make(map[elem]string) 896 for _, field := range fields { 897 // Get string value the field is tagged with. 898 tag := field.Tag.Get("ebpf") 899 if strings.Contains(tag, ",") { 900 return fmt.Errorf("field %s: ebpf tag contains a comma", field.Name) 901 } 902 903 // Check if the eBPF object with the requested 904 // type and tag was already assigned elsewhere. 905 e := elem{field.Type, tag} 906 if af := assigned[e]; af != "" { 907 return fmt.Errorf("field %s: object %q was already assigned to %s", field.Name, tag, af) 908 } 909 910 // Get the eBPF object referred to by the tag. 911 value, err := getValue(field.Type, tag) 912 if err != nil { 913 return fmt.Errorf("field %s: %w", field.Name, err) 914 } 915 916 if !field.value.CanSet() { 917 return fmt.Errorf("field %s: can't set value", field.Name) 918 } 919 field.value.Set(reflect.ValueOf(value)) 920 921 assigned[e] = field.Name 922 } 923 924 return nil 925 }