@@ -13,6 +13,7 @@ import (
1313 "github.com/cilium/ebpf/internal/kallsyms"
1414 "github.com/cilium/ebpf/internal/kconfig"
1515 "github.com/cilium/ebpf/internal/linux"
16+ "github.com/cilium/ebpf/internal/sys"
1617)
1718
1819// CollectionOptions control loading a collection into the kernel.
@@ -260,6 +261,7 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions)
260261 // Support assigning Programs and Maps, lazy-loading the required objects.
261262 assignedMaps := make (map [string ]bool )
262263 assignedProgs := make (map [string ]bool )
264+ assignedVars := make (map [string ]bool )
263265
264266 getValue := func (typ reflect.Type , name string ) (interface {}, error ) {
265267 switch typ {
@@ -272,6 +274,10 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions)
272274 assignedMaps [name ] = true
273275 return loader .loadMap (name )
274276
277+ case reflect .TypeOf ((* Variable )(nil )):
278+ assignedVars [name ] = true
279+ return loader .loadVariable (name )
280+
275281 default :
276282 return nil , fmt .Errorf ("unsupported type %s" , typ )
277283 }
@@ -312,15 +318,22 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions)
312318 for p := range assignedProgs {
313319 delete (loader .programs , p )
314320 }
321+ for p := range assignedVars {
322+ delete (loader .vars , p )
323+ }
315324
316325 return nil
317326}
318327
319- // Collection is a collection of Programs and Maps associated
320- // with their symbols
328+ // Collection is a collection of live BPF resources present in the kernel.
321329type Collection struct {
322330 Programs map [string ]* Program
323331 Maps map [string ]* Map
332+
333+ // Variables contains global variables used by the Collection's program(s).
334+ // Only populated on Linux 5.5 and later or on kernels supporting
335+ // BPF_F_MMAPABLE.
336+ Variables map [string ]* Variable
324337}
325338
326339// NewCollection creates a Collection from the given spec, creating and
@@ -361,19 +374,31 @@ func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Co
361374 }
362375 }
363376
377+ for varName := range spec .Variables {
378+ _ , err := loader .loadVariable (varName )
379+ if errors .Is (err , ErrNotSupported ) {
380+ // Don't emit Variable if the kernel lacks support for mmapable maps.
381+ continue
382+ }
383+ if err != nil {
384+ return nil , err
385+ }
386+ }
387+
364388 // Maps can contain Program and Map stubs, so populate them after
365389 // all Maps and Programs have been successfully loaded.
366390 if err := loader .populateDeferredMaps (); err != nil {
367391 return nil , err
368392 }
369393
370- // Prevent loader.cleanup from closing maps and programs .
371- maps , progs := loader .maps , loader .programs
372- loader .maps , loader .programs = nil , nil
394+ // Prevent loader.cleanup from closing maps, programs and vars .
395+ maps , progs , vars := loader .maps , loader .programs , loader . vars
396+ loader .maps , loader .programs , loader . vars = nil , nil , nil
373397
374398 return & Collection {
375399 progs ,
376400 maps ,
401+ vars ,
377402 }, nil
378403}
379404
@@ -382,6 +407,7 @@ type collectionLoader struct {
382407 opts * CollectionOptions
383408 maps map [string ]* Map
384409 programs map [string ]* Program
410+ vars map [string ]* Variable
385411}
386412
387413func newCollectionLoader (coll * CollectionSpec , opts * CollectionOptions ) (* collectionLoader , error ) {
@@ -410,6 +436,7 @@ func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collec
410436 opts ,
411437 make (map [string ]* Map ),
412438 make (map [string ]* Program ),
439+ make (map [string ]* Variable ),
413440 }, nil
414441}
415442
@@ -483,6 +510,13 @@ func (cl *collectionLoader) loadMap(mapName string) (*Map, error) {
483510 return m , nil
484511 }
485512
513+ // Defer setting the mmapable flag on maps until load time. This avoids the
514+ // MapSpec having different flags on some kernel versions. Also avoid running
515+ // syscalls during ELF loading, so platforms like wasm can also parse an ELF.
516+ if isDataSection (mapSpec .Name ) && haveMmapableMaps () == nil {
517+ mapSpec .Flags |= sys .BPF_F_MMAPABLE
518+ }
519+
486520 m , err := newMapWithOptions (mapSpec , cl .opts .Maps )
487521 if err != nil {
488522 return nil , fmt .Errorf ("map %s: %w" , mapName , err )
@@ -554,6 +588,53 @@ func (cl *collectionLoader) loadProgram(progName string) (*Program, error) {
554588 return prog , nil
555589}
556590
591+ func (cl * collectionLoader ) loadVariable (varName string ) (* Variable , error ) {
592+ if v := cl .vars [varName ]; v != nil {
593+ return v , nil
594+ }
595+
596+ varSpec := cl .coll .Variables [varName ]
597+ if varSpec == nil {
598+ return nil , fmt .Errorf ("unknown variable %s" , varName )
599+ }
600+
601+ // Get the key of the VariableSpec's MapSpec in the CollectionSpec.
602+ var mapName string
603+ for n , ms := range cl .coll .Maps {
604+ if ms == varSpec .m {
605+ mapName = n
606+ break
607+ }
608+ }
609+ if mapName == "" {
610+ return nil , fmt .Errorf ("variable %s: underlying MapSpec %s was removed from CollectionSpec" , varName , varSpec .m .Name )
611+ }
612+
613+ m , err := cl .loadMap (mapName )
614+ if err != nil {
615+ return nil , fmt .Errorf ("variable %s: %w" , varName , err )
616+ }
617+
618+ // If the kernel is too old or the underlying map was created without
619+ // BPF_F_MMAPABLE, [Map.Memory] will return ErrNotSupported. In this case,
620+ // emit a Variable with a nil Memory.
621+ mm , err := m .Memory ()
622+ if err != nil && ! errors .Is (err , ErrNotSupported ) {
623+ return nil , fmt .Errorf ("variable %s: getting memory for map %s: %w" , varName , mapName , err )
624+ }
625+
626+ v := & Variable {
627+ varSpec .name ,
628+ varSpec .offset ,
629+ varSpec .size ,
630+ varSpec .t ,
631+ mm ,
632+ }
633+
634+ cl .vars [varName ] = v
635+ return v , nil
636+ }
637+
557638// populateDeferredMaps iterates maps holding programs or other maps and loads
558639// any dependencies. Populates all maps in cl and freezes them if specified.
559640func (cl * collectionLoader ) populateDeferredMaps () error {
@@ -740,6 +821,7 @@ func LoadCollection(file string) (*Collection, error) {
740821func (coll * Collection ) Assign (to interface {}) error {
741822 assignedMaps := make (map [string ]bool )
742823 assignedProgs := make (map [string ]bool )
824+ assignedVars := make (map [string ]bool )
743825
744826 // Assign() only transfers already-loaded Maps and Programs. No extra
745827 // loading is done.
@@ -760,6 +842,13 @@ func (coll *Collection) Assign(to interface{}) error {
760842 }
761843 return nil , fmt .Errorf ("missing map %q" , name )
762844
845+ case reflect .TypeOf ((* Variable )(nil )):
846+ if v := coll .Variables [name ]; v != nil {
847+ assignedVars [name ] = true
848+ return v , nil
849+ }
850+ return nil , fmt .Errorf ("missing variable %q" , name )
851+
763852 default :
764853 return nil , fmt .Errorf ("unsupported type %s" , typ )
765854 }
@@ -776,6 +865,9 @@ func (coll *Collection) Assign(to interface{}) error {
776865 for m := range assignedMaps {
777866 delete (coll .Maps , m )
778867 }
868+ for s := range assignedVars {
869+ delete (coll .Variables , s )
870+ }
779871
780872 return nil
781873}
0 commit comments