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
15
16
17 type CollectionOptions struct {
18 Maps MapOptions
19 Programs ProgramOptions
20
21
22
23
24
25
26
27
28
29
30 MapReplacements map[string]*Map
31 }
32
33
34 type CollectionSpec struct {
35 Maps map[string]*MapSpec
36 Programs map[string]*ProgramSpec
37
38
39
40 Types *btf.Spec
41
42
43
44 ByteOrder binary.ByteOrder
45 }
46
47
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
72
73
74
75
76
77
78
79
80 func (cs *CollectionSpec) RewriteMaps(maps map[string]*Map) error {
81 for symbol, m := range maps {
82
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
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
104 delete(cs.Maps, symbol)
105 }
106
107 return nil
108 }
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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
135
136 continue
137 }
138 if err != nil {
139 return fmt.Errorf("map %s: %w", name, err)
140 }
141
142
143
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 replaced[vname] {
155 return fmt.Errorf("section %s: duplicate variable %s", name, vname)
156 }
157
158 if int(v.Offset+v.Size) > len(cpy) {
159 return fmt.Errorf("section %s: offset %d(+%d) for variable %s is out of bounds", name, v.Offset, v.Size, vname)
160 }
161
162 b, err := marshalBytes(replacement, int(v.Size))
163 if err != nil {
164 return fmt.Errorf("marshaling constant replacement %s: %w", vname, err)
165 }
166
167 copy(cpy[v.Offset:v.Offset+v.Size], b)
168
169 replaced[vname] = true
170 }
171
172 spec.Contents[0] = MapKV{Key: uint32(0), Value: cpy}
173 }
174
175 var missing []string
176 for c := range consts {
177 if !replaced[c] {
178 missing = append(missing, c)
179 }
180 }
181
182 if len(missing) != 0 {
183 return fmt.Errorf("spec is missing one or more constants: %s", strings.Join(missing, ","))
184 }
185
186 return nil
187 }
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209 func (cs *CollectionSpec) Assign(to interface{}) error {
210
211
212 getValue := func(typ reflect.Type, name string) (interface{}, error) {
213 switch typ {
214
215 case reflect.TypeOf((*ProgramSpec)(nil)):
216 if p := cs.Programs[name]; p != nil {
217 return p, nil
218 }
219 return nil, fmt.Errorf("missing program %q", name)
220
221 case reflect.TypeOf((*MapSpec)(nil)):
222 if m := cs.Maps[name]; m != nil {
223 return m, nil
224 }
225 return nil, fmt.Errorf("missing map %q", name)
226
227 default:
228 return nil, fmt.Errorf("unsupported type %s", typ)
229 }
230 }
231
232 return assignValues(to, getValue)
233 }
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262 func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) error {
263 loader, err := newCollectionLoader(cs, opts)
264 if err != nil {
265 return err
266 }
267 defer loader.close()
268
269
270 assignedMaps := make(map[string]bool)
271 assignedProgs := make(map[string]bool)
272
273 getValue := func(typ reflect.Type, name string) (interface{}, error) {
274 switch typ {
275
276 case reflect.TypeOf((*Program)(nil)):
277 assignedProgs[name] = true
278 return loader.loadProgram(name)
279
280 case reflect.TypeOf((*Map)(nil)):
281 assignedMaps[name] = true
282 return loader.loadMap(name)
283
284 default:
285 return nil, fmt.Errorf("unsupported type %s", typ)
286 }
287 }
288
289
290 if err := assignValues(to, getValue); err != nil {
291 return err
292 }
293
294
295 if err := loader.populateMaps(); err != nil {
296 return err
297 }
298
299
300 for n, m := range loader.maps {
301 switch m.typ {
302 case ProgramArray:
303
304
305
306
307
308
309
310
311 if !assignedMaps[n] && len(cs.Maps[n].Contents) > 0 {
312 return fmt.Errorf("ProgramArray %s must be assigned to prevent missed tail calls", n)
313 }
314 }
315 }
316
317
318 for m := range assignedMaps {
319 delete(loader.maps, m)
320 }
321 for p := range assignedProgs {
322 delete(loader.programs, p)
323 }
324
325 return nil
326 }
327
328
329
330 type Collection struct {
331 Programs map[string]*Program
332 Maps map[string]*Map
333 }
334
335
336
337
338
339
340 func NewCollection(spec *CollectionSpec) (*Collection, error) {
341 return NewCollectionWithOptions(spec, CollectionOptions{})
342 }
343
344
345
346
347
348
349 func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Collection, error) {
350 loader, err := newCollectionLoader(spec, &opts)
351 if err != nil {
352 return nil, err
353 }
354 defer loader.close()
355
356
357 for mapName := range spec.Maps {
358 if _, err := loader.loadMap(mapName); err != nil {
359 return nil, err
360 }
361 }
362
363 for progName, prog := range spec.Programs {
364 if prog.Type == UnspecifiedProgram {
365 continue
366 }
367
368 if _, err := loader.loadProgram(progName); err != nil {
369 return nil, err
370 }
371 }
372
373
374
375 if err := loader.populateMaps(); err != nil {
376 return nil, err
377 }
378
379
380 maps, progs := loader.maps, loader.programs
381 loader.maps, loader.programs = nil, nil
382
383 return &Collection{
384 progs,
385 maps,
386 }, nil
387 }
388
389 type handleCache struct {
390 btfHandles map[*btf.Spec]*btf.Handle
391 }
392
393 func newHandleCache() *handleCache {
394 return &handleCache{
395 btfHandles: make(map[*btf.Spec]*btf.Handle),
396 }
397 }
398
399 func (hc handleCache) btfHandle(spec *btf.Spec) (*btf.Handle, error) {
400 if hc.btfHandles[spec] != nil {
401 return hc.btfHandles[spec], nil
402 }
403
404 handle, err := btf.NewHandle(spec)
405 if err != nil {
406 return nil, err
407 }
408
409 hc.btfHandles[spec] = handle
410 return handle, nil
411 }
412
413 func (hc handleCache) close() {
414 for _, handle := range hc.btfHandles {
415 handle.Close()
416 }
417 }
418
419 type collectionLoader struct {
420 coll *CollectionSpec
421 opts *CollectionOptions
422 maps map[string]*Map
423 programs map[string]*Program
424 handles *handleCache
425 }
426
427 func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collectionLoader, error) {
428 if opts == nil {
429 opts = &CollectionOptions{}
430 }
431
432
433 for name, m := range opts.MapReplacements {
434 spec, ok := coll.Maps[name]
435 if !ok {
436 return nil, fmt.Errorf("replacement map %s not found in CollectionSpec", name)
437 }
438
439 if err := spec.checkCompatibility(m); err != nil {
440 return nil, fmt.Errorf("using replacement map %s: %w", spec.Name, err)
441 }
442 }
443
444 return &collectionLoader{
445 coll,
446 opts,
447 make(map[string]*Map),
448 make(map[string]*Program),
449 newHandleCache(),
450 }, nil
451 }
452
453
454 func (cl *collectionLoader) close() {
455 cl.handles.close()
456 for _, m := range cl.maps {
457 m.Close()
458 }
459 for _, p := range cl.programs {
460 p.Close()
461 }
462 }
463
464 func (cl *collectionLoader) loadMap(mapName string) (*Map, error) {
465 if m := cl.maps[mapName]; m != nil {
466 return m, nil
467 }
468
469 mapSpec := cl.coll.Maps[mapName]
470 if mapSpec == nil {
471 return nil, fmt.Errorf("missing map %s", mapName)
472 }
473
474 if mapSpec.BTF != nil && cl.coll.Types != mapSpec.BTF {
475 return nil, fmt.Errorf("map %s: BTF doesn't match collection", mapName)
476 }
477
478 if replaceMap, ok := cl.opts.MapReplacements[mapName]; ok {
479
480 m, err := replaceMap.Clone()
481 if err != nil {
482 return nil, err
483 }
484
485 cl.maps[mapName] = m
486 return m, nil
487 }
488
489 m, err := newMapWithOptions(mapSpec, cl.opts.Maps, cl.handles)
490 if err != nil {
491 return nil, fmt.Errorf("map %s: %w", mapName, err)
492 }
493
494 cl.maps[mapName] = m
495 return m, nil
496 }
497
498 func (cl *collectionLoader) loadProgram(progName string) (*Program, error) {
499 if prog := cl.programs[progName]; prog != nil {
500 return prog, nil
501 }
502
503 progSpec := cl.coll.Programs[progName]
504 if progSpec == nil {
505 return nil, fmt.Errorf("unknown program %s", progName)
506 }
507
508
509
510 if progSpec.Type == UnspecifiedProgram {
511 return nil, fmt.Errorf("cannot load program %s: program type is unspecified", progName)
512 }
513
514 if progSpec.BTF != nil && cl.coll.Types != progSpec.BTF {
515 return nil, fmt.Errorf("program %s: BTF doesn't match collection", progName)
516 }
517
518 progSpec = progSpec.Copy()
519
520
521
522 for i := range progSpec.Instructions {
523 ins := &progSpec.Instructions[i]
524
525 if !ins.IsLoadFromMap() || ins.Reference() == "" {
526 continue
527 }
528
529
530
531
532 if int32(ins.Constant) > 0 {
533 continue
534 }
535
536 m, err := cl.loadMap(ins.Reference())
537 if err != nil {
538 return nil, fmt.Errorf("program %s: %w", progName, err)
539 }
540
541 if err := ins.AssociateMap(m); err != nil {
542 return nil, fmt.Errorf("program %s: map %s: %w", progName, ins.Reference(), err)
543 }
544 }
545
546 prog, err := newProgramWithOptions(progSpec, cl.opts.Programs, cl.handles)
547 if err != nil {
548 return nil, fmt.Errorf("program %s: %w", progName, err)
549 }
550
551 cl.programs[progName] = prog
552 return prog, nil
553 }
554
555 func (cl *collectionLoader) populateMaps() error {
556 for mapName, m := range cl.maps {
557 mapSpec, ok := cl.coll.Maps[mapName]
558 if !ok {
559 return fmt.Errorf("missing map spec %s", mapName)
560 }
561
562 mapSpec = mapSpec.Copy()
563
564
565
566
567
568
569 for i, kv := range mapSpec.Contents {
570 if objName, ok := kv.Value.(string); ok {
571 switch mapSpec.Type {
572 case ProgramArray:
573
574 prog, err := cl.loadProgram(objName)
575 if err != nil {
576 return fmt.Errorf("loading program %s, for map %s: %w", objName, mapName, err)
577 }
578 mapSpec.Contents[i] = MapKV{kv.Key, prog}
579
580 case ArrayOfMaps, HashOfMaps:
581
582 innerMap, err := cl.loadMap(objName)
583 if err != nil {
584 return fmt.Errorf("loading inner map %s, for map %s: %w", objName, mapName, err)
585 }
586 mapSpec.Contents[i] = MapKV{kv.Key, innerMap}
587 }
588 }
589 }
590
591
592 if err := m.finalize(mapSpec); err != nil {
593 return fmt.Errorf("populating map %s: %w", mapName, err)
594 }
595 }
596
597 return nil
598 }
599
600
601
602
603
604
605 func LoadCollection(file string) (*Collection, error) {
606 spec, err := LoadCollectionSpec(file)
607 if err != nil {
608 return nil, err
609 }
610 return NewCollection(spec)
611 }
612
613
614
615
616 func (coll *Collection) Close() {
617 for _, prog := range coll.Programs {
618 prog.Close()
619 }
620 for _, m := range coll.Maps {
621 m.Close()
622 }
623 }
624
625
626
627
628
629
630 func (coll *Collection) DetachMap(name string) *Map {
631 m := coll.Maps[name]
632 delete(coll.Maps, name)
633 return m
634 }
635
636
637
638
639
640
641 func (coll *Collection) DetachProgram(name string) *Program {
642 p := coll.Programs[name]
643 delete(coll.Programs, name)
644 return p
645 }
646
647
648 type structField struct {
649 reflect.StructField
650 value reflect.Value
651 }
652
653
654
655 func ebpfFields(structVal reflect.Value, visited map[reflect.Type]bool) ([]structField, error) {
656 if visited == nil {
657 visited = make(map[reflect.Type]bool)
658 }
659
660 structType := structVal.Type()
661 if structType.Kind() != reflect.Struct {
662 return nil, fmt.Errorf("%s is not a struct", structType)
663 }
664
665 if visited[structType] {
666 return nil, fmt.Errorf("recursion on type %s", structType)
667 }
668
669 fields := make([]structField, 0, structType.NumField())
670 for i := 0; i < structType.NumField(); i++ {
671 field := structField{structType.Field(i), structVal.Field(i)}
672
673
674 name := field.Tag.Get("ebpf")
675 if name != "" {
676 fields = append(fields, field)
677 continue
678 }
679
680
681
682 var v reflect.Value
683 switch field.Type.Kind() {
684 case reflect.Ptr:
685 if field.Type.Elem().Kind() != reflect.Struct {
686 continue
687 }
688
689 if field.value.IsNil() {
690 return nil, fmt.Errorf("nil pointer to %s", structType)
691 }
692
693
694 v = field.value.Elem()
695
696 case reflect.Struct:
697
698 v = field.value
699
700 default:
701 continue
702 }
703
704 inner, err := ebpfFields(v, visited)
705 if err != nil {
706 return nil, fmt.Errorf("field %s: %w", field.Name, err)
707 }
708
709 fields = append(fields, inner...)
710 }
711
712 return fields, nil
713 }
714
715
716
717
718
719 func assignValues(to interface{},
720 getValue func(typ reflect.Type, name string) (interface{}, error)) error {
721
722 toValue := reflect.ValueOf(to)
723 if toValue.Type().Kind() != reflect.Ptr {
724 return fmt.Errorf("%T is not a pointer to struct", to)
725 }
726
727 if toValue.IsNil() {
728 return fmt.Errorf("nil pointer to %T", to)
729 }
730
731 fields, err := ebpfFields(toValue.Elem(), nil)
732 if err != nil {
733 return err
734 }
735
736 type elem struct {
737
738 typ reflect.Type
739 name string
740 }
741
742 assigned := make(map[elem]string)
743 for _, field := range fields {
744
745 tag := field.Tag.Get("ebpf")
746 if strings.Contains(tag, ",") {
747 return fmt.Errorf("field %s: ebpf tag contains a comma", field.Name)
748 }
749
750
751
752 e := elem{field.Type, tag}
753 if af := assigned[e]; af != "" {
754 return fmt.Errorf("field %s: object %q was already assigned to %s", field.Name, tag, af)
755 }
756
757
758 value, err := getValue(field.Type, tag)
759 if err != nil {
760 return fmt.Errorf("field %s: %w", field.Name, err)
761 }
762
763 if !field.value.CanSet() {
764 return fmt.Errorf("field %s: can't set value", field.Name)
765 }
766 field.value.Set(reflect.ValueOf(value))
767
768 assigned[e] = field.Name
769 }
770
771 return nil
772 }
773
View as plain text