collection.go 21 KB

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