@@ -12,6 +12,7 @@ import (
1212 "github.com/cilium/ebpf/internal"
1313 "github.com/cilium/ebpf/internal/kconfig"
1414 "github.com/cilium/ebpf/internal/linux"
15+ "github.com/cilium/ebpf/internal/sys"
1516)
1617
1718// CollectionOptions control loading a collection into the kernel.
@@ -259,6 +260,7 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions)
259260 // Support assigning Programs and Maps, lazy-loading the required objects.
260261 assignedMaps := make (map [string ]bool )
261262 assignedProgs := make (map [string ]bool )
263+ assignedVars := make (map [string ]bool )
262264
263265 getValue := func (typ reflect.Type , name string ) (interface {}, error ) {
264266 switch typ {
@@ -271,6 +273,10 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions)
271273 assignedMaps [name ] = true
272274 return loader .loadMap (name )
273275
276+ case reflect .TypeOf ((* Variable )(nil )):
277+ assignedVars [name ] = true
278+ return loader .loadVariable (name )
279+
274280 default :
275281 return nil , fmt .Errorf ("unsupported type %s" , typ )
276282 }
@@ -311,15 +317,22 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions)
311317 for p := range assignedProgs {
312318 delete (loader .programs , p )
313319 }
320+ for p := range assignedVars {
321+ delete (loader .vars , p )
322+ }
314323
315324 return nil
316325}
317326
318- // Collection is a collection of Programs and Maps associated
319- // with their symbols
327+ // Collection is a collection of live BPF resources present in the kernel.
320328type Collection struct {
321329 Programs map [string ]* Program
322330 Maps map [string ]* Map
331+
332+ // Variables contains global variables used by the Collection's program(s).
333+ // Only populated on Linux 5.5 and later or on kernels supporting
334+ // BPF_F_MMAPABLE.
335+ Variables map [string ]* Variable
323336}
324337
325338// NewCollection creates a Collection from the given spec, creating and
@@ -360,19 +373,31 @@ func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Co
360373 }
361374 }
362375
376+ for varName := range spec .Variables {
377+ _ , err := loader .loadVariable (varName )
378+ if errors .Is (err , ErrNotSupported ) {
379+ // Don't emit Variable if the kernel lacks support for mmapable maps.
380+ continue
381+ }
382+ if err != nil {
383+ return nil , err
384+ }
385+ }
386+
363387 // Maps can contain Program and Map stubs, so populate them after
364388 // all Maps and Programs have been successfully loaded.
365389 if err := loader .populateDeferredMaps (); err != nil {
366390 return nil , err
367391 }
368392
369- // Prevent loader.cleanup from closing maps and programs .
370- maps , progs := loader .maps , loader .programs
371- loader .maps , loader .programs = nil , nil
393+ // Prevent loader.cleanup from closing maps, programs and vars .
394+ maps , progs , vars := loader .maps , loader .programs , loader . vars
395+ loader .maps , loader .programs , loader . vars = nil , nil , nil
372396
373397 return & Collection {
374398 progs ,
375399 maps ,
400+ vars ,
376401 }, nil
377402}
378403
@@ -381,6 +406,7 @@ type collectionLoader struct {
381406 opts * CollectionOptions
382407 maps map [string ]* Map
383408 programs map [string ]* Program
409+ vars map [string ]* Variable
384410}
385411
386412func newCollectionLoader (coll * CollectionSpec , opts * CollectionOptions ) (* collectionLoader , error ) {
@@ -405,6 +431,7 @@ func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collec
405431 opts ,
406432 make (map [string ]* Map ),
407433 make (map [string ]* Program ),
434+ make (map [string ]* Variable ),
408435 }, nil
409436}
410437
@@ -439,6 +466,13 @@ func (cl *collectionLoader) loadMap(mapName string) (*Map, error) {
439466 return m , nil
440467 }
441468
469+ // Defer setting the mmapable flag on maps until load time. This avoids the
470+ // MapSpec having different flags on some kernel versions. Also avoid running
471+ // syscalls during ELF loading, so platforms like wasm can also parse an ELF.
472+ if isDataSection (mapSpec .Name ) && haveMmapableMaps () == nil {
473+ mapSpec .Flags |= sys .BPF_F_MMAPABLE
474+ }
475+
442476 m , err := newMapWithOptions (mapSpec , cl .opts .Maps )
443477 if err != nil {
444478 return nil , fmt .Errorf ("map %s: %w" , mapName , err )
@@ -510,6 +544,50 @@ func (cl *collectionLoader) loadProgram(progName string) (*Program, error) {
510544 return prog , nil
511545}
512546
547+ func (cl * collectionLoader ) loadVariable (varName string ) (* Variable , error ) {
548+ if v := cl .vars [varName ]; v != nil {
549+ return v , nil
550+ }
551+
552+ varSpec := cl .coll .Variables [varName ]
553+ if varSpec == nil {
554+ return nil , fmt .Errorf ("unknown variable %s" , varName )
555+ }
556+
557+ // Get the key of the VariableSpec's MapSpec in the CollectionSpec.
558+ var mapName string
559+ for n , ms := range cl .coll .Maps {
560+ if ms == varSpec .m {
561+ mapName = n
562+ break
563+ }
564+ }
565+ if mapName == "" {
566+ return nil , fmt .Errorf ("variable %s: underlying MapSpec %s was removed from CollectionSpec" , varName , varSpec .m .Name )
567+ }
568+
569+ m , err := cl .loadMap (mapName )
570+ if err != nil {
571+ return nil , fmt .Errorf ("variable %s: %w" , varName , err )
572+ }
573+
574+ mm , err := m .Memory ()
575+ if err != nil {
576+ return nil , fmt .Errorf ("variable %s: getting memory of map %s: %w" , varName , mapName , err )
577+ }
578+
579+ v := & Variable {
580+ varSpec .name ,
581+ varSpec .offset ,
582+ varSpec .size ,
583+ mm ,
584+ varSpec .t ,
585+ }
586+
587+ cl .vars [varName ] = v
588+ return v , nil
589+ }
590+
513591// populateDeferredMaps iterates maps holding programs or other maps and loads
514592// any dependencies. Populates all maps in cl and freezes them if specified.
515593func (cl * collectionLoader ) populateDeferredMaps () error {
@@ -696,6 +774,7 @@ func LoadCollection(file string) (*Collection, error) {
696774func (coll * Collection ) Assign (to interface {}) error {
697775 assignedMaps := make (map [string ]bool )
698776 assignedProgs := make (map [string ]bool )
777+ assignedVars := make (map [string ]bool )
699778
700779 // Assign() only transfers already-loaded Maps and Programs. No extra
701780 // loading is done.
@@ -716,6 +795,13 @@ func (coll *Collection) Assign(to interface{}) error {
716795 }
717796 return nil , fmt .Errorf ("missing map %q" , name )
718797
798+ case reflect .TypeOf ((* Variable )(nil )):
799+ if v := coll .Variables [name ]; v != nil {
800+ assignedVars [name ] = true
801+ return v , nil
802+ }
803+ return nil , fmt .Errorf ("missing variable %q" , name )
804+
719805 default :
720806 return nil , fmt .Errorf ("unsupported type %s" , typ )
721807 }
@@ -732,6 +818,9 @@ func (coll *Collection) Assign(to interface{}) error {
732818 for m := range assignedMaps {
733819 delete (coll .Maps , m )
734820 }
821+ for s := range assignedVars {
822+ delete (coll .Variables , s )
823+ }
735824
736825 return nil
737826}
0 commit comments