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  }