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