1 package btf
2
3 import (
4 "bufio"
5 "bytes"
6 "debug/elf"
7 "encoding/binary"
8 "errors"
9 "fmt"
10 "io"
11 "math"
12 "os"
13 "reflect"
14
15 "github.com/cilium/ebpf/internal"
16 "github.com/cilium/ebpf/internal/sys"
17 "github.com/cilium/ebpf/internal/unix"
18 )
19
20 const btfMagic = 0xeB9F
21
22
23 var (
24 ErrNotSupported = internal.ErrNotSupported
25 ErrNotFound = errors.New("not found")
26 ErrNoExtendedInfo = errors.New("no extended info")
27 )
28
29
30 type ID = sys.BTFID
31
32
33 type Spec struct {
34
35 rawTypes []rawType
36 strings *stringTable
37
38
39
40 types types
41
42
43 typeIDs map[Type]TypeID
44
45
46
47 namedTypes map[essentialName][]Type
48
49 byteOrder binary.ByteOrder
50 }
51
52 type btfHeader struct {
53 Magic uint16
54 Version uint8
55 Flags uint8
56 HdrLen uint32
57
58 TypeOff uint32
59 TypeLen uint32
60 StringOff uint32
61 StringLen uint32
62 }
63
64
65
66 func (h *btfHeader) typeStart() int64 {
67 return int64(h.HdrLen + h.TypeOff)
68 }
69
70
71
72 func (h *btfHeader) stringStart() int64 {
73 return int64(h.HdrLen + h.StringOff)
74 }
75
76
77 func LoadSpec(file string) (*Spec, error) {
78 fh, err := os.Open(file)
79 if err != nil {
80 return nil, err
81 }
82 defer fh.Close()
83
84 return LoadSpecFromReader(fh)
85 }
86
87
88
89
90
91 func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) {
92 file, err := internal.NewSafeELFFile(rd)
93 if err != nil {
94 if bo := guessRawBTFByteOrder(rd); bo != nil {
95
96
97 spec, err := loadRawSpec(io.NewSectionReader(rd, 0, math.MaxInt64), bo, nil, nil)
98 return spec, err
99 }
100
101 return nil, err
102 }
103
104 return loadSpecFromELF(file)
105 }
106
107
108
109
110
111 func LoadSpecAndExtInfosFromReader(rd io.ReaderAt) (*Spec, *ExtInfos, error) {
112 file, err := internal.NewSafeELFFile(rd)
113 if err != nil {
114 return nil, nil, err
115 }
116
117 spec, err := loadSpecFromELF(file)
118 if err != nil {
119 return nil, nil, err
120 }
121
122 extInfos, err := loadExtInfosFromELF(file, spec.types, spec.strings)
123 if err != nil && !errors.Is(err, ErrNotFound) {
124 return nil, nil, err
125 }
126
127 return spec, extInfos, nil
128 }
129
130
131
132
133
134
135
136
137 func variableOffsets(file *internal.SafeELFFile) (map[variable]uint32, error) {
138 symbols, err := file.Symbols()
139 if err != nil {
140 return nil, fmt.Errorf("can't read symbols: %v", err)
141 }
142
143 variableOffsets := make(map[variable]uint32)
144 for _, symbol := range symbols {
145 if idx := symbol.Section; idx >= elf.SHN_LORESERVE && idx <= elf.SHN_HIRESERVE {
146
147 continue
148 }
149
150 if symbol.Value > math.MaxUint32 {
151
152 continue
153 }
154
155 if int(symbol.Section) >= len(file.Sections) {
156 return nil, fmt.Errorf("symbol %s: invalid section %d", symbol.Name, symbol.Section)
157 }
158
159 secName := file.Sections[symbol.Section].Name
160 variableOffsets[variable{secName, symbol.Name}] = uint32(symbol.Value)
161 }
162
163 return variableOffsets, nil
164 }
165
166 func loadSpecFromELF(file *internal.SafeELFFile) (*Spec, error) {
167 var (
168 btfSection *elf.Section
169 sectionSizes = make(map[string]uint32)
170 )
171
172 for _, sec := range file.Sections {
173 switch sec.Name {
174 case ".BTF":
175 btfSection = sec
176 default:
177 if sec.Type != elf.SHT_PROGBITS && sec.Type != elf.SHT_NOBITS {
178 break
179 }
180
181 if sec.Size > math.MaxUint32 {
182 return nil, fmt.Errorf("section %s exceeds maximum size", sec.Name)
183 }
184
185 sectionSizes[sec.Name] = uint32(sec.Size)
186 }
187 }
188
189 if btfSection == nil {
190 return nil, fmt.Errorf("btf: %w", ErrNotFound)
191 }
192
193 vars, err := variableOffsets(file)
194 if err != nil {
195 return nil, err
196 }
197
198 if btfSection.ReaderAt == nil {
199 return nil, fmt.Errorf("compressed BTF is not supported")
200 }
201
202 rawTypes, rawStrings, err := parseBTF(btfSection.ReaderAt, file.ByteOrder, nil)
203 if err != nil {
204 return nil, err
205 }
206
207 err = fixupDatasec(rawTypes, rawStrings, sectionSizes, vars)
208 if err != nil {
209 return nil, err
210 }
211
212 return inflateSpec(rawTypes, rawStrings, file.ByteOrder, nil)
213 }
214
215 func loadRawSpec(btf io.ReaderAt, bo binary.ByteOrder,
216 baseTypes types, baseStrings *stringTable) (*Spec, error) {
217
218 rawTypes, rawStrings, err := parseBTF(btf, bo, baseStrings)
219 if err != nil {
220 return nil, err
221 }
222
223 return inflateSpec(rawTypes, rawStrings, bo, baseTypes)
224 }
225
226 func inflateSpec(rawTypes []rawType, rawStrings *stringTable, bo binary.ByteOrder,
227 baseTypes types) (*Spec, error) {
228
229 types, err := inflateRawTypes(rawTypes, baseTypes, rawStrings)
230 if err != nil {
231 return nil, err
232 }
233
234 typeIDs, typesByName := indexTypes(types, TypeID(len(baseTypes)))
235
236 return &Spec{
237 rawTypes: rawTypes,
238 namedTypes: typesByName,
239 typeIDs: typeIDs,
240 types: types,
241 strings: rawStrings,
242 byteOrder: bo,
243 }, nil
244 }
245
246 func indexTypes(types []Type, typeIDOffset TypeID) (map[Type]TypeID, map[essentialName][]Type) {
247 namedTypes := 0
248 for _, typ := range types {
249 if typ.TypeName() != "" {
250
251
252
253 namedTypes++
254 }
255 }
256
257 typeIDs := make(map[Type]TypeID, len(types))
258 typesByName := make(map[essentialName][]Type, namedTypes)
259
260 for i, typ := range types {
261 if name := newEssentialName(typ.TypeName()); name != "" {
262 typesByName[name] = append(typesByName[name], typ)
263 }
264 typeIDs[typ] = TypeID(i) + typeIDOffset
265 }
266
267 return typeIDs, typesByName
268 }
269
270
271
272
273
274 func LoadKernelSpec() (*Spec, error) {
275 fh, err := os.Open("/sys/kernel/btf/vmlinux")
276 if err == nil {
277 defer fh.Close()
278
279 return loadRawSpec(fh, internal.NativeEndian, nil, nil)
280 }
281
282 file, err := findVMLinux()
283 if err != nil {
284 return nil, err
285 }
286 defer file.Close()
287
288 return loadSpecFromELF(file)
289 }
290
291
292 func findVMLinux() (*internal.SafeELFFile, error) {
293 release, err := internal.KernelRelease()
294 if err != nil {
295 return nil, err
296 }
297
298
299
300 locations := []string{
301 "/boot/vmlinux-%s",
302 "/lib/modules/%s/vmlinux-%[1]s",
303 "/lib/modules/%s/build/vmlinux",
304 "/usr/lib/modules/%s/kernel/vmlinux",
305 "/usr/lib/debug/boot/vmlinux-%s",
306 "/usr/lib/debug/boot/vmlinux-%s.debug",
307 "/usr/lib/debug/lib/modules/%s/vmlinux",
308 }
309
310 for _, loc := range locations {
311 file, err := internal.OpenSafeELFFile(fmt.Sprintf(loc, release))
312 if errors.Is(err, os.ErrNotExist) {
313 continue
314 }
315 return file, err
316 }
317
318 return nil, fmt.Errorf("no BTF found for kernel version %s: %w", release, internal.ErrNotSupported)
319 }
320
321
322 func parseBTFHeader(r io.Reader, bo binary.ByteOrder) (*btfHeader, error) {
323 var header btfHeader
324 if err := binary.Read(r, bo, &header); err != nil {
325 return nil, fmt.Errorf("can't read header: %v", err)
326 }
327
328 if header.Magic != btfMagic {
329 return nil, fmt.Errorf("incorrect magic value %v", header.Magic)
330 }
331
332 if header.Version != 1 {
333 return nil, fmt.Errorf("unexpected version %v", header.Version)
334 }
335
336 if header.Flags != 0 {
337 return nil, fmt.Errorf("unsupported flags %v", header.Flags)
338 }
339
340 remainder := int64(header.HdrLen) - int64(binary.Size(&header))
341 if remainder < 0 {
342 return nil, errors.New("header length shorter than btfHeader size")
343 }
344
345 if _, err := io.CopyN(internal.DiscardZeroes{}, r, remainder); err != nil {
346 return nil, fmt.Errorf("header padding: %v", err)
347 }
348
349 return &header, nil
350 }
351
352 func guessRawBTFByteOrder(r io.ReaderAt) binary.ByteOrder {
353 buf := new(bufio.Reader)
354 for _, bo := range []binary.ByteOrder{
355 binary.LittleEndian,
356 binary.BigEndian,
357 } {
358 buf.Reset(io.NewSectionReader(r, 0, math.MaxInt64))
359 if _, err := parseBTFHeader(buf, bo); err == nil {
360 return bo
361 }
362 }
363
364 return nil
365 }
366
367
368
369 func parseBTF(btf io.ReaderAt, bo binary.ByteOrder, baseStrings *stringTable) ([]rawType, *stringTable, error) {
370 buf := internal.NewBufferedSectionReader(btf, 0, math.MaxInt64)
371 header, err := parseBTFHeader(buf, bo)
372 if err != nil {
373 return nil, nil, fmt.Errorf("parsing .BTF header: %v", err)
374 }
375
376 rawStrings, err := readStringTable(io.NewSectionReader(btf, header.stringStart(), int64(header.StringLen)),
377 baseStrings)
378 if err != nil {
379 return nil, nil, fmt.Errorf("can't read type names: %w", err)
380 }
381
382 buf.Reset(io.NewSectionReader(btf, header.typeStart(), int64(header.TypeLen)))
383 rawTypes, err := readTypes(buf, bo, header.TypeLen)
384 if err != nil {
385 return nil, nil, fmt.Errorf("can't read types: %w", err)
386 }
387
388 return rawTypes, rawStrings, nil
389 }
390
391 type variable struct {
392 section string
393 name string
394 }
395
396 func fixupDatasec(rawTypes []rawType, rawStrings *stringTable, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) error {
397 for i, rawType := range rawTypes {
398 if rawType.Kind() != kindDatasec {
399 continue
400 }
401
402 name, err := rawStrings.Lookup(rawType.NameOff)
403 if err != nil {
404 return err
405 }
406
407 if name == ".kconfig" || name == ".ksyms" {
408 return fmt.Errorf("reference to %s: %w", name, ErrNotSupported)
409 }
410
411 if rawTypes[i].SizeType != 0 {
412 continue
413 }
414
415 size, ok := sectionSizes[name]
416 if !ok {
417 return fmt.Errorf("data section %s: missing size", name)
418 }
419
420 rawTypes[i].SizeType = size
421
422 secinfos := rawType.data.([]btfVarSecinfo)
423 for j, secInfo := range secinfos {
424 id := int(secInfo.Type - 1)
425 if id >= len(rawTypes) {
426 return fmt.Errorf("data section %s: invalid type id %d for variable %d", name, id, j)
427 }
428
429 varName, err := rawStrings.Lookup(rawTypes[id].NameOff)
430 if err != nil {
431 return fmt.Errorf("data section %s: can't get name for type %d: %w", name, id, err)
432 }
433
434 offset, ok := variableOffsets[variable{name, varName}]
435 if !ok {
436 return fmt.Errorf("data section %s: missing offset for variable %s", name, varName)
437 }
438
439 secinfos[j].Offset = offset
440 }
441 }
442
443 return nil
444 }
445
446
447 func (s *Spec) Copy() *Spec {
448 types := copyTypes(s.types, nil)
449
450 typeIDOffset := TypeID(0)
451 if len(s.types) != 0 {
452 typeIDOffset = s.typeIDs[s.types[0]]
453 }
454 typeIDs, typesByName := indexTypes(types, typeIDOffset)
455
456
457 return &Spec{
458 s.rawTypes,
459 s.strings,
460 types,
461 typeIDs,
462 typesByName,
463 s.byteOrder,
464 }
465 }
466
467 type marshalOpts struct {
468 ByteOrder binary.ByteOrder
469 StripFuncLinkage bool
470 }
471
472 func (s *Spec) marshal(opts marshalOpts) ([]byte, error) {
473 var (
474 buf bytes.Buffer
475 header = new(btfHeader)
476 headerLen = binary.Size(header)
477 )
478
479
480
481 _, _ = buf.Write(make([]byte, headerLen))
482
483
484 for _, raw := range s.rawTypes {
485 switch {
486 case opts.StripFuncLinkage && raw.Kind() == kindFunc:
487 raw.SetLinkage(StaticFunc)
488 }
489
490 if err := raw.Marshal(&buf, opts.ByteOrder); err != nil {
491 return nil, fmt.Errorf("can't marshal BTF: %w", err)
492 }
493 }
494
495 typeLen := uint32(buf.Len() - headerLen)
496
497
498 stringsLen := s.strings.Length()
499 buf.Grow(stringsLen)
500 if err := s.strings.Marshal(&buf); err != nil {
501 return nil, err
502 }
503
504
505 header = &btfHeader{
506 Magic: btfMagic,
507 Version: 1,
508 Flags: 0,
509 HdrLen: uint32(headerLen),
510 TypeOff: 0,
511 TypeLen: typeLen,
512 StringOff: typeLen,
513 StringLen: uint32(stringsLen),
514 }
515
516 raw := buf.Bytes()
517 err := binary.Write(sliceWriter(raw[:headerLen]), opts.ByteOrder, header)
518 if err != nil {
519 return nil, fmt.Errorf("can't write header: %v", err)
520 }
521
522 return raw, nil
523 }
524
525 type sliceWriter []byte
526
527 func (sw sliceWriter) Write(p []byte) (int, error) {
528 if len(p) != len(sw) {
529 return 0, errors.New("size doesn't match")
530 }
531
532 return copy(sw, p), nil
533 }
534
535
536
537
538
539 func (s *Spec) TypeByID(id TypeID) (Type, error) {
540 return s.types.ByID(id)
541 }
542
543
544
545
546 func (s *Spec) TypeID(typ Type) (TypeID, error) {
547 if _, ok := typ.(*Void); ok {
548
549 return 0, nil
550 }
551
552 id, ok := s.typeIDs[typ]
553 if !ok {
554 return 0, fmt.Errorf("no ID for type %s: %w", typ, ErrNotFound)
555 }
556
557 return id, nil
558 }
559
560
561
562
563
564
565
566
567 func (s *Spec) AnyTypesByName(name string) ([]Type, error) {
568 types := s.namedTypes[newEssentialName(name)]
569 if len(types) == 0 {
570 return nil, fmt.Errorf("type name %s: %w", name, ErrNotFound)
571 }
572
573
574 result := make([]Type, 0, len(types))
575 for _, t := range types {
576
577
578 if t.TypeName() == name {
579 result = append(result, t)
580 }
581 }
582 return result, nil
583 }
584
585
586
587
588 func (s *Spec) AnyTypeByName(name string) (Type, error) {
589 types, err := s.AnyTypesByName(name)
590 if err != nil {
591 return nil, err
592 }
593
594 if len(types) > 1 {
595 return nil, fmt.Errorf("found multiple types: %v", types)
596 }
597
598 return types[0], nil
599 }
600
601
602
603
604
605
606
607
608
609
610
611 func (s *Spec) TypeByName(name string, typ interface{}) error {
612 typValue := reflect.ValueOf(typ)
613 if typValue.Kind() != reflect.Ptr {
614 return fmt.Errorf("%T is not a pointer", typ)
615 }
616
617 typPtr := typValue.Elem()
618 if !typPtr.CanSet() {
619 return fmt.Errorf("%T cannot be set", typ)
620 }
621
622 wanted := typPtr.Type()
623 if !wanted.AssignableTo(reflect.TypeOf((*Type)(nil)).Elem()) {
624 return fmt.Errorf("%T does not satisfy Type interface", typ)
625 }
626
627 types, err := s.AnyTypesByName(name)
628 if err != nil {
629 return err
630 }
631
632 var candidate Type
633 for _, typ := range types {
634 if reflect.TypeOf(typ) != wanted {
635 continue
636 }
637
638 if candidate != nil {
639 return fmt.Errorf("type %s: multiple candidates for %T", name, typ)
640 }
641
642 candidate = typ
643 }
644
645 if candidate == nil {
646 return fmt.Errorf("type %s: %w", name, ErrNotFound)
647 }
648
649 typPtr.Set(reflect.ValueOf(candidate))
650
651 return nil
652 }
653
654
655
656
657
658 func LoadSplitSpecFromReader(r io.ReaderAt, base *Spec) (*Spec, error) {
659 return loadRawSpec(r, internal.NativeEndian, base.types, base.strings)
660 }
661
662
663 type TypesIterator struct {
664 spec *Spec
665 index int
666
667 Type Type
668 }
669
670
671 func (s *Spec) Iterate() *TypesIterator {
672 return &TypesIterator{spec: s, index: 0}
673 }
674
675
676 func (iter *TypesIterator) Next() bool {
677 if len(iter.spec.types) <= iter.index {
678 return false
679 }
680
681 iter.Type = iter.spec.types[iter.index]
682 iter.index++
683 return true
684 }
685
686
687 type Handle struct {
688 fd *sys.FD
689
690
691 size uint32
692 }
693
694
695
696
697 func NewHandle(spec *Spec) (*Handle, error) {
698 if err := haveBTF(); err != nil {
699 return nil, err
700 }
701
702 if spec.byteOrder != internal.NativeEndian {
703 return nil, fmt.Errorf("can't load %s BTF on %s", spec.byteOrder, internal.NativeEndian)
704 }
705
706 btf, err := spec.marshal(marshalOpts{
707 ByteOrder: internal.NativeEndian,
708 StripFuncLinkage: haveFuncLinkage() != nil,
709 })
710 if err != nil {
711 return nil, fmt.Errorf("can't marshal BTF: %w", err)
712 }
713
714 if uint64(len(btf)) > math.MaxUint32 {
715 return nil, errors.New("BTF exceeds the maximum size")
716 }
717
718 attr := &sys.BtfLoadAttr{
719 Btf: sys.NewSlicePointer(btf),
720 BtfSize: uint32(len(btf)),
721 }
722
723 fd, err := sys.BtfLoad(attr)
724 if err != nil {
725 logBuf := make([]byte, 64*1024)
726 attr.BtfLogBuf = sys.NewSlicePointer(logBuf)
727 attr.BtfLogSize = uint32(len(logBuf))
728 attr.BtfLogLevel = 1
729
730 _, _ = sys.BtfLoad(attr)
731 return nil, internal.ErrorWithLog(err, logBuf)
732 }
733
734 return &Handle{fd, attr.BtfSize}, nil
735 }
736
737
738
739
740
741
742
743
744 func NewHandleFromID(id ID) (*Handle, error) {
745 fd, err := sys.BtfGetFdById(&sys.BtfGetFdByIdAttr{
746 Id: uint32(id),
747 })
748 if err != nil {
749 return nil, fmt.Errorf("get FD for ID %d: %w", id, err)
750 }
751
752 info, err := newHandleInfoFromFD(fd)
753 if err != nil {
754 _ = fd.Close()
755 return nil, err
756 }
757
758 return &Handle{fd, info.size}, nil
759 }
760
761
762
763
764 func (h *Handle) Spec(base *Spec) (*Spec, error) {
765 var btfInfo sys.BtfInfo
766 btfBuffer := make([]byte, h.size)
767 btfInfo.Btf, btfInfo.BtfSize = sys.NewSlicePointerLen(btfBuffer)
768
769 if err := sys.ObjInfo(h.fd, &btfInfo); err != nil {
770 return nil, err
771 }
772
773 var baseTypes types
774 var baseStrings *stringTable
775 if base != nil {
776 baseTypes = base.types
777 baseStrings = base.strings
778 }
779
780 return loadRawSpec(bytes.NewReader(btfBuffer), internal.NativeEndian, baseTypes, baseStrings)
781 }
782
783
784
785
786 func (h *Handle) Close() error {
787 if h == nil {
788 return nil
789 }
790
791 return h.fd.Close()
792 }
793
794
795 func (h *Handle) FD() int {
796 return h.fd.Int()
797 }
798
799
800 func (h *Handle) Info() (*HandleInfo, error) {
801 return newHandleInfoFromFD(h.fd)
802 }
803
804 func marshalBTF(types interface{}, strings []byte, bo binary.ByteOrder) []byte {
805 const minHeaderLength = 24
806
807 typesLen := uint32(binary.Size(types))
808 header := btfHeader{
809 Magic: btfMagic,
810 Version: 1,
811 HdrLen: minHeaderLength,
812 TypeOff: 0,
813 TypeLen: typesLen,
814 StringOff: typesLen,
815 StringLen: uint32(len(strings)),
816 }
817
818 buf := new(bytes.Buffer)
819 _ = binary.Write(buf, bo, &header)
820 _ = binary.Write(buf, bo, types)
821 buf.Write(strings)
822
823 return buf.Bytes()
824 }
825
826 var haveBTF = internal.FeatureTest("BTF", "5.1", func() error {
827 var (
828 types struct {
829 Integer btfType
830 Var btfType
831 btfVar struct{ Linkage uint32 }
832 }
833 strings = []byte{0, 'a', 0}
834 )
835
836
837
838
839 types.Integer.SetKind(kindPointer)
840 types.Var.NameOff = 1
841 types.Var.SetKind(kindVar)
842 types.Var.SizeType = 1
843
844 btf := marshalBTF(&types, strings, internal.NativeEndian)
845
846 fd, err := sys.BtfLoad(&sys.BtfLoadAttr{
847 Btf: sys.NewSlicePointer(btf),
848 BtfSize: uint32(len(btf)),
849 })
850 if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) {
851
852
853 return internal.ErrNotSupported
854 }
855 if err != nil {
856 return err
857 }
858
859 fd.Close()
860 return nil
861 })
862
863 var haveFuncLinkage = internal.FeatureTest("BTF func linkage", "5.6", func() error {
864 if err := haveBTF(); err != nil {
865 return err
866 }
867
868 var (
869 types struct {
870 FuncProto btfType
871 Func btfType
872 }
873 strings = []byte{0, 'a', 0}
874 )
875
876 types.FuncProto.SetKind(kindFuncProto)
877 types.Func.SetKind(kindFunc)
878 types.Func.SizeType = 1
879 types.Func.NameOff = 1
880 types.Func.SetLinkage(GlobalFunc)
881
882 btf := marshalBTF(&types, strings, internal.NativeEndian)
883
884 fd, err := sys.BtfLoad(&sys.BtfLoadAttr{
885 Btf: sys.NewSlicePointer(btf),
886 BtfSize: uint32(len(btf)),
887 })
888 if errors.Is(err, unix.EINVAL) {
889 return internal.ErrNotSupported
890 }
891 if err != nil {
892 return err
893 }
894
895 fd.Close()
896 return nil
897 })
898
View as plain text